From cbc112a7617528c9449ba3c4ababacdb4baa725a Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Mon, 21 Mar 2016 08:26:56 -0700 Subject: [PATCH 001/233] Support this.prop = expr; assignments as declarations for ES6 JS classes --- src/compiler/binder.ts | 8 ++++++ src/compiler/checker.ts | 5 ++++ tests/cases/fourslash/javaScriptClass1.ts | 32 +++++++++++++++++++++++ tests/cases/fourslash/javaScriptClass2.ts | 22 ++++++++++++++++ tests/cases/fourslash/javaScriptClass3.ts | 24 +++++++++++++++++ 5 files changed, 91 insertions(+) create mode 100644 tests/cases/fourslash/javaScriptClass1.ts create mode 100644 tests/cases/fourslash/javaScriptClass2.ts create mode 100644 tests/cases/fourslash/javaScriptClass3.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 6957be484f3..dc0ff55b89a 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1478,6 +1478,14 @@ namespace ts { // It's acceptable for multiple 'this' assignments of the same identifier to occur declareSymbol(container.symbol.members, container.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property); } + else if (container.kind === SyntaxKind.Constructor && isInJavaScriptFile(node)) { + // this.foo assignment in a JavaScript class + // Bind this property to the containing class + const saveContainer = container; + container = container.parent; + bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property, SymbolFlags.None); + container = saveContainer; + } } function bindPrototypePropertyAssignment(node: BinaryExpression) { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 27b08397205..63d2afe72eb 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8037,6 +8037,11 @@ namespace ts { const binaryExpression = node.parent; const operator = binaryExpression.operatorToken.kind; if (operator >= SyntaxKind.FirstAssignment && operator <= SyntaxKind.LastAssignment) { + // Don't do this for special property assignments to avoid circularity + if (getSpecialPropertyAssignmentKind(binaryExpression) !== SpecialPropertyAssignmentKind.None) { + return undefined; + } + // In an assignment expression, the right operand is contextually typed by the type of the left operand. if (node === binaryExpression.right) { return checkExpression(binaryExpression.left); diff --git a/tests/cases/fourslash/javaScriptClass1.ts b/tests/cases/fourslash/javaScriptClass1.ts new file mode 100644 index 00000000000..117fc5be9be --- /dev/null +++ b/tests/cases/fourslash/javaScriptClass1.ts @@ -0,0 +1,32 @@ +/// + +// Classes have their shape inferred from assignments +// to properties of 'this' in the constructor + +// @allowNonTsExtensions: true +// @Filename: Foo.js +//// class Foo { +//// constructor() { +//// this.bar = 'world'; +//// this.thing = 42; +//// this.union = 'foo'; +//// this.union = 100; +//// } +//// } +//// var x = new Foo(); +//// x/**/ + + +goTo.marker(); +edit.insert('.'); +verify.completionListContains("bar", /*displayText*/ undefined, /*documentation*/ undefined, "property"); +verify.completionListContains("thing", /*displayText*/ undefined, /*documentation*/ undefined, "property"); +verify.completionListContains("union", /*displayText*/ undefined, /*documentation*/ undefined, "property"); + +edit.insert('bar.'); +verify.completionListContains("substr", /*displayText*/ undefined, /*documentation*/ undefined, "method"); + +edit.backspace('bar.'.length); + +edit.insert('union.'); +verify.completionListContains("toString", /*displayText*/ undefined, /*documentation*/ undefined, "method"); \ No newline at end of file diff --git a/tests/cases/fourslash/javaScriptClass2.ts b/tests/cases/fourslash/javaScriptClass2.ts new file mode 100644 index 00000000000..d6daa320c5a --- /dev/null +++ b/tests/cases/fourslash/javaScriptClass2.ts @@ -0,0 +1,22 @@ +/// + +// In an inferred class, we can rename successfully + +// @allowNonTsExtensions: true +// @Filename: Foo.js +//// class Foo { +//// constructor() { +//// this.[|union|] = 'foo'; +//// this./*1*/[|union|] = 100; +//// } +//// method() { return this./*2*/[|union|]; } +//// } +//// var x = new Foo(); +//// x./*3*/[|union|]; + +goTo.marker('1'); +verify.renameLocations(/*findInStrings*/false, /*findInComments*/false); +goTo.marker('2'); +verify.renameLocations(/*findInStrings*/false, /*findInComments*/false); +goTo.marker('3'); +verify.renameLocations(/*findInStrings*/false, /*findInComments*/false); diff --git a/tests/cases/fourslash/javaScriptClass3.ts b/tests/cases/fourslash/javaScriptClass3.ts new file mode 100644 index 00000000000..47004d53b04 --- /dev/null +++ b/tests/cases/fourslash/javaScriptClass3.ts @@ -0,0 +1,24 @@ +/// + +// In an inferred class, we can to-to-def successfully + +// @allowNonTsExtensions: true +// @Filename: Foo.js +//// class Foo { +//// constructor() { +//// /*dst1*/this.alpha = 10; +//// /*dst2*/this.beta = 'gamma'; +//// } +//// method() { return this.alpha; } +//// } +//// var x = new Foo(); +//// x.alpha/*src1*/; +//// x.beta/*src2*/; + +goTo.marker('src1'); +goTo.definition(); +verify.caretAtMarker('dst1'); + +goTo.marker('src2'); +goTo.definition(); +verify.caretAtMarker('dst2'); From c8cd748e4807172871764d7726beddcb9deec250 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Mon, 21 Mar 2016 09:23:33 -0700 Subject: [PATCH 002/233] Handle JSDoc tags on 'this' properties --- src/compiler/checker.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 63d2afe72eb..49efcc7dc0b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2937,6 +2937,14 @@ namespace ts { } // Handle module.exports = expr if (declaration.kind === SyntaxKind.BinaryExpression) { + // Use JS Doc type if present on parent expression statement + if (declaration.flags & NodeFlags.JavaScriptFile) { + const typeTag = getJSDocTypeTag(declaration.parent); + if (typeTag && typeTag.typeExpression) { + return links.type = getTypeFromTypeNode(typeTag.typeExpression.type); + } + } + return links.type = getUnionType(map(symbol.declarations, (decl: BinaryExpression) => checkExpressionCached(decl.right))); } if (declaration.kind === SyntaxKind.PropertyAccessExpression) { From 32b4e46c1d2979e43967345b08fd7e135ed1519c Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Mon, 21 Mar 2016 09:23:41 -0700 Subject: [PATCH 003/233] Update tests --- tests/cases/fourslash/javaScriptClass1.ts | 3 +-- tests/cases/fourslash/javaScriptClass4.ts | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 tests/cases/fourslash/javaScriptClass4.ts diff --git a/tests/cases/fourslash/javaScriptClass1.ts b/tests/cases/fourslash/javaScriptClass1.ts index 117fc5be9be..19f7a39b615 100644 --- a/tests/cases/fourslash/javaScriptClass1.ts +++ b/tests/cases/fourslash/javaScriptClass1.ts @@ -8,7 +8,7 @@ //// class Foo { //// constructor() { //// this.bar = 'world'; -//// this.thing = 42; +//// this.thing = () => 0; //// this.union = 'foo'; //// this.union = 100; //// } @@ -25,7 +25,6 @@ verify.completionListContains("union", /*displayText*/ undefined, /*documentatio edit.insert('bar.'); verify.completionListContains("substr", /*displayText*/ undefined, /*documentation*/ undefined, "method"); - edit.backspace('bar.'.length); edit.insert('union.'); diff --git a/tests/cases/fourslash/javaScriptClass4.ts b/tests/cases/fourslash/javaScriptClass4.ts new file mode 100644 index 00000000000..1148d5f320e --- /dev/null +++ b/tests/cases/fourslash/javaScriptClass4.ts @@ -0,0 +1,22 @@ +/// + +// Classes have their shape inferred from assignments +// to properties of 'this' in the constructor + +// @allowNonTsExtensions: true +// @Filename: Foo.js +//// class Foo { +//// constructor() { +//// /** +//// * @type {string} +//// */ +//// this.baz = null; +//// } +//// } +//// var x = new Foo(); +//// x/**/ + +goTo.marker(); +edit.insert('.baz.'); +verify.completionListContains("substr", /*displayText*/ undefined, /*documentation*/ undefined, "method"); + From dfee3de3a664d2925110b919e3f3d0b780732fe4 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 21 Apr 2016 10:57:54 -0700 Subject: [PATCH 004/233] Add test case for #8229 --- ...tAnyDestructuringVarDeclaration.errors.txt | 18 ++++-- ...oImplicitAnyDestructuringVarDeclaration.js | 4 +- ...AnyDestructuringVarDeclaration2.errors.txt | 63 +++++++++++++++++++ ...ImplicitAnyDestructuringVarDeclaration2.js | 25 ++++++++ ...oImplicitAnyDestructuringVarDeclaration.ts | 2 +- ...ImplicitAnyDestructuringVarDeclaration2.ts | 12 ++++ 6 files changed, 115 insertions(+), 9 deletions(-) create mode 100644 tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration2.errors.txt create mode 100644 tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration2.js create mode 100644 tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts diff --git a/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration.errors.txt b/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration.errors.txt index 10d6b05c622..e54261b117f 100644 --- a/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration.errors.txt +++ b/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration.errors.txt @@ -15,11 +15,13 @@ tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts(5,18): error TS tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts(7,5): error TS1182: A destructuring declaration must have an initializer. tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts(7,13): error TS7008: Member 'b3' implicitly has an 'any' type. tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts(7,25): error TS7008: Member 'b3' implicitly has an 'any' type. -tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts(9,6): error TS7031: Binding element 'a1' implicitly has an 'any' type. -tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts(9,26): error TS7031: Binding element 'b1' implicitly has an 'any' type. +tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts(9,6): error TS7031: Binding element 'a4' implicitly has an 'any' type. +tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts(9,26): error TS7031: Binding element 'b4' implicitly has an 'any' type. +tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts(9,46): error TS7005: Variable 'c4' implicitly has an 'any' type. +tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts(9,62): error TS7005: Variable 'd4' implicitly has an 'any' type. -==== tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts (19 errors) ==== +==== tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts (21 errors) ==== var [a], {b}, c, d; // error ~~~ !!! error TS1182: A destructuring declaration must have an initializer. @@ -62,8 +64,12 @@ tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts(9,26): error TS ~~ !!! error TS7008: Member 'b3' implicitly has an 'any' type. - var [a1] = [undefined], {b1} = { b1: null }, c1 = undefined, d1 = null; // error + var [a4] = [undefined], {b4} = { b4: null }, c4 = undefined, d4 = null; // error ~~ -!!! error TS7031: Binding element 'a1' implicitly has an 'any' type. +!!! error TS7031: Binding element 'a4' implicitly has an 'any' type. ~~ -!!! error TS7031: Binding element 'b1' implicitly has an 'any' type. \ No newline at end of file +!!! error TS7031: Binding element 'b4' implicitly has an 'any' type. + ~~ +!!! error TS7005: Variable 'c4' implicitly has an 'any' type. + ~~ +!!! error TS7005: Variable 'd4' implicitly has an 'any' type. \ No newline at end of file diff --git a/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration.js b/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration.js index cbc15c01e9e..4e9606d9c53 100644 --- a/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration.js +++ b/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration.js @@ -7,11 +7,11 @@ var [a2]: [any], {b2}: { b2: any }, c2: any, d2: any; var {b3}: { b3 }, c3: { b3 }; // error in type instead -var [a1] = [undefined], {b1} = { b1: null }, c1 = undefined, d1 = null; // error +var [a4] = [undefined], {b4} = { b4: null }, c4 = undefined, d4 = null; // error //// [noImplicitAnyDestructuringVarDeclaration.js] var a = (void 0)[0], b = (void 0).b, c, d; // error var _a = (void 0)[0], a1 = _a === void 0 ? undefined : _a, _b = (void 0).b1, b1 = _b === void 0 ? null : _b, c1 = undefined, d1 = null; // error var a2 = (void 0)[0], b2 = (void 0).b2, c2, d2; var b3 = (void 0).b3, c3; // error in type instead -var a1 = [undefined][0], b1 = { b1: null }.b1, c1 = undefined, d1 = null; // error +var a4 = [undefined][0], b4 = { b4: null }.b4, c4 = undefined, d4 = null; // error diff --git a/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration2.errors.txt b/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration2.errors.txt new file mode 100644 index 00000000000..d84a2975665 --- /dev/null +++ b/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration2.errors.txt @@ -0,0 +1,63 @@ +tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(1,6): error TS7031: Binding element 'a' implicitly has an 'any' type. +tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(1,9): error TS7031: Binding element 'b' implicitly has an 'any' type. +tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(1,12): error TS7031: Binding element 'c' implicitly has an 'any' type. +tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(3,6): error TS7031: Binding element 'a2' implicitly has an 'any' type. +tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(3,22): error TS7031: Binding element 'b2' implicitly has an 'any' type. +tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(3,38): error TS7031: Binding element 'c2' implicitly has an 'any' type. +tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(5,6): error TS7031: Binding element 'a4' implicitly has an 'any' type. +tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(5,31): error TS7031: Binding element 'b4' implicitly has an 'any' type. +tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(7,6): error TS7031: Binding element 'x' implicitly has an 'any' type. +tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(7,9): error TS7031: Binding element 'y' implicitly has an 'any' type. +tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(7,12): error TS7031: Binding element 'z' implicitly has an 'any' type. +tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(9,6): error TS7031: Binding element 'x2' implicitly has an 'any' type. +tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(9,22): error TS7031: Binding element 'y2' implicitly has an 'any' type. +tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(9,38): error TS7031: Binding element 'z2' implicitly has an 'any' type. +tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(11,6): error TS7031: Binding element 'x4' implicitly has an 'any' type. +tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(11,37): error TS7031: Binding element 'y4' implicitly has an 'any' type. + + +==== tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts (16 errors) ==== + let [a, b, c] = [1, 2, 3]; // no error + ~ +!!! error TS7031: Binding element 'a' implicitly has an 'any' type. + ~ +!!! error TS7031: Binding element 'b' implicitly has an 'any' type. + ~ +!!! error TS7031: Binding element 'c' implicitly has an 'any' type. + let [a1 = 10, b1 = 10, c1 = 10] = [1, 2, 3]; // no error + let [a2 = undefined, b2 = undefined, c2 = undefined] = [1, 2, 3]; // no error + ~~ +!!! error TS7031: Binding element 'a2' implicitly has an 'any' type. + ~~ +!!! error TS7031: Binding element 'b2' implicitly has an 'any' type. + ~~ +!!! error TS7031: Binding element 'c2' implicitly has an 'any' type. + let [a3 = undefined, b3 = null, c3 = undefined] = [1, 2, 3]; // no error + let [a4] = [undefined], [b4] = [null], c4 = undefined, d4 = null; // no error + ~~ +!!! error TS7031: Binding element 'a4' implicitly has an 'any' type. + ~~ +!!! error TS7031: Binding element 'b4' implicitly has an 'any' type. + + let {x, y, z} = { x: 1, y: 2, z: 3 }; // no error + ~ +!!! error TS7031: Binding element 'x' implicitly has an 'any' type. + ~ +!!! error TS7031: Binding element 'y' implicitly has an 'any' type. + ~ +!!! error TS7031: Binding element 'z' implicitly has an 'any' type. + let {x1 = 10, y1 = 10, z1 = 10} = { x1: 1, y1: 2, z1: 3 }; // no error + let {x2 = undefined, y2 = undefined, z2 = undefined} = { x2: 1, y2: 2, z2: 3 }; // no error + ~~ +!!! error TS7031: Binding element 'x2' implicitly has an 'any' type. + ~~ +!!! error TS7031: Binding element 'y2' implicitly has an 'any' type. + ~~ +!!! error TS7031: Binding element 'z2' implicitly has an 'any' type. + let {x3 = undefined, y3 = null, z3 = undefined} = { x3: 1, y3: 2, z3: 3 }; // no error + let {x4} = { x4: undefined }, {y4} = { y4: null }; // no error + ~~ +!!! error TS7031: Binding element 'x4' implicitly has an 'any' type. + ~~ +!!! error TS7031: Binding element 'y4' implicitly has an 'any' type. + \ No newline at end of file diff --git a/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration2.js b/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration2.js new file mode 100644 index 00000000000..79ee0b8830f --- /dev/null +++ b/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration2.js @@ -0,0 +1,25 @@ +//// [noImplicitAnyDestructuringVarDeclaration2.ts] +let [a, b, c] = [1, 2, 3]; // no error +let [a1 = 10, b1 = 10, c1 = 10] = [1, 2, 3]; // no error +let [a2 = undefined, b2 = undefined, c2 = undefined] = [1, 2, 3]; // no error +let [a3 = undefined, b3 = null, c3 = undefined] = [1, 2, 3]; // no error +let [a4] = [undefined], [b4] = [null], c4 = undefined, d4 = null; // no error + +let {x, y, z} = { x: 1, y: 2, z: 3 }; // no error +let {x1 = 10, y1 = 10, z1 = 10} = { x1: 1, y1: 2, z1: 3 }; // no error +let {x2 = undefined, y2 = undefined, z2 = undefined} = { x2: 1, y2: 2, z2: 3 }; // no error +let {x3 = undefined, y3 = null, z3 = undefined} = { x3: 1, y3: 2, z3: 3 }; // no error +let {x4} = { x4: undefined }, {y4} = { y4: null }; // no error + + +//// [noImplicitAnyDestructuringVarDeclaration2.js] +var _a = [1, 2, 3], a = _a[0], b = _a[1], c = _a[2]; // no error +var _b = [1, 2, 3], _c = _b[0], a1 = _c === void 0 ? 10 : _c, _d = _b[1], b1 = _d === void 0 ? 10 : _d, _e = _b[2], c1 = _e === void 0 ? 10 : _e; // no error +var _f = [1, 2, 3], _g = _f[0], a2 = _g === void 0 ? undefined : _g, _h = _f[1], b2 = _h === void 0 ? undefined : _h, _j = _f[2], c2 = _j === void 0 ? undefined : _j; // no error +var _k = [1, 2, 3], _l = _k[0], a3 = _l === void 0 ? undefined : _l, _m = _k[1], b3 = _m === void 0 ? null : _m, _o = _k[2], c3 = _o === void 0 ? undefined : _o; // no error +var a4 = [undefined][0], b4 = [null][0], c4 = undefined, d4 = null; // no error +var _p = { x: 1, y: 2, z: 3 }, x = _p.x, y = _p.y, z = _p.z; // no error +var _q = { x1: 1, y1: 2, z1: 3 }, _r = _q.x1, x1 = _r === void 0 ? 10 : _r, _s = _q.y1, y1 = _s === void 0 ? 10 : _s, _t = _q.z1, z1 = _t === void 0 ? 10 : _t; // no error +var _u = { x2: 1, y2: 2, z2: 3 }, _v = _u.x2, x2 = _v === void 0 ? undefined : _v, _w = _u.y2, y2 = _w === void 0 ? undefined : _w, _x = _u.z2, z2 = _x === void 0 ? undefined : _x; // no error +var _y = { x3: 1, y3: 2, z3: 3 }, _z = _y.x3, x3 = _z === void 0 ? undefined : _z, _0 = _y.y3, y3 = _0 === void 0 ? null : _0, _1 = _y.z3, z3 = _1 === void 0 ? undefined : _1; // no error +var x4 = { x4: undefined }.x4, y4 = { y4: null }.y4; // no error diff --git a/tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts b/tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts index 6988416d4de..b9ecd8e72a8 100644 --- a/tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts +++ b/tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts @@ -7,4 +7,4 @@ var [a2]: [any], {b2}: { b2: any }, c2: any, d2: any; var {b3}: { b3 }, c3: { b3 }; // error in type instead -var [a1] = [undefined], {b1} = { b1: null }, c1 = undefined, d1 = null; // error \ No newline at end of file +var [a4] = [undefined], {b4} = { b4: null }, c4 = undefined, d4 = null; // error \ No newline at end of file diff --git a/tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts b/tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts new file mode 100644 index 00000000000..a964bd96dba --- /dev/null +++ b/tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts @@ -0,0 +1,12 @@ +// @noimplicitany: true +let [a, b, c] = [1, 2, 3]; // no error +let [a1 = 10, b1 = 10, c1 = 10] = [1, 2, 3]; // no error +let [a2 = undefined, b2 = undefined, c2 = undefined] = [1, 2, 3]; // no error +let [a3 = undefined, b3 = null, c3 = undefined] = [1, 2, 3]; // no error +let [a4] = [undefined], [b4] = [null], c4 = undefined, d4 = null; // no error + +let {x, y, z} = { x: 1, y: 2, z: 3 }; // no error +let {x1 = 10, y1 = 10, z1 = 10} = { x1: 1, y1: 2, z1: 3 }; // no error +let {x2 = undefined, y2 = undefined, z2 = undefined} = { x2: 1, y2: 2, z2: 3 }; // no error +let {x3 = undefined, y3 = null, z3 = undefined} = { x3: 1, y3: 2, z3: 3 }; // no error +let {x4} = { x4: undefined }, {y4} = { y4: null }; // no error From 90d347e855d94bfb24ef644731cc623552a0c854 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 21 Apr 2016 13:25:43 -0700 Subject: [PATCH 005/233] Do not report errors during contextual typecheck Fixes #8229 --- src/compiler/checker.ts | 28 ++-- ...AnyDestructuringVarDeclaration2.errors.txt | 63 -------- ...citAnyDestructuringVarDeclaration2.symbols | 78 ++++++++++ ...licitAnyDestructuringVarDeclaration2.types | 137 ++++++++++++++++++ 4 files changed, 230 insertions(+), 76 deletions(-) delete mode 100644 tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration2.errors.txt create mode 100644 tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration2.symbols create mode 100644 tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration2.types diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 899ee479578..f84988e68ba 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2873,7 +2873,7 @@ namespace ts { // If the declaration specifies a binding pattern, use the type implied by the binding pattern if (isBindingPattern(declaration.name)) { - return getTypeFromBindingPattern(declaration.name, /*includePatternInType*/ false); + return getTypeFromBindingPattern(declaration.name, /*includePatternInType*/ false, /*reportErrors*/ true); } // No type specified and nothing can be inferred @@ -2883,23 +2883,25 @@ namespace ts { // Return the type implied by a binding pattern element. This is the type of the initializer of the element if // one is present. Otherwise, if the element is itself a binding pattern, it is the type implied by the binding // pattern. Otherwise, it is the type any. - function getTypeFromBindingElement(element: BindingElement, includePatternInType?: boolean): Type { + function getTypeFromBindingElement(element: BindingElement, includePatternInType?: boolean, reportErrors?: boolean): Type { if (element.initializer) { const type = checkExpressionCached(element.initializer); - reportErrorsFromWidening(element, type); + if (reportErrors) { + reportErrorsFromWidening(element, type); + } return getWidenedType(type); } if (isBindingPattern(element.name)) { - return getTypeFromBindingPattern(element.name, includePatternInType); + return getTypeFromBindingPattern(element.name, includePatternInType, reportErrors); } - if (compilerOptions.noImplicitAny && !declarationBelongsToPrivateAmbientMember(element)) { + if (reportErrors && compilerOptions.noImplicitAny && !declarationBelongsToPrivateAmbientMember(element)) { reportImplicitAnyError(element, anyType); } return anyType; } // Return the type implied by an object binding pattern - function getTypeFromObjectBindingPattern(pattern: BindingPattern, includePatternInType: boolean): Type { + function getTypeFromObjectBindingPattern(pattern: BindingPattern, includePatternInType: boolean, reportErrors: boolean): Type { const members: SymbolTable = {}; let hasComputedProperties = false; forEach(pattern.elements, e => { @@ -2913,7 +2915,7 @@ namespace ts { const text = getTextOfPropertyName(name); const flags = SymbolFlags.Property | SymbolFlags.Transient | (e.initializer ? SymbolFlags.Optional : 0); const symbol = createSymbol(flags, text); - symbol.type = getTypeFromBindingElement(e, includePatternInType); + symbol.type = getTypeFromBindingElement(e, includePatternInType, reportErrors); symbol.bindingElement = e; members[symbol.name] = symbol; }); @@ -2928,13 +2930,13 @@ namespace ts { } // Return the type implied by an array binding pattern - function getTypeFromArrayBindingPattern(pattern: BindingPattern, includePatternInType: boolean): Type { + function getTypeFromArrayBindingPattern(pattern: BindingPattern, includePatternInType: boolean, reportErrors: boolean): Type { const elements = pattern.elements; if (elements.length === 0 || elements[elements.length - 1].dotDotDotToken) { return languageVersion >= ScriptTarget.ES6 ? createIterableType(anyType) : anyArrayType; } // If the pattern has at least one element, and no rest element, then it should imply a tuple type. - const elementTypes = map(elements, e => e.kind === SyntaxKind.OmittedExpression ? anyType : getTypeFromBindingElement(e, includePatternInType)); + const elementTypes = map(elements, e => e.kind === SyntaxKind.OmittedExpression ? anyType : getTypeFromBindingElement(e, includePatternInType, reportErrors)); if (includePatternInType) { const result = createNewTupleType(elementTypes); result.pattern = pattern; @@ -2950,10 +2952,10 @@ namespace ts { // used as the contextual type of an initializer associated with the binding pattern. Also, for a destructuring // parameter with no type annotation or initializer, the type implied by the binding pattern becomes the type of // the parameter. - function getTypeFromBindingPattern(pattern: BindingPattern, includePatternInType?: boolean): Type { + function getTypeFromBindingPattern(pattern: BindingPattern, includePatternInType?: boolean, reportErrors?: boolean): Type { return pattern.kind === SyntaxKind.ObjectBindingPattern - ? getTypeFromObjectBindingPattern(pattern, includePatternInType) - : getTypeFromArrayBindingPattern(pattern, includePatternInType); + ? getTypeFromObjectBindingPattern(pattern, includePatternInType, reportErrors) + : getTypeFromArrayBindingPattern(pattern, includePatternInType, reportErrors); } // Return the type associated with a variable, parameter, or property declaration. In the simple case this is the type @@ -8467,7 +8469,7 @@ namespace ts { } } if (isBindingPattern(declaration.name)) { - return getTypeFromBindingPattern(declaration.name, /*includePatternInType*/ true); + return getTypeFromBindingPattern(declaration.name, /*includePatternInType*/ true, /*reportErrors*/ false); } if (isBindingPattern(declaration.parent)) { const parentDeclaration = declaration.parent.parent; diff --git a/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration2.errors.txt b/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration2.errors.txt deleted file mode 100644 index d84a2975665..00000000000 --- a/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration2.errors.txt +++ /dev/null @@ -1,63 +0,0 @@ -tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(1,6): error TS7031: Binding element 'a' implicitly has an 'any' type. -tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(1,9): error TS7031: Binding element 'b' implicitly has an 'any' type. -tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(1,12): error TS7031: Binding element 'c' implicitly has an 'any' type. -tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(3,6): error TS7031: Binding element 'a2' implicitly has an 'any' type. -tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(3,22): error TS7031: Binding element 'b2' implicitly has an 'any' type. -tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(3,38): error TS7031: Binding element 'c2' implicitly has an 'any' type. -tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(5,6): error TS7031: Binding element 'a4' implicitly has an 'any' type. -tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(5,31): error TS7031: Binding element 'b4' implicitly has an 'any' type. -tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(7,6): error TS7031: Binding element 'x' implicitly has an 'any' type. -tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(7,9): error TS7031: Binding element 'y' implicitly has an 'any' type. -tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(7,12): error TS7031: Binding element 'z' implicitly has an 'any' type. -tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(9,6): error TS7031: Binding element 'x2' implicitly has an 'any' type. -tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(9,22): error TS7031: Binding element 'y2' implicitly has an 'any' type. -tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(9,38): error TS7031: Binding element 'z2' implicitly has an 'any' type. -tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(11,6): error TS7031: Binding element 'x4' implicitly has an 'any' type. -tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts(11,37): error TS7031: Binding element 'y4' implicitly has an 'any' type. - - -==== tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts (16 errors) ==== - let [a, b, c] = [1, 2, 3]; // no error - ~ -!!! error TS7031: Binding element 'a' implicitly has an 'any' type. - ~ -!!! error TS7031: Binding element 'b' implicitly has an 'any' type. - ~ -!!! error TS7031: Binding element 'c' implicitly has an 'any' type. - let [a1 = 10, b1 = 10, c1 = 10] = [1, 2, 3]; // no error - let [a2 = undefined, b2 = undefined, c2 = undefined] = [1, 2, 3]; // no error - ~~ -!!! error TS7031: Binding element 'a2' implicitly has an 'any' type. - ~~ -!!! error TS7031: Binding element 'b2' implicitly has an 'any' type. - ~~ -!!! error TS7031: Binding element 'c2' implicitly has an 'any' type. - let [a3 = undefined, b3 = null, c3 = undefined] = [1, 2, 3]; // no error - let [a4] = [undefined], [b4] = [null], c4 = undefined, d4 = null; // no error - ~~ -!!! error TS7031: Binding element 'a4' implicitly has an 'any' type. - ~~ -!!! error TS7031: Binding element 'b4' implicitly has an 'any' type. - - let {x, y, z} = { x: 1, y: 2, z: 3 }; // no error - ~ -!!! error TS7031: Binding element 'x' implicitly has an 'any' type. - ~ -!!! error TS7031: Binding element 'y' implicitly has an 'any' type. - ~ -!!! error TS7031: Binding element 'z' implicitly has an 'any' type. - let {x1 = 10, y1 = 10, z1 = 10} = { x1: 1, y1: 2, z1: 3 }; // no error - let {x2 = undefined, y2 = undefined, z2 = undefined} = { x2: 1, y2: 2, z2: 3 }; // no error - ~~ -!!! error TS7031: Binding element 'x2' implicitly has an 'any' type. - ~~ -!!! error TS7031: Binding element 'y2' implicitly has an 'any' type. - ~~ -!!! error TS7031: Binding element 'z2' implicitly has an 'any' type. - let {x3 = undefined, y3 = null, z3 = undefined} = { x3: 1, y3: 2, z3: 3 }; // no error - let {x4} = { x4: undefined }, {y4} = { y4: null }; // no error - ~~ -!!! error TS7031: Binding element 'x4' implicitly has an 'any' type. - ~~ -!!! error TS7031: Binding element 'y4' implicitly has an 'any' type. - \ No newline at end of file diff --git a/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration2.symbols b/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration2.symbols new file mode 100644 index 00000000000..ee7fe9f7edd --- /dev/null +++ b/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration2.symbols @@ -0,0 +1,78 @@ +=== tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts === +let [a, b, c] = [1, 2, 3]; // no error +>a : Symbol(a, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 0, 5)) +>b : Symbol(b, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 0, 7)) +>c : Symbol(c, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 0, 10)) + +let [a1 = 10, b1 = 10, c1 = 10] = [1, 2, 3]; // no error +>a1 : Symbol(a1, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 1, 5)) +>b1 : Symbol(b1, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 1, 13)) +>c1 : Symbol(c1, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 1, 22)) + +let [a2 = undefined, b2 = undefined, c2 = undefined] = [1, 2, 3]; // no error +>a2 : Symbol(a2, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 2, 5)) +>undefined : Symbol(undefined) +>b2 : Symbol(b2, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 2, 20)) +>undefined : Symbol(undefined) +>c2 : Symbol(c2, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 2, 36)) +>undefined : Symbol(undefined) + +let [a3 = undefined, b3 = null, c3 = undefined] = [1, 2, 3]; // no error +>a3 : Symbol(a3, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 3, 5)) +>undefined : Symbol(undefined) +>b3 : Symbol(b3, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 3, 25)) +>c3 : Symbol(c3, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 3, 41)) +>undefined : Symbol(undefined) + +let [a4] = [undefined], [b4] = [null], c4 = undefined, d4 = null; // no error +>a4 : Symbol(a4, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 4, 5)) +>undefined : Symbol(undefined) +>b4 : Symbol(b4, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 4, 30)) +>c4 : Symbol(c4, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 4, 48)) +>undefined : Symbol(undefined) +>d4 : Symbol(d4, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 4, 69)) + +let {x, y, z} = { x: 1, y: 2, z: 3 }; // no error +>x : Symbol(x, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 6, 5)) +>y : Symbol(y, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 6, 7)) +>z : Symbol(z, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 6, 10)) +>x : Symbol(x, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 6, 17)) +>y : Symbol(y, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 6, 23)) +>z : Symbol(z, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 6, 29)) + +let {x1 = 10, y1 = 10, z1 = 10} = { x1: 1, y1: 2, z1: 3 }; // no error +>x1 : Symbol(x1, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 7, 5)) +>y1 : Symbol(y1, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 7, 13)) +>z1 : Symbol(z1, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 7, 22)) +>x1 : Symbol(x1, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 7, 35)) +>y1 : Symbol(y1, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 7, 42)) +>z1 : Symbol(z1, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 7, 49)) + +let {x2 = undefined, y2 = undefined, z2 = undefined} = { x2: 1, y2: 2, z2: 3 }; // no error +>x2 : Symbol(x2, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 8, 5)) +>undefined : Symbol(undefined) +>y2 : Symbol(y2, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 8, 20)) +>undefined : Symbol(undefined) +>z2 : Symbol(z2, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 8, 36)) +>undefined : Symbol(undefined) +>x2 : Symbol(x2, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 8, 56)) +>y2 : Symbol(y2, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 8, 63)) +>z2 : Symbol(z2, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 8, 70)) + +let {x3 = undefined, y3 = null, z3 = undefined} = { x3: 1, y3: 2, z3: 3 }; // no error +>x3 : Symbol(x3, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 9, 5)) +>undefined : Symbol(undefined) +>y3 : Symbol(y3, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 9, 25)) +>z3 : Symbol(z3, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 9, 41)) +>undefined : Symbol(undefined) +>x3 : Symbol(x3, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 9, 66)) +>y3 : Symbol(y3, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 9, 73)) +>z3 : Symbol(z3, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 9, 80)) + +let {x4} = { x4: undefined }, {y4} = { y4: null }; // no error +>x4 : Symbol(x4, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 10, 5)) +>x4 : Symbol(x4, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 10, 12)) +>undefined : Symbol(undefined) +>y4 : Symbol(y4, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 10, 36)) +>y4 : Symbol(y4, Decl(noImplicitAnyDestructuringVarDeclaration2.ts, 10, 43)) + diff --git a/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration2.types b/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration2.types new file mode 100644 index 00000000000..ef0a3f2c25b --- /dev/null +++ b/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration2.types @@ -0,0 +1,137 @@ +=== tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration2.ts === +let [a, b, c] = [1, 2, 3]; // no error +>a : number +>b : number +>c : number +>[1, 2, 3] : [number, number, number] +>1 : number +>2 : number +>3 : number + +let [a1 = 10, b1 = 10, c1 = 10] = [1, 2, 3]; // no error +>a1 : number +>10 : number +>b1 : number +>10 : number +>c1 : number +>10 : number +>[1, 2, 3] : [number, number, number] +>1 : number +>2 : number +>3 : number + +let [a2 = undefined, b2 = undefined, c2 = undefined] = [1, 2, 3]; // no error +>a2 : number +>undefined : undefined +>b2 : number +>undefined : undefined +>c2 : number +>undefined : undefined +>[1, 2, 3] : [number, number, number] +>1 : number +>2 : number +>3 : number + +let [a3 = undefined, b3 = null, c3 = undefined] = [1, 2, 3]; // no error +>a3 : number +>undefined : any +>undefined : undefined +>b3 : number +>null : any +>null : null +>c3 : number +>undefined : any +>undefined : undefined +>[1, 2, 3] : [number, number, number] +>1 : number +>2 : number +>3 : number + +let [a4] = [undefined], [b4] = [null], c4 = undefined, d4 = null; // no error +>a4 : any +>[undefined] : [any] +>undefined : any +>undefined : undefined +>b4 : any +>[null] : [any] +>null : any +>null : null +>c4 : any +>undefined : any +>undefined : undefined +>d4 : any +>null : any +>null : null + +let {x, y, z} = { x: 1, y: 2, z: 3 }; // no error +>x : number +>y : number +>z : number +>{ x: 1, y: 2, z: 3 } : { x: number; y: number; z: number; } +>x : number +>1 : number +>y : number +>2 : number +>z : number +>3 : number + +let {x1 = 10, y1 = 10, z1 = 10} = { x1: 1, y1: 2, z1: 3 }; // no error +>x1 : number +>10 : number +>y1 : number +>10 : number +>z1 : number +>10 : number +>{ x1: 1, y1: 2, z1: 3 } : { x1?: number; y1?: number; z1?: number; } +>x1 : number +>1 : number +>y1 : number +>2 : number +>z1 : number +>3 : number + +let {x2 = undefined, y2 = undefined, z2 = undefined} = { x2: 1, y2: 2, z2: 3 }; // no error +>x2 : number +>undefined : undefined +>y2 : number +>undefined : undefined +>z2 : number +>undefined : undefined +>{ x2: 1, y2: 2, z2: 3 } : { x2?: number; y2?: number; z2?: number; } +>x2 : number +>1 : number +>y2 : number +>2 : number +>z2 : number +>3 : number + +let {x3 = undefined, y3 = null, z3 = undefined} = { x3: 1, y3: 2, z3: 3 }; // no error +>x3 : number +>undefined : any +>undefined : undefined +>y3 : number +>null : any +>null : null +>z3 : number +>undefined : any +>undefined : undefined +>{ x3: 1, y3: 2, z3: 3 } : { x3?: number; y3?: number; z3?: number; } +>x3 : number +>1 : number +>y3 : number +>2 : number +>z3 : number +>3 : number + +let {x4} = { x4: undefined }, {y4} = { y4: null }; // no error +>x4 : any +>{ x4: undefined } : { x4: any; } +>x4 : any +>undefined : any +>undefined : undefined +>y4 : any +>{ y4: null } : { y4: any; } +>y4 : any +>null : any +>null : null + From 34f7f2caed8fe1198e095332500c6bb6f34de819 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Fri, 22 Apr 2016 10:40:06 -0700 Subject: [PATCH 006/233] Handle the scenario when let [a=undefined]=[] --- src/compiler/checker.ts | 6 +----- .../noImplicitAnyDestructuringVarDeclaration.errors.txt | 9 +++++++-- .../noImplicitAnyDestructuringVarDeclaration.js | 5 ++++- .../compiler/noImplicitAnyDestructuringVarDeclaration.ts | 4 +++- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f84988e68ba..a6d1680a6e3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2885,11 +2885,7 @@ namespace ts { // pattern. Otherwise, it is the type any. function getTypeFromBindingElement(element: BindingElement, includePatternInType?: boolean, reportErrors?: boolean): Type { if (element.initializer) { - const type = checkExpressionCached(element.initializer); - if (reportErrors) { - reportErrorsFromWidening(element, type); - } - return getWidenedType(type); + return checkExpressionCached(element.initializer); } if (isBindingPattern(element.name)) { return getTypeFromBindingPattern(element.name, includePatternInType, reportErrors); diff --git a/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration.errors.txt b/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration.errors.txt index e54261b117f..f7e6475b971 100644 --- a/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration.errors.txt +++ b/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration.errors.txt @@ -19,9 +19,10 @@ tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts(9,6): error TS7 tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts(9,26): error TS7031: Binding element 'b4' implicitly has an 'any' type. tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts(9,46): error TS7005: Variable 'c4' implicitly has an 'any' type. tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts(9,62): error TS7005: Variable 'd4' implicitly has an 'any' type. +tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts(11,6): error TS7031: Binding element 'a5' implicitly has an 'any' type. -==== tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts (21 errors) ==== +==== tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts (22 errors) ==== var [a], {b}, c, d; // error ~~~ !!! error TS1182: A destructuring declaration must have an initializer. @@ -72,4 +73,8 @@ tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts(9,62): error TS ~~ !!! error TS7005: Variable 'c4' implicitly has an 'any' type. ~~ -!!! error TS7005: Variable 'd4' implicitly has an 'any' type. \ No newline at end of file +!!! error TS7005: Variable 'd4' implicitly has an 'any' type. + + var [a5 = undefined] = []; // error + ~~ +!!! error TS7031: Binding element 'a5' implicitly has an 'any' type. \ No newline at end of file diff --git a/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration.js b/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration.js index 4e9606d9c53..85358df666e 100644 --- a/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration.js +++ b/tests/baselines/reference/noImplicitAnyDestructuringVarDeclaration.js @@ -7,7 +7,9 @@ var [a2]: [any], {b2}: { b2: any }, c2: any, d2: any; var {b3}: { b3 }, c3: { b3 }; // error in type instead -var [a4] = [undefined], {b4} = { b4: null }, c4 = undefined, d4 = null; // error +var [a4] = [undefined], {b4} = { b4: null }, c4 = undefined, d4 = null; // error + +var [a5 = undefined] = []; // error //// [noImplicitAnyDestructuringVarDeclaration.js] var a = (void 0)[0], b = (void 0).b, c, d; // error @@ -15,3 +17,4 @@ var _a = (void 0)[0], a1 = _a === void 0 ? undefined : _a, _b = (void 0).b1, b1 var a2 = (void 0)[0], b2 = (void 0).b2, c2, d2; var b3 = (void 0).b3, c3; // error in type instead var a4 = [undefined][0], b4 = { b4: null }.b4, c4 = undefined, d4 = null; // error +var _c = [][0], a5 = _c === void 0 ? undefined : _c; // error diff --git a/tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts b/tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts index b9ecd8e72a8..ea99d03bd7c 100644 --- a/tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts +++ b/tests/cases/compiler/noImplicitAnyDestructuringVarDeclaration.ts @@ -7,4 +7,6 @@ var [a2]: [any], {b2}: { b2: any }, c2: any, d2: any; var {b3}: { b3 }, c3: { b3 }; // error in type instead -var [a4] = [undefined], {b4} = { b4: null }, c4 = undefined, d4 = null; // error \ No newline at end of file +var [a4] = [undefined], {b4} = { b4: null }, c4 = undefined, d4 = null; // error + +var [a5 = undefined] = []; // error \ No newline at end of file From ad56220c4562085b0d638d731269a7b327aeffa8 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 14 Jul 2016 14:17:50 -0700 Subject: [PATCH 007/233] Instantiate contextual this parameters if needed --- src/compiler/checker.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index aea8c982a95..08ee6bf4b8a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4632,6 +4632,9 @@ namespace ts { function getThisTypeOfSignature(signature: Signature): Type | undefined { if (signature.thisParameter) { + if (signature.mapper) { + signature = instantiateSignature(signature, signature.mapper); + } return getTypeOfSymbol(signature.thisParameter); } } @@ -11770,6 +11773,10 @@ namespace ts { function assignContextualParameterTypes(signature: Signature, context: Signature, mapper: TypeMapper) { const len = signature.parameters.length - (signature.hasRestParameter ? 1 : 0); + if (context.thisParameter) { + // save the mapper in case we need to type `this` later + context.mapper = mapper; + } for (let i = 0; i < len; i++) { const parameter = signature.parameters[i]; const contextualParameterType = getTypeAtPosition(context, i); From 78f807c7729f2c3cf092b539a597c58824f45d2a Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 14 Jul 2016 14:26:10 -0700 Subject: [PATCH 008/233] Test that contextually typed generic this parameters are instantiated --- ...instantiateContextuallyTypedGenericThis.js | 20 +++++++ ...ntiateContextuallyTypedGenericThis.symbols | 44 ++++++++++++++++ ...tantiateContextuallyTypedGenericThis.types | 52 +++++++++++++++++++ ...instantiateContextuallyTypedGenericThis.ts | 11 ++++ 4 files changed, 127 insertions(+) create mode 100644 tests/baselines/reference/instantiateContextuallyTypedGenericThis.js create mode 100644 tests/baselines/reference/instantiateContextuallyTypedGenericThis.symbols create mode 100644 tests/baselines/reference/instantiateContextuallyTypedGenericThis.types create mode 100644 tests/cases/compiler/instantiateContextuallyTypedGenericThis.ts diff --git a/tests/baselines/reference/instantiateContextuallyTypedGenericThis.js b/tests/baselines/reference/instantiateContextuallyTypedGenericThis.js new file mode 100644 index 00000000000..0233180873d --- /dev/null +++ b/tests/baselines/reference/instantiateContextuallyTypedGenericThis.js @@ -0,0 +1,20 @@ +//// [instantiateContextuallyTypedGenericThis.ts] +interface JQuery { + each( + collection: T[], callback: (this: T, dit: T) => T + ): any; +} + +let $: JQuery; +let lines: string[]; +$.each(lines, function(dit) { + return dit.charAt(0) + this.charAt(1); +}); + + +//// [instantiateContextuallyTypedGenericThis.js] +var $; +var lines; +$.each(lines, function (dit) { + return dit.charAt(0) + this.charAt(1); +}); diff --git a/tests/baselines/reference/instantiateContextuallyTypedGenericThis.symbols b/tests/baselines/reference/instantiateContextuallyTypedGenericThis.symbols new file mode 100644 index 00000000000..34a477b706a --- /dev/null +++ b/tests/baselines/reference/instantiateContextuallyTypedGenericThis.symbols @@ -0,0 +1,44 @@ +=== tests/cases/compiler/instantiateContextuallyTypedGenericThis.ts === +interface JQuery { +>JQuery : Symbol(JQuery, Decl(instantiateContextuallyTypedGenericThis.ts, 0, 0)) + + each( +>each : Symbol(JQuery.each, Decl(instantiateContextuallyTypedGenericThis.ts, 0, 18)) +>T : Symbol(T, Decl(instantiateContextuallyTypedGenericThis.ts, 1, 9)) + + collection: T[], callback: (this: T, dit: T) => T +>collection : Symbol(collection, Decl(instantiateContextuallyTypedGenericThis.ts, 1, 12)) +>T : Symbol(T, Decl(instantiateContextuallyTypedGenericThis.ts, 1, 9)) +>callback : Symbol(callback, Decl(instantiateContextuallyTypedGenericThis.ts, 2, 24)) +>this : Symbol(this, Decl(instantiateContextuallyTypedGenericThis.ts, 2, 36)) +>T : Symbol(T, Decl(instantiateContextuallyTypedGenericThis.ts, 1, 9)) +>dit : Symbol(dit, Decl(instantiateContextuallyTypedGenericThis.ts, 2, 44)) +>T : Symbol(T, Decl(instantiateContextuallyTypedGenericThis.ts, 1, 9)) +>T : Symbol(T, Decl(instantiateContextuallyTypedGenericThis.ts, 1, 9)) + + ): any; +} + +let $: JQuery; +>$ : Symbol($, Decl(instantiateContextuallyTypedGenericThis.ts, 6, 3)) +>JQuery : Symbol(JQuery, Decl(instantiateContextuallyTypedGenericThis.ts, 0, 0)) + +let lines: string[]; +>lines : Symbol(lines, Decl(instantiateContextuallyTypedGenericThis.ts, 7, 3)) + +$.each(lines, function(dit) { +>$.each : Symbol(JQuery.each, Decl(instantiateContextuallyTypedGenericThis.ts, 0, 18)) +>$ : Symbol($, Decl(instantiateContextuallyTypedGenericThis.ts, 6, 3)) +>each : Symbol(JQuery.each, Decl(instantiateContextuallyTypedGenericThis.ts, 0, 18)) +>lines : Symbol(lines, Decl(instantiateContextuallyTypedGenericThis.ts, 7, 3)) +>dit : Symbol(dit, Decl(instantiateContextuallyTypedGenericThis.ts, 8, 23)) + + return dit.charAt(0) + this.charAt(1); +>dit.charAt : Symbol(String.charAt, Decl(lib.d.ts, --, --)) +>dit : Symbol(dit, Decl(instantiateContextuallyTypedGenericThis.ts, 8, 23)) +>charAt : Symbol(String.charAt, Decl(lib.d.ts, --, --)) +>this.charAt : Symbol(String.charAt, Decl(lib.d.ts, --, --)) +>charAt : Symbol(String.charAt, Decl(lib.d.ts, --, --)) + +}); + diff --git a/tests/baselines/reference/instantiateContextuallyTypedGenericThis.types b/tests/baselines/reference/instantiateContextuallyTypedGenericThis.types new file mode 100644 index 00000000000..060d35be21a --- /dev/null +++ b/tests/baselines/reference/instantiateContextuallyTypedGenericThis.types @@ -0,0 +1,52 @@ +=== tests/cases/compiler/instantiateContextuallyTypedGenericThis.ts === +interface JQuery { +>JQuery : JQuery + + each( +>each : (collection: T[], callback: (this: T, dit: T) => T) => any +>T : T + + collection: T[], callback: (this: T, dit: T) => T +>collection : T[] +>T : T +>callback : (this: T, dit: T) => T +>this : T +>T : T +>dit : T +>T : T +>T : T + + ): any; +} + +let $: JQuery; +>$ : JQuery +>JQuery : JQuery + +let lines: string[]; +>lines : string[] + +$.each(lines, function(dit) { +>$.each(lines, function(dit) { return dit.charAt(0) + this.charAt(1);}) : any +>$.each : (collection: T[], callback: (this: T, dit: T) => T) => any +>$ : JQuery +>each : (collection: T[], callback: (this: T, dit: T) => T) => any +>lines : string[] +>function(dit) { return dit.charAt(0) + this.charAt(1);} : (dit: string) => string +>dit : string + + return dit.charAt(0) + this.charAt(1); +>dit.charAt(0) + this.charAt(1) : string +>dit.charAt(0) : string +>dit.charAt : (pos: number) => string +>dit : string +>charAt : (pos: number) => string +>0 : number +>this.charAt(1) : string +>this.charAt : (pos: number) => string +>this : string +>charAt : (pos: number) => string +>1 : number + +}); + diff --git a/tests/cases/compiler/instantiateContextuallyTypedGenericThis.ts b/tests/cases/compiler/instantiateContextuallyTypedGenericThis.ts new file mode 100644 index 00000000000..f25e66ecfb8 --- /dev/null +++ b/tests/cases/compiler/instantiateContextuallyTypedGenericThis.ts @@ -0,0 +1,11 @@ +interface JQuery { + each( + collection: T[], callback: (this: T, dit: T) => T + ): any; +} + +let $: JQuery; +let lines: string[]; +$.each(lines, function(dit) { + return dit.charAt(0) + this.charAt(1); +}); From 124305d00780bd24c40e756a239999eee9644553 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Fri, 15 Jul 2016 15:56:22 -0700 Subject: [PATCH 009/233] Emit parens around type-asserted binary operators Fixes #9766 --- src/compiler/emitter.ts | 3 +- tests/baselines/reference/asOpEmitParens.js | 19 ++++++++++++ .../reference/asOpEmitParens.symbols | 16 ++++++++++ .../baselines/reference/asOpEmitParens.types | 30 +++++++++++++++++++ .../expressions/asOperator/asOpEmitParens.ts | 9 ++++++ 5 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/asOpEmitParens.js create mode 100644 tests/baselines/reference/asOpEmitParens.symbols create mode 100644 tests/baselines/reference/asOpEmitParens.types create mode 100644 tests/cases/conformance/expressions/asOperator/asOpEmitParens.ts diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 101778e7322..682d9b26fe1 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2553,7 +2553,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge operand = (operand).expression; } - // We have an expression of the form: (SubExpr) + // We have an expression of the form: (SubExpr) or (SubExpr as Type) // Emitting this as (SubExpr) is really not desirable. We would like to emit the subexpr as is. // Omitting the parentheses, however, could cause change in the semantics of the generated // code if the casted expression has a lower precedence than the rest of the expression, e.g.: @@ -2567,6 +2567,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge operand.kind !== SyntaxKind.DeleteExpression && operand.kind !== SyntaxKind.PostfixUnaryExpression && operand.kind !== SyntaxKind.NewExpression && + !(operand.kind === SyntaxKind.BinaryExpression && node.expression.kind === SyntaxKind.AsExpression) && !(operand.kind === SyntaxKind.CallExpression && node.parent.kind === SyntaxKind.NewExpression) && !(operand.kind === SyntaxKind.FunctionExpression && node.parent.kind === SyntaxKind.CallExpression) && !(operand.kind === SyntaxKind.NumericLiteral && node.parent.kind === SyntaxKind.PropertyAccessExpression)) { diff --git a/tests/baselines/reference/asOpEmitParens.js b/tests/baselines/reference/asOpEmitParens.js new file mode 100644 index 00000000000..dee8d263eae --- /dev/null +++ b/tests/baselines/reference/asOpEmitParens.js @@ -0,0 +1,19 @@ +//// [asOpEmitParens.ts] +declare var x; +// Must emit as (x + 1) * 3 +(x + 1 as number) * 3; + +// Should still emit as x.y +(x as any).y; + +// Emit as new (x()) +new (x() as any); + + +//// [asOpEmitParens.js] +// Must emit as (x + 1) * 3 +(x + 1) * 3; +// Should still emit as x.y +x.y; +// Emit as new (x()) +new (x()); diff --git a/tests/baselines/reference/asOpEmitParens.symbols b/tests/baselines/reference/asOpEmitParens.symbols new file mode 100644 index 00000000000..210d9f3ea0d --- /dev/null +++ b/tests/baselines/reference/asOpEmitParens.symbols @@ -0,0 +1,16 @@ +=== tests/cases/conformance/expressions/asOperator/asOpEmitParens.ts === +declare var x; +>x : Symbol(x, Decl(asOpEmitParens.ts, 0, 11)) + +// Must emit as (x + 1) * 3 +(x + 1 as number) * 3; +>x : Symbol(x, Decl(asOpEmitParens.ts, 0, 11)) + +// Should still emit as x.y +(x as any).y; +>x : Symbol(x, Decl(asOpEmitParens.ts, 0, 11)) + +// Emit as new (x()) +new (x() as any); +>x : Symbol(x, Decl(asOpEmitParens.ts, 0, 11)) + diff --git a/tests/baselines/reference/asOpEmitParens.types b/tests/baselines/reference/asOpEmitParens.types new file mode 100644 index 00000000000..b87d7f4d91f --- /dev/null +++ b/tests/baselines/reference/asOpEmitParens.types @@ -0,0 +1,30 @@ +=== tests/cases/conformance/expressions/asOperator/asOpEmitParens.ts === +declare var x; +>x : any + +// Must emit as (x + 1) * 3 +(x + 1 as number) * 3; +>(x + 1 as number) * 3 : number +>(x + 1 as number) : number +>x + 1 as number : number +>x + 1 : any +>x : any +>1 : number +>3 : number + +// Should still emit as x.y +(x as any).y; +>(x as any).y : any +>(x as any) : any +>x as any : any +>x : any +>y : any + +// Emit as new (x()) +new (x() as any); +>new (x() as any) : any +>(x() as any) : any +>x() as any : any +>x() : any +>x : any + diff --git a/tests/cases/conformance/expressions/asOperator/asOpEmitParens.ts b/tests/cases/conformance/expressions/asOperator/asOpEmitParens.ts new file mode 100644 index 00000000000..7a97a74168a --- /dev/null +++ b/tests/cases/conformance/expressions/asOperator/asOpEmitParens.ts @@ -0,0 +1,9 @@ +declare var x; +// Must emit as (x + 1) * 3 +(x + 1 as number) * 3; + +// Should still emit as x.y +(x as any).y; + +// Emit as new (x()) +new (x() as any); From 40ca4a06b55d9493a7ed45bd92543a0a14a7ac3a Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Fri, 15 Jul 2016 16:07:24 -0700 Subject: [PATCH 010/233] Incorrectly included some unrelated files --- .../reference/umdGlobalMerge.errors.txt | 20 ------------------- tests/cases/compiler/umdGlobalMerge.ts | 14 ------------- 2 files changed, 34 deletions(-) delete mode 100644 tests/baselines/reference/umdGlobalMerge.errors.txt delete mode 100644 tests/cases/compiler/umdGlobalMerge.ts diff --git a/tests/baselines/reference/umdGlobalMerge.errors.txt b/tests/baselines/reference/umdGlobalMerge.errors.txt deleted file mode 100644 index eb4a116ef09..00000000000 --- a/tests/baselines/reference/umdGlobalMerge.errors.txt +++ /dev/null @@ -1,20 +0,0 @@ -tests/cases/compiler/b.d.ts(2,20): error TS2305: Module '"tests/cases/compiler/a".ns' has no exported member 'IFoo'. - - -==== tests/cases/compiler/a.d.ts (0 errors) ==== - export = ns; - - export as namespace ns; - - declare namespace ns { - export var x: number; - export interface IFoo { } - } - -==== tests/cases/compiler/b.d.ts (1 errors) ==== - declare namespace ns.something { - export var p: ns.IFoo; - ~~~~ -!!! error TS2305: Module '"tests/cases/compiler/a".ns' has no exported member 'IFoo'. - } - \ No newline at end of file diff --git a/tests/cases/compiler/umdGlobalMerge.ts b/tests/cases/compiler/umdGlobalMerge.ts deleted file mode 100644 index 1f42e2fda71..00000000000 --- a/tests/cases/compiler/umdGlobalMerge.ts +++ /dev/null @@ -1,14 +0,0 @@ -// @filename: a.d.ts -export = ns; - -export as namespace ns; - -declare namespace ns { - export var x: number; - export interface IFoo { } -} - -// @filename: b.d.ts -declare namespace ns.something { - export var p: ns.IFoo; -} From 448a480aa8d592378732b1052c645b5c681fa7e3 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 12 Jul 2016 08:37:52 -0700 Subject: [PATCH 011/233] Don't allow `.ts` to appear in an import --- src/compiler/program.ts | 61 +++++++++++-------- src/harness/unittests/moduleResolution.ts | 8 +-- .../missingFunctionImplementation2.errors.txt | 2 +- .../missingFunctionImplementation2.js | 2 +- .../reference/moduleResolutionNoTs.errors.txt | 11 ++++ .../reference/moduleResolutionNoTs.js | 15 +++++ .../moduleResolutionWithExtensions.js | 7 --- .../moduleResolutionWithExtensions.symbols | 5 -- .../moduleResolutionWithExtensions.trace.json | 6 -- .../moduleResolutionWithExtensions.types | 5 -- .../staticInstanceResolution5.errors.txt | 2 +- .../reference/staticInstanceResolution5.js | 2 +- .../missingFunctionImplementation2.ts | 2 +- tests/cases/compiler/moduleResolutionNoTs.ts | 5 ++ .../compiler/staticInstanceResolution5.ts | 2 +- .../moduleResolutionWithExtensions.ts | 4 -- 16 files changed, 77 insertions(+), 62 deletions(-) create mode 100644 tests/baselines/reference/moduleResolutionNoTs.errors.txt create mode 100644 tests/baselines/reference/moduleResolutionNoTs.js create mode 100644 tests/cases/compiler/moduleResolutionNoTs.ts diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 988714be2ab..5ddf5f7d065 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -668,24 +668,37 @@ namespace ts { * @param {boolean} onlyRecordFailures - if true then function won't try to actually load files but instead record all attempts as failures. This flag is necessary * in cases when we know upfront that all load attempts will fail (because containing folder does not exists) however we still need to record all failed lookup locations. */ - function loadModuleFromFile(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string { - // First try to keep/add an extension: importing "./foo.ts" can be matched by a file "./foo.ts", and "./foo" by "./foo.d.ts" - const resolvedByAddingOrKeepingExtension = loadModuleFromFileWorker(candidate, extensions, failedLookupLocation, onlyRecordFailures, state); - if (resolvedByAddingOrKeepingExtension) { - return resolvedByAddingOrKeepingExtension; + function loadModuleFromFile(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string | undefined { + // If the candidate already has an extension load that or quit. + if (hasTypeScriptFileExtension(candidate)) { + // Don't allow `.ts` to appear at the end + if (!fileExtensionIs(candidate, ".d.ts")) { + return undefined; + } + return tryFile(candidate, failedLookupLocation, onlyRecordFailures, state); } - // Then try stripping a ".js" or ".jsx" extension and replacing it with a TypeScript one, e.g. "./foo.js" can be matched by "./foo.ts" or "./foo.d.ts" + + // Next, try adding an extension. + // We don't allow an import of "foo.ts" to be matched by "foo.ts.ts", but we do allow "foo.js" to be matched by "foo.js.ts". + const resolvedByAddingExtension = tryAddingExtensions(candidate, extensions, failedLookupLocation, onlyRecordFailures, state); + if (resolvedByAddingExtension) { + return resolvedByAddingExtension; + } + + // If that didn't work, try stripping a ".js" or ".jsx" extension and replacing it with a TypeScript one; + // e.g. "./foo.js" can be matched by "./foo.ts" or "./foo.d.ts" if (hasJavaScriptFileExtension(candidate)) { const extensionless = removeFileExtension(candidate); if (state.traceEnabled) { const extension = candidate.substring(extensionless.length); trace(state.host, Diagnostics.File_name_0_has_a_1_extension_stripping_it, candidate, extension); } - return loadModuleFromFileWorker(extensionless, extensions, failedLookupLocation, onlyRecordFailures, state); + return tryAddingExtensions(extensionless, extensions, failedLookupLocation, onlyRecordFailures, state); } } - function loadModuleFromFileWorker(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string { + /** Try to return an existing file that adds one of the `extensions` to `candidate`. */ + function tryAddingExtensions(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string | undefined { if (!onlyRecordFailures) { // check if containing folder exists - if it doesn't then just record failures for all supported extensions without disk probing const directory = getDirectoryPath(candidate); @@ -693,26 +706,24 @@ namespace ts { onlyRecordFailures = !directoryProbablyExists(directory, state.host); } } - return forEach(extensions, tryLoad); + return forEach(extensions, ext => + !(state.skipTsx && isJsxOrTsxExtension(ext)) && tryFile(candidate + ext, failedLookupLocation, onlyRecordFailures, state)); + } - function tryLoad(ext: string): string { - if (state.skipTsx && isJsxOrTsxExtension(ext)) { - return undefined; + /** Return the file if it exists. */ + function tryFile(fileName: string, failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string | undefined { + if (!onlyRecordFailures && state.host.fileExists(fileName)) { + if (state.traceEnabled) { + trace(state.host, Diagnostics.File_0_exist_use_it_as_a_name_resolution_result, fileName); } - const fileName = fileExtensionIs(candidate, ext) ? candidate : candidate + ext; - if (!onlyRecordFailures && state.host.fileExists(fileName)) { - if (state.traceEnabled) { - trace(state.host, Diagnostics.File_0_exist_use_it_as_a_name_resolution_result, fileName); - } - return fileName; - } - else { - if (state.traceEnabled) { - trace(state.host, Diagnostics.File_0_does_not_exist, fileName); - } - failedLookupLocation.push(fileName); - return undefined; + return fileName; + } + else { + if (state.traceEnabled) { + trace(state.host, Diagnostics.File_0_does_not_exist, fileName); } + failedLookupLocation.push(fileName); + return undefined; } } diff --git a/src/harness/unittests/moduleResolution.ts b/src/harness/unittests/moduleResolution.ts index b3f2102d903..0c312f4701a 100644 --- a/src/harness/unittests/moduleResolution.ts +++ b/src/harness/unittests/moduleResolution.ts @@ -465,8 +465,8 @@ export = C; "/a/B/c/moduleB.ts": `import a = require("./moduleC")`, "/a/B/c/moduleC.ts": "export var x", "/a/B/c/moduleD.ts": ` -import a = require("./moduleA.ts"); -import b = require("./moduleB.ts"); +import a = require("./moduleA"); +import b = require("./moduleB"); ` }; test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "/a/B/c", /*useCaseSensitiveFileNames*/ false, ["moduleD.ts"], [1149]); @@ -477,8 +477,8 @@ import b = require("./moduleB.ts"); "/a/B/c/moduleB.ts": `import a = require("./moduleC")`, "/a/B/c/moduleC.ts": "export var x", "/a/B/c/moduleD.ts": ` -import a = require("./moduleA.ts"); -import b = require("./moduleB.ts"); +import a = require("./moduleA"); +import b = require("./moduleB"); ` }; test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "/a/B/c", /*useCaseSensitiveFileNames*/ false, ["moduleD.ts"], []); diff --git a/tests/baselines/reference/missingFunctionImplementation2.errors.txt b/tests/baselines/reference/missingFunctionImplementation2.errors.txt index a695482e1fb..86d8baba6f7 100644 --- a/tests/baselines/reference/missingFunctionImplementation2.errors.txt +++ b/tests/baselines/reference/missingFunctionImplementation2.errors.txt @@ -4,7 +4,7 @@ tests/cases/compiler/missingFunctionImplementation2_b.ts(1,17): error TS2391: Fu ==== tests/cases/compiler/missingFunctionImplementation2_a.ts (1 errors) ==== export {}; - declare module "./missingFunctionImplementation2_b.ts" { + declare module "./missingFunctionImplementation2_b" { export function f(a, b): void; ~ !!! error TS2384: Overload signatures must all be ambient or non-ambient. diff --git a/tests/baselines/reference/missingFunctionImplementation2.js b/tests/baselines/reference/missingFunctionImplementation2.js index 104b6dd9443..7b3456015c8 100644 --- a/tests/baselines/reference/missingFunctionImplementation2.js +++ b/tests/baselines/reference/missingFunctionImplementation2.js @@ -2,7 +2,7 @@ //// [missingFunctionImplementation2_a.ts] export {}; -declare module "./missingFunctionImplementation2_b.ts" { +declare module "./missingFunctionImplementation2_b" { export function f(a, b): void; } diff --git a/tests/baselines/reference/moduleResolutionNoTs.errors.txt b/tests/baselines/reference/moduleResolutionNoTs.errors.txt new file mode 100644 index 00000000000..d1d6270ae13 --- /dev/null +++ b/tests/baselines/reference/moduleResolutionNoTs.errors.txt @@ -0,0 +1,11 @@ +tests/cases/compiler/user.ts(1,15): error TS2307: Cannot find module './m.ts'. + + +==== tests/cases/compiler/m.ts (0 errors) ==== + export default 0; + +==== tests/cases/compiler/user.ts (1 errors) ==== + import x from "./m.ts"; + ~~~~~~~~ +!!! error TS2307: Cannot find module './m.ts'. + \ No newline at end of file diff --git a/tests/baselines/reference/moduleResolutionNoTs.js b/tests/baselines/reference/moduleResolutionNoTs.js new file mode 100644 index 00000000000..2a56947ce19 --- /dev/null +++ b/tests/baselines/reference/moduleResolutionNoTs.js @@ -0,0 +1,15 @@ +//// [tests/cases/compiler/moduleResolutionNoTs.ts] //// + +//// [m.ts] +export default 0; + +//// [user.ts] +import x from "./m.ts"; + + +//// [m.js] +"use strict"; +exports.__esModule = true; +exports["default"] = 0; +//// [user.js] +"use strict"; diff --git a/tests/baselines/reference/moduleResolutionWithExtensions.js b/tests/baselines/reference/moduleResolutionWithExtensions.js index df12a3531bc..16d176872ca 100644 --- a/tests/baselines/reference/moduleResolutionWithExtensions.js +++ b/tests/baselines/reference/moduleResolutionWithExtensions.js @@ -8,10 +8,6 @@ export default 0; //// [b.ts] import a from './a'; -// Matching extension -//// [c.ts] -import a from './a.ts'; - // '.js' extension: stripped and replaced with '.ts' //// [d.ts] import a from './a.js'; @@ -36,9 +32,6 @@ exports["default"] = 0; // No extension: '.ts' added //// [b.js] "use strict"; -// Matching extension -//// [c.js] -"use strict"; // '.js' extension: stripped and replaced with '.ts' //// [d.js] "use strict"; diff --git a/tests/baselines/reference/moduleResolutionWithExtensions.symbols b/tests/baselines/reference/moduleResolutionWithExtensions.symbols index e9934946a64..eaaa1d51cc0 100644 --- a/tests/baselines/reference/moduleResolutionWithExtensions.symbols +++ b/tests/baselines/reference/moduleResolutionWithExtensions.symbols @@ -7,11 +7,6 @@ No type information for this code.=== /src/b.ts === import a from './a'; >a : Symbol(a, Decl(b.ts, 0, 6)) -// Matching extension -=== /src/c.ts === -import a from './a.ts'; ->a : Symbol(a, Decl(c.ts, 0, 6)) - // '.js' extension: stripped and replaced with '.ts' === /src/d.ts === import a from './a.js'; diff --git a/tests/baselines/reference/moduleResolutionWithExtensions.trace.json b/tests/baselines/reference/moduleResolutionWithExtensions.trace.json index 7dc9e8c104b..5060006ac56 100644 --- a/tests/baselines/reference/moduleResolutionWithExtensions.trace.json +++ b/tests/baselines/reference/moduleResolutionWithExtensions.trace.json @@ -5,12 +5,6 @@ "File '/src/a.ts' exist - use it as a name resolution result.", "Resolving real path for '/src/a.ts', result '/src/a.ts'", "======== Module name './a' was successfully resolved to '/src/a.ts'. ========", - "======== Resolving module './a.ts' from '/src/c.ts'. ========", - "Module resolution kind is not specified, using 'NodeJs'.", - "Loading module as file / folder, candidate module location '/src/a.ts'.", - "File '/src/a.ts' exist - use it as a name resolution result.", - "Resolving real path for '/src/a.ts', result '/src/a.ts'", - "======== Module name './a.ts' was successfully resolved to '/src/a.ts'. ========", "======== Resolving module './a.js' from '/src/d.ts'. ========", "Module resolution kind is not specified, using 'NodeJs'.", "Loading module as file / folder, candidate module location '/src/a.js'.", diff --git a/tests/baselines/reference/moduleResolutionWithExtensions.types b/tests/baselines/reference/moduleResolutionWithExtensions.types index fbc0091ee3b..f59eda1a81e 100644 --- a/tests/baselines/reference/moduleResolutionWithExtensions.types +++ b/tests/baselines/reference/moduleResolutionWithExtensions.types @@ -7,11 +7,6 @@ No type information for this code.=== /src/b.ts === import a from './a'; >a : number -// Matching extension -=== /src/c.ts === -import a from './a.ts'; ->a : number - // '.js' extension: stripped and replaced with '.ts' === /src/d.ts === import a from './a.js'; diff --git a/tests/baselines/reference/staticInstanceResolution5.errors.txt b/tests/baselines/reference/staticInstanceResolution5.errors.txt index da6f0fde143..f36851b2b97 100644 --- a/tests/baselines/reference/staticInstanceResolution5.errors.txt +++ b/tests/baselines/reference/staticInstanceResolution5.errors.txt @@ -4,7 +4,7 @@ tests/cases/compiler/staticInstanceResolution5_1.ts(6,16): error TS2304: Cannot ==== tests/cases/compiler/staticInstanceResolution5_1.ts (3 errors) ==== - import WinJS = require('staticInstanceResolution5_0.ts'); + import WinJS = require('staticInstanceResolution5_0'); // these 3 should be errors var x = (w1: WinJS) => { }; diff --git a/tests/baselines/reference/staticInstanceResolution5.js b/tests/baselines/reference/staticInstanceResolution5.js index 0809d1af5f0..b3514e00aad 100644 --- a/tests/baselines/reference/staticInstanceResolution5.js +++ b/tests/baselines/reference/staticInstanceResolution5.js @@ -8,7 +8,7 @@ export class Promise { } //// [staticInstanceResolution5_1.ts] -import WinJS = require('staticInstanceResolution5_0.ts'); +import WinJS = require('staticInstanceResolution5_0'); // these 3 should be errors var x = (w1: WinJS) => { }; diff --git a/tests/cases/compiler/missingFunctionImplementation2.ts b/tests/cases/compiler/missingFunctionImplementation2.ts index 25909b6add4..1717bc663f2 100644 --- a/tests/cases/compiler/missingFunctionImplementation2.ts +++ b/tests/cases/compiler/missingFunctionImplementation2.ts @@ -1,6 +1,6 @@ // @Filename: missingFunctionImplementation2_a.ts export {}; -declare module "./missingFunctionImplementation2_b.ts" { +declare module "./missingFunctionImplementation2_b" { export function f(a, b): void; } diff --git a/tests/cases/compiler/moduleResolutionNoTs.ts b/tests/cases/compiler/moduleResolutionNoTs.ts new file mode 100644 index 00000000000..18b042d905f --- /dev/null +++ b/tests/cases/compiler/moduleResolutionNoTs.ts @@ -0,0 +1,5 @@ +// @filename: m.ts +export default 0; + +// @filename: user.ts +import x from "./m.ts"; diff --git a/tests/cases/compiler/staticInstanceResolution5.ts b/tests/cases/compiler/staticInstanceResolution5.ts index 3adb8667be5..ed34dbd535f 100644 --- a/tests/cases/compiler/staticInstanceResolution5.ts +++ b/tests/cases/compiler/staticInstanceResolution5.ts @@ -7,7 +7,7 @@ export class Promise { } // @Filename: staticInstanceResolution5_1.ts -import WinJS = require('staticInstanceResolution5_0.ts'); +import WinJS = require('staticInstanceResolution5_0'); // these 3 should be errors var x = (w1: WinJS) => { }; diff --git a/tests/cases/conformance/externalModules/moduleResolutionWithExtensions.ts b/tests/cases/conformance/externalModules/moduleResolutionWithExtensions.ts index 6ad35658623..429289546c5 100644 --- a/tests/cases/conformance/externalModules/moduleResolutionWithExtensions.ts +++ b/tests/cases/conformance/externalModules/moduleResolutionWithExtensions.ts @@ -7,10 +7,6 @@ export default 0; // @Filename: /src/b.ts import a from './a'; -// Matching extension -// @Filename: /src/c.ts -import a from './a.ts'; - // '.js' extension: stripped and replaced with '.ts' // @Filename: /src/d.ts import a from './a.js'; From a8c05a98e98b0e2f7d50d983ac08d385d6dd3641 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 12 Jul 2016 11:11:03 -0700 Subject: [PATCH 012/233] Add specific error message for unwanted '.ts' extension --- src/compiler/checker.ts | 7 ++++++- src/compiler/core.ts | 3 ++- src/compiler/diagnosticMessages.json | 4 ++++ src/compiler/utilities.ts | 4 ++++ .../reference/moduleResolutionNoTs.errors.txt | 13 ++++++++----- tests/baselines/reference/moduleResolutionNoTs.js | 5 +++++ tests/cases/compiler/moduleResolutionNoTs.ts | 3 +++ 7 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1cebd469388..88565bcba92 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1342,7 +1342,12 @@ namespace ts { if (moduleNotFoundError) { // report errors only if it was requested - error(moduleReferenceLiteral, moduleNotFoundError, moduleName); + if (hasTypeScriptFileExtensionNonDts(moduleName)) { + error(moduleReferenceLiteral, Diagnostics.Module_name_should_not_include_a_ts_extension_Colon_0, moduleName); + } + else { + error(moduleReferenceLiteral, moduleNotFoundError, moduleName); + } } return undefined; } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 926ee38a795..9b12f6a7f68 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1144,7 +1144,8 @@ namespace ts { /** * List of supported extensions in order of file resolution precedence. */ - export const supportedTypeScriptExtensions = [".ts", ".tsx", ".d.ts"]; + export const supportedTypeScriptExtensionsNonDts = [".ts", ".tsx"]; + export const supportedTypeScriptExtensions = supportedTypeScriptExtensionsNonDts.concat([".d.ts"]); export const supportedJavascriptExtensions = [".js", ".jsx"]; const allSupportedExtensions = supportedTypeScriptExtensions.concat(supportedJavascriptExtensions); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 8ffb02a8974..d85dc20afdf 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1943,6 +1943,10 @@ "category": "Error", "code": 2689 }, + "Module name should not include a '.ts' extension: '{0}'.": { + "category": "Error", + "code": 2690 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", "code": 4000 diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 7219a5bbd24..bfe8e4bd657 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2726,6 +2726,10 @@ namespace ts { return forEach(supportedTypeScriptExtensions, extension => fileExtensionIs(fileName, extension)); } + export function hasTypeScriptFileExtensionNonDts(fileName: string) { + return forEach(supportedTypeScriptExtensionsNonDts, extension => fileExtensionIs(fileName, extension)); + } + /** * Replace each instance of non-ascii characters by one, two, three, or four escape sequences * representing the UTF-8 encoding of the character, and return the expanded char code list. diff --git a/tests/baselines/reference/moduleResolutionNoTs.errors.txt b/tests/baselines/reference/moduleResolutionNoTs.errors.txt index d1d6270ae13..de105f73539 100644 --- a/tests/baselines/reference/moduleResolutionNoTs.errors.txt +++ b/tests/baselines/reference/moduleResolutionNoTs.errors.txt @@ -1,11 +1,14 @@ -tests/cases/compiler/user.ts(1,15): error TS2307: Cannot find module './m.ts'. +tests/cases/compiler/user.ts(4,15): error TS2690: Module name should not include a '.ts' extension: './m.ts'. -==== tests/cases/compiler/m.ts (0 errors) ==== - export default 0; - ==== tests/cases/compiler/user.ts (1 errors) ==== + // '.ts' extension is OK in a reference + /// + import x from "./m.ts"; ~~~~~~~~ -!!! error TS2307: Cannot find module './m.ts'. +!!! error TS2690: Module name should not include a '.ts' extension: './m.ts'. + +==== tests/cases/compiler/m.ts (0 errors) ==== + export default 0; \ No newline at end of file diff --git a/tests/baselines/reference/moduleResolutionNoTs.js b/tests/baselines/reference/moduleResolutionNoTs.js index 2a56947ce19..8a912f33011 100644 --- a/tests/baselines/reference/moduleResolutionNoTs.js +++ b/tests/baselines/reference/moduleResolutionNoTs.js @@ -4,6 +4,9 @@ export default 0; //// [user.ts] +// '.ts' extension is OK in a reference +/// + import x from "./m.ts"; @@ -12,4 +15,6 @@ import x from "./m.ts"; exports.__esModule = true; exports["default"] = 0; //// [user.js] +// '.ts' extension is OK in a reference +/// "use strict"; diff --git a/tests/cases/compiler/moduleResolutionNoTs.ts b/tests/cases/compiler/moduleResolutionNoTs.ts index 18b042d905f..29d81589cd9 100644 --- a/tests/cases/compiler/moduleResolutionNoTs.ts +++ b/tests/cases/compiler/moduleResolutionNoTs.ts @@ -2,4 +2,7 @@ export default 0; // @filename: user.ts +// '.ts' extension is OK in a reference +/// + import x from "./m.ts"; From 95e391ec72f289e5049d7601af758bdc7b0dcc69 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Fri, 22 Jul 2016 05:56:05 -0700 Subject: [PATCH 013/233] Allow `await` in a simple unary expression --- src/compiler/parser.ts | 2 ++ tests/baselines/reference/castOfAwait.js | 20 +++++++++++++++++++ tests/baselines/reference/castOfAwait.symbols | 7 +++++++ tests/baselines/reference/castOfAwait.types | 10 ++++++++++ tests/cases/compiler/castOfAwait.ts | 4 ++++ 5 files changed, 43 insertions(+) create mode 100644 tests/baselines/reference/castOfAwait.js create mode 100644 tests/baselines/reference/castOfAwait.symbols create mode 100644 tests/baselines/reference/castOfAwait.types create mode 100644 tests/cases/compiler/castOfAwait.ts diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 774cea60e09..8545273afec 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3400,6 +3400,8 @@ namespace ts { return parseTypeOfExpression(); case SyntaxKind.VoidKeyword: return parseVoidExpression(); + case SyntaxKind.AwaitKeyword: + return parseAwaitExpression(); case SyntaxKind.LessThanToken: // This is modified UnaryExpression grammar in TypeScript // UnaryExpression (modified): diff --git a/tests/baselines/reference/castOfAwait.js b/tests/baselines/reference/castOfAwait.js new file mode 100644 index 00000000000..8833a6aa972 --- /dev/null +++ b/tests/baselines/reference/castOfAwait.js @@ -0,0 +1,20 @@ +//// [castOfAwait.ts] +async function f() { + return await 0; +} + + +//// [castOfAwait.js] +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments)).next()); + }); +}; +function f() { + return __awaiter(this, void 0, void 0, function* () { + return yield 0; + }); +} diff --git a/tests/baselines/reference/castOfAwait.symbols b/tests/baselines/reference/castOfAwait.symbols new file mode 100644 index 00000000000..b6ee77ac793 --- /dev/null +++ b/tests/baselines/reference/castOfAwait.symbols @@ -0,0 +1,7 @@ +=== tests/cases/compiler/castOfAwait.ts === +async function f() { +>f : Symbol(f, Decl(castOfAwait.ts, 0, 0)) + + return await 0; +} + diff --git a/tests/baselines/reference/castOfAwait.types b/tests/baselines/reference/castOfAwait.types new file mode 100644 index 00000000000..7b6d8ef035b --- /dev/null +++ b/tests/baselines/reference/castOfAwait.types @@ -0,0 +1,10 @@ +=== tests/cases/compiler/castOfAwait.ts === +async function f() { +>f : () => Promise + + return await 0; +> await 0 : number +>await 0 : number +>0 : number +} + diff --git a/tests/cases/compiler/castOfAwait.ts b/tests/cases/compiler/castOfAwait.ts new file mode 100644 index 00000000000..64e96be70f3 --- /dev/null +++ b/tests/cases/compiler/castOfAwait.ts @@ -0,0 +1,4 @@ +// @target: es6 +async function f() { + return await 0; +} From bc5c7b654ad876f7a75422c58cbbc2d331bdf5f7 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Fri, 22 Jul 2016 13:33:22 -0700 Subject: [PATCH 014/233] More tests --- src/compiler/parser.ts | 1 + tests/baselines/reference/castOfAwait.js | 10 ++++++-- tests/baselines/reference/castOfAwait.symbols | 5 +++- tests/baselines/reference/castOfAwait.types | 24 +++++++++++++++++-- .../reference/castOfYield.errors.txt | 12 ++++++++++ tests/baselines/reference/castOfYield.js | 15 ++++++++++++ tests/cases/compiler/castOfAwait.ts | 5 +++- tests/cases/compiler/castOfYield.ts | 5 ++++ 8 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 tests/baselines/reference/castOfYield.errors.txt create mode 100644 tests/baselines/reference/castOfYield.js create mode 100644 tests/cases/compiler/castOfYield.ts diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 8545273afec..61c7698c59d 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3386,6 +3386,7 @@ namespace ts { * 6) - UnaryExpression[?yield] * 7) ~ UnaryExpression[?yield] * 8) ! UnaryExpression[?yield] + * 9) await AwaitExpression[?yield] */ function parseSimpleUnaryExpression(): UnaryExpression { switch (token) { diff --git a/tests/baselines/reference/castOfAwait.js b/tests/baselines/reference/castOfAwait.js index 8833a6aa972..9d1399d9d65 100644 --- a/tests/baselines/reference/castOfAwait.js +++ b/tests/baselines/reference/castOfAwait.js @@ -1,6 +1,9 @@ //// [castOfAwait.ts] async function f() { - return await 0; + await 0; + typeof await 0; + void await 0; + await void typeof void await 0; } @@ -15,6 +18,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }; function f() { return __awaiter(this, void 0, void 0, function* () { - return yield 0; + yield 0; + typeof yield 0; + void yield 0; + yield void typeof void yield 0; }); } diff --git a/tests/baselines/reference/castOfAwait.symbols b/tests/baselines/reference/castOfAwait.symbols index b6ee77ac793..d0bca9f38c7 100644 --- a/tests/baselines/reference/castOfAwait.symbols +++ b/tests/baselines/reference/castOfAwait.symbols @@ -2,6 +2,9 @@ async function f() { >f : Symbol(f, Decl(castOfAwait.ts, 0, 0)) - return await 0; + await 0; + typeof await 0; + void await 0; + await void typeof void await 0; } diff --git a/tests/baselines/reference/castOfAwait.types b/tests/baselines/reference/castOfAwait.types index 7b6d8ef035b..ba1590fe03d 100644 --- a/tests/baselines/reference/castOfAwait.types +++ b/tests/baselines/reference/castOfAwait.types @@ -1,10 +1,30 @@ === tests/cases/compiler/castOfAwait.ts === async function f() { ->f : () => Promise +>f : () => Promise - return await 0; + await 0; > await 0 : number >await 0 : number +>0 : number + + typeof await 0; +>typeof await 0 : string +>await 0 : number +>0 : number + + void await 0; +>void await 0 : undefined +>await 0 : number +>0 : number + + await void typeof void await 0; +>await void typeof void await 0 : any +>void typeof void await 0 : undefined +> typeof void await 0 : string +>typeof void await 0 : string +> void await 0 : number +>void await 0 : undefined +>await 0 : number >0 : number } diff --git a/tests/baselines/reference/castOfYield.errors.txt b/tests/baselines/reference/castOfYield.errors.txt new file mode 100644 index 00000000000..abafe678784 --- /dev/null +++ b/tests/baselines/reference/castOfYield.errors.txt @@ -0,0 +1,12 @@ +tests/cases/compiler/castOfYield.ts(4,14): error TS1109: Expression expected. + + +==== tests/cases/compiler/castOfYield.ts (1 errors) ==== + function* f() { + (yield 0); + // Unlike await, yield is not allowed to appear in a simple unary expression. + yield 0; + ~~~~~ +!!! error TS1109: Expression expected. + } + \ No newline at end of file diff --git a/tests/baselines/reference/castOfYield.js b/tests/baselines/reference/castOfYield.js new file mode 100644 index 00000000000..652cf28e9e1 --- /dev/null +++ b/tests/baselines/reference/castOfYield.js @@ -0,0 +1,15 @@ +//// [castOfYield.ts] +function* f() { + (yield 0); + // Unlike await, yield is not allowed to appear in a simple unary expression. + yield 0; +} + + +//// [castOfYield.js] +function f() { + (yield 0); + // Unlike await, yield is not allowed to appear in a simple unary expression. + ; + yield 0; +} diff --git a/tests/cases/compiler/castOfAwait.ts b/tests/cases/compiler/castOfAwait.ts index 64e96be70f3..6a152226ea2 100644 --- a/tests/cases/compiler/castOfAwait.ts +++ b/tests/cases/compiler/castOfAwait.ts @@ -1,4 +1,7 @@ // @target: es6 async function f() { - return await 0; + await 0; + typeof await 0; + void await 0; + await void typeof void await 0; } diff --git a/tests/cases/compiler/castOfYield.ts b/tests/cases/compiler/castOfYield.ts new file mode 100644 index 00000000000..9a85e6b21c2 --- /dev/null +++ b/tests/cases/compiler/castOfYield.ts @@ -0,0 +1,5 @@ +function* f() { + (yield 0); + // Unlike await, yield is not allowed to appear in a simple unary expression. + yield 0; +} From 275dbc7537accc869da2e47d7f62c519ee223ad4 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Fri, 22 Jul 2016 14:01:59 -0700 Subject: [PATCH 015/233] Forbid `await await` --- src/compiler/parser.ts | 5 ++++- .../baselines/reference/awaitAwait.errors.txt | 10 +++++++++ tests/baselines/reference/awaitAwait.js | 21 +++++++++++++++++++ tests/cases/compiler/awaitAwait.ts | 4 ++++ 4 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/awaitAwait.errors.txt create mode 100644 tests/baselines/reference/awaitAwait.js create mode 100644 tests/cases/compiler/awaitAwait.ts diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 61c7698c59d..9c54de6ba13 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3337,7 +3337,10 @@ namespace ts { function parseAwaitExpression() { const node = createNode(SyntaxKind.AwaitExpression); nextToken(); - node.expression = parseSimpleUnaryExpression(); + node.expression = token === SyntaxKind.AwaitKeyword + // Forbid `await await` + ? createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ false, Diagnostics.Expression_expected) + : parseSimpleUnaryExpression(); return finishNode(node); } diff --git a/tests/baselines/reference/awaitAwait.errors.txt b/tests/baselines/reference/awaitAwait.errors.txt new file mode 100644 index 00000000000..5a04051139c --- /dev/null +++ b/tests/baselines/reference/awaitAwait.errors.txt @@ -0,0 +1,10 @@ +tests/cases/compiler/awaitAwait.ts(2,11): error TS1109: Expression expected. + + +==== tests/cases/compiler/awaitAwait.ts (1 errors) ==== + async function f() { + await await 0; + ~~~~~ +!!! error TS1109: Expression expected. + } + \ No newline at end of file diff --git a/tests/baselines/reference/awaitAwait.js b/tests/baselines/reference/awaitAwait.js new file mode 100644 index 00000000000..14fb3a11505 --- /dev/null +++ b/tests/baselines/reference/awaitAwait.js @@ -0,0 +1,21 @@ +//// [awaitAwait.ts] +async function f() { + await await 0; +} + + +//// [awaitAwait.js] +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments)).next()); + }); +}; +function f() { + return __awaiter(this, void 0, void 0, function* () { + yield ; + yield 0; + }); +} diff --git a/tests/cases/compiler/awaitAwait.ts b/tests/cases/compiler/awaitAwait.ts new file mode 100644 index 00000000000..5a405804b00 --- /dev/null +++ b/tests/cases/compiler/awaitAwait.ts @@ -0,0 +1,4 @@ +// @target: es6 +async function f() { + await await 0; +} From 52fd0334be9439d459757660b4b6cdf1e2778c6d Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 25 Jul 2016 11:08:02 -0700 Subject: [PATCH 016/233] Allow `await await` --- src/compiler/parser.ts | 7 ++----- .../baselines/reference/awaitAwait.errors.txt | 10 --------- tests/baselines/reference/awaitAwait.js | 21 ------------------- tests/baselines/reference/castOfAwait.js | 2 ++ tests/baselines/reference/castOfAwait.symbols | 1 + tests/baselines/reference/castOfAwait.types | 5 +++++ tests/cases/compiler/awaitAwait.ts | 4 ---- tests/cases/compiler/castOfAwait.ts | 1 + 8 files changed, 11 insertions(+), 40 deletions(-) delete mode 100644 tests/baselines/reference/awaitAwait.errors.txt delete mode 100644 tests/baselines/reference/awaitAwait.js delete mode 100644 tests/cases/compiler/awaitAwait.ts diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 9c54de6ba13..68753fdb638 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3337,10 +3337,7 @@ namespace ts { function parseAwaitExpression() { const node = createNode(SyntaxKind.AwaitExpression); nextToken(); - node.expression = token === SyntaxKind.AwaitKeyword - // Forbid `await await` - ? createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ false, Diagnostics.Expression_expected) - : parseSimpleUnaryExpression(); + node.expression = parseSimpleUnaryExpression(); return finishNode(node); } @@ -3389,7 +3386,7 @@ namespace ts { * 6) - UnaryExpression[?yield] * 7) ~ UnaryExpression[?yield] * 8) ! UnaryExpression[?yield] - * 9) await AwaitExpression[?yield] + * 9) [+Await] await AwaitExpression[?yield] */ function parseSimpleUnaryExpression(): UnaryExpression { switch (token) { diff --git a/tests/baselines/reference/awaitAwait.errors.txt b/tests/baselines/reference/awaitAwait.errors.txt deleted file mode 100644 index 5a04051139c..00000000000 --- a/tests/baselines/reference/awaitAwait.errors.txt +++ /dev/null @@ -1,10 +0,0 @@ -tests/cases/compiler/awaitAwait.ts(2,11): error TS1109: Expression expected. - - -==== tests/cases/compiler/awaitAwait.ts (1 errors) ==== - async function f() { - await await 0; - ~~~~~ -!!! error TS1109: Expression expected. - } - \ No newline at end of file diff --git a/tests/baselines/reference/awaitAwait.js b/tests/baselines/reference/awaitAwait.js deleted file mode 100644 index 14fb3a11505..00000000000 --- a/tests/baselines/reference/awaitAwait.js +++ /dev/null @@ -1,21 +0,0 @@ -//// [awaitAwait.ts] -async function f() { - await await 0; -} - - -//// [awaitAwait.js] -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments)).next()); - }); -}; -function f() { - return __awaiter(this, void 0, void 0, function* () { - yield ; - yield 0; - }); -} diff --git a/tests/baselines/reference/castOfAwait.js b/tests/baselines/reference/castOfAwait.js index 9d1399d9d65..95817b7f0e6 100644 --- a/tests/baselines/reference/castOfAwait.js +++ b/tests/baselines/reference/castOfAwait.js @@ -4,6 +4,7 @@ async function f() { typeof await 0; void await 0; await void typeof void await 0; + await await 0; } @@ -22,5 +23,6 @@ function f() { typeof yield 0; void yield 0; yield void typeof void yield 0; + yield yield 0; }); } diff --git a/tests/baselines/reference/castOfAwait.symbols b/tests/baselines/reference/castOfAwait.symbols index d0bca9f38c7..574ee21f773 100644 --- a/tests/baselines/reference/castOfAwait.symbols +++ b/tests/baselines/reference/castOfAwait.symbols @@ -6,5 +6,6 @@ async function f() { typeof await 0; void await 0; await void typeof void await 0; + await await 0; } diff --git a/tests/baselines/reference/castOfAwait.types b/tests/baselines/reference/castOfAwait.types index ba1590fe03d..ad3597f6c01 100644 --- a/tests/baselines/reference/castOfAwait.types +++ b/tests/baselines/reference/castOfAwait.types @@ -25,6 +25,11 @@ async function f() { > void await 0 : number >void await 0 : undefined >await 0 : number +>0 : number + + await await 0; +>await await 0 : number +>await 0 : number >0 : number } diff --git a/tests/cases/compiler/awaitAwait.ts b/tests/cases/compiler/awaitAwait.ts deleted file mode 100644 index 5a405804b00..00000000000 --- a/tests/cases/compiler/awaitAwait.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @target: es6 -async function f() { - await await 0; -} diff --git a/tests/cases/compiler/castOfAwait.ts b/tests/cases/compiler/castOfAwait.ts index 6a152226ea2..fcd8f999e9f 100644 --- a/tests/cases/compiler/castOfAwait.ts +++ b/tests/cases/compiler/castOfAwait.ts @@ -4,4 +4,5 @@ async function f() { typeof await 0; void await 0; await void typeof void await 0; + await await 0; } From 34744802bcf903018c6f5823fc259edfebe68c53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20M=C5=82okosiewicz?= Date: Tue, 26 Jul 2016 14:58:16 +0200 Subject: [PATCH 017/233] Fix typo in comment for MAX_SAFE_INTEGER --- src/lib/es2015.core.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/es2015.core.d.ts b/src/lib/es2015.core.d.ts index 206b8b8f9bf..fef0b0ce3ce 100644 --- a/src/lib/es2015.core.d.ts +++ b/src/lib/es2015.core.d.ts @@ -226,7 +226,7 @@ interface NumberConstructor { /** * The value of the largest integer n such that n and n + 1 are both exactly representable as * a Number value. - * The value of Number.MIN_SAFE_INTEGER is 9007199254740991 2^53 − 1. + * The value of Number.MAX_SAFE_INTEGER is 9007199254740991 2^53 − 1. */ readonly MAX_SAFE_INTEGER: number; From a6642d68c92ce7ddd16e8da71a1f2eb909fde74d Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 27 Jul 2016 13:21:42 -0700 Subject: [PATCH 018/233] JSDoc understands string literal types Unfortunately, I didn't find a way to reuse the normal string literal type, so I had to extend the existing JSDoc type hierarchy. Otherwise, this feature is very simple. --- src/compiler/checker.ts | 2 ++ src/compiler/parser.ts | 9 ++++++++- src/compiler/types.ts | 5 +++++ tests/baselines/reference/jsdocStringLiteral.js | 16 ++++++++++++++++ .../reference/jsdocStringLiteral.symbols | 12 ++++++++++++ .../baselines/reference/jsdocStringLiteral.types | 14 ++++++++++++++ .../conformance/jsdoc/jsdocStringLiteral.ts | 12 ++++++++++++ 7 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/jsdocStringLiteral.js create mode 100644 tests/baselines/reference/jsdocStringLiteral.symbols create mode 100644 tests/baselines/reference/jsdocStringLiteral.types create mode 100644 tests/cases/conformance/jsdoc/jsdocStringLiteral.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6da456afcbc..321bfcc74a3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5326,6 +5326,8 @@ namespace ts { return getTypeFromThisTypeNode(node); case SyntaxKind.StringLiteralType: return getTypeFromStringLiteralTypeNode(node); + case SyntaxKind.JSDocStringLiteralType: + return getTypeFromStringLiteralTypeNode((node).stringLiteral); case SyntaxKind.TypeReference: case SyntaxKind.JSDocTypeReference: return getTypeFromTypeReference(node); diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 774cea60e09..2e76093cde2 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -5860,9 +5860,10 @@ namespace ts { case SyntaxKind.SymbolKeyword: case SyntaxKind.VoidKeyword: return parseTokenNode(); + case SyntaxKind.StringLiteral: + return parseJSDocStringLiteralType(); } - // TODO (drosen): Parse string literal types in JSDoc as well. return parseJSDocTypeReference(); } @@ -6041,6 +6042,12 @@ namespace ts { return finishNode(result); } + function parseJSDocStringLiteralType(): JSDocStringLiteralType { + const result = createNode(SyntaxKind.JSDocStringLiteralType); + result.stringLiteral = parseStringLiteralTypeNode(); + return finishNode(result); + } + function parseJSDocUnknownOrNullableType(): JSDocUnknownType | JSDocNullableType { const pos = scanner.getStartPos(); // skip the ? diff --git a/src/compiler/types.ts b/src/compiler/types.ts index dc332b24567..17e8a25038d 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -346,6 +346,7 @@ namespace ts { JSDocTypedefTag, JSDocPropertyTag, JSDocTypeLiteral, + JSDocStringLiteralType, // Synthesized list SyntaxList, @@ -1491,6 +1492,10 @@ namespace ts { type: JSDocType; } + export interface JSDocStringLiteralType extends JSDocType { + stringLiteral: StringLiteralTypeNode; + } + export type JSDocTypeReferencingNode = JSDocThisType | JSDocConstructorType | JSDocVariadicType | JSDocOptionalType | JSDocNullableType | JSDocNonNullableType; // @kind(SyntaxKind.JSDocRecordMember) diff --git a/tests/baselines/reference/jsdocStringLiteral.js b/tests/baselines/reference/jsdocStringLiteral.js new file mode 100644 index 00000000000..f74faf3519e --- /dev/null +++ b/tests/baselines/reference/jsdocStringLiteral.js @@ -0,0 +1,16 @@ +//// [in.js] +/** + * @param {'literal'} input + */ +function f(input) { + return input + '.'; +} + + +//// [out.js] +/** + * @param {'literal'} input + */ +function f(input) { + return input + '.'; +} diff --git a/tests/baselines/reference/jsdocStringLiteral.symbols b/tests/baselines/reference/jsdocStringLiteral.symbols new file mode 100644 index 00000000000..0414efc24d9 --- /dev/null +++ b/tests/baselines/reference/jsdocStringLiteral.symbols @@ -0,0 +1,12 @@ +=== tests/cases/compiler/in.js === +/** + * @param {'literal'} input + */ +function f(input) { +>f : Symbol(f, Decl(in.js, 0, 0)) +>input : Symbol(input, Decl(in.js, 3, 11)) + + return input + '.'; +>input : Symbol(input, Decl(in.js, 3, 11)) +} + diff --git a/tests/baselines/reference/jsdocStringLiteral.types b/tests/baselines/reference/jsdocStringLiteral.types new file mode 100644 index 00000000000..c7b87857d18 --- /dev/null +++ b/tests/baselines/reference/jsdocStringLiteral.types @@ -0,0 +1,14 @@ +=== tests/cases/compiler/in.js === +/** + * @param {'literal'} input + */ +function f(input) { +>f : (input: "literal") => string +>input : "literal" + + return input + '.'; +>input + '.' : string +>input : "literal" +>'.' : string +} + diff --git a/tests/cases/conformance/jsdoc/jsdocStringLiteral.ts b/tests/cases/conformance/jsdoc/jsdocStringLiteral.ts new file mode 100644 index 00000000000..d2a0d6c644d --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocStringLiteral.ts @@ -0,0 +1,12 @@ +// @allowJs: true +// @filename: in.js +// @out: out.js +/** + * @param {'literal'} p1 + * @param {"literal"} p2 + * @param {'literal' | 'other'} p3 + * @param {'literal' | number} p4 + */ +function f(p1, p2, p3, p4) { + return p1 + p2 + p3 + p4 + '.'; +} From 6fbd79b70954682f8a131a4899f8a227237e91f5 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 27 Jul 2016 13:25:16 -0700 Subject: [PATCH 019/233] Update baselines to be current --- .../baselines/reference/jsdocStringLiteral.js | 18 ++++++++----- .../reference/jsdocStringLiteral.symbols | 19 ++++++++++---- .../reference/jsdocStringLiteral.types | 26 ++++++++++++++----- 3 files changed, 45 insertions(+), 18 deletions(-) diff --git a/tests/baselines/reference/jsdocStringLiteral.js b/tests/baselines/reference/jsdocStringLiteral.js index f74faf3519e..e1210690f91 100644 --- a/tests/baselines/reference/jsdocStringLiteral.js +++ b/tests/baselines/reference/jsdocStringLiteral.js @@ -1,16 +1,22 @@ //// [in.js] /** - * @param {'literal'} input + * @param {'literal'} p1 + * @param {"literal"} p2 + * @param {'literal' | 'other'} p3 + * @param {'literal' | number} p4 */ -function f(input) { - return input + '.'; +function f(p1, p2, p3, p4) { + return p1 + p2 + p3 + p4 + '.'; } //// [out.js] /** - * @param {'literal'} input + * @param {'literal'} p1 + * @param {"literal"} p2 + * @param {'literal' | 'other'} p3 + * @param {'literal' | number} p4 */ -function f(input) { - return input + '.'; +function f(p1, p2, p3, p4) { + return p1 + p2 + p3 + p4 + '.'; } diff --git a/tests/baselines/reference/jsdocStringLiteral.symbols b/tests/baselines/reference/jsdocStringLiteral.symbols index 0414efc24d9..83f0f4b1e65 100644 --- a/tests/baselines/reference/jsdocStringLiteral.symbols +++ b/tests/baselines/reference/jsdocStringLiteral.symbols @@ -1,12 +1,21 @@ === tests/cases/compiler/in.js === /** - * @param {'literal'} input + * @param {'literal'} p1 + * @param {"literal"} p2 + * @param {'literal' | 'other'} p3 + * @param {'literal' | number} p4 */ -function f(input) { +function f(p1, p2, p3, p4) { >f : Symbol(f, Decl(in.js, 0, 0)) ->input : Symbol(input, Decl(in.js, 3, 11)) +>p1 : Symbol(p1, Decl(in.js, 6, 11)) +>p2 : Symbol(p2, Decl(in.js, 6, 14)) +>p3 : Symbol(p3, Decl(in.js, 6, 18)) +>p4 : Symbol(p4, Decl(in.js, 6, 22)) - return input + '.'; ->input : Symbol(input, Decl(in.js, 3, 11)) + return p1 + p2 + p3 + p4 + '.'; +>p1 : Symbol(p1, Decl(in.js, 6, 11)) +>p2 : Symbol(p2, Decl(in.js, 6, 14)) +>p3 : Symbol(p3, Decl(in.js, 6, 18)) +>p4 : Symbol(p4, Decl(in.js, 6, 22)) } diff --git a/tests/baselines/reference/jsdocStringLiteral.types b/tests/baselines/reference/jsdocStringLiteral.types index c7b87857d18..834ec65b916 100644 --- a/tests/baselines/reference/jsdocStringLiteral.types +++ b/tests/baselines/reference/jsdocStringLiteral.types @@ -1,14 +1,26 @@ === tests/cases/compiler/in.js === /** - * @param {'literal'} input + * @param {'literal'} p1 + * @param {"literal"} p2 + * @param {'literal' | 'other'} p3 + * @param {'literal' | number} p4 */ -function f(input) { ->f : (input: "literal") => string ->input : "literal" +function f(p1, p2, p3, p4) { +>f : (p1: "literal", p2: "literal", p3: "literal" | "other", p4: "literal" | number) => string +>p1 : "literal" +>p2 : "literal" +>p3 : "literal" | "other" +>p4 : "literal" | number - return input + '.'; ->input + '.' : string ->input : "literal" + return p1 + p2 + p3 + p4 + '.'; +>p1 + p2 + p3 + p4 + '.' : string +>p1 + p2 + p3 + p4 : string +>p1 + p2 + p3 : string +>p1 + p2 : string +>p1 : "literal" +>p2 : "literal" +>p3 : "literal" | "other" +>p4 : "literal" | number >'.' : string } From 3446557eae0dd6096ca388872a0ad28b7e2d4202 Mon Sep 17 00:00:00 2001 From: Josh Abernathy Date: Wed, 27 Jul 2016 16:40:43 -0400 Subject: [PATCH 020/233] Add find and findIndex to ReadonlyArray --- src/lib/es2015.core.d.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/lib/es2015.core.d.ts b/src/lib/es2015.core.d.ts index 206b8b8f9bf..dcfe9a0b92e 100644 --- a/src/lib/es2015.core.d.ts +++ b/src/lib/es2015.core.d.ts @@ -343,6 +343,30 @@ interface ObjectConstructor { defineProperty(o: any, propertyKey: PropertyKey, attributes: PropertyDescriptor): any; } +interface ReadonlyArray { + /** + * Returns the value of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, find + * immediately returns that element value. Otherwise, find returns undefined. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + find(predicate: (value: T, index: number, obj: Array) => boolean, thisArg?: any): T | undefined; + + /** + * Returns the index of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, + * findIndex immediately returns that element index. Otherwise, findIndex returns -1. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + findIndex(predicate: (value: T) => boolean, thisArg?: any): number; +} + interface RegExp { /** * Returns a string indicating the flags of the regular expression in question. This field is read-only. From 34e78e6dc1487c061c363e9fac49ee613dcf5def Mon Sep 17 00:00:00 2001 From: Josh Abernathy Date: Wed, 27 Jul 2016 16:42:14 -0400 Subject: [PATCH 021/233] The optional this should be readonly too. --- src/lib/es2015.core.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/es2015.core.d.ts b/src/lib/es2015.core.d.ts index dcfe9a0b92e..42e4a8c2b57 100644 --- a/src/lib/es2015.core.d.ts +++ b/src/lib/es2015.core.d.ts @@ -353,7 +353,7 @@ interface ReadonlyArray { * @param thisArg If provided, it will be used as the this value for each invocation of * predicate. If it is not provided, undefined is used instead. */ - find(predicate: (value: T, index: number, obj: Array) => boolean, thisArg?: any): T | undefined; + find(predicate: (value: T, index: number, obj: ReadonlyArray) => boolean, thisArg?: any): T | undefined; /** * Returns the index of the first element in the array where predicate is true, and undefined From 5c2ba01bebd81e4b0afb146a1fa4e5fe8d4b6aa9 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 27 Jul 2016 13:44:45 -0700 Subject: [PATCH 022/233] Update baseline source location --- tests/baselines/reference/jsdocStringLiteral.symbols | 2 +- tests/baselines/reference/jsdocStringLiteral.types | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/jsdocStringLiteral.symbols b/tests/baselines/reference/jsdocStringLiteral.symbols index 83f0f4b1e65..982a1536069 100644 --- a/tests/baselines/reference/jsdocStringLiteral.symbols +++ b/tests/baselines/reference/jsdocStringLiteral.symbols @@ -1,4 +1,4 @@ -=== tests/cases/compiler/in.js === +=== tests/cases/conformance/jsdoc/in.js === /** * @param {'literal'} p1 * @param {"literal"} p2 diff --git a/tests/baselines/reference/jsdocStringLiteral.types b/tests/baselines/reference/jsdocStringLiteral.types index 834ec65b916..0400416a259 100644 --- a/tests/baselines/reference/jsdocStringLiteral.types +++ b/tests/baselines/reference/jsdocStringLiteral.types @@ -1,4 +1,4 @@ -=== tests/cases/compiler/in.js === +=== tests/cases/conformance/jsdoc/in.js === /** * @param {'literal'} p1 * @param {"literal"} p2 From 09f518243d32453444cb7de850f6af003a742404 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 27 Jul 2016 15:33:36 -0700 Subject: [PATCH 023/233] Re-add concat overload to support inferring tuples --- src/lib/es5.d.ts | 5 ++++ tests/baselines/reference/concatTuples.js | 8 +++++++ .../baselines/reference/concatTuples.symbols | 10 ++++++++ tests/baselines/reference/concatTuples.types | 23 +++++++++++++++++++ tests/cases/compiler/concatTuples.ts | 2 ++ 5 files changed, 48 insertions(+) create mode 100644 tests/baselines/reference/concatTuples.js create mode 100644 tests/baselines/reference/concatTuples.symbols create mode 100644 tests/baselines/reference/concatTuples.types create mode 100644 tests/cases/compiler/concatTuples.ts diff --git a/src/lib/es5.d.ts b/src/lib/es5.d.ts index 6f017c8a343..496df578c19 100644 --- a/src/lib/es5.d.ts +++ b/src/lib/es5.d.ts @@ -1108,6 +1108,11 @@ interface Array { * Removes the last element from an array and returns it. */ pop(): T | undefined; + /** + * Combines two or more arrays. + * @param items Additional items to add to the end of array1. + */ + concat(...items: T[][]): T[]; /** * Combines two or more arrays. * @param items Additional items to add to the end of array1. diff --git a/tests/baselines/reference/concatTuples.js b/tests/baselines/reference/concatTuples.js new file mode 100644 index 00000000000..699c43f210e --- /dev/null +++ b/tests/baselines/reference/concatTuples.js @@ -0,0 +1,8 @@ +//// [concatTuples.ts] +let ijs: [number, number][] = [[1, 2]]; +ijs = ijs.concat([[3, 4], [5, 6]]); + + +//// [concatTuples.js] +var ijs = [[1, 2]]; +ijs = ijs.concat([[3, 4], [5, 6]]); diff --git a/tests/baselines/reference/concatTuples.symbols b/tests/baselines/reference/concatTuples.symbols new file mode 100644 index 00000000000..a6524f3e1ff --- /dev/null +++ b/tests/baselines/reference/concatTuples.symbols @@ -0,0 +1,10 @@ +=== tests/cases/compiler/concatTuples.ts === +let ijs: [number, number][] = [[1, 2]]; +>ijs : Symbol(ijs, Decl(concatTuples.ts, 0, 3)) + +ijs = ijs.concat([[3, 4], [5, 6]]); +>ijs : Symbol(ijs, Decl(concatTuples.ts, 0, 3)) +>ijs.concat : Symbol(Array.concat, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>ijs : Symbol(ijs, Decl(concatTuples.ts, 0, 3)) +>concat : Symbol(Array.concat, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + diff --git a/tests/baselines/reference/concatTuples.types b/tests/baselines/reference/concatTuples.types new file mode 100644 index 00000000000..3923470b78d --- /dev/null +++ b/tests/baselines/reference/concatTuples.types @@ -0,0 +1,23 @@ +=== tests/cases/compiler/concatTuples.ts === +let ijs: [number, number][] = [[1, 2]]; +>ijs : [number, number][] +>[[1, 2]] : [number, number][] +>[1, 2] : [number, number] +>1 : number +>2 : number + +ijs = ijs.concat([[3, 4], [5, 6]]); +>ijs = ijs.concat([[3, 4], [5, 6]]) : [number, number][] +>ijs : [number, number][] +>ijs.concat([[3, 4], [5, 6]]) : [number, number][] +>ijs.concat : { (...items: [number, number][][]): [number, number][]; (...items: ([number, number] | [number, number][])[]): [number, number][]; } +>ijs : [number, number][] +>concat : { (...items: [number, number][][]): [number, number][]; (...items: ([number, number] | [number, number][])[]): [number, number][]; } +>[[3, 4], [5, 6]] : [number, number][] +>[3, 4] : [number, number] +>3 : number +>4 : number +>[5, 6] : [number, number] +>5 : number +>6 : number + diff --git a/tests/cases/compiler/concatTuples.ts b/tests/cases/compiler/concatTuples.ts new file mode 100644 index 00000000000..9a62f22bb63 --- /dev/null +++ b/tests/cases/compiler/concatTuples.ts @@ -0,0 +1,2 @@ +let ijs: [number, number][] = [[1, 2]]; +ijs = ijs.concat([[3, 4], [5, 6]]); From e8bd372bb2b9815a589b16759323d089390d2be0 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 27 Jul 2016 15:36:32 -0700 Subject: [PATCH 024/233] Update baselines with new concat overload --- tests/baselines/reference/arrayConcat2.symbols | 12 ++++++------ tests/baselines/reference/arrayConcat2.types | 12 ++++++------ tests/baselines/reference/arrayConcatMap.symbols | 4 ++-- tests/baselines/reference/arrayConcatMap.types | 4 ++-- tests/baselines/reference/concatError.symbols | 8 ++++---- tests/baselines/reference/concatError.types | 8 ++++---- .../reference/emitSkipsThisWithRestParameter.symbols | 4 ++-- .../reference/emitSkipsThisWithRestParameter.types | 4 ++-- .../reference/iteratorSpreadInArray7.symbols | 4 ++-- .../baselines/reference/iteratorSpreadInArray7.types | 4 ++-- ...cAnonymousTypeNotReferencingTypeParameter.symbols | 4 ++-- ...ticAnonymousTypeNotReferencingTypeParameter.types | 4 ++-- tests/baselines/reference/underscoreTest1.symbols | 4 ++-- tests/baselines/reference/underscoreTest1.types | 4 ++-- 14 files changed, 40 insertions(+), 40 deletions(-) diff --git a/tests/baselines/reference/arrayConcat2.symbols b/tests/baselines/reference/arrayConcat2.symbols index fb84193c113..daedee6e9c8 100644 --- a/tests/baselines/reference/arrayConcat2.symbols +++ b/tests/baselines/reference/arrayConcat2.symbols @@ -3,21 +3,21 @@ var a: string[] = []; >a : Symbol(a, Decl(arrayConcat2.ts, 0, 3)) a.concat("hello", 'world'); ->a.concat : Symbol(Array.concat, Decl(lib.d.ts, --, --)) +>a.concat : Symbol(Array.concat, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) >a : Symbol(a, Decl(arrayConcat2.ts, 0, 3)) ->concat : Symbol(Array.concat, Decl(lib.d.ts, --, --)) +>concat : Symbol(Array.concat, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) a.concat('Hello'); ->a.concat : Symbol(Array.concat, Decl(lib.d.ts, --, --)) +>a.concat : Symbol(Array.concat, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) >a : Symbol(a, Decl(arrayConcat2.ts, 0, 3)) ->concat : Symbol(Array.concat, Decl(lib.d.ts, --, --)) +>concat : Symbol(Array.concat, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) var b = new Array(); >b : Symbol(b, Decl(arrayConcat2.ts, 5, 3)) >Array : Symbol(Array, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) b.concat('hello'); ->b.concat : Symbol(Array.concat, Decl(lib.d.ts, --, --)) +>b.concat : Symbol(Array.concat, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) >b : Symbol(b, Decl(arrayConcat2.ts, 5, 3)) ->concat : Symbol(Array.concat, Decl(lib.d.ts, --, --)) +>concat : Symbol(Array.concat, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) diff --git a/tests/baselines/reference/arrayConcat2.types b/tests/baselines/reference/arrayConcat2.types index b7df3749116..27fe754c485 100644 --- a/tests/baselines/reference/arrayConcat2.types +++ b/tests/baselines/reference/arrayConcat2.types @@ -5,17 +5,17 @@ var a: string[] = []; a.concat("hello", 'world'); >a.concat("hello", 'world') : string[] ->a.concat : (...items: (string | string[])[]) => string[] +>a.concat : { (...items: string[][]): string[]; (...items: (string | string[])[]): string[]; } >a : string[] ->concat : (...items: (string | string[])[]) => string[] +>concat : { (...items: string[][]): string[]; (...items: (string | string[])[]): string[]; } >"hello" : string >'world' : string a.concat('Hello'); >a.concat('Hello') : string[] ->a.concat : (...items: (string | string[])[]) => string[] +>a.concat : { (...items: string[][]): string[]; (...items: (string | string[])[]): string[]; } >a : string[] ->concat : (...items: (string | string[])[]) => string[] +>concat : { (...items: string[][]): string[]; (...items: (string | string[])[]): string[]; } >'Hello' : string var b = new Array(); @@ -25,8 +25,8 @@ var b = new Array(); b.concat('hello'); >b.concat('hello') : string[] ->b.concat : (...items: (string | string[])[]) => string[] +>b.concat : { (...items: string[][]): string[]; (...items: (string | string[])[]): string[]; } >b : string[] ->concat : (...items: (string | string[])[]) => string[] +>concat : { (...items: string[][]): string[]; (...items: (string | string[])[]): string[]; } >'hello' : string diff --git a/tests/baselines/reference/arrayConcatMap.symbols b/tests/baselines/reference/arrayConcatMap.symbols index bde2686e259..5ef9d811723 100644 --- a/tests/baselines/reference/arrayConcatMap.symbols +++ b/tests/baselines/reference/arrayConcatMap.symbols @@ -2,8 +2,8 @@ var x = [].concat([{ a: 1 }], [{ a: 2 }]) >x : Symbol(x, Decl(arrayConcatMap.ts, 0, 3)) >[].concat([{ a: 1 }], [{ a: 2 }]) .map : Symbol(Array.map, Decl(lib.d.ts, --, --)) ->[].concat : Symbol(Array.concat, Decl(lib.d.ts, --, --)) ->concat : Symbol(Array.concat, Decl(lib.d.ts, --, --)) +>[].concat : Symbol(Array.concat, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>concat : Symbol(Array.concat, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) >a : Symbol(a, Decl(arrayConcatMap.ts, 0, 20)) >a : Symbol(a, Decl(arrayConcatMap.ts, 0, 32)) diff --git a/tests/baselines/reference/arrayConcatMap.types b/tests/baselines/reference/arrayConcatMap.types index 38e1a1c49c9..647406ef1c3 100644 --- a/tests/baselines/reference/arrayConcatMap.types +++ b/tests/baselines/reference/arrayConcatMap.types @@ -4,9 +4,9 @@ var x = [].concat([{ a: 1 }], [{ a: 2 }]) >[].concat([{ a: 1 }], [{ a: 2 }]) .map(b => b.a) : any[] >[].concat([{ a: 1 }], [{ a: 2 }]) .map : (callbackfn: (value: any, index: number, array: any[]) => U, thisArg?: any) => U[] >[].concat([{ a: 1 }], [{ a: 2 }]) : any[] ->[].concat : (...items: any[]) => any[] +>[].concat : { (...items: any[][]): any[]; (...items: any[]): any[]; } >[] : undefined[] ->concat : (...items: any[]) => any[] +>concat : { (...items: any[][]): any[]; (...items: any[]): any[]; } >[{ a: 1 }] : { a: number; }[] >{ a: 1 } : { a: number; } >a : number diff --git a/tests/baselines/reference/concatError.symbols b/tests/baselines/reference/concatError.symbols index 23cc55c85be..d4f04531fc7 100644 --- a/tests/baselines/reference/concatError.symbols +++ b/tests/baselines/reference/concatError.symbols @@ -14,15 +14,15 @@ var fa: number[]; fa = fa.concat([0]); >fa : Symbol(fa, Decl(concatError.ts, 8, 3)) ->fa.concat : Symbol(Array.concat, Decl(lib.d.ts, --, --)) +>fa.concat : Symbol(Array.concat, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) >fa : Symbol(fa, Decl(concatError.ts, 8, 3)) ->concat : Symbol(Array.concat, Decl(lib.d.ts, --, --)) +>concat : Symbol(Array.concat, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) fa = fa.concat(0); >fa : Symbol(fa, Decl(concatError.ts, 8, 3)) ->fa.concat : Symbol(Array.concat, Decl(lib.d.ts, --, --)) +>fa.concat : Symbol(Array.concat, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) >fa : Symbol(fa, Decl(concatError.ts, 8, 3)) ->concat : Symbol(Array.concat, Decl(lib.d.ts, --, --)) +>concat : Symbol(Array.concat, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) diff --git a/tests/baselines/reference/concatError.types b/tests/baselines/reference/concatError.types index 21a5cc5d089..9624342bd21 100644 --- a/tests/baselines/reference/concatError.types +++ b/tests/baselines/reference/concatError.types @@ -16,9 +16,9 @@ fa = fa.concat([0]); >fa = fa.concat([0]) : number[] >fa : number[] >fa.concat([0]) : number[] ->fa.concat : (...items: (number | number[])[]) => number[] +>fa.concat : { (...items: number[][]): number[]; (...items: (number | number[])[]): number[]; } >fa : number[] ->concat : (...items: (number | number[])[]) => number[] +>concat : { (...items: number[][]): number[]; (...items: (number | number[])[]): number[]; } >[0] : number[] >0 : number @@ -26,9 +26,9 @@ fa = fa.concat(0); >fa = fa.concat(0) : number[] >fa : number[] >fa.concat(0) : number[] ->fa.concat : (...items: (number | number[])[]) => number[] +>fa.concat : { (...items: number[][]): number[]; (...items: (number | number[])[]): number[]; } >fa : number[] ->concat : (...items: (number | number[])[]) => number[] +>concat : { (...items: number[][]): number[]; (...items: (number | number[])[]): number[]; } >0 : number diff --git a/tests/baselines/reference/emitSkipsThisWithRestParameter.symbols b/tests/baselines/reference/emitSkipsThisWithRestParameter.symbols index 4f1e2bc1aec..04a699a322d 100644 --- a/tests/baselines/reference/emitSkipsThisWithRestParameter.symbols +++ b/tests/baselines/reference/emitSkipsThisWithRestParameter.symbols @@ -15,9 +15,9 @@ function rebase(fn: (base: any, ...args: any[]) => any): (...args: any[]) => any >fn : Symbol(fn, Decl(emitSkipsThisWithRestParameter.ts, 0, 16)) >apply : Symbol(Function.apply, Decl(lib.d.ts, --, --)) >this : Symbol(this, Decl(emitSkipsThisWithRestParameter.ts, 1, 20)) ->[ this ].concat : Symbol(Array.concat, Decl(lib.d.ts, --, --)) +>[ this ].concat : Symbol(Array.concat, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) >this : Symbol(this, Decl(emitSkipsThisWithRestParameter.ts, 1, 20)) ->concat : Symbol(Array.concat, Decl(lib.d.ts, --, --)) +>concat : Symbol(Array.concat, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) >args : Symbol(args, Decl(emitSkipsThisWithRestParameter.ts, 1, 30)) }; diff --git a/tests/baselines/reference/emitSkipsThisWithRestParameter.types b/tests/baselines/reference/emitSkipsThisWithRestParameter.types index 97276fa117d..ff6f1e6f73a 100644 --- a/tests/baselines/reference/emitSkipsThisWithRestParameter.types +++ b/tests/baselines/reference/emitSkipsThisWithRestParameter.types @@ -18,10 +18,10 @@ function rebase(fn: (base: any, ...args: any[]) => any): (...args: any[]) => any >apply : (this: Function, thisArg: any, argArray?: any) => any >this : any >[ this ].concat(args) : any[] ->[ this ].concat : (...items: any[]) => any[] +>[ this ].concat : { (...items: any[][]): any[]; (...items: any[]): any[]; } >[ this ] : any[] >this : any ->concat : (...items: any[]) => any[] +>concat : { (...items: any[][]): any[]; (...items: any[]): any[]; } >args : any[] }; diff --git a/tests/baselines/reference/iteratorSpreadInArray7.symbols b/tests/baselines/reference/iteratorSpreadInArray7.symbols index 853e07b908a..57774bcd7ee 100644 --- a/tests/baselines/reference/iteratorSpreadInArray7.symbols +++ b/tests/baselines/reference/iteratorSpreadInArray7.symbols @@ -3,9 +3,9 @@ var array: symbol[]; >array : Symbol(array, Decl(iteratorSpreadInArray7.ts, 0, 3)) array.concat([...new SymbolIterator]); ->array.concat : Symbol(Array.concat, Decl(lib.es5.d.ts, --, --)) +>array.concat : Symbol(Array.concat, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) >array : Symbol(array, Decl(iteratorSpreadInArray7.ts, 0, 3)) ->concat : Symbol(Array.concat, Decl(lib.es5.d.ts, --, --)) +>concat : Symbol(Array.concat, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) >SymbolIterator : Symbol(SymbolIterator, Decl(iteratorSpreadInArray7.ts, 1, 38)) class SymbolIterator { diff --git a/tests/baselines/reference/iteratorSpreadInArray7.types b/tests/baselines/reference/iteratorSpreadInArray7.types index f1dd8ebf855..38549a606ab 100644 --- a/tests/baselines/reference/iteratorSpreadInArray7.types +++ b/tests/baselines/reference/iteratorSpreadInArray7.types @@ -4,9 +4,9 @@ var array: symbol[]; array.concat([...new SymbolIterator]); >array.concat([...new SymbolIterator]) : symbol[] ->array.concat : (...items: (symbol | symbol[])[]) => symbol[] +>array.concat : { (...items: symbol[][]): symbol[]; (...items: (symbol | symbol[])[]): symbol[]; } >array : symbol[] ->concat : (...items: (symbol | symbol[])[]) => symbol[] +>concat : { (...items: symbol[][]): symbol[]; (...items: (symbol | symbol[])[]): symbol[]; } >[...new SymbolIterator] : symbol[] >...new SymbolIterator : symbol >new SymbolIterator : SymbolIterator diff --git a/tests/baselines/reference/staticAnonymousTypeNotReferencingTypeParameter.symbols b/tests/baselines/reference/staticAnonymousTypeNotReferencingTypeParameter.symbols index 2596959de9e..f217f42a8d6 100644 --- a/tests/baselines/reference/staticAnonymousTypeNotReferencingTypeParameter.symbols +++ b/tests/baselines/reference/staticAnonymousTypeNotReferencingTypeParameter.symbols @@ -300,9 +300,9 @@ class ListWrapper { >ListWrapper : Symbol(ListWrapper, Decl(staticAnonymousTypeNotReferencingTypeParameter.ts, 38, 1)) >a : Symbol(a, Decl(staticAnonymousTypeNotReferencingTypeParameter.ts, 68, 40)) >b : Symbol(b, Decl(staticAnonymousTypeNotReferencingTypeParameter.ts, 68, 50)) ->a.concat : Symbol(Array.concat, Decl(lib.d.ts, --, --)) +>a.concat : Symbol(Array.concat, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) >a : Symbol(a, Decl(staticAnonymousTypeNotReferencingTypeParameter.ts, 68, 40)) ->concat : Symbol(Array.concat, Decl(lib.d.ts, --, --)) +>concat : Symbol(Array.concat, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) >b : Symbol(b, Decl(staticAnonymousTypeNotReferencingTypeParameter.ts, 68, 50)) static insert(dit: typeof ListWrapper, list: T[], index: number, value: T) { list.splice(index, 0, value); } diff --git a/tests/baselines/reference/staticAnonymousTypeNotReferencingTypeParameter.types b/tests/baselines/reference/staticAnonymousTypeNotReferencingTypeParameter.types index 8d35f32707a..9aa24f01059 100644 --- a/tests/baselines/reference/staticAnonymousTypeNotReferencingTypeParameter.types +++ b/tests/baselines/reference/staticAnonymousTypeNotReferencingTypeParameter.types @@ -348,9 +348,9 @@ class ListWrapper { >a : any[] >b : any[] >a.concat(b) : any[] ->a.concat : (...items: any[]) => any[] +>a.concat : { (...items: any[][]): any[]; (...items: any[]): any[]; } >a : any[] ->concat : (...items: any[]) => any[] +>concat : { (...items: any[][]): any[]; (...items: any[]): any[]; } >b : any[] static insert(dit: typeof ListWrapper, list: T[], index: number, value: T) { list.splice(index, 0, value); } diff --git a/tests/baselines/reference/underscoreTest1.symbols b/tests/baselines/reference/underscoreTest1.symbols index 3506f23510e..89815bd2371 100644 --- a/tests/baselines/reference/underscoreTest1.symbols +++ b/tests/baselines/reference/underscoreTest1.symbols @@ -71,9 +71,9 @@ var flat = _.reduceRight(list, (a, b) => a.concat(b), []); >list : Symbol(list, Decl(underscoreTest1_underscoreTests.ts, 13, 3)) >a : Symbol(a, Decl(underscoreTest1_underscoreTests.ts, 14, 32)) >b : Symbol(b, Decl(underscoreTest1_underscoreTests.ts, 14, 34)) ->a.concat : Symbol(Array.concat, Decl(lib.d.ts, --, --)) +>a.concat : Symbol(Array.concat, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) >a : Symbol(a, Decl(underscoreTest1_underscoreTests.ts, 14, 32)) ->concat : Symbol(Array.concat, Decl(lib.d.ts, --, --)) +>concat : Symbol(Array.concat, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) >b : Symbol(b, Decl(underscoreTest1_underscoreTests.ts, 14, 34)) var even = _.find([1, 2, 3, 4, 5, 6], (num) => num % 2 == 0); diff --git a/tests/baselines/reference/underscoreTest1.types b/tests/baselines/reference/underscoreTest1.types index 37f3bdb12ab..0a261883ed8 100644 --- a/tests/baselines/reference/underscoreTest1.types +++ b/tests/baselines/reference/underscoreTest1.types @@ -124,9 +124,9 @@ var flat = _.reduceRight(list, (a, b) => a.concat(b), []); >a : number[] >b : number[] >a.concat(b) : number[] ->a.concat : (...items: (number | number[])[]) => number[] +>a.concat : { (...items: number[][]): number[]; (...items: (number | number[])[]): number[]; } >a : number[] ->concat : (...items: (number | number[])[]) => number[] +>concat : { (...items: number[][]): number[]; (...items: (number | number[])[]): number[]; } >b : number[] >[] : undefined[] From f8103b596091d717753ee8200f9f45a3c8ca6ef7 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 27 Jul 2016 15:58:13 -0700 Subject: [PATCH 025/233] Update LastJSDoc[Tag]Node --- src/compiler/types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 17e8a25038d..d5d5546a0cd 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -377,9 +377,9 @@ namespace ts { LastBinaryOperator = CaretEqualsToken, FirstNode = QualifiedName, FirstJSDocNode = JSDocTypeExpression, - LastJSDocNode = JSDocTypeLiteral, + LastJSDocNode = JSDocStringLiteralType, FirstJSDocTagNode = JSDocComment, - LastJSDocTagNode = JSDocTypeLiteral + LastJSDocTagNode = JSDocStringLiteralType } export const enum NodeFlags { From 9e962f5356a41d194907c883f5bc5d2bad1b5e5a Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 28 Jul 2016 09:15:09 -0700 Subject: [PATCH 026/233] null/undefined are allowed as index expressions `null` and `undefined` are not allowed with `--strictNullChecks` turned on. Previously, they were disallowed whether or not it was on. --- src/compiler/checker.ts | 5 +- .../reference/indexWithUndefinedAndNull.js | 22 +++++++++ .../indexWithUndefinedAndNull.symbols | 39 +++++++++++++++ .../reference/indexWithUndefinedAndNull.types | 47 +++++++++++++++++++ ...ndefinedAndNullStrictNullChecks.errors.txt | 40 ++++++++++++++++ ...dexWithUndefinedAndNullStrictNullChecks.js | 22 +++++++++ .../compiler/indexWithUndefinedAndNull.ts | 13 +++++ ...dexWithUndefinedAndNullStrictNullChecks.ts | 13 +++++ 8 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/indexWithUndefinedAndNull.js create mode 100644 tests/baselines/reference/indexWithUndefinedAndNull.symbols create mode 100644 tests/baselines/reference/indexWithUndefinedAndNull.types create mode 100644 tests/baselines/reference/indexWithUndefinedAndNullStrictNullChecks.errors.txt create mode 100644 tests/baselines/reference/indexWithUndefinedAndNullStrictNullChecks.js create mode 100644 tests/cases/compiler/indexWithUndefinedAndNull.ts create mode 100644 tests/cases/compiler/indexWithUndefinedAndNullStrictNullChecks.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6da456afcbc..38079c1bea0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10394,10 +10394,11 @@ namespace ts { } // Check for compatible indexer types. - if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol)) { + const allowedOptionalFlags = strictNullChecks ? 0 : TypeFlags.Null | TypeFlags.Undefined; + if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol | allowedOptionalFlags)) { // Try to use a number indexer. - if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.NumberLike) || isForInVariableForNumericPropertyNames(node.argumentExpression)) { + if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.NumberLike | allowedOptionalFlags) || isForInVariableForNumericPropertyNames(node.argumentExpression)) { const numberIndexInfo = getIndexInfoOfType(objectType, IndexKind.Number); if (numberIndexInfo) { getNodeLinks(node).resolvedIndexInfo = numberIndexInfo; diff --git a/tests/baselines/reference/indexWithUndefinedAndNull.js b/tests/baselines/reference/indexWithUndefinedAndNull.js new file mode 100644 index 00000000000..f885323efae --- /dev/null +++ b/tests/baselines/reference/indexWithUndefinedAndNull.js @@ -0,0 +1,22 @@ +//// [indexWithUndefinedAndNull.ts] +interface N { + [n: number]: string; +} +interface S { + [s: string]: number; +} +let n: N; +let s: S; +let str: string = n[undefined]; +str = n[null]; +let num: number = s[undefined]; +num = s[null]; + + +//// [indexWithUndefinedAndNull.js] +var n; +var s; +var str = n[undefined]; +str = n[null]; +var num = s[undefined]; +num = s[null]; diff --git a/tests/baselines/reference/indexWithUndefinedAndNull.symbols b/tests/baselines/reference/indexWithUndefinedAndNull.symbols new file mode 100644 index 00000000000..15e8bbdbf41 --- /dev/null +++ b/tests/baselines/reference/indexWithUndefinedAndNull.symbols @@ -0,0 +1,39 @@ +=== tests/cases/compiler/indexWithUndefinedAndNull.ts === +interface N { +>N : Symbol(N, Decl(indexWithUndefinedAndNull.ts, 0, 0)) + + [n: number]: string; +>n : Symbol(n, Decl(indexWithUndefinedAndNull.ts, 1, 5)) +} +interface S { +>S : Symbol(S, Decl(indexWithUndefinedAndNull.ts, 2, 1)) + + [s: string]: number; +>s : Symbol(s, Decl(indexWithUndefinedAndNull.ts, 4, 5)) +} +let n: N; +>n : Symbol(n, Decl(indexWithUndefinedAndNull.ts, 6, 3)) +>N : Symbol(N, Decl(indexWithUndefinedAndNull.ts, 0, 0)) + +let s: S; +>s : Symbol(s, Decl(indexWithUndefinedAndNull.ts, 7, 3)) +>S : Symbol(S, Decl(indexWithUndefinedAndNull.ts, 2, 1)) + +let str: string = n[undefined]; +>str : Symbol(str, Decl(indexWithUndefinedAndNull.ts, 8, 3)) +>n : Symbol(n, Decl(indexWithUndefinedAndNull.ts, 6, 3)) +>undefined : Symbol(undefined) + +str = n[null]; +>str : Symbol(str, Decl(indexWithUndefinedAndNull.ts, 8, 3)) +>n : Symbol(n, Decl(indexWithUndefinedAndNull.ts, 6, 3)) + +let num: number = s[undefined]; +>num : Symbol(num, Decl(indexWithUndefinedAndNull.ts, 10, 3)) +>s : Symbol(s, Decl(indexWithUndefinedAndNull.ts, 7, 3)) +>undefined : Symbol(undefined) + +num = s[null]; +>num : Symbol(num, Decl(indexWithUndefinedAndNull.ts, 10, 3)) +>s : Symbol(s, Decl(indexWithUndefinedAndNull.ts, 7, 3)) + diff --git a/tests/baselines/reference/indexWithUndefinedAndNull.types b/tests/baselines/reference/indexWithUndefinedAndNull.types new file mode 100644 index 00000000000..07b6050503a --- /dev/null +++ b/tests/baselines/reference/indexWithUndefinedAndNull.types @@ -0,0 +1,47 @@ +=== tests/cases/compiler/indexWithUndefinedAndNull.ts === +interface N { +>N : N + + [n: number]: string; +>n : number +} +interface S { +>S : S + + [s: string]: number; +>s : string +} +let n: N; +>n : N +>N : N + +let s: S; +>s : S +>S : S + +let str: string = n[undefined]; +>str : string +>n[undefined] : string +>n : N +>undefined : undefined + +str = n[null]; +>str = n[null] : string +>str : string +>n[null] : string +>n : N +>null : null + +let num: number = s[undefined]; +>num : number +>s[undefined] : number +>s : S +>undefined : undefined + +num = s[null]; +>num = s[null] : number +>num : number +>s[null] : number +>s : S +>null : null + diff --git a/tests/baselines/reference/indexWithUndefinedAndNullStrictNullChecks.errors.txt b/tests/baselines/reference/indexWithUndefinedAndNullStrictNullChecks.errors.txt new file mode 100644 index 00000000000..a1fce75c54e --- /dev/null +++ b/tests/baselines/reference/indexWithUndefinedAndNullStrictNullChecks.errors.txt @@ -0,0 +1,40 @@ +tests/cases/compiler/indexWithUndefinedAndNullStrictNullChecks.ts(9,19): error TS2454: Variable 'n' is used before being assigned. +tests/cases/compiler/indexWithUndefinedAndNullStrictNullChecks.ts(9,19): error TS2342: An index expression argument must be of type 'string', 'number', 'symbol', or 'any'. +tests/cases/compiler/indexWithUndefinedAndNullStrictNullChecks.ts(10,7): error TS2454: Variable 'n' is used before being assigned. +tests/cases/compiler/indexWithUndefinedAndNullStrictNullChecks.ts(10,7): error TS2342: An index expression argument must be of type 'string', 'number', 'symbol', or 'any'. +tests/cases/compiler/indexWithUndefinedAndNullStrictNullChecks.ts(11,19): error TS2454: Variable 's' is used before being assigned. +tests/cases/compiler/indexWithUndefinedAndNullStrictNullChecks.ts(11,19): error TS2342: An index expression argument must be of type 'string', 'number', 'symbol', or 'any'. +tests/cases/compiler/indexWithUndefinedAndNullStrictNullChecks.ts(12,7): error TS2454: Variable 's' is used before being assigned. +tests/cases/compiler/indexWithUndefinedAndNullStrictNullChecks.ts(12,7): error TS2342: An index expression argument must be of type 'string', 'number', 'symbol', or 'any'. + + +==== tests/cases/compiler/indexWithUndefinedAndNullStrictNullChecks.ts (8 errors) ==== + interface N { + [n: number]: string; + } + interface S { + [s: string]: number; + } + let n: N; + let s: S; + let str: string = n[undefined]; + ~ +!!! error TS2454: Variable 'n' is used before being assigned. + ~~~~~~~~~~~~ +!!! error TS2342: An index expression argument must be of type 'string', 'number', 'symbol', or 'any'. + str = n[null]; + ~ +!!! error TS2454: Variable 'n' is used before being assigned. + ~~~~~~~ +!!! error TS2342: An index expression argument must be of type 'string', 'number', 'symbol', or 'any'. + let num: number = s[undefined]; + ~ +!!! error TS2454: Variable 's' is used before being assigned. + ~~~~~~~~~~~~ +!!! error TS2342: An index expression argument must be of type 'string', 'number', 'symbol', or 'any'. + num = s[null]; + ~ +!!! error TS2454: Variable 's' is used before being assigned. + ~~~~~~~ +!!! error TS2342: An index expression argument must be of type 'string', 'number', 'symbol', or 'any'. + \ No newline at end of file diff --git a/tests/baselines/reference/indexWithUndefinedAndNullStrictNullChecks.js b/tests/baselines/reference/indexWithUndefinedAndNullStrictNullChecks.js new file mode 100644 index 00000000000..9300b7c0549 --- /dev/null +++ b/tests/baselines/reference/indexWithUndefinedAndNullStrictNullChecks.js @@ -0,0 +1,22 @@ +//// [indexWithUndefinedAndNullStrictNullChecks.ts] +interface N { + [n: number]: string; +} +interface S { + [s: string]: number; +} +let n: N; +let s: S; +let str: string = n[undefined]; +str = n[null]; +let num: number = s[undefined]; +num = s[null]; + + +//// [indexWithUndefinedAndNullStrictNullChecks.js] +var n; +var s; +var str = n[undefined]; +str = n[null]; +var num = s[undefined]; +num = s[null]; diff --git a/tests/cases/compiler/indexWithUndefinedAndNull.ts b/tests/cases/compiler/indexWithUndefinedAndNull.ts new file mode 100644 index 00000000000..2aeb2ee0b1d --- /dev/null +++ b/tests/cases/compiler/indexWithUndefinedAndNull.ts @@ -0,0 +1,13 @@ +// @strictNullChecks: false +interface N { + [n: number]: string; +} +interface S { + [s: string]: number; +} +let n: N; +let s: S; +let str: string = n[undefined]; +str = n[null]; +let num: number = s[undefined]; +num = s[null]; diff --git a/tests/cases/compiler/indexWithUndefinedAndNullStrictNullChecks.ts b/tests/cases/compiler/indexWithUndefinedAndNullStrictNullChecks.ts new file mode 100644 index 00000000000..f8fe0a323c6 --- /dev/null +++ b/tests/cases/compiler/indexWithUndefinedAndNullStrictNullChecks.ts @@ -0,0 +1,13 @@ +// @strictNullChecks: true +interface N { + [n: number]: string; +} +interface S { + [s: string]: number; +} +let n: N; +let s: S; +let str: string = n[undefined]; +str = n[null]; +let num: number = s[undefined]; +num = s[null]; From e2709d801dd3f48f8e4d718ecdcded2bb5dd66f6 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 28 Jul 2016 09:19:25 -0700 Subject: [PATCH 027/233] Use correct nullable terminology --- src/compiler/checker.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 38079c1bea0..c8b03876c46 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10394,11 +10394,11 @@ namespace ts { } // Check for compatible indexer types. - const allowedOptionalFlags = strictNullChecks ? 0 : TypeFlags.Null | TypeFlags.Undefined; - if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol | allowedOptionalFlags)) { + const allowedNullableFlags = strictNullChecks ? 0 : TypeFlags.Nullable; + if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol | allowedNullableFlags)) { // Try to use a number indexer. - if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.NumberLike | allowedOptionalFlags) || isForInVariableForNumericPropertyNames(node.argumentExpression)) { + if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.NumberLike | allowedNullableFlags) || isForInVariableForNumericPropertyNames(node.argumentExpression)) { const numberIndexInfo = getIndexInfoOfType(objectType, IndexKind.Number); if (numberIndexInfo) { getNodeLinks(node).resolvedIndexInfo = numberIndexInfo; From 3e3b80826fe26d1306cd1c5e9de18def227a8ec3 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 29 Jul 2016 07:54:19 -0700 Subject: [PATCH 028/233] Properly reset type guards in loops --- src/compiler/checker.ts | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 04c12eb613c..691d7451895 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7764,16 +7764,17 @@ namespace ts { } function isMatchingReference(source: Node, target: Node): boolean { - if (source.kind === target.kind) { - switch (source.kind) { - case SyntaxKind.Identifier: - return getResolvedSymbol(source) === getResolvedSymbol(target); - case SyntaxKind.ThisKeyword: - return true; - case SyntaxKind.PropertyAccessExpression: - return (source).name.text === (target).name.text && - isMatchingReference((source).expression, (target).expression); - } + switch (source.kind) { + case SyntaxKind.Identifier: + return target.kind === SyntaxKind.Identifier && getResolvedSymbol(source) === getResolvedSymbol(target) || + (target.kind === SyntaxKind.VariableDeclaration || target.kind === SyntaxKind.BindingElement) && + getExportSymbolOfValueSymbolIfExported(getResolvedSymbol(source)) === getSymbolOfNode(target); + case SyntaxKind.ThisKeyword: + return target.kind === SyntaxKind.ThisKeyword; + case SyntaxKind.PropertyAccessExpression: + return target.kind === SyntaxKind.PropertyAccessExpression && + (source).name.text === (target).name.text && + isMatchingReference((source).expression, (target).expression); } return false; } @@ -8031,6 +8032,12 @@ namespace ts { getInitialTypeOfBindingElement(node); } + function getInitialOrAssignedType(node: VariableDeclaration | BindingElement | Expression) { + return node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement ? + getInitialType(node) : + getAssignedType(node); + } + function getReferenceCandidate(node: Expression): Expression { switch (node.kind) { case SyntaxKind.ParenthesizedExpression: @@ -8153,19 +8160,9 @@ namespace ts { const node = flow.node; // Assignments only narrow the computed type if the declared type is a union type. Thus, we // only need to evaluate the assigned type if the declared type is a union type. - if ((node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement) && - reference.kind === SyntaxKind.Identifier && - getExportSymbolOfValueSymbolIfExported(getResolvedSymbol(reference)) === getSymbolOfNode(node)) { - return declaredType.flags & TypeFlags.Union ? - getAssignmentReducedType(declaredType, getInitialType(node)) : - declaredType; - } - // If the node is not a variable declaration or binding element, it is an identifier - // or a dotted name that is the target of an assignment. If we have a match, reduce - // the declared type by the assigned type. if (isMatchingReference(reference, node)) { return declaredType.flags & TypeFlags.Union ? - getAssignmentReducedType(declaredType, getAssignedType(node)) : + getAssignmentReducedType(declaredType, getInitialOrAssignedType(node)) : declaredType; } // We didn't have a direct match. However, if the reference is a dotted name, this From d9b67231e125b5dcdec526a17cf5be48b28b78c8 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 29 Jul 2016 07:54:40 -0700 Subject: [PATCH 029/233] Add regression test --- .../narrowingByDiscriminantInLoop.js | 82 ++++++++++ .../narrowingByDiscriminantInLoop.symbols | 140 ++++++++++++++++ .../narrowingByDiscriminantInLoop.types | 153 ++++++++++++++++++ .../compiler/narrowingByDiscriminantInLoop.ts | 50 ++++++ 4 files changed, 425 insertions(+) create mode 100644 tests/baselines/reference/narrowingByDiscriminantInLoop.js create mode 100644 tests/baselines/reference/narrowingByDiscriminantInLoop.symbols create mode 100644 tests/baselines/reference/narrowingByDiscriminantInLoop.types create mode 100644 tests/cases/compiler/narrowingByDiscriminantInLoop.ts diff --git a/tests/baselines/reference/narrowingByDiscriminantInLoop.js b/tests/baselines/reference/narrowingByDiscriminantInLoop.js new file mode 100644 index 00000000000..b58357cff5b --- /dev/null +++ b/tests/baselines/reference/narrowingByDiscriminantInLoop.js @@ -0,0 +1,82 @@ +//// [narrowingByDiscriminantInLoop.ts] +// Repro from #9977 + +type IDLMemberTypes = OperationMemberType | ConstantMemberType; + +interface IDLTypeDescription { + origin: string; +} + +interface InterfaceType { + members: IDLMemberTypes[]; +} + +interface OperationMemberType { + type: "operation"; + idlType: IDLTypeDescription | null; +} + +interface ConstantMemberType { + type: "const"; + idlType: string; +} + +function insertInterface(callbackType: InterfaceType) { + for (const memberType of callbackType.members) { + if (memberType.type === "const") { + memberType.idlType; // string + } + else if (memberType.type === "operation") { + memberType.idlType.origin; // string + (memberType.idlType as IDLTypeDescription); + } + } +} + +function insertInterface2(callbackType: InterfaceType) { + for (const memberType of callbackType.members) { + if (memberType.type === "operation") { + memberType.idlType.origin; // string + } + } +} + +function foo(memberType: IDLMemberTypes) { + if (memberType.type === "const") { + memberType.idlType; // string + } + else if (memberType.type === "operation") { + memberType.idlType.origin; // string + } +} + +//// [narrowingByDiscriminantInLoop.js] +// Repro from #9977 +function insertInterface(callbackType) { + for (var _i = 0, _a = callbackType.members; _i < _a.length; _i++) { + var memberType = _a[_i]; + if (memberType.type === "const") { + memberType.idlType; // string + } + else if (memberType.type === "operation") { + memberType.idlType.origin; // string + memberType.idlType; + } + } +} +function insertInterface2(callbackType) { + for (var _i = 0, _a = callbackType.members; _i < _a.length; _i++) { + var memberType = _a[_i]; + if (memberType.type === "operation") { + memberType.idlType.origin; // string + } + } +} +function foo(memberType) { + if (memberType.type === "const") { + memberType.idlType; // string + } + else if (memberType.type === "operation") { + memberType.idlType.origin; // string + } +} diff --git a/tests/baselines/reference/narrowingByDiscriminantInLoop.symbols b/tests/baselines/reference/narrowingByDiscriminantInLoop.symbols new file mode 100644 index 00000000000..30cd87f3c2b --- /dev/null +++ b/tests/baselines/reference/narrowingByDiscriminantInLoop.symbols @@ -0,0 +1,140 @@ +=== tests/cases/compiler/narrowingByDiscriminantInLoop.ts === +// Repro from #9977 + +type IDLMemberTypes = OperationMemberType | ConstantMemberType; +>IDLMemberTypes : Symbol(IDLMemberTypes, Decl(narrowingByDiscriminantInLoop.ts, 0, 0)) +>OperationMemberType : Symbol(OperationMemberType, Decl(narrowingByDiscriminantInLoop.ts, 10, 1)) +>ConstantMemberType : Symbol(ConstantMemberType, Decl(narrowingByDiscriminantInLoop.ts, 15, 1)) + +interface IDLTypeDescription { +>IDLTypeDescription : Symbol(IDLTypeDescription, Decl(narrowingByDiscriminantInLoop.ts, 2, 63)) + + origin: string; +>origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) +} + +interface InterfaceType { +>InterfaceType : Symbol(InterfaceType, Decl(narrowingByDiscriminantInLoop.ts, 6, 1)) + + members: IDLMemberTypes[]; +>members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 8, 25)) +>IDLMemberTypes : Symbol(IDLMemberTypes, Decl(narrowingByDiscriminantInLoop.ts, 0, 0)) +} + +interface OperationMemberType { +>OperationMemberType : Symbol(OperationMemberType, Decl(narrowingByDiscriminantInLoop.ts, 10, 1)) + + type: "operation"; +>type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31)) + + idlType: IDLTypeDescription | null; +>idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) +>IDLTypeDescription : Symbol(IDLTypeDescription, Decl(narrowingByDiscriminantInLoop.ts, 2, 63)) +} + +interface ConstantMemberType { +>ConstantMemberType : Symbol(ConstantMemberType, Decl(narrowingByDiscriminantInLoop.ts, 15, 1)) + + type: "const"; +>type : Symbol(ConstantMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) + + idlType: string; +>idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 18, 18)) +} + +function insertInterface(callbackType: InterfaceType) { +>insertInterface : Symbol(insertInterface, Decl(narrowingByDiscriminantInLoop.ts, 20, 1)) +>callbackType : Symbol(callbackType, Decl(narrowingByDiscriminantInLoop.ts, 22, 25)) +>InterfaceType : Symbol(InterfaceType, Decl(narrowingByDiscriminantInLoop.ts, 6, 1)) + + for (const memberType of callbackType.members) { +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 23, 14)) +>callbackType.members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 8, 25)) +>callbackType : Symbol(callbackType, Decl(narrowingByDiscriminantInLoop.ts, 22, 25)) +>members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 8, 25)) + + if (memberType.type === "const") { +>memberType.type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31), Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 23, 14)) +>type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31), Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) + + memberType.idlType; // string +>memberType.idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 18, 18)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 23, 14)) +>idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 18, 18)) + } + else if (memberType.type === "operation") { +>memberType.type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 23, 14)) +>type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31)) + + memberType.idlType.origin; // string +>memberType.idlType.origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) +>memberType.idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 23, 14)) +>idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) +>origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) + + (memberType.idlType as IDLTypeDescription); +>memberType.idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 23, 14)) +>idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) +>IDLTypeDescription : Symbol(IDLTypeDescription, Decl(narrowingByDiscriminantInLoop.ts, 2, 63)) + } + } +} + +function insertInterface2(callbackType: InterfaceType) { +>insertInterface2 : Symbol(insertInterface2, Decl(narrowingByDiscriminantInLoop.ts, 32, 1)) +>callbackType : Symbol(callbackType, Decl(narrowingByDiscriminantInLoop.ts, 34, 26)) +>InterfaceType : Symbol(InterfaceType, Decl(narrowingByDiscriminantInLoop.ts, 6, 1)) + + for (const memberType of callbackType.members) { +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 35, 14)) +>callbackType.members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 8, 25)) +>callbackType : Symbol(callbackType, Decl(narrowingByDiscriminantInLoop.ts, 34, 26)) +>members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 8, 25)) + + if (memberType.type === "operation") { +>memberType.type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31), Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 35, 14)) +>type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31), Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) + + memberType.idlType.origin; // string +>memberType.idlType.origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) +>memberType.idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 35, 14)) +>idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) +>origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) + } + } +} + +function foo(memberType: IDLMemberTypes) { +>foo : Symbol(foo, Decl(narrowingByDiscriminantInLoop.ts, 40, 1)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 42, 13)) +>IDLMemberTypes : Symbol(IDLMemberTypes, Decl(narrowingByDiscriminantInLoop.ts, 0, 0)) + + if (memberType.type === "const") { +>memberType.type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31), Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 42, 13)) +>type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31), Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) + + memberType.idlType; // string +>memberType.idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 18, 18)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 42, 13)) +>idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 18, 18)) + } + else if (memberType.type === "operation") { +>memberType.type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 42, 13)) +>type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31)) + + memberType.idlType.origin; // string +>memberType.idlType.origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) +>memberType.idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 42, 13)) +>idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) +>origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) + } +} diff --git a/tests/baselines/reference/narrowingByDiscriminantInLoop.types b/tests/baselines/reference/narrowingByDiscriminantInLoop.types new file mode 100644 index 00000000000..db8cb502a13 --- /dev/null +++ b/tests/baselines/reference/narrowingByDiscriminantInLoop.types @@ -0,0 +1,153 @@ +=== tests/cases/compiler/narrowingByDiscriminantInLoop.ts === +// Repro from #9977 + +type IDLMemberTypes = OperationMemberType | ConstantMemberType; +>IDLMemberTypes : IDLMemberTypes +>OperationMemberType : OperationMemberType +>ConstantMemberType : ConstantMemberType + +interface IDLTypeDescription { +>IDLTypeDescription : IDLTypeDescription + + origin: string; +>origin : string +} + +interface InterfaceType { +>InterfaceType : InterfaceType + + members: IDLMemberTypes[]; +>members : IDLMemberTypes[] +>IDLMemberTypes : IDLMemberTypes +} + +interface OperationMemberType { +>OperationMemberType : OperationMemberType + + type: "operation"; +>type : "operation" + + idlType: IDLTypeDescription | null; +>idlType : IDLTypeDescription +>IDLTypeDescription : IDLTypeDescription +>null : null +} + +interface ConstantMemberType { +>ConstantMemberType : ConstantMemberType + + type: "const"; +>type : "const" + + idlType: string; +>idlType : string +} + +function insertInterface(callbackType: InterfaceType) { +>insertInterface : (callbackType: InterfaceType) => void +>callbackType : InterfaceType +>InterfaceType : InterfaceType + + for (const memberType of callbackType.members) { +>memberType : IDLMemberTypes +>callbackType.members : IDLMemberTypes[] +>callbackType : InterfaceType +>members : IDLMemberTypes[] + + if (memberType.type === "const") { +>memberType.type === "const" : boolean +>memberType.type : "operation" | "const" +>memberType : IDLMemberTypes +>type : "operation" | "const" +>"const" : "const" + + memberType.idlType; // string +>memberType.idlType : string +>memberType : ConstantMemberType +>idlType : string + } + else if (memberType.type === "operation") { +>memberType.type === "operation" : boolean +>memberType.type : "operation" +>memberType : OperationMemberType +>type : "operation" +>"operation" : "operation" + + memberType.idlType.origin; // string +>memberType.idlType.origin : string +>memberType.idlType : IDLTypeDescription +>memberType : OperationMemberType +>idlType : IDLTypeDescription +>origin : string + + (memberType.idlType as IDLTypeDescription); +>(memberType.idlType as IDLTypeDescription) : IDLTypeDescription +>memberType.idlType as IDLTypeDescription : IDLTypeDescription +>memberType.idlType : IDLTypeDescription +>memberType : OperationMemberType +>idlType : IDLTypeDescription +>IDLTypeDescription : IDLTypeDescription + } + } +} + +function insertInterface2(callbackType: InterfaceType) { +>insertInterface2 : (callbackType: InterfaceType) => void +>callbackType : InterfaceType +>InterfaceType : InterfaceType + + for (const memberType of callbackType.members) { +>memberType : IDLMemberTypes +>callbackType.members : IDLMemberTypes[] +>callbackType : InterfaceType +>members : IDLMemberTypes[] + + if (memberType.type === "operation") { +>memberType.type === "operation" : boolean +>memberType.type : "operation" | "const" +>memberType : IDLMemberTypes +>type : "operation" | "const" +>"operation" : "operation" + + memberType.idlType.origin; // string +>memberType.idlType.origin : string +>memberType.idlType : IDLTypeDescription +>memberType : OperationMemberType +>idlType : IDLTypeDescription +>origin : string + } + } +} + +function foo(memberType: IDLMemberTypes) { +>foo : (memberType: IDLMemberTypes) => void +>memberType : IDLMemberTypes +>IDLMemberTypes : IDLMemberTypes + + if (memberType.type === "const") { +>memberType.type === "const" : boolean +>memberType.type : "operation" | "const" +>memberType : IDLMemberTypes +>type : "operation" | "const" +>"const" : "const" + + memberType.idlType; // string +>memberType.idlType : string +>memberType : ConstantMemberType +>idlType : string + } + else if (memberType.type === "operation") { +>memberType.type === "operation" : boolean +>memberType.type : "operation" +>memberType : OperationMemberType +>type : "operation" +>"operation" : "operation" + + memberType.idlType.origin; // string +>memberType.idlType.origin : string +>memberType.idlType : IDLTypeDescription +>memberType : OperationMemberType +>idlType : IDLTypeDescription +>origin : string + } +} diff --git a/tests/cases/compiler/narrowingByDiscriminantInLoop.ts b/tests/cases/compiler/narrowingByDiscriminantInLoop.ts new file mode 100644 index 00000000000..d9519c57a62 --- /dev/null +++ b/tests/cases/compiler/narrowingByDiscriminantInLoop.ts @@ -0,0 +1,50 @@ +// Repro from #9977 + +type IDLMemberTypes = OperationMemberType | ConstantMemberType; + +interface IDLTypeDescription { + origin: string; +} + +interface InterfaceType { + members: IDLMemberTypes[]; +} + +interface OperationMemberType { + type: "operation"; + idlType: IDLTypeDescription | null; +} + +interface ConstantMemberType { + type: "const"; + idlType: string; +} + +function insertInterface(callbackType: InterfaceType) { + for (const memberType of callbackType.members) { + if (memberType.type === "const") { + memberType.idlType; // string + } + else if (memberType.type === "operation") { + memberType.idlType.origin; // string + (memberType.idlType as IDLTypeDescription); + } + } +} + +function insertInterface2(callbackType: InterfaceType) { + for (const memberType of callbackType.members) { + if (memberType.type === "operation") { + memberType.idlType.origin; // string + } + } +} + +function foo(memberType: IDLMemberTypes) { + if (memberType.type === "const") { + memberType.idlType; // string + } + else if (memberType.type === "operation") { + memberType.idlType.origin; // string + } +} \ No newline at end of file From 97bbbd729e049ee644e954f4dff0c3756a0677db Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 28 Jul 2016 07:20:51 -0700 Subject: [PATCH 030/233] Introduce the `EntityNameExpression` type --- src/compiler/checker.ts | 102 ++++++++++-------- src/compiler/declarationEmitter.ts | 4 +- src/compiler/types.ts | 18 +++- src/compiler/utilities.ts | 36 ++++--- .../reference/exportDefaultProperty.js | 8 ++ .../reference/exportDefaultProperty.symbols | 5 + .../reference/exportDefaultProperty.types | 6 ++ tests/cases/compiler/exportDefaultProperty.ts | 1 + 8 files changed, 113 insertions(+), 67 deletions(-) create mode 100644 tests/baselines/reference/exportDefaultProperty.js create mode 100644 tests/baselines/reference/exportDefaultProperty.symbols create mode 100644 tests/baselines/reference/exportDefaultProperty.types create mode 100644 tests/cases/compiler/exportDefaultProperty.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 04c12eb613c..6d670c70541 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -968,28 +968,39 @@ namespace ts { function checkAndReportErrorForExtendingInterface(errorLocation: Node): boolean { - let parentClassExpression = errorLocation; - while (parentClassExpression) { - const kind = parentClassExpression.kind; - if (kind === SyntaxKind.Identifier || kind === SyntaxKind.PropertyAccessExpression) { - parentClassExpression = parentClassExpression.parent; - continue; - } - if (kind === SyntaxKind.ExpressionWithTypeArguments) { - break; - } + const parentExpression = climbToSupportedExpressionWithTypeArguments(errorLocation); + if (!parentExpression) { return false; } - if (!parentClassExpression) { - return false; - } - const expression = (parentClassExpression).expression; + const expression = parentExpression.expression; + if (resolveEntityName(expression, SymbolFlags.Interface, /*ignoreErrors*/ true)) { error(errorLocation, Diagnostics.Cannot_extend_an_interface_0_Did_you_mean_implements, getTextOfNode(expression)); return true; } return false; } + /** + * Climbs up parents to a SupportedExpressionWIthTypeArguments. + * Does *not* just climb to an ExpressionWithTypeArguments; instead, ensures that this really is supported. + */ + function climbToSupportedExpressionWithTypeArguments(node: Node): SupportedExpressionWithTypeArguments | undefined { + while (node) { + switch (node.kind) { + case SyntaxKind.Identifier: + case SyntaxKind.PropertyAccessExpression: + node = node.parent; + break; + case SyntaxKind.ExpressionWithTypeArguments: + Debug.assert(isSupportedExpressionWithTypeArguments(node)); + return node; + default: + return undefined; + } + } + return undefined; + } + function checkResolvedBlockScopedVariable(result: Symbol, errorLocation: Node): void { Debug.assert((result.flags & SymbolFlags.BlockScopedVariable) !== 0); @@ -1274,7 +1285,7 @@ namespace ts { } // Resolves a qualified name and any involved aliases - function resolveEntityName(name: EntityName | Expression, meaning: SymbolFlags, ignoreErrors?: boolean, dontResolveAlias?: boolean): Symbol { + function resolveEntityName(name: EntityNameOrEntityNameExpression, meaning: SymbolFlags, ignoreErrors?: boolean, dontResolveAlias?: boolean): Symbol | undefined { if (nodeIsMissing(name)) { return undefined; } @@ -1289,7 +1300,7 @@ namespace ts { } } else if (name.kind === SyntaxKind.QualifiedName || name.kind === SyntaxKind.PropertyAccessExpression) { - const left = name.kind === SyntaxKind.QualifiedName ? (name).left : (name).expression; + const left = name.kind === SyntaxKind.QualifiedName ? (name).left : (name).expression; const right = name.kind === SyntaxKind.QualifiedName ? (name).right : (name).name; const namespace = resolveEntityName(left, SymbolFlags.Namespace, ignoreErrors); @@ -1845,7 +1856,7 @@ namespace ts { } } - function isEntityNameVisible(entityName: EntityName | Expression, enclosingDeclaration: Node): SymbolVisibilityResult { + function isEntityNameVisible(entityName: EntityNameOrEntityNameExpression, enclosingDeclaration: Node): SymbolVisibilityResult { // get symbol of the first identifier of the entityName let meaning: SymbolFlags; if (entityName.parent.kind === SyntaxKind.TypeQuery || isExpressionWithTypeArgumentsInClassExtendsClause(entityName.parent)) { @@ -5022,7 +5033,7 @@ namespace ts { return getDeclaredTypeOfSymbol(symbol); } - function getTypeReferenceName(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference): LeftHandSideExpression | EntityName { + function getTypeReferenceName(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference): EntityNameOrEntityNameExpression | undefined { switch (node.kind) { case SyntaxKind.TypeReference: return (node).typeName; @@ -5031,8 +5042,9 @@ namespace ts { case SyntaxKind.ExpressionWithTypeArguments: // We only support expressions that are simple qualified names. For other // expressions this produces undefined. - if (isSupportedExpressionWithTypeArguments(node)) { - return (node).expression; + const expr = node; + if (isSupportedExpressionWithTypeArguments(expr)) { + return expr.expression; } // fall through; @@ -5043,7 +5055,7 @@ namespace ts { function resolveTypeReferenceName( node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference, - typeReferenceName: LeftHandSideExpression | EntityName) { + typeReferenceName: EntityNameExpression | EntityName) { if (!typeReferenceName) { return unknownSymbol; @@ -5084,15 +5096,14 @@ namespace ts { const typeReferenceName = getTypeReferenceName(node); symbol = resolveTypeReferenceName(node, typeReferenceName); type = getTypeReferenceType(node, symbol); - - links.resolvedSymbol = symbol; - links.resolvedType = type; } else { // We only support expressions that are simple qualified names. For other expressions this produces undefined. - const typeNameOrExpression = node.kind === SyntaxKind.TypeReference ? (node).typeName : - isSupportedExpressionWithTypeArguments(node) ? (node).expression : - undefined; + const typeNameOrExpression: EntityNameOrEntityNameExpression = node.kind === SyntaxKind.TypeReference + ? (node).typeName + : isSupportedExpressionWithTypeArguments(node) + ? (node).expression + : undefined; symbol = typeNameOrExpression && resolveEntityName(typeNameOrExpression, SymbolFlags.Type) || unknownSymbol; type = symbol === unknownSymbol ? unknownType : symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface) ? getTypeFromClassOrInterfaceReference(node, symbol) : @@ -16951,20 +16962,21 @@ namespace ts { } } - function getFirstIdentifier(node: EntityName | Expression): Identifier { - while (true) { - if (node.kind === SyntaxKind.QualifiedName) { - node = (node).left; - } - else if (node.kind === SyntaxKind.PropertyAccessExpression) { - node = (node).expression; - } - else { - break; - } + function getFirstIdentifier(node: EntityNameOrEntityNameExpression): Identifier { + switch (node.kind) { + case SyntaxKind.Identifier: + return node; + case SyntaxKind.QualifiedName: + do { + node = (node).left; + } while (node.kind !== SyntaxKind.Identifier); + return node; + case SyntaxKind.PropertyAccessExpression: + do { + node = (node).expression; + } while (node.kind !== SyntaxKind.Identifier); + return node; } - Debug.assert(node.kind === SyntaxKind.Identifier); - return node; } function checkExternalImportOrExportDeclaration(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration): boolean { @@ -17663,7 +17675,7 @@ namespace ts { return getLeftSideOfImportEqualsOrExportAssignment(node) !== undefined; } - function getSymbolOfEntityNameOrPropertyAccessExpression(entityName: EntityName | PropertyAccessExpression): Symbol { + function getSymbolOfEntityNameOrPropertyAccessExpression(entityName: EntityName | PropertyAccessExpression): Symbol | undefined { if (isDeclarationName(entityName)) { return getSymbolOfNode(entityName.parent); } @@ -17682,8 +17694,8 @@ namespace ts { } } - if (entityName.parent.kind === SyntaxKind.ExportAssignment) { - return resolveEntityName(entityName, + if (entityName.parent.kind === SyntaxKind.ExportAssignment && isEntityNameExpression(entityName)) { + return resolveEntityName(entityName, /*all meanings*/ SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias); } @@ -17697,7 +17709,7 @@ namespace ts { } if (isRightSideOfQualifiedNameOrPropertyAccess(entityName)) { - entityName = entityName.parent; + entityName = entityName.parent; } if (isHeritageClauseElementIdentifier(entityName)) { @@ -18410,7 +18422,7 @@ namespace ts { }; // defined here to avoid outer scope pollution - function getTypeReferenceDirectivesForEntityName(node: EntityName | PropertyAccessExpression): string[] { + function getTypeReferenceDirectivesForEntityName(node: EntityNameOrEntityNameExpression): string[] { // program does not have any files with type reference directives - bail out if (!fileToDirective) { return undefined; diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index d93a8a0aed0..220244c55af 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -441,7 +441,7 @@ namespace ts { } } - function emitEntityName(entityName: EntityName | PropertyAccessExpression) { + function emitEntityName(entityName: EntityNameOrEntityNameExpression) { const visibilityResult = resolver.isEntityNameVisible(entityName, // Aliases can be written asynchronously so use correct enclosing declaration entityName.parent.kind === SyntaxKind.ImportEqualsDeclaration ? entityName.parent : enclosingDeclaration); @@ -454,7 +454,7 @@ namespace ts { function emitExpressionWithTypeArguments(node: ExpressionWithTypeArguments) { if (isSupportedExpressionWithTypeArguments(node)) { Debug.assert(node.expression.kind === SyntaxKind.Identifier || node.expression.kind === SyntaxKind.PropertyAccessExpression); - emitEntityName(node.expression); + emitEntityName(node.expression); if (node.typeArguments) { write("<"); emitCommaList(node.typeArguments, emitType); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 072209aa496..983ed25c848 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -982,13 +982,19 @@ namespace ts { multiLine?: boolean; } + export type EntityNameExpression = Identifier | PropertyAccessEntityNameExpression; + export type EntityNameOrEntityNameExpression = EntityName | EntityNameExpression; + // @kind(SyntaxKind.PropertyAccessExpression) export interface PropertyAccessExpression extends MemberExpression, Declaration { expression: LeftHandSideExpression; name: Identifier; } - - export type IdentifierOrPropertyAccess = Identifier | PropertyAccessExpression; + /** Brand for a PropertyAccessExpression which, like a QualifiedName, consists of a sequence of identifiers separated by dots. */ + export interface PropertyAccessEntityNameExpression extends PropertyAccessExpression { + _propertyAccessExpressionLikeQualifiedNameBrand?: any; + expression: EntityNameExpression; + } // @kind(SyntaxKind.ElementAccessExpression) export interface ElementAccessExpression extends MemberExpression { @@ -1008,6 +1014,10 @@ namespace ts { expression: LeftHandSideExpression; typeArguments?: NodeArray; } + export interface SupportedExpressionWithTypeArguments extends ExpressionWithTypeArguments { + _supportedExpressionWithTypeArgumentsBrand?: any; + expression: EntityNameExpression; + } // @kind(SyntaxKind.NewExpression) export interface NewExpression extends CallExpression, PrimaryExpression { } @@ -2021,7 +2031,7 @@ namespace ts { writeTypeOfExpression(expr: Expression, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void; writeBaseConstructorTypeOfClass(node: ClassLikeDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void; isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags): SymbolAccessibilityResult; - isEntityNameVisible(entityName: EntityName | Expression, enclosingDeclaration: Node): SymbolVisibilityResult; + isEntityNameVisible(entityName: EntityNameOrEntityNameExpression, enclosingDeclaration: Node): SymbolVisibilityResult; // Returns the constant value this property access resolves to, or 'undefined' for a non-constant getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number; getReferencedValueDeclaration(reference: Identifier): Declaration; @@ -2030,7 +2040,7 @@ namespace ts { moduleExportsSomeValue(moduleReferenceExpression: Expression): boolean; isArgumentsLocalBinding(node: Identifier): boolean; getExternalModuleFileFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration): SourceFile; - getTypeReferenceDirectivesForEntityName(name: EntityName | PropertyAccessExpression): string[]; + getTypeReferenceDirectivesForEntityName(name: EntityNameOrEntityNameExpression): string[]; getTypeReferenceDirectivesForSymbol(symbol: Symbol, meaning?: SymbolFlags): string[]; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 4ae0005e9c5..acace78396b 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1033,14 +1033,14 @@ namespace ts { && (node).expression.kind === SyntaxKind.SuperKeyword; } - - export function getEntityNameFromTypeNode(node: TypeNode): EntityName | Expression { + export function getEntityNameFromTypeNode(node: TypeNode): EntityNameOrEntityNameExpression { if (node) { switch (node.kind) { case SyntaxKind.TypeReference: return (node).typeName; case SyntaxKind.ExpressionWithTypeArguments: - return (node).expression; + Debug.assert(isSupportedExpressionWithTypeArguments(node)); + return (node).expression; case SyntaxKind.Identifier: case SyntaxKind.QualifiedName: return (node); @@ -2680,24 +2680,28 @@ namespace ts { isClassLike(node.parent.parent); } - // Returns false if this heritage clause element's expression contains something unsupported - // (i.e. not a name or dotted name). - export function isSupportedExpressionWithTypeArguments(node: ExpressionWithTypeArguments): boolean { - return isSupportedExpressionWithTypeArgumentsRest(node.expression); + export function isSupportedExpressionWithTypeArguments(node: ExpressionWithTypeArguments): node is SupportedExpressionWithTypeArguments { + return isEntityNameExpression(node.expression); } - function isSupportedExpressionWithTypeArgumentsRest(node: Expression): boolean { - if (node.kind === SyntaxKind.Identifier) { - return true; - } - else if (isPropertyAccessExpression(node)) { - return isSupportedExpressionWithTypeArgumentsRest(node.expression); - } - else { - return false; + export function isEntityNameExpression(node: Expression): node is EntityNameExpression { + for (; ; ) { + switch (node.kind) { + case SyntaxKind.Identifier: + return true; + case SyntaxKind.PropertyAccessExpression: + node = (node).expression; + break; + default: + return false; + } } } + export function isPropertyAccessAnEntityNameExpression(node: PropertyAccessExpression): node is PropertyAccessEntityNameExpression { + return isEntityNameExpression(node.expression); + } + export function isRightSideOfQualifiedNameOrPropertyAccess(node: Node) { return (node.parent.kind === SyntaxKind.QualifiedName && (node.parent).right === node) || (node.parent.kind === SyntaxKind.PropertyAccessExpression && (node.parent).name === node); diff --git a/tests/baselines/reference/exportDefaultProperty.js b/tests/baselines/reference/exportDefaultProperty.js new file mode 100644 index 00000000000..efb4ee8bff3 --- /dev/null +++ b/tests/baselines/reference/exportDefaultProperty.js @@ -0,0 +1,8 @@ +//// [exportDefaultProperty.ts] +export default "".length + + +//// [exportDefaultProperty.js] +"use strict"; +exports.__esModule = true; +exports["default"] = "".length; diff --git a/tests/baselines/reference/exportDefaultProperty.symbols b/tests/baselines/reference/exportDefaultProperty.symbols new file mode 100644 index 00000000000..2bc00e48fec --- /dev/null +++ b/tests/baselines/reference/exportDefaultProperty.symbols @@ -0,0 +1,5 @@ +=== tests/cases/compiler/exportDefaultProperty.ts === +export default "".length +>"".length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) + diff --git a/tests/baselines/reference/exportDefaultProperty.types b/tests/baselines/reference/exportDefaultProperty.types new file mode 100644 index 00000000000..82e6277b292 --- /dev/null +++ b/tests/baselines/reference/exportDefaultProperty.types @@ -0,0 +1,6 @@ +=== tests/cases/compiler/exportDefaultProperty.ts === +export default "".length +>"".length : number +>"" : string +>length : number + diff --git a/tests/cases/compiler/exportDefaultProperty.ts b/tests/cases/compiler/exportDefaultProperty.ts new file mode 100644 index 00000000000..4df2eb692a8 --- /dev/null +++ b/tests/cases/compiler/exportDefaultProperty.ts @@ -0,0 +1 @@ +export default "".length From f9fd4967af519473bdf32395df677b2bc966c64e Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Fri, 29 Jul 2016 08:06:04 -0700 Subject: [PATCH 031/233] Allow `export =` and `export default` to alias any EntityNameExpression, not just identifiers. --- src/compiler/binder.ts | 13 ++- src/compiler/checker.ts | 4 +- src/compiler/core.ts | 15 ++- src/compiler/utilities.ts | 10 +- .../reference/exportDefaultProperty.js | 76 +++++++++++++- .../reference/exportDefaultProperty.symbols | 93 ++++++++++++++++- .../reference/exportDefaultProperty.types | 99 ++++++++++++++++++- .../reference/exportEqualsProperty.js | 72 ++++++++++++++ .../reference/exportEqualsProperty.symbols | 87 ++++++++++++++++ .../reference/exportEqualsProperty.types | 92 +++++++++++++++++ tests/cases/compiler/exportDefaultProperty.ts | 40 +++++++- tests/cases/compiler/exportEqualsProperty.ts | 38 +++++++ 12 files changed, 613 insertions(+), 26 deletions(-) create mode 100644 tests/baselines/reference/exportEqualsProperty.js create mode 100644 tests/baselines/reference/exportEqualsProperty.symbols create mode 100644 tests/baselines/reference/exportEqualsProperty.types create mode 100644 tests/cases/compiler/exportEqualsProperty.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 8d8666a67ab..502cb39e8fe 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1887,18 +1887,17 @@ namespace ts { } function bindExportAssignment(node: ExportAssignment | BinaryExpression) { - const boundExpression = node.kind === SyntaxKind.ExportAssignment ? (node).expression : (node).right; if (!container.symbol || !container.symbol.exports) { // Export assignment in some sort of block construct bindAnonymousDeclaration(node, SymbolFlags.Alias, getDeclarationName(node)); } - else if (boundExpression.kind === SyntaxKind.Identifier && node.kind === SyntaxKind.ExportAssignment) { - // An export default clause with an identifier exports all meanings of that identifier - declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.Alias, SymbolFlags.PropertyExcludes | SymbolFlags.AliasExcludes); - } else { - // An export default clause with an expression exports a value - declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes | SymbolFlags.AliasExcludes); + const flags = node.kind === SyntaxKind.ExportAssignment && exportAssignmentIsAlias(node) + // An export default clause with an EntityNameExpression exports all meanings of that identifier + ? SymbolFlags.Alias + // An export default clause with any other expression exports a value + : SymbolFlags.Property; + declareSymbol(container.symbol.exports, container.symbol, node, flags, SymbolFlags.PropertyExcludes | SymbolFlags.AliasExcludes); } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6d670c70541..21d74bb24b8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1044,7 +1044,7 @@ namespace ts { } function getDeclarationOfAliasSymbol(symbol: Symbol): Declaration { - return forEach(symbol.declarations, d => isAliasSymbolDeclaration(d) ? d : undefined); + return find(symbol.declarations, d => isAliasSymbolDeclaration(d) ? d : undefined); } function getTargetOfImportEqualsDeclaration(node: ImportEqualsDeclaration): Symbol { @@ -1175,7 +1175,7 @@ namespace ts { } function getTargetOfExportAssignment(node: ExportAssignment): Symbol { - return resolveEntityName(node.expression, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace); + return resolveEntityName(node.expression, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace); } function getTargetOfAliasDeclaration(node: Declaration): Symbol { diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 709a331e022..cd332b6bef3 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -81,7 +81,7 @@ namespace ts { * returns a truthy value, then returns that value. * If no such value is found, the callback is applied to each element of array and undefined is returned. */ - export function forEach(array: T[], callback: (element: T, index: number) => U): U { + export function forEach(array: T[] | undefined, callback: (element: T, index: number) => U | undefined): U | undefined { if (array) { for (let i = 0, len = array.length; i < len; i++) { const result = callback(array[i], i); @@ -93,6 +93,17 @@ namespace ts { return undefined; } + /** Like `forEach`, but assumes existence of array and fails if no truthy value is found. */ + export function find(array: T[], callback: (element: T, index: number) => U | undefined): U { + for (let i = 0, len = array.length; i < len; i++) { + const result = callback(array[i], i); + if (result) { + return result; + } + } + Debug.fail(); + } + export function contains(array: T[], value: T): boolean { if (array) { for (const v of array) { @@ -941,7 +952,7 @@ namespace ts { * [^./] # matches everything up to the first . character (excluding directory seperators) * (\\.(?!min\\.js$))? # matches . characters but not if they are part of the .min.js file extension */ - const singleAsteriskRegexFragmentFiles = "([^./]|(\\.(?!min\\.js$))?)*"; + const singleAsteriskRegexFragmentFiles = "([^./]|(\\.(?!min\\.js$))?)*"; const singleAsteriskRegexFragmentOther = "[^/]*"; export function getRegularExpressionForWildcard(specs: string[], basePath: string, usage: "files" | "directories" | "exclude") { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index acace78396b..466cf8c2b77 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1693,8 +1693,8 @@ namespace ts { // import * as from ... // import { x as } from ... // export { x as } from ... - // export = ... - // export default ... + // export = + // export default export function isAliasSymbolDeclaration(node: Node): boolean { return node.kind === SyntaxKind.ImportEqualsDeclaration || node.kind === SyntaxKind.NamespaceExportDeclaration || @@ -1702,7 +1702,11 @@ namespace ts { node.kind === SyntaxKind.NamespaceImport || node.kind === SyntaxKind.ImportSpecifier || node.kind === SyntaxKind.ExportSpecifier || - node.kind === SyntaxKind.ExportAssignment && (node).expression.kind === SyntaxKind.Identifier; + node.kind === SyntaxKind.ExportAssignment && exportAssignmentIsAlias(node); + } + + export function exportAssignmentIsAlias(node: ExportAssignment): boolean { + return isEntityNameExpression(node.expression); } export function getClassExtendsHeritageClauseElement(node: ClassLikeDeclaration | InterfaceDeclaration) { diff --git a/tests/baselines/reference/exportDefaultProperty.js b/tests/baselines/reference/exportDefaultProperty.js index efb4ee8bff3..10ba6d6419a 100644 --- a/tests/baselines/reference/exportDefaultProperty.js +++ b/tests/baselines/reference/exportDefaultProperty.js @@ -1,8 +1,76 @@ -//// [exportDefaultProperty.ts] -export default "".length +//// [tests/cases/compiler/exportDefaultProperty.ts] //// + +//// [declarations.d.ts] +// This test is just like exportEqualsProperty, but with `export default`. + +declare namespace foo.bar { + export type X = number; + export const X: number; +} + +declare module "foobar" { + export default foo.bar; +} + +declare module "foobarx" { + export default foo.bar.X; +} + +//// [a.ts] +namespace A { + export class B { constructor(b: number) {} } + export namespace B { export const b: number = 0; } +} +export default A.B; + +//// [b.ts] +export default "foo".length; + +//// [index.ts] +/// +import fooBar from "foobar"; +import X = fooBar.X; +import X2 from "foobarx"; +const x: X = X; +const x2: X2 = X2; + +import B from "./a"; +const b: B = new B(B.b); + +import fooLength from "./b"; +fooLength + 1; -//// [exportDefaultProperty.js] +//// [a.js] +"use strict"; +var A; +(function (A) { + var B = (function () { + function B(b) { + } + return B; + }()); + A.B = B; + var B; + (function (B) { + B.b = 0; + })(B = A.B || (A.B = {})); +})(A || (A = {})); +exports.__esModule = true; +exports["default"] = A.B; +//// [b.js] "use strict"; exports.__esModule = true; -exports["default"] = "".length; +exports["default"] = "foo".length; +//// [index.js] +"use strict"; +/// +var foobar_1 = require("foobar"); +var X = foobar_1["default"].X; +var foobarx_1 = require("foobarx"); +var x = X; +var x2 = foobarx_1["default"]; +var a_1 = require("./a"); +var b = new a_1["default"](a_1["default"].b); +var b_1 = require("./b"); +b_1["default"] + 1; diff --git a/tests/baselines/reference/exportDefaultProperty.symbols b/tests/baselines/reference/exportDefaultProperty.symbols index 2bc00e48fec..f9edcd154cc 100644 --- a/tests/baselines/reference/exportDefaultProperty.symbols +++ b/tests/baselines/reference/exportDefaultProperty.symbols @@ -1,5 +1,92 @@ -=== tests/cases/compiler/exportDefaultProperty.ts === -export default "".length ->"".length : Symbol(String.length, Decl(lib.d.ts, --, --)) +=== tests/cases/compiler/index.ts === +/// +import fooBar from "foobar"; +>fooBar : Symbol(fooBar, Decl(index.ts, 1, 6)) + +import X = fooBar.X; +>X : Symbol(X, Decl(index.ts, 1, 28)) +>fooBar : Symbol(fooBar, Decl(index.ts, 1, 6)) +>X : Symbol(fooBar.X, Decl(declarations.d.ts, 2, 27), Decl(declarations.d.ts, 4, 16)) + +import X2 from "foobarx"; +>X2 : Symbol(X2, Decl(index.ts, 3, 6)) + +const x: X = X; +>x : Symbol(x, Decl(index.ts, 4, 5)) +>X : Symbol(X, Decl(index.ts, 1, 28)) +>X : Symbol(X, Decl(index.ts, 1, 28)) + +const x2: X2 = X2; +>x2 : Symbol(x2, Decl(index.ts, 5, 5)) +>X2 : Symbol(X2, Decl(index.ts, 3, 6)) +>X2 : Symbol(X2, Decl(index.ts, 3, 6)) + +import B from "./a"; +>B : Symbol(B, Decl(index.ts, 7, 6)) + +const b: B = new B(B.b); +>b : Symbol(b, Decl(index.ts, 8, 5)) +>B : Symbol(B, Decl(index.ts, 7, 6)) +>B : Symbol(B, Decl(index.ts, 7, 6)) +>B.b : Symbol(B.b, Decl(a.ts, 2, 37)) +>B : Symbol(B, Decl(index.ts, 7, 6)) +>b : Symbol(B.b, Decl(a.ts, 2, 37)) + +import fooLength from "./b"; +>fooLength : Symbol(fooLength, Decl(index.ts, 10, 6)) + +fooLength + 1; +>fooLength : Symbol(fooLength, Decl(index.ts, 10, 6)) + +=== tests/cases/compiler/declarations.d.ts === +// This test is just like exportEqualsProperty, but with `export default`. + +declare namespace foo.bar { +>foo : Symbol(foo, Decl(declarations.d.ts, 0, 0)) +>bar : Symbol(bar, Decl(declarations.d.ts, 2, 22)) + + export type X = number; +>X : Symbol(X, Decl(declarations.d.ts, 2, 27), Decl(declarations.d.ts, 4, 16)) + + export const X: number; +>X : Symbol(X, Decl(declarations.d.ts, 2, 27), Decl(declarations.d.ts, 4, 16)) +} + +declare module "foobar" { + export default foo.bar; +>foo.bar : Symbol(default, Decl(declarations.d.ts, 2, 22)) +>foo : Symbol(foo, Decl(declarations.d.ts, 0, 0)) +>bar : Symbol(default, Decl(declarations.d.ts, 2, 22)) +} + +declare module "foobarx" { + export default foo.bar.X; +>foo.bar.X : Symbol(default, Decl(declarations.d.ts, 2, 27), Decl(declarations.d.ts, 4, 16)) +>foo.bar : Symbol(foo.bar, Decl(declarations.d.ts, 2, 22)) +>foo : Symbol(foo, Decl(declarations.d.ts, 0, 0)) +>bar : Symbol(foo.bar, Decl(declarations.d.ts, 2, 22)) +>X : Symbol(default, Decl(declarations.d.ts, 2, 27), Decl(declarations.d.ts, 4, 16)) +} + +=== tests/cases/compiler/a.ts === +namespace A { +>A : Symbol(A, Decl(a.ts, 0, 0)) + + export class B { constructor(b: number) {} } +>B : Symbol(B, Decl(a.ts, 0, 13), Decl(a.ts, 1, 48)) +>b : Symbol(b, Decl(a.ts, 1, 33)) + + export namespace B { export const b: number = 0; } +>B : Symbol(B, Decl(a.ts, 0, 13), Decl(a.ts, 1, 48)) +>b : Symbol(b, Decl(a.ts, 2, 37)) +} +export default A.B; +>A.B : Symbol(default, Decl(a.ts, 0, 13), Decl(a.ts, 1, 48)) +>A : Symbol(A, Decl(a.ts, 0, 0)) +>B : Symbol(default, Decl(a.ts, 0, 13), Decl(a.ts, 1, 48)) + +=== tests/cases/compiler/b.ts === +export default "foo".length; +>"foo".length : Symbol(String.length, Decl(lib.d.ts, --, --)) >length : Symbol(String.length, Decl(lib.d.ts, --, --)) diff --git a/tests/baselines/reference/exportDefaultProperty.types b/tests/baselines/reference/exportDefaultProperty.types index 82e6277b292..47cfabfbc16 100644 --- a/tests/baselines/reference/exportDefaultProperty.types +++ b/tests/baselines/reference/exportDefaultProperty.types @@ -1,6 +1,97 @@ -=== tests/cases/compiler/exportDefaultProperty.ts === -export default "".length ->"".length : number ->"" : string +=== tests/cases/compiler/index.ts === +/// +import fooBar from "foobar"; +>fooBar : typeof fooBar + +import X = fooBar.X; +>X : number +>fooBar : typeof fooBar +>X : number + +import X2 from "foobarx"; +>X2 : number + +const x: X = X; +>x : number +>X : number +>X : number + +const x2: X2 = X2; +>x2 : number +>X2 : number +>X2 : number + +import B from "./a"; +>B : typeof B + +const b: B = new B(B.b); +>b : B +>B : B +>new B(B.b) : B +>B : typeof B +>B.b : number +>B : typeof B +>b : number + +import fooLength from "./b"; +>fooLength : number + +fooLength + 1; +>fooLength + 1 : number +>fooLength : number +>1 : number + +=== tests/cases/compiler/declarations.d.ts === +// This test is just like exportEqualsProperty, but with `export default`. + +declare namespace foo.bar { +>foo : typeof foo +>bar : typeof bar + + export type X = number; +>X : number + + export const X: number; +>X : number +} + +declare module "foobar" { + export default foo.bar; +>foo.bar : typeof default +>foo : typeof foo +>bar : typeof default +} + +declare module "foobarx" { + export default foo.bar.X; +>foo.bar.X : number +>foo.bar : typeof foo.bar +>foo : typeof foo +>bar : typeof foo.bar +>X : number +} + +=== tests/cases/compiler/a.ts === +namespace A { +>A : typeof A + + export class B { constructor(b: number) {} } +>B : B +>b : number + + export namespace B { export const b: number = 0; } +>B : typeof B +>b : number +>0 : number +} +export default A.B; +>A.B : typeof default +>A : typeof A +>B : typeof default + +=== tests/cases/compiler/b.ts === +export default "foo".length; +>"foo".length : number +>"foo" : string >length : number diff --git a/tests/baselines/reference/exportEqualsProperty.js b/tests/baselines/reference/exportEqualsProperty.js new file mode 100644 index 00000000000..2fd8a8c8511 --- /dev/null +++ b/tests/baselines/reference/exportEqualsProperty.js @@ -0,0 +1,72 @@ +//// [tests/cases/compiler/exportEqualsProperty.ts] //// + +//// [declarations.d.ts] +// This test is just like exportDefaultProperty, but with `export =`. + +declare namespace foo.bar { + export type X = number; + export const X: number; +} + +declare module "foobar" { + export = foo.bar; +} + +declare module "foobarx" { + export = foo.bar.X; +} + +//// [a.ts] +namespace A { + export class B { constructor(b: number) {} } + export namespace B { export const b: number = 0; } +} +export = A.B; + +//// [b.ts] +export = "foo".length; + +//// [index.ts] +/// +import { X } from "foobar"; +import X2 = require("foobarx"); +const x: X = X; +const x2: X2 = X2; + +import B = require("./a"); +const b: B = new B(B.b); + +import fooLength = require("./b"); +fooLength + 1; + + +//// [a.js] +"use strict"; +var A; +(function (A) { + var B = (function () { + function B(b) { + } + return B; + }()); + A.B = B; + var B; + (function (B) { + B.b = 0; + })(B = A.B || (A.B = {})); +})(A || (A = {})); +module.exports = A.B; +//// [b.js] +"use strict"; +module.exports = "foo".length; +//// [index.js] +"use strict"; +/// +var foobar_1 = require("foobar"); +var X2 = require("foobarx"); +var x = foobar_1.X; +var x2 = X2; +var B = require("./a"); +var b = new B(B.b); +var fooLength = require("./b"); +fooLength + 1; diff --git a/tests/baselines/reference/exportEqualsProperty.symbols b/tests/baselines/reference/exportEqualsProperty.symbols new file mode 100644 index 00000000000..43c9ed32518 --- /dev/null +++ b/tests/baselines/reference/exportEqualsProperty.symbols @@ -0,0 +1,87 @@ +=== tests/cases/compiler/index.ts === +/// +import { X } from "foobar"; +>X : Symbol(X, Decl(index.ts, 1, 8)) + +import X2 = require("foobarx"); +>X2 : Symbol(X2, Decl(index.ts, 1, 27)) + +const x: X = X; +>x : Symbol(x, Decl(index.ts, 3, 5)) +>X : Symbol(X, Decl(index.ts, 1, 8)) +>X : Symbol(X, Decl(index.ts, 1, 8)) + +const x2: X2 = X2; +>x2 : Symbol(x2, Decl(index.ts, 4, 5)) +>X2 : Symbol(X2, Decl(index.ts, 1, 27)) +>X2 : Symbol(X2, Decl(index.ts, 1, 27)) + +import B = require("./a"); +>B : Symbol(B, Decl(index.ts, 4, 18)) + +const b: B = new B(B.b); +>b : Symbol(b, Decl(index.ts, 7, 5)) +>B : Symbol(B, Decl(index.ts, 4, 18)) +>B : Symbol(B, Decl(index.ts, 4, 18)) +>B.b : Symbol(B.b, Decl(a.ts, 2, 37)) +>B : Symbol(B, Decl(index.ts, 4, 18)) +>b : Symbol(B.b, Decl(a.ts, 2, 37)) + +import fooLength = require("./b"); +>fooLength : Symbol(fooLength, Decl(index.ts, 7, 24)) + +fooLength + 1; +>fooLength : Symbol(fooLength, Decl(index.ts, 7, 24)) + +=== tests/cases/compiler/declarations.d.ts === +// This test is just like exportDefaultProperty, but with `export =`. + +declare namespace foo.bar { +>foo : Symbol(foo, Decl(declarations.d.ts, 0, 0)) +>bar : Symbol(bar, Decl(declarations.d.ts, 2, 22)) + + export type X = number; +>X : Symbol(X, Decl(declarations.d.ts, 2, 27), Decl(declarations.d.ts, 4, 16)) + + export const X: number; +>X : Symbol(X, Decl(declarations.d.ts, 2, 27), Decl(declarations.d.ts, 4, 16)) +} + +declare module "foobar" { + export = foo.bar; +>foo.bar : Symbol(foo.bar, Decl(declarations.d.ts, 2, 22)) +>foo : Symbol(foo, Decl(declarations.d.ts, 0, 0)) +>bar : Symbol(foo.bar, Decl(declarations.d.ts, 2, 22)) +} + +declare module "foobarx" { + export = foo.bar.X; +>foo.bar.X : Symbol(foo.bar.X, Decl(declarations.d.ts, 2, 27), Decl(declarations.d.ts, 4, 16)) +>foo.bar : Symbol(foo.bar, Decl(declarations.d.ts, 2, 22)) +>foo : Symbol(foo, Decl(declarations.d.ts, 0, 0)) +>bar : Symbol(foo.bar, Decl(declarations.d.ts, 2, 22)) +>X : Symbol(foo.bar.X, Decl(declarations.d.ts, 2, 27), Decl(declarations.d.ts, 4, 16)) +} + +=== tests/cases/compiler/a.ts === +namespace A { +>A : Symbol(A, Decl(a.ts, 0, 0)) + + export class B { constructor(b: number) {} } +>B : Symbol(B, Decl(a.ts, 0, 13), Decl(a.ts, 1, 48)) +>b : Symbol(b, Decl(a.ts, 1, 33)) + + export namespace B { export const b: number = 0; } +>B : Symbol(B, Decl(a.ts, 0, 13), Decl(a.ts, 1, 48)) +>b : Symbol(b, Decl(a.ts, 2, 37)) +} +export = A.B; +>A.B : Symbol(A.B, Decl(a.ts, 0, 13), Decl(a.ts, 1, 48)) +>A : Symbol(A, Decl(a.ts, 0, 0)) +>B : Symbol(A.B, Decl(a.ts, 0, 13), Decl(a.ts, 1, 48)) + +=== tests/cases/compiler/b.ts === +export = "foo".length; +>"foo".length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) + diff --git a/tests/baselines/reference/exportEqualsProperty.types b/tests/baselines/reference/exportEqualsProperty.types new file mode 100644 index 00000000000..a92af53b12b --- /dev/null +++ b/tests/baselines/reference/exportEqualsProperty.types @@ -0,0 +1,92 @@ +=== tests/cases/compiler/index.ts === +/// +import { X } from "foobar"; +>X : number + +import X2 = require("foobarx"); +>X2 : number + +const x: X = X; +>x : number +>X : number +>X : number + +const x2: X2 = X2; +>x2 : number +>X2 : number +>X2 : number + +import B = require("./a"); +>B : typeof B + +const b: B = new B(B.b); +>b : B +>B : B +>new B(B.b) : B +>B : typeof B +>B.b : number +>B : typeof B +>b : number + +import fooLength = require("./b"); +>fooLength : number + +fooLength + 1; +>fooLength + 1 : number +>fooLength : number +>1 : number + +=== tests/cases/compiler/declarations.d.ts === +// This test is just like exportDefaultProperty, but with `export =`. + +declare namespace foo.bar { +>foo : typeof foo +>bar : typeof bar + + export type X = number; +>X : number + + export const X: number; +>X : number +} + +declare module "foobar" { + export = foo.bar; +>foo.bar : typeof foo.bar +>foo : typeof foo +>bar : typeof foo.bar +} + +declare module "foobarx" { + export = foo.bar.X; +>foo.bar.X : number +>foo.bar : typeof foo.bar +>foo : typeof foo +>bar : typeof foo.bar +>X : number +} + +=== tests/cases/compiler/a.ts === +namespace A { +>A : typeof A + + export class B { constructor(b: number) {} } +>B : B +>b : number + + export namespace B { export const b: number = 0; } +>B : typeof B +>b : number +>0 : number +} +export = A.B; +>A.B : typeof A.B +>A : typeof A +>B : typeof A.B + +=== tests/cases/compiler/b.ts === +export = "foo".length; +>"foo".length : number +>"foo" : string +>length : number + diff --git a/tests/cases/compiler/exportDefaultProperty.ts b/tests/cases/compiler/exportDefaultProperty.ts index 4df2eb692a8..4a4b4139025 100644 --- a/tests/cases/compiler/exportDefaultProperty.ts +++ b/tests/cases/compiler/exportDefaultProperty.ts @@ -1 +1,39 @@ -export default "".length +// This test is just like exportEqualsProperty, but with `export default`. + +// @Filename: declarations.d.ts +declare namespace foo.bar { + export type X = number; + export const X: number; +} + +declare module "foobar" { + export default foo.bar; +} + +declare module "foobarx" { + export default foo.bar.X; +} + +// @Filename: a.ts +namespace A { + export class B { constructor(b: number) {} } + export namespace B { export const b: number = 0; } +} +export default A.B; + +// @Filename: b.ts +export default "foo".length; + +// @Filename: index.ts +/// +import fooBar from "foobar"; +import X = fooBar.X; +import X2 from "foobarx"; +const x: X = X; +const x2: X2 = X2; + +import B from "./a"; +const b: B = new B(B.b); + +import fooLength from "./b"; +fooLength + 1; diff --git a/tests/cases/compiler/exportEqualsProperty.ts b/tests/cases/compiler/exportEqualsProperty.ts new file mode 100644 index 00000000000..0d14815a5bd --- /dev/null +++ b/tests/cases/compiler/exportEqualsProperty.ts @@ -0,0 +1,38 @@ +// This test is just like exportDefaultProperty, but with `export =`. + +// @Filename: declarations.d.ts +declare namespace foo.bar { + export type X = number; + export const X: number; +} + +declare module "foobar" { + export = foo.bar; +} + +declare module "foobarx" { + export = foo.bar.X; +} + +// @Filename: a.ts +namespace A { + export class B { constructor(b: number) {} } + export namespace B { export const b: number = 0; } +} +export = A.B; + +// @Filename: b.ts +export = "foo".length; + +// @Filename: index.ts +/// +import { X } from "foobar"; +import X2 = require("foobarx"); +const x: X = X; +const x2: X2 = X2; + +import B = require("./a"); +const b: B = new B(B.b); + +import fooLength = require("./b"); +fooLength + 1; From a485aab055244b56cd242e7c76d6e3d2ed3927f2 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 27 Jul 2016 07:26:28 -0700 Subject: [PATCH 032/233] Lint tests helper files --- Gulpfile.ts | 1 + Jakefile.js | 3 +- tests/perfsys.ts | 53 +++++------ tests/perftsc.ts | 12 +-- tests/webTestServer.ts | 203 +++++++++++++++++++++------------------- tests/webhost/webtsc.ts | 20 ++-- 6 files changed, 150 insertions(+), 142 deletions(-) diff --git a/Gulpfile.ts b/Gulpfile.ts index a070c80da64..6c91ef52cbb 100644 --- a/Gulpfile.ts +++ b/Gulpfile.ts @@ -956,6 +956,7 @@ const lintTargets = [ "src/server/**/*.ts", "scripts/tslint/**/*.ts", "src/services/**/*.ts", + "tests/*.ts", "tests/webhost/*.ts" // Note: does *not* descend recursively ]; diff --git a/Jakefile.js b/Jakefile.js index f63275284d0..174be5e702f 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -1041,7 +1041,8 @@ var lintTargets = compilerSources .concat(serverCoreSources) .concat(tslintRulesFiles) .concat(servicesSources) - .concat(["Gulpfile.ts"]); + .concat(["Gulpfile.ts"]) + .concat([nodeServerInFile, perftscPath, "tests/perfsys.ts", webhostPath]); desc("Runs tslint on the compiler sources. Optional arguments are: f[iles]=regex"); diff --git a/tests/perfsys.ts b/tests/perfsys.ts index 143d0d637d4..abe09c88a0f 100644 --- a/tests/perfsys.ts +++ b/tests/perfsys.ts @@ -1,8 +1,7 @@ /// /// -module perftest { - +namespace perftest { interface IOLog { resolvePath: ts.Map; fileNames: string[]; @@ -12,20 +11,20 @@ module perftest { getOut(): string; } - export var readFile = ts.sys.readFile; - var writeFile = ts.sys.writeFile; - export var write = ts.sys.write; - var resolvePath = ts.sys.resolvePath; - export var getExecutingFilePath = ts.sys.getExecutingFilePath; - export var getCurrentDirectory = ts.sys.getCurrentDirectory; - var exit = ts.sys.exit; + export const readFile = ts.sys.readFile; + const writeFile = ts.sys.writeFile; + export const write = ts.sys.write; + const resolvePath = ts.sys.resolvePath; + export const getExecutingFilePath = ts.sys.getExecutingFilePath; + export const getCurrentDirectory = ts.sys.getCurrentDirectory; + // const exit = ts.sys.exit; - var args = ts.sys.args; + const args = ts.sys.args; // augment sys so first ts.executeCommandLine call will be finish silently ts.sys.write = (s: string) => { }; ts.sys.exit = (code: number) => { }; - ts.sys.args = [] + ts.sys.args = []; export function restoreSys() { ts.sys.args = args; @@ -44,19 +43,19 @@ module perftest { return args.slice(1); } - var resolvePathLog: ts.Map = {}; - + const resolvePathLog: ts.Map = {}; + export function interceptIO() { ts.sys.resolvePath = (s) => { - var result = resolvePath(s); + const result = resolvePath(s); resolvePathLog[s] = result; return result; }; } export function writeIOLog(fileNames: string[]) { - var path = args[1]; - var log: IOLog = { + const path = args[1]; + const log: IOLog = { fileNames: fileNames, resolvePath: resolvePathLog }; @@ -65,36 +64,36 @@ module perftest { } export function prepare(): IO { - var log = JSON.parse(readFile(args[0])); + const log = JSON.parse(readFile(args[0])); + + const files: ts.Map = {}; + log.fileNames.forEach(f => { files[f] = readFile(f); }); - var files: ts.Map = {}; - log.fileNames.forEach(f => { files[f] = readFile(f); }) - ts.sys.createDirectory = (s: string) => { }; ts.sys.directoryExists = (s: string) => true; ts.sys.fileExists = (s: string) => true; - var currentDirectory = ts.sys.getCurrentDirectory(); + const currentDirectory = ts.sys.getCurrentDirectory(); ts.sys.getCurrentDirectory = () => currentDirectory; - var executingFilePath = ts.sys.getExecutingFilePath(); + const executingFilePath = ts.sys.getExecutingFilePath(); ts.sys.getExecutingFilePath = () => executingFilePath; ts.sys.readFile = (s: string) => { return files[s]; - } + }; ts.sys.resolvePath = (s: string) => { - var path = log.resolvePath[s]; + const path = log.resolvePath[s]; if (!path) { throw new Error("Unexpected path '" + s + "'"); } - return path - } + return path; + }; ts.sys.writeFile = (path: string, data: string) => { }; - var out: string = ""; + let out = ""; ts.sys.write = (s: string) => { out += s; }; diff --git a/tests/perftsc.ts b/tests/perftsc.ts index 89bff1cc006..0753023853e 100644 --- a/tests/perftsc.ts +++ b/tests/perftsc.ts @@ -5,9 +5,9 @@ if (perftest.hasLogIOFlag()) { perftest.interceptIO(); - var compilerHost: ts.CompilerHost = { + const compilerHost: ts.CompilerHost = { getSourceFile: (s, v) => { - var content = perftest.readFile(s); + const content = perftest.readFile(s); return content !== undefined ? ts.createSourceFile(s, content, v) : undefined; }, getDefaultLibFileName: () => ts.combinePaths(ts.getDirectoryPath(ts.normalizePath(perftest.getExecutingFilePath())), "lib.d.ts"), @@ -18,13 +18,13 @@ if (perftest.hasLogIOFlag()) { getNewLine: () => ts.sys.newLine }; - var commandLine = ts.parseCommandLine(perftest.getArgsWithoutLogIOFlag()); - var program = ts.createProgram(commandLine.fileNames, commandLine.options, compilerHost); - var fileNames = program.getSourceFiles().map(f => f.fileName); + const commandLine = ts.parseCommandLine(perftest.getArgsWithoutLogIOFlag()); + const program = ts.createProgram(commandLine.fileNames, commandLine.options, compilerHost); + const fileNames = program.getSourceFiles().map(f => f.fileName); perftest.writeIOLog(fileNames); } else { - var io = perftest.prepare(); + const io = perftest.prepare(); ts.executeCommandLine(perftest.getArgsWithoutIOLogFile()); perftest.write(io.getOut()); } diff --git a/tests/webTestServer.ts b/tests/webTestServer.ts index d9adff05842..ac5f046c1dd 100644 --- a/tests/webTestServer.ts +++ b/tests/webTestServer.ts @@ -9,35 +9,35 @@ import os = require("os"); /// Command line processing /// -if (process.argv[2] == '--help') { - console.log('Runs a node server on port 8888, looking for tests folder in the current directory\n'); - console.log('Syntax: node nodeServer.js [typescriptEnlistmentDirectory] [tests] [--browser] [--verbose]\n'); - console.log('Examples: \n\tnode nodeServer.js .'); - console.log('\tnode nodeServer.js 3000 D:/src/typescript/public --verbose IE'); +if (process.argv[2] == "--help") { + console.log("Runs a node server on port 8888, looking for tests folder in the current directory\n"); + console.log("Syntax: node nodeServer.js [typescriptEnlistmentDirectory] [tests] [--browser] [--verbose]\n"); + console.log("Examples: \n\tnode nodeServer.js ."); + console.log("\tnode nodeServer.js 3000 D:/src/typescript/public --verbose IE"); } function switchToForwardSlashes(path: string) { - return path.replace(/\\/g, "/").replace(/\/\//g, '/'); + return path.replace(/\\/g, "/").replace(/\/\//g, "/"); } -var port = 8888; // harness.ts and webTestResults.html depend on this exact port number. -var rootDir = switchToForwardSlashes(__dirname + '/../'); +const port = 8888; // harness.ts and webTestResults.html depend on this exact port number. -var browser: string; +let browser: string; if (process.argv[2]) { browser = process.argv[2]; - if (browser !== 'chrome' && browser !== 'IE') { - console.log('Invalid command line arguments. Got ' + browser + ' but expected chrome, IE or nothing.'); + if (browser !== "chrome" && browser !== "IE") { + console.log(`Invalid command line arguments. Got ${browser} but expected chrome, IE or nothing.`); } } -var grep = process.argv[3]; +const grep = process.argv[3]; -var verbose = false; -if (process.argv[4] == '--verbose') { +let verbose = false; +if (process.argv[4] == "--verbose") { verbose = true; -} else if (process.argv[4] && process.argv[4] !== '--verbose') { - console.log('Invalid command line arguments. Got ' + process.argv[4] + ' but expected --verbose or nothing.'); +} +else if (process.argv[4] && process.argv[4] !== "--verbose") { + console.log(`Invalid command line arguments. Got ${process.argv[4]} but expected --verbose or nothing.`); } /// Utils /// @@ -48,58 +48,61 @@ function log(msg: string) { } // Copied from the compiler sources -function dir(path: string, spec?: string, options?: any) { +function dir(dirPath: string, spec?: string, options?: any) { options = options || <{ recursive?: boolean; }>{}; + return filesInFolder(dirPath); function filesInFolder(folder: string): string[] { - var folder = switchToForwardSlashes(folder); - var paths: string[] = []; + folder = switchToForwardSlashes(folder); + let paths: string[] = []; // Everything after the current directory is relative - var baseDirectoryLength = process.cwd().length + 1; + const baseDirectoryLength = process.cwd().length + 1; try { - var files = fs.readdirSync(folder); - for (var i = 0; i < files.length; i++) { - var stat = fs.statSync(folder + "/" + files[i]); + const files = fs.readdirSync(folder); + for (let i = 0; i < files.length; i++) { + const stat = fs.statSync(path.join(folder, files[i])); if (options.recursive && stat.isDirectory()) { - paths = paths.concat(filesInFolder(folder + "/" + files[i])); - } else if (stat.isFile() && (!spec || files[i].match(spec))) { - var relativePath = folder.substring(baseDirectoryLength); - paths.push(relativePath + "/" + files[i]); + paths = paths.concat(filesInFolder(path.join(folder, files[i]))); + } + else if (stat.isFile() && (!spec || files[i].match(spec))) { + const relativePath = folder.substring(baseDirectoryLength); + paths.push(path.join(relativePath, files[i])); } } - } catch (err) { + } + catch (err) { // Skip folders that are inaccessible } return paths; } - - return filesInFolder(path); } // fs.rmdirSync won't delete directories with files in it -function deleteFolderRecursive(path: string) { - if (fs.existsSync(path)) { - fs.readdirSync(path).forEach(function (file, index) { - var curPath = path + "/" + file; +function deleteFolderRecursive(dirPath: string) { + if (fs.existsSync(dirPath)) { + fs.readdirSync(dirPath).forEach((file, index) => { + const curPath = path.join(path, file); if (fs.statSync(curPath).isDirectory()) { // recurse deleteFolderRecursive(curPath); - } else { // delete file + } + else { // delete file fs.unlinkSync(curPath); } }); - fs.rmdirSync(path); + fs.rmdirSync(dirPath); } }; function writeFile(path: string, data: any, opts: { recursive: boolean }) { try { fs.writeFileSync(path, data); - } catch (e) { + } + catch (e) { // assume file was written to a directory that exists, if not, start recursively creating them as necessary - var parts = switchToForwardSlashes(path).split('/'); - for (var i = 0; i < parts.length; i++) { - var subDir = parts.slice(0, i).join('/'); + const parts = switchToForwardSlashes(path).split("/"); + for (let i = 0; i < parts.length; i++) { + const subDir = parts.slice(0, i).join("/"); if (!fs.existsSync(subDir)) { fs.mkdir(subDir); } @@ -111,10 +114,10 @@ function writeFile(path: string, data: any, opts: { recursive: boolean }) { /// Request Handling /// function handleResolutionRequest(filePath: string, res: http.ServerResponse) { - var resolvedPath = path.resolve(filePath, ''); - resolvedPath = resolvedPath.substring(resolvedPath.indexOf('tests')); + let resolvedPath = path.resolve(filePath, ""); + resolvedPath = resolvedPath.substring(resolvedPath.indexOf("tests")); resolvedPath = switchToForwardSlashes(resolvedPath); - send('success', res, resolvedPath); + send("success", res, resolvedPath); return; } @@ -123,7 +126,7 @@ function send(result: "success", res: http.ServerResponse, contents: string, con function send(result: "unknown", res: http.ServerResponse, contents: string, contentType?: string): void; function send(result: string, res: http.ServerResponse, contents: string, contentType?: string): void function send(result: string, res: http.ServerResponse, contents: string, contentType = "binary"): void { - var responseCode = result === "success" ? 200 : result === "fail" ? 500 : result === 'unknown' ? 404 : parseInt(result); + const responseCode = result === "success" ? 200 : result === "fail" ? 500 : result === "unknown" ? 404 : parseInt(result); res.writeHead(responseCode, { "Content-Type": contentType }); res.end(contents); return; @@ -131,27 +134,28 @@ function send(result: string, res: http.ServerResponse, contents: string, conten // Reads the data from a post request and passes it to the given callback function processPost(req: http.ServerRequest, res: http.ServerResponse, callback: (data: string) => any): void { - var queryData = ""; - if (typeof callback !== 'function') return null; + let queryData = ""; + if (typeof callback !== "function") return; - if (req.method == 'POST') { - req.on('data', function (data: string) { + if (req.method == "POST") { + req.on("data", (data: string) => { queryData += data; if (queryData.length > 1e8) { queryData = ""; - send("413", res, null); + send("413", res, undefined); console.log("ERROR: destroying connection"); req.connection.destroy(); } }); - req.on('end', function () { - //res.post = url.parse(req.url).query; + req.on("end", () => { + // res.post = url.parse(req.url).query; callback(queryData); }); - } else { - send("405", res, null); + } + else { + send("405", res, undefined); } } @@ -168,108 +172,108 @@ enum RequestType { } function getRequestOperation(req: http.ServerRequest, filename: string) { - if (req.method === 'GET' && req.url.indexOf('?') === -1) { - if (req.url.indexOf('.') !== -1) return RequestType.GetFile; + if (req.method === "GET" && req.url.indexOf("?") === -1) { + if (req.url.indexOf(".") !== -1) return RequestType.GetFile; else return RequestType.GetDir; } else { - var queryData: any = url.parse(req.url, true).query; - if (req.method === 'GET' && queryData.resolve !== undefined) return RequestType.ResolveFile + const queryData: any = url.parse(req.url, /*parseQueryString*/ true).query; + if (req.method === "GET" && queryData.resolve !== undefined) return RequestType.ResolveFile; // mocha uses ?grep= query string as equivalent to the --grep command line option used to filter tests - if (req.method === 'GET' && queryData.grep !== undefined) return RequestType.GetFile - if (req.method === 'POST' && queryData.action) { - var path = req.url.substr(0, req.url.lastIndexOf('?')); - var isFile = path.substring(path.lastIndexOf('/')).indexOf('.') !== -1; + if (req.method === "GET" && queryData.grep !== undefined) return RequestType.GetFile; + if (req.method === "POST" && queryData.action) { + const path = req.url.substr(0, req.url.lastIndexOf("?")); + const isFile = path.substring(path.lastIndexOf("/")).indexOf(".") !== -1; switch (queryData.action.toUpperCase()) { - case 'WRITE': + case "WRITE": return isFile ? RequestType.WriteFile : RequestType.WriteDir; - case 'DELETE': + case "DELETE": return isFile ? RequestType.DeleteFile : RequestType.DeleteDir; - case 'APPEND': + case "APPEND": return isFile ? RequestType.AppendFile : RequestType.Unknown; } } - return RequestType.Unknown + return RequestType.Unknown; } } function handleRequestOperation(req: http.ServerRequest, res: http.ServerResponse, operation: RequestType, reqPath: string) { switch (operation) { case RequestType.GetDir: - var filesInFolder = dir(reqPath, "", { recursive: true }); - send('success', res, filesInFolder.join(',')); + const filesInFolder = dir(reqPath, "", { recursive: true }); + send("success", res, filesInFolder.join(",")); break; case RequestType.GetFile: - fs.readFile(reqPath, function (err, file) { + fs.readFile(reqPath, (err, file) => { const contentType = contentTypeForExtension(path.extname(reqPath)); if (err) { - send('fail', res, err.message, contentType); + send("fail", res, err.message, contentType); } else { - send('success', res, file, contentType); + send("success", res, file, contentType); } }); break; case RequestType.ResolveFile: - var resolveRequest = req.url.match(/(.*)\?resolve/); + const resolveRequest = req.url.match(/(.*)\?resolve/); handleResolutionRequest(resolveRequest[1], res); break; case RequestType.WriteFile: processPost(req, res, (data) => { writeFile(reqPath, data, { recursive: true }); }); - send('success', res, null); + send("success", res, undefined); break; case RequestType.WriteDir: fs.mkdirSync(reqPath); - send('success', res, null); + send("success", res, undefined); break; case RequestType.DeleteFile: if (fs.existsSync(reqPath)) { fs.unlinkSync(reqPath); } - send('success', res, null); + send("success", res, undefined); break; case RequestType.DeleteDir: if (fs.existsSync(reqPath)) { fs.rmdirSync(reqPath); } - send('success', res, null); + send("success", res, undefined); break; case RequestType.AppendFile: processPost(req, res, (data) => { fs.appendFileSync(reqPath, data); }); - send('success', res, null); + send("success", res, undefined); break; case RequestType.Unknown: default: - send('unknown', res, null); + send("unknown", res, undefined); break; } function contentTypeForExtension(ext: string) { switch (ext) { - case '.js': return 'text/javascript'; - case '.css': return 'text/css'; - case '.html': return 'text/html'; - default: return 'binary'; + case ".js": return "text/javascript"; + case ".css": return "text/css"; + case ".html": return "text/html"; + default: return "binary"; } } } -console.log("Static file server running at\n => http://localhost:" + port + "/\nCTRL + C to shutdown"); +console.log(`Static file server running at\n => http://localhost:${port}/\nCTRL + C to shutdown`); -http.createServer(function (req: http.ServerRequest, res: http.ServerResponse) { - log(req.method + ' ' + req.url); - var uri = url.parse(req.url).pathname - var reqPath = path.join(process.cwd(), uri); - var operation = getRequestOperation(req, reqPath); +http.createServer((req: http.ServerRequest, res: http.ServerResponse) => { + log(`${req.method} ${req.url}`); + const uri = url.parse(req.url).pathname; + const reqPath = path.join(process.cwd(), uri); + const operation = getRequestOperation(req, reqPath); handleRequestOperation(req, res, operation, reqPath); }).listen(port); -var browserPath: string; -if ((browser && browser === 'chrome')) { +let browserPath: string; +if (browser === "chrome") { let defaultChromePath = ""; switch (os.platform()) { case "win32": @@ -280,7 +284,7 @@ if ((browser && browser === 'chrome')) { defaultChromePath = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"; break; case "linux": - defaultChromePath = "/opt/google/chrome/chrome" + defaultChromePath = "/opt/google/chrome/chrome"; break; default: console.log(`default Chrome location is unknown for platform '${os.platform()}'`); @@ -288,21 +292,24 @@ if ((browser && browser === 'chrome')) { } if (fs.existsSync(defaultChromePath)) { browserPath = defaultChromePath; - } else { + } + else { browserPath = browser; } -} else { - var defaultIEPath = 'C:/Program Files/Internet Explorer/iexplore.exe'; +} +else { + const defaultIEPath = "C:/Program Files/Internet Explorer/iexplore.exe"; if (fs.existsSync(defaultIEPath)) { browserPath = defaultIEPath; - } else { + } + else { browserPath = browser; } } -console.log('Using browser: ' + browserPath); +console.log(`Using browser: ${browserPath}`); -var queryString = grep ? "?grep=" + grep : ''; -child_process.spawn(browserPath, ['http://localhost:' + port + '/tests/webTestResults.html' + queryString], { - stdio: 'inherit' +const queryString = grep ? `?grep=${grep}` : ""; +child_process.spawn(browserPath, [`http://localhost:${port}/tests/webTestResults.html${queryString}`], { + stdio: "inherit" }); diff --git a/tests/webhost/webtsc.ts b/tests/webhost/webtsc.ts index ad3c06c8565..3c682df6274 100644 --- a/tests/webhost/webtsc.ts +++ b/tests/webhost/webtsc.ts @@ -1,17 +1,17 @@ /// -module TypeScript.WebTsc { +namespace TypeScript.WebTsc { declare var RealActiveXObject: { new (s: string): any }; function getWScriptSystem() { - var fso = new RealActiveXObject("Scripting.FileSystemObject"); + const fso = new RealActiveXObject("Scripting.FileSystemObject"); - var fileStream = new ActiveXObject("ADODB.Stream"); + const fileStream = new ActiveXObject("ADODB.Stream"); fileStream.Type = 2 /*text*/; - var args: string[] = []; - for (var i = 0; i < WScript.Arguments.length; i++) { + const args: string[] = []; + for (let i = 0; i < WScript.Arguments.length; i++) { args[i] = WScript.Arguments.Item(i); } return { @@ -37,7 +37,7 @@ module TypeScript.WebTsc { // Load file and read the first two bytes into a string with no interpretation fileStream.Charset = "x-ansi"; fileStream.LoadFromFile(fileName); - var bom = fileStream.ReadText(2) || ""; + const bom = fileStream.ReadText(2) || ""; // Position must be at 0 before encoding can be changed fileStream.Position = 0; // [0xFF,0xFE] and [0xFE,0xFF] mean utf-16 (little or big endian), otherwise default to utf-8 @@ -54,7 +54,7 @@ module TypeScript.WebTsc { } }, writeFile(fileName: string, data: string): boolean { - var f = fso.CreateTextFile(fileName, true); + const f = fso.CreateTextFile(fileName, true); f.Write(data); f.Close(); return true; @@ -90,15 +90,15 @@ module TypeScript.WebTsc { } export function prepareCompiler(currentDir: string, stdOut: ITextWriter, stdErr: ITextWriter) { - var shell = new RealActiveXObject("WScript.Shell"); + const shell = new RealActiveXObject("WScript.Shell"); shell.CurrentDirectory = currentDir; WScript.ScriptFullName = currentDir + "\\tc.js"; WScript.StdOut = stdOut; WScript.StdErr = stdErr; sys = getWScriptSystem(); - return function (commandLine: string) { + return (commandLine: string) => { ts.executeCommandLine(commandLine.split(" ")); - } + }; } } From 0f1585f0af48ffdc505ed174e8d0d6d51f0dea16 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Fri, 29 Jul 2016 11:02:33 -0700 Subject: [PATCH 033/233] recreate program if baseUrl or paths changed in tsconfig --- src/services/services.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/services/services.ts b/src/services/services.ts index ad630fd444e..fd4354923c8 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -3097,7 +3097,9 @@ namespace ts { oldSettings.noResolve !== newSettings.noResolve || oldSettings.jsx !== newSettings.jsx || oldSettings.allowJs !== newSettings.allowJs || - oldSettings.disableSizeLimit !== oldSettings.disableSizeLimit); + oldSettings.disableSizeLimit !== oldSettings.disableSizeLimit || + oldSettings.baseUrl !== newSettings.baseUrl || + !mapIsEqualTo(oldSettings.paths, newSettings.paths)); // Now create a new compiler const compilerHost: CompilerHost = { From c50ccbf9616bbb06e7f73203a7e0134ab852f0d6 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Fri, 29 Jul 2016 12:31:02 -0700 Subject: [PATCH 034/233] Simplify some code --- src/compiler/checker.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 21d74bb24b8..ca665346036 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17699,13 +17699,11 @@ namespace ts { /*all meanings*/ SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias); } - if (entityName.kind !== SyntaxKind.PropertyAccessExpression) { - if (isInRightSideOfImportOrExportAssignment(entityName)) { - // Since we already checked for ExportAssignment, this really could only be an Import - const importEqualsDeclaration = getAncestor(entityName, SyntaxKind.ImportEqualsDeclaration); - Debug.assert(importEqualsDeclaration !== undefined); - return getSymbolOfPartOfRightHandSideOfImportEquals(entityName, importEqualsDeclaration, /*dontResolveAlias*/ true); - } + if (entityName.kind !== SyntaxKind.PropertyAccessExpression && isInRightSideOfImportOrExportAssignment(entityName)) { + // Since we already checked for ExportAssignment, this really could only be an Import + const importEqualsDeclaration = getAncestor(entityName, SyntaxKind.ImportEqualsDeclaration); + Debug.assert(importEqualsDeclaration !== undefined); + return getSymbolOfPartOfRightHandSideOfImportEquals(entityName, importEqualsDeclaration, /*dontResolveAlias*/ true); } if (isRightSideOfQualifiedNameOrPropertyAccess(entityName)) { From cbb06ab13a97fcb362a78dad4edc429510c55405 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Sat, 30 Jul 2016 08:17:46 -0700 Subject: [PATCH 035/233] Correctly check for ambient class flag --- src/compiler/checker.ts | 2 +- .../ambientClassDeclaredBeforeBase.symbols | 13 +++++++++++++ .../reference/ambientClassDeclaredBeforeBase.types | 13 +++++++++++++ .../compiler/ambientClassDeclaredBeforeBase.ts | 6 ++++++ 4 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/ambientClassDeclaredBeforeBase.symbols create mode 100644 tests/baselines/reference/ambientClassDeclaredBeforeBase.types create mode 100644 tests/cases/compiler/ambientClassDeclaredBeforeBase.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 33abcb2af12..7b85189b784 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16227,7 +16227,7 @@ namespace ts { checkTypeAssignableTo(staticType, getTypeWithoutSignatures(staticBaseType), node.name || node, Diagnostics.Class_static_side_0_incorrectly_extends_base_class_static_side_1); - if (baseType.symbol.valueDeclaration && !(baseType.symbol.valueDeclaration.flags & NodeFlags.Ambient)) { + if (baseType.symbol.valueDeclaration && !isInAmbientContext(baseType.symbol.valueDeclaration)) { if (!isBlockScopedNameDeclaredBeforeUse(baseType.symbol.valueDeclaration, node)) { error(baseTypeNode, Diagnostics.A_class_must_be_declared_after_its_base_class); } diff --git a/tests/baselines/reference/ambientClassDeclaredBeforeBase.symbols b/tests/baselines/reference/ambientClassDeclaredBeforeBase.symbols new file mode 100644 index 00000000000..0d9fed77240 --- /dev/null +++ b/tests/baselines/reference/ambientClassDeclaredBeforeBase.symbols @@ -0,0 +1,13 @@ +=== tests/cases/compiler/a.d.ts === + +declare namespace ns { +>ns : Symbol(ns, Decl(a.d.ts, 0, 0)) + + class SecondNS extends FirstNS { } +>SecondNS : Symbol(SecondNS, Decl(a.d.ts, 1, 22)) +>FirstNS : Symbol(FirstNS, Decl(a.d.ts, 2, 36)) + + class FirstNS { } +>FirstNS : Symbol(FirstNS, Decl(a.d.ts, 2, 36)) +} + diff --git a/tests/baselines/reference/ambientClassDeclaredBeforeBase.types b/tests/baselines/reference/ambientClassDeclaredBeforeBase.types new file mode 100644 index 00000000000..554d3cfcce8 --- /dev/null +++ b/tests/baselines/reference/ambientClassDeclaredBeforeBase.types @@ -0,0 +1,13 @@ +=== tests/cases/compiler/a.d.ts === + +declare namespace ns { +>ns : typeof ns + + class SecondNS extends FirstNS { } +>SecondNS : SecondNS +>FirstNS : FirstNS + + class FirstNS { } +>FirstNS : FirstNS +} + diff --git a/tests/cases/compiler/ambientClassDeclaredBeforeBase.ts b/tests/cases/compiler/ambientClassDeclaredBeforeBase.ts new file mode 100644 index 00000000000..7e564c9d37d --- /dev/null +++ b/tests/cases/compiler/ambientClassDeclaredBeforeBase.ts @@ -0,0 +1,6 @@ +// @filename: a.d.ts + +declare namespace ns { + class SecondNS extends FirstNS { } + class FirstNS { } +} From fc85bc5a8aeafd69215bfdb308fbd064909b11c9 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 31 Jul 2016 07:57:01 -0700 Subject: [PATCH 036/233] Use "best choice type" for || and ?: operators --- src/compiler/checker.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 33abcb2af12..7d878765494 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12896,6 +12896,14 @@ namespace ts { return (target.flags & TypeFlags.Nullable) !== 0 || isTypeComparableTo(source, target); } + function getBestChoiceType(type1: Type, type2: Type): Type { + const firstAssignableToSecond = isTypeAssignableTo(type1, type2); + const secondAssignableToFirst = isTypeAssignableTo(type2, type1); + return secondAssignableToFirst && !firstAssignableToSecond ? type1 : + firstAssignableToSecond && !secondAssignableToFirst ? type2 : + getUnionType([type1, type2], /*subtypeReduction*/ true); + } + function checkBinaryExpression(node: BinaryExpression, contextualMapper?: TypeMapper) { return checkBinaryLikeExpression(node.left, node.operatorToken, node.right, contextualMapper, node); } @@ -13039,7 +13047,7 @@ namespace ts { leftType; case SyntaxKind.BarBarToken: return getTypeFacts(leftType) & TypeFacts.Falsy ? - getUnionType([removeDefinitelyFalsyTypes(leftType), rightType], /*subtypeReduction*/ true) : + getBestChoiceType(removeDefinitelyFalsyTypes(leftType), rightType) : leftType; case SyntaxKind.EqualsToken: checkAssignmentOperator(rightType); @@ -13166,7 +13174,7 @@ namespace ts { checkExpression(node.condition); const type1 = checkExpression(node.whenTrue, contextualMapper); const type2 = checkExpression(node.whenFalse, contextualMapper); - return getUnionType([type1, type2], /*subtypeReduction*/ true); + return getBestChoiceType(type1, type2); } function typeContainsLiteralFromEnum(type: Type, enumType: EnumType) { From 5c4c997efabefaf80f9d96f49c87c6f2fe684f9a Mon Sep 17 00:00:00 2001 From: Kagami Sascha Rosylight Date: Mon, 1 Aug 2016 00:35:34 +0900 Subject: [PATCH 037/233] jsx opening element formatting --- src/services/formatting/formatting.ts | 12 ++--- src/services/formatting/rules.ts | 46 +++++++++++++++---- .../cases/fourslash/formattingJsxElements.ts | 10 ++-- 3 files changed, 48 insertions(+), 20 deletions(-) diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index 5dae6393b99..e131fbb4c1d 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -453,9 +453,9 @@ namespace ts.formatting { case SyntaxKind.MethodDeclaration: if ((node).asteriskToken) { return SyntaxKind.AsteriskToken; - } - // fall-through - + }/* + fall-through + */ case SyntaxKind.PropertyDeclaration: case SyntaxKind.Parameter: return (node).name.kind; @@ -729,7 +729,7 @@ namespace ts.formatting { else { // indent token only if end line of previous range does not match start line of the token const prevEndLine = savePreviousRange && sourceFile.getLineAndCharacterOfPosition(savePreviousRange.end).line; - indentToken = lastTriviaWasNewLine && tokenStart.line !== prevEndLine; + indentToken = lastTriviaWasNewLine && tokenStart.line !== prevEndLine; } } } @@ -889,7 +889,7 @@ namespace ts.formatting { } function indentationIsDifferent(indentationString: string, startLinePosition: number): boolean { - return indentationString !== sourceFile.text.substr(startLinePosition , indentationString.length); + return indentationString !== sourceFile.text.substr(startLinePosition, indentationString.length); } function indentMultilineComment(commentRange: TextRange, indentation: number, firstLineIsIndented: boolean) { @@ -933,7 +933,7 @@ namespace ts.formatting { // shift all parts on the delta size const delta = indentation - nonWhitespaceColumnInFirstPart.column; - for (let i = startIndex, len = parts.length; i < len; i++, startLine++) { + for (let i = startIndex, len = parts.length; i < len; i++ , startLine++) { const startLinePos = getStartPositionOfLine(startLine, sourceFile); const nonWhitespaceCharacterAndColumn = i === 0 diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index 110ac9bf4ea..1839726b9ad 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -231,6 +231,13 @@ namespace ts.formatting { public NoSpaceBeforeCloseBraceInJsxExpression: Rule; public SpaceBeforeCloseBraceInJsxExpression: Rule; + // JSX opening elements + public SpaceBeforeJsxAttribute: Rule; + public SpaceBeforeSlashInJsxOpeningElement: Rule; + public NoSpaceBeforeGreaterThanTokenInJsxOpeningElement: Rule; + public NoSpaceBeforeEqualInJsxAttribute: Rule; + public NoSpaceAfterEqualInJsxAttribute: Rule; + constructor() { /// /// Common Rules @@ -322,7 +329,7 @@ namespace ts.formatting { // Add a space between statements. All keywords except (do,else,case) has open/close parens after them. // So, we have a rule to add a space for [),Any], [do,Any], [else,Any], and [case,Any] - this.SpaceBetweenStatements = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.CloseParenToken, SyntaxKind.DoKeyword, SyntaxKind.ElseKeyword, SyntaxKind.CaseKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.isNonJsxElementContext, Rules.IsNotForContext), RuleAction.Space)); + this.SpaceBetweenStatements = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.CloseParenToken, SyntaxKind.DoKeyword, SyntaxKind.ElseKeyword, SyntaxKind.CaseKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsNonJsxElementContext, Rules.IsNotForContext), RuleAction.Space)); // This low-pri rule takes care of "try {" and "finally {" in case the rule SpaceBeforeOpenBraceInControl didn't execute on FormatOnEnter. this.SpaceAfterTryFinally = new Rule(RuleDescriptor.create2(Shared.TokenRange.FromTokens([SyntaxKind.TryKeyword, SyntaxKind.FinallyKeyword]), SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space)); @@ -386,6 +393,13 @@ namespace ts.formatting { // template string this.NoSpaceBetweenTagAndTemplateString = new Rule(RuleDescriptor.create3(SyntaxKind.Identifier, Shared.TokenRange.FromTokens([SyntaxKind.NoSubstitutionTemplateLiteral, SyntaxKind.TemplateHead])), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete)); + // jsx opening element + this.SpaceBeforeJsxAttribute = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.Identifier), RuleOperation.create2(new RuleOperationContext(Rules.IsNextTokenParentJsxAttribute, Rules.IsNonJsxSameLineTokenContext), RuleAction.Space)); + this.SpaceBeforeSlashInJsxOpeningElement = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.SlashToken), RuleOperation.create2(new RuleOperationContext(Rules.IsJsxSelfClosingElementContext, Rules.IsNonJsxSameLineTokenContext), RuleAction.Space)); + this.NoSpaceBeforeGreaterThanTokenInJsxOpeningElement = new Rule(RuleDescriptor.create1(SyntaxKind.SlashToken, SyntaxKind.GreaterThanToken), RuleOperation.create2(new RuleOperationContext(Rules.IsJsxSelfClosingElementContext, Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete)); + this.NoSpaceBeforeEqualInJsxAttribute = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.EqualsToken), RuleOperation.create2(new RuleOperationContext(Rules.IsJsxAttributeContext, Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete)); + this.NoSpaceAfterEqualInJsxAttribute = new Rule(RuleDescriptor.create3(SyntaxKind.EqualsToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsJsxAttributeContext, Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete)); + // These rules are higher in priority than user-configurable rules. this.HighPriorityCommonRules = [ this.IgnoreBeforeComment, this.IgnoreAfterLineComment, @@ -413,6 +427,8 @@ namespace ts.formatting { this.SpaceAfterVoidOperator, this.SpaceBetweenAsyncAndOpenParen, this.SpaceBetweenAsyncAndFunctionKeyword, this.NoSpaceBetweenTagAndTemplateString, + this.SpaceBeforeJsxAttribute, this.SpaceBeforeSlashInJsxOpeningElement, this.NoSpaceBeforeGreaterThanTokenInJsxOpeningElement, + this.NoSpaceBeforeEqualInJsxAttribute, this.NoSpaceAfterEqualInJsxAttribute, // TypeScript-specific rules this.NoSpaceAfterConstructor, this.NoSpaceAfterModuleImport, @@ -450,8 +466,8 @@ namespace ts.formatting { /// // Insert space after comma delimiter - this.SpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.isNonJsxElementContext, Rules.IsNextTokenNotCloseBracket), RuleAction.Space)); - this.NoSpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.isNonJsxElementContext), RuleAction.Delete)); + this.SpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsNonJsxElementContext, Rules.IsNextTokenNotCloseBracket), RuleAction.Space)); + this.NoSpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsNonJsxElementContext), RuleAction.Delete)); // Insert space before and after binary operators this.SpaceBeforeBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.BinaryOperators), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Space)); @@ -498,10 +514,10 @@ namespace ts.formatting { this.SpaceBeforeTemplateMiddleAndTail = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail])), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space)); // No space after { and before } in JSX expression - this.NoSpaceAfterOpenBraceInJsxExpression = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.isJsxExpressionContext), RuleAction.Delete)); - this.SpaceAfterOpenBraceInJsxExpression = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.isJsxExpressionContext), RuleAction.Space)); - this.NoSpaceBeforeCloseBraceInJsxExpression = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.isJsxExpressionContext), RuleAction.Delete)); - this.SpaceBeforeCloseBraceInJsxExpression = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.isJsxExpressionContext), RuleAction.Space)); + this.NoSpaceAfterOpenBraceInJsxExpression = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Delete)); + this.SpaceAfterOpenBraceInJsxExpression = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Space)); + this.NoSpaceBeforeCloseBraceInJsxExpression = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Delete)); + this.SpaceBeforeCloseBraceInJsxExpression = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Space)); // Insert space after function keyword for anonymous functions this.SpaceAfterAnonymousFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.FunctionKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Space)); @@ -741,14 +757,26 @@ namespace ts.formatting { return context.TokensAreOnSameLine() && context.contextNode.kind !== SyntaxKind.JsxText; } - static isNonJsxElementContext(context: FormattingContext): boolean { + static IsNonJsxElementContext(context: FormattingContext): boolean { return context.contextNode.kind !== SyntaxKind.JsxElement; } - static isJsxExpressionContext(context: FormattingContext): boolean { + static IsJsxExpressionContext(context: FormattingContext): boolean { return context.contextNode.kind === SyntaxKind.JsxExpression; } + static IsNextTokenParentJsxAttribute(context: FormattingContext): boolean { + return context.nextTokenParent.kind === SyntaxKind.JsxAttribute; + } + + static IsJsxAttributeContext(context: FormattingContext): boolean { + return context.contextNode.kind === SyntaxKind.JsxAttribute; + } + + static IsJsxSelfClosingElementContext(context: FormattingContext): boolean { + return context.contextNode.kind === SyntaxKind.JsxSelfClosingElement; + } + static IsNotBeforeBlockInFunctionDeclarationContext(context: FormattingContext): boolean { return !Rules.IsFunctionDeclContext(context) && !Rules.IsBeforeBlockContext(context); } diff --git a/tests/cases/fourslash/formattingJsxElements.ts b/tests/cases/fourslash/formattingJsxElements.ts index e07149960db..2b3b396ed4c 100644 --- a/tests/cases/fourslash/formattingJsxElements.ts +++ b/tests/cases/fourslash/formattingJsxElements.ts @@ -70,7 +70,7 @@ ////
, {integer}
;/*commaInJsxElement2*/ ////);/*closingParenInJsxElement*/ ////) ;/*closingParenInJsxElement2*/ -////;/*jsxExpressionSpaces*/ +////;/*jsxExpressionSpaces*/ ////;/*jsxExpressionSpaces2*/ format.document(); @@ -85,7 +85,7 @@ goTo.marker("indent1"); verify.indentationIs(12); goTo.marker("1"); -verify.currentLineContentIs(' class1= {'); +verify.currentLineContentIs(' class1={'); goTo.marker("2"); verify.currentLineContentIs(' }>'); @@ -95,7 +95,7 @@ goTo.marker("indent2"); verify.indentationIs(12); goTo.marker("3"); -verify.currentLineContentIs(' class2= {'); +verify.currentLineContentIs(' class2={'); goTo.marker("4"); verify.currentLineContentIs(' }>'); @@ -105,9 +105,9 @@ goTo.marker("indent3"); verify.indentationIs(12); goTo.marker("5"); -verify.currentLineContentIs(' class3= {'); +verify.currentLineContentIs(' class3={'); goTo.marker("6"); -verify.currentLineContentIs(' }/>'); +verify.currentLineContentIs(' } />'); goTo.marker("attrAutoformat"); From e8066158eba22aff78d6899d0bc0dc67b20404d4 Mon Sep 17 00:00:00 2001 From: Yuichi Nukiyama Date: Mon, 1 Aug 2016 15:15:11 +0900 Subject: [PATCH 038/233] change error message for unused parameter property fix --- src/compiler/checker.ts | 2 +- src/compiler/diagnosticMessages.json | 4 ++++ .../unusedParameterProperty1.errors.txt | 14 ++++++++++++++ .../reference/unusedParameterProperty1.js | 19 +++++++++++++++++++ .../unusedParameterProperty2.errors.txt | 14 ++++++++++++++ .../reference/unusedParameterProperty2.js | 19 +++++++++++++++++++ .../compiler/unusedParameterProperty1.ts | 9 +++++++++ .../compiler/unusedParameterProperty2.ts | 9 +++++++++ 8 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/unusedParameterProperty1.errors.txt create mode 100644 tests/baselines/reference/unusedParameterProperty1.js create mode 100644 tests/baselines/reference/unusedParameterProperty2.errors.txt create mode 100644 tests/baselines/reference/unusedParameterProperty2.js create mode 100644 tests/cases/compiler/unusedParameterProperty1.ts create mode 100644 tests/cases/compiler/unusedParameterProperty2.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7b85189b784..4b12e2e61f5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14967,7 +14967,7 @@ namespace ts { else if (member.kind === SyntaxKind.Constructor) { for (const parameter of (member).parameters) { if (!parameter.symbol.isReferenced && parameter.flags & NodeFlags.Private) { - error(parameter.name, Diagnostics._0_is_declared_but_never_used, parameter.symbol.name); + error(parameter.name, Diagnostics.Property_0_is_declared_but_never_used, parameter.symbol.name); } } } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 8126d5c605e..c1eead94f14 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2828,6 +2828,10 @@ "category": "Message", "code": 6137 }, + "Property '{0}' is declared but never used.": { + "category": "Error", + "code": 6138 + }, "Variable '{0}' implicitly has an '{1}' type.": { "category": "Error", "code": 7005 diff --git a/tests/baselines/reference/unusedParameterProperty1.errors.txt b/tests/baselines/reference/unusedParameterProperty1.errors.txt new file mode 100644 index 00000000000..0581199313f --- /dev/null +++ b/tests/baselines/reference/unusedParameterProperty1.errors.txt @@ -0,0 +1,14 @@ +tests/cases/compiler/unusedParameterProperty1.ts(3,25): error TS6138: Property 'used' is declared but never used. + + +==== tests/cases/compiler/unusedParameterProperty1.ts (1 errors) ==== + + class A { + constructor(private used: string) { + ~~~~ +!!! error TS6138: Property 'used' is declared but never used. + let foge = used; + foge += ""; + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/unusedParameterProperty1.js b/tests/baselines/reference/unusedParameterProperty1.js new file mode 100644 index 00000000000..d0b608a6d87 --- /dev/null +++ b/tests/baselines/reference/unusedParameterProperty1.js @@ -0,0 +1,19 @@ +//// [unusedParameterProperty1.ts] + +class A { + constructor(private used: string) { + let foge = used; + foge += ""; + } +} + + +//// [unusedParameterProperty1.js] +var A = (function () { + function A(used) { + this.used = used; + var foge = used; + foge += ""; + } + return A; +}()); diff --git a/tests/baselines/reference/unusedParameterProperty2.errors.txt b/tests/baselines/reference/unusedParameterProperty2.errors.txt new file mode 100644 index 00000000000..cb3b3e95556 --- /dev/null +++ b/tests/baselines/reference/unusedParameterProperty2.errors.txt @@ -0,0 +1,14 @@ +tests/cases/compiler/unusedParameterProperty2.ts(3,25): error TS6138: Property 'used' is declared but never used. + + +==== tests/cases/compiler/unusedParameterProperty2.ts (1 errors) ==== + + class A { + constructor(private used) { + ~~~~ +!!! error TS6138: Property 'used' is declared but never used. + let foge = used; + foge += ""; + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/unusedParameterProperty2.js b/tests/baselines/reference/unusedParameterProperty2.js new file mode 100644 index 00000000000..2bb04fde088 --- /dev/null +++ b/tests/baselines/reference/unusedParameterProperty2.js @@ -0,0 +1,19 @@ +//// [unusedParameterProperty2.ts] + +class A { + constructor(private used) { + let foge = used; + foge += ""; + } +} + + +//// [unusedParameterProperty2.js] +var A = (function () { + function A(used) { + this.used = used; + var foge = used; + foge += ""; + } + return A; +}()); diff --git a/tests/cases/compiler/unusedParameterProperty1.ts b/tests/cases/compiler/unusedParameterProperty1.ts new file mode 100644 index 00000000000..61c4374c60a --- /dev/null +++ b/tests/cases/compiler/unusedParameterProperty1.ts @@ -0,0 +1,9 @@ +//@noUnusedLocals:true +//@noUnusedParameters:true + +class A { + constructor(private used: string) { + let foge = used; + foge += ""; + } +} diff --git a/tests/cases/compiler/unusedParameterProperty2.ts b/tests/cases/compiler/unusedParameterProperty2.ts new file mode 100644 index 00000000000..b9e05fbc967 --- /dev/null +++ b/tests/cases/compiler/unusedParameterProperty2.ts @@ -0,0 +1,9 @@ +//@noUnusedLocals:true +//@noUnusedParameters:true + +class A { + constructor(private used) { + let foge = used; + foge += ""; + } +} From ade89a60327763ac09bc7083d1f9ffc9935682b1 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 1 Aug 2016 06:41:05 -0700 Subject: [PATCH 039/233] Fix issue related to this and #8383 --- src/compiler/checker.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 691d7451895..155f8a2e676 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7789,6 +7789,10 @@ namespace ts { return false; } + function rootContainsMatchingReference(source: Node, target: Node) { + return target.kind === SyntaxKind.PropertyAccessExpression && containsMatchingReference(source, (target).expression); + } + function isOrContainsMatchingReference(source: Node, target: Node) { return isMatchingReference(source, target) || containsMatchingReference(source, target); } @@ -8294,6 +8298,9 @@ namespace ts { if (isMatchingPropertyAccess(expr)) { return narrowTypeByDiscriminant(type, expr, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy)); } + if (rootContainsMatchingReference(reference, expr)) { + return declaredType; + } return type; } @@ -8326,6 +8333,9 @@ namespace ts { if (isMatchingPropertyAccess(right)) { return narrowTypeByDiscriminant(type, right, t => narrowTypeByEquality(t, operator, left, assumeTrue)); } + if (rootContainsMatchingReference(reference, left) || rootContainsMatchingReference(reference, right)) { + return declaredType; + } break; case SyntaxKind.InstanceOfKeyword: return narrowTypeByInstanceof(type, expr, assumeTrue); From b3b4c34b609aeac08230251a9c68aa17d8eaf036 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 1 Aug 2016 07:04:43 -0700 Subject: [PATCH 040/233] Add additional tests --- .../compiler/narrowingByDiscriminantInLoop.ts | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/tests/cases/compiler/narrowingByDiscriminantInLoop.ts b/tests/cases/compiler/narrowingByDiscriminantInLoop.ts index d9519c57a62..f394018d2ce 100644 --- a/tests/cases/compiler/narrowingByDiscriminantInLoop.ts +++ b/tests/cases/compiler/narrowingByDiscriminantInLoop.ts @@ -1,3 +1,5 @@ +// @strictNullChecks: true + // Repro from #9977 type IDLMemberTypes = OperationMemberType | ConstantMemberType; @@ -12,7 +14,7 @@ interface InterfaceType { interface OperationMemberType { type: "operation"; - idlType: IDLTypeDescription | null; + idlType: IDLTypeDescription; } interface ConstantMemberType { @@ -47,4 +49,39 @@ function foo(memberType: IDLMemberTypes) { else if (memberType.type === "operation") { memberType.idlType.origin; // string } +} + +// Repro for issue similar to #8383 + +interface A { + kind: true; + prop: { a: string; }; +} + +interface B { + kind: false; + prop: { b: string; } +} + +function f1(x: A | B) { + while (true) { + x.prop; + if (x.kind === true) { + x.prop.a; + } + if (x.kind === false) { + x.prop.b; + } + } +} + +function f2(x: A | B) { + while (true) { + if (x.kind) { + x.prop.a; + } + if (!x.kind) { + x.prop.b; + } + } } \ No newline at end of file From 0a90a4b27c83222748f1c2ca7776667c57ff044b Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 1 Aug 2016 07:04:54 -0700 Subject: [PATCH 041/233] Accept new baselines --- .../narrowingByDiscriminantInLoop.js | 59 ++++- .../narrowingByDiscriminantInLoop.symbols | 238 ++++++++++++------ .../narrowingByDiscriminantInLoop.types | 112 ++++++++- 3 files changed, 336 insertions(+), 73 deletions(-) diff --git a/tests/baselines/reference/narrowingByDiscriminantInLoop.js b/tests/baselines/reference/narrowingByDiscriminantInLoop.js index b58357cff5b..88cc87772dd 100644 --- a/tests/baselines/reference/narrowingByDiscriminantInLoop.js +++ b/tests/baselines/reference/narrowingByDiscriminantInLoop.js @@ -1,4 +1,5 @@ //// [narrowingByDiscriminantInLoop.ts] + // Repro from #9977 type IDLMemberTypes = OperationMemberType | ConstantMemberType; @@ -13,7 +14,7 @@ interface InterfaceType { interface OperationMemberType { type: "operation"; - idlType: IDLTypeDescription | null; + idlType: IDLTypeDescription; } interface ConstantMemberType { @@ -48,6 +49,41 @@ function foo(memberType: IDLMemberTypes) { else if (memberType.type === "operation") { memberType.idlType.origin; // string } +} + +// Repro for issue similar to #8383 + +interface A { + kind: true; + prop: { a: string; }; +} + +interface B { + kind: false; + prop: { b: string; } +} + +function f1(x: A | B) { + while (true) { + x.prop; + if (x.kind === true) { + x.prop.a; + } + if (x.kind === false) { + x.prop.b; + } + } +} + +function f2(x: A | B) { + while (true) { + if (x.kind) { + x.prop.a; + } + if (!x.kind) { + x.prop.b; + } + } } //// [narrowingByDiscriminantInLoop.js] @@ -80,3 +116,24 @@ function foo(memberType) { memberType.idlType.origin; // string } } +function f1(x) { + while (true) { + x.prop; + if (x.kind === true) { + x.prop.a; + } + if (x.kind === false) { + x.prop.b; + } + } +} +function f2(x) { + while (true) { + if (x.kind) { + x.prop.a; + } + if (!x.kind) { + x.prop.b; + } + } +} diff --git a/tests/baselines/reference/narrowingByDiscriminantInLoop.symbols b/tests/baselines/reference/narrowingByDiscriminantInLoop.symbols index 30cd87f3c2b..edb0f1958c2 100644 --- a/tests/baselines/reference/narrowingByDiscriminantInLoop.symbols +++ b/tests/baselines/reference/narrowingByDiscriminantInLoop.symbols @@ -1,140 +1,238 @@ === tests/cases/compiler/narrowingByDiscriminantInLoop.ts === + // Repro from #9977 type IDLMemberTypes = OperationMemberType | ConstantMemberType; >IDLMemberTypes : Symbol(IDLMemberTypes, Decl(narrowingByDiscriminantInLoop.ts, 0, 0)) ->OperationMemberType : Symbol(OperationMemberType, Decl(narrowingByDiscriminantInLoop.ts, 10, 1)) ->ConstantMemberType : Symbol(ConstantMemberType, Decl(narrowingByDiscriminantInLoop.ts, 15, 1)) +>OperationMemberType : Symbol(OperationMemberType, Decl(narrowingByDiscriminantInLoop.ts, 11, 1)) +>ConstantMemberType : Symbol(ConstantMemberType, Decl(narrowingByDiscriminantInLoop.ts, 16, 1)) interface IDLTypeDescription { ->IDLTypeDescription : Symbol(IDLTypeDescription, Decl(narrowingByDiscriminantInLoop.ts, 2, 63)) +>IDLTypeDescription : Symbol(IDLTypeDescription, Decl(narrowingByDiscriminantInLoop.ts, 3, 63)) origin: string; ->origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) +>origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 5, 30)) } interface InterfaceType { ->InterfaceType : Symbol(InterfaceType, Decl(narrowingByDiscriminantInLoop.ts, 6, 1)) +>InterfaceType : Symbol(InterfaceType, Decl(narrowingByDiscriminantInLoop.ts, 7, 1)) members: IDLMemberTypes[]; ->members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 8, 25)) +>members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 9, 25)) >IDLMemberTypes : Symbol(IDLMemberTypes, Decl(narrowingByDiscriminantInLoop.ts, 0, 0)) } interface OperationMemberType { ->OperationMemberType : Symbol(OperationMemberType, Decl(narrowingByDiscriminantInLoop.ts, 10, 1)) +>OperationMemberType : Symbol(OperationMemberType, Decl(narrowingByDiscriminantInLoop.ts, 11, 1)) type: "operation"; ->type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31)) +>type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 13, 31)) - idlType: IDLTypeDescription | null; ->idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) ->IDLTypeDescription : Symbol(IDLTypeDescription, Decl(narrowingByDiscriminantInLoop.ts, 2, 63)) + idlType: IDLTypeDescription; +>idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 14, 22)) +>IDLTypeDescription : Symbol(IDLTypeDescription, Decl(narrowingByDiscriminantInLoop.ts, 3, 63)) } interface ConstantMemberType { ->ConstantMemberType : Symbol(ConstantMemberType, Decl(narrowingByDiscriminantInLoop.ts, 15, 1)) +>ConstantMemberType : Symbol(ConstantMemberType, Decl(narrowingByDiscriminantInLoop.ts, 16, 1)) type: "const"; ->type : Symbol(ConstantMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) +>type : Symbol(ConstantMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 18, 30)) idlType: string; ->idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 18, 18)) +>idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 19, 18)) } function insertInterface(callbackType: InterfaceType) { ->insertInterface : Symbol(insertInterface, Decl(narrowingByDiscriminantInLoop.ts, 20, 1)) ->callbackType : Symbol(callbackType, Decl(narrowingByDiscriminantInLoop.ts, 22, 25)) ->InterfaceType : Symbol(InterfaceType, Decl(narrowingByDiscriminantInLoop.ts, 6, 1)) +>insertInterface : Symbol(insertInterface, Decl(narrowingByDiscriminantInLoop.ts, 21, 1)) +>callbackType : Symbol(callbackType, Decl(narrowingByDiscriminantInLoop.ts, 23, 25)) +>InterfaceType : Symbol(InterfaceType, Decl(narrowingByDiscriminantInLoop.ts, 7, 1)) for (const memberType of callbackType.members) { ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 23, 14)) ->callbackType.members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 8, 25)) ->callbackType : Symbol(callbackType, Decl(narrowingByDiscriminantInLoop.ts, 22, 25)) ->members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 8, 25)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 24, 14)) +>callbackType.members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 9, 25)) +>callbackType : Symbol(callbackType, Decl(narrowingByDiscriminantInLoop.ts, 23, 25)) +>members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 9, 25)) if (memberType.type === "const") { ->memberType.type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31), Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 23, 14)) ->type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31), Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) +>memberType.type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 13, 31), Decl(narrowingByDiscriminantInLoop.ts, 18, 30)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 24, 14)) +>type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 13, 31), Decl(narrowingByDiscriminantInLoop.ts, 18, 30)) memberType.idlType; // string ->memberType.idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 18, 18)) ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 23, 14)) ->idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 18, 18)) +>memberType.idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 19, 18)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 24, 14)) +>idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 19, 18)) } else if (memberType.type === "operation") { ->memberType.type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31)) ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 23, 14)) ->type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31)) +>memberType.type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 13, 31)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 24, 14)) +>type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 13, 31)) memberType.idlType.origin; // string ->memberType.idlType.origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) ->memberType.idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 23, 14)) ->idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) ->origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) +>memberType.idlType.origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 5, 30)) +>memberType.idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 14, 22)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 24, 14)) +>idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 14, 22)) +>origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 5, 30)) (memberType.idlType as IDLTypeDescription); ->memberType.idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 23, 14)) ->idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) ->IDLTypeDescription : Symbol(IDLTypeDescription, Decl(narrowingByDiscriminantInLoop.ts, 2, 63)) +>memberType.idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 14, 22)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 24, 14)) +>idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 14, 22)) +>IDLTypeDescription : Symbol(IDLTypeDescription, Decl(narrowingByDiscriminantInLoop.ts, 3, 63)) } } } function insertInterface2(callbackType: InterfaceType) { ->insertInterface2 : Symbol(insertInterface2, Decl(narrowingByDiscriminantInLoop.ts, 32, 1)) ->callbackType : Symbol(callbackType, Decl(narrowingByDiscriminantInLoop.ts, 34, 26)) ->InterfaceType : Symbol(InterfaceType, Decl(narrowingByDiscriminantInLoop.ts, 6, 1)) +>insertInterface2 : Symbol(insertInterface2, Decl(narrowingByDiscriminantInLoop.ts, 33, 1)) +>callbackType : Symbol(callbackType, Decl(narrowingByDiscriminantInLoop.ts, 35, 26)) +>InterfaceType : Symbol(InterfaceType, Decl(narrowingByDiscriminantInLoop.ts, 7, 1)) for (const memberType of callbackType.members) { ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 35, 14)) ->callbackType.members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 8, 25)) ->callbackType : Symbol(callbackType, Decl(narrowingByDiscriminantInLoop.ts, 34, 26)) ->members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 8, 25)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 36, 14)) +>callbackType.members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 9, 25)) +>callbackType : Symbol(callbackType, Decl(narrowingByDiscriminantInLoop.ts, 35, 26)) +>members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 9, 25)) if (memberType.type === "operation") { ->memberType.type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31), Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 35, 14)) ->type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31), Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) +>memberType.type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 13, 31), Decl(narrowingByDiscriminantInLoop.ts, 18, 30)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 36, 14)) +>type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 13, 31), Decl(narrowingByDiscriminantInLoop.ts, 18, 30)) memberType.idlType.origin; // string ->memberType.idlType.origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) ->memberType.idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 35, 14)) ->idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) ->origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) +>memberType.idlType.origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 5, 30)) +>memberType.idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 14, 22)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 36, 14)) +>idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 14, 22)) +>origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 5, 30)) } } } function foo(memberType: IDLMemberTypes) { ->foo : Symbol(foo, Decl(narrowingByDiscriminantInLoop.ts, 40, 1)) ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 42, 13)) +>foo : Symbol(foo, Decl(narrowingByDiscriminantInLoop.ts, 41, 1)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 43, 13)) >IDLMemberTypes : Symbol(IDLMemberTypes, Decl(narrowingByDiscriminantInLoop.ts, 0, 0)) if (memberType.type === "const") { ->memberType.type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31), Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 42, 13)) ->type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31), Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) +>memberType.type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 13, 31), Decl(narrowingByDiscriminantInLoop.ts, 18, 30)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 43, 13)) +>type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 13, 31), Decl(narrowingByDiscriminantInLoop.ts, 18, 30)) memberType.idlType; // string ->memberType.idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 18, 18)) ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 42, 13)) ->idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 18, 18)) +>memberType.idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 19, 18)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 43, 13)) +>idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 19, 18)) } else if (memberType.type === "operation") { ->memberType.type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31)) ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 42, 13)) ->type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31)) +>memberType.type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 13, 31)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 43, 13)) +>type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 13, 31)) memberType.idlType.origin; // string ->memberType.idlType.origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) ->memberType.idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 42, 13)) ->idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) ->origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) +>memberType.idlType.origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 5, 30)) +>memberType.idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 14, 22)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 43, 13)) +>idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 14, 22)) +>origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 5, 30)) + } +} + +// Repro for issue similar to #8383 + +interface A { +>A : Symbol(A, Decl(narrowingByDiscriminantInLoop.ts, 50, 1)) + + kind: true; +>kind : Symbol(A.kind, Decl(narrowingByDiscriminantInLoop.ts, 54, 13)) + + prop: { a: string; }; +>prop : Symbol(A.prop, Decl(narrowingByDiscriminantInLoop.ts, 55, 15)) +>a : Symbol(a, Decl(narrowingByDiscriminantInLoop.ts, 56, 11)) +} + +interface B { +>B : Symbol(B, Decl(narrowingByDiscriminantInLoop.ts, 57, 1)) + + kind: false; +>kind : Symbol(B.kind, Decl(narrowingByDiscriminantInLoop.ts, 59, 13)) + + prop: { b: string; } +>prop : Symbol(B.prop, Decl(narrowingByDiscriminantInLoop.ts, 60, 16)) +>b : Symbol(b, Decl(narrowingByDiscriminantInLoop.ts, 61, 11)) +} + +function f1(x: A | B) { +>f1 : Symbol(f1, Decl(narrowingByDiscriminantInLoop.ts, 62, 1)) +>x : Symbol(x, Decl(narrowingByDiscriminantInLoop.ts, 64, 12)) +>A : Symbol(A, Decl(narrowingByDiscriminantInLoop.ts, 50, 1)) +>B : Symbol(B, Decl(narrowingByDiscriminantInLoop.ts, 57, 1)) + + while (true) { + x.prop; +>x.prop : Symbol(prop, Decl(narrowingByDiscriminantInLoop.ts, 55, 15), Decl(narrowingByDiscriminantInLoop.ts, 60, 16)) +>x : Symbol(x, Decl(narrowingByDiscriminantInLoop.ts, 64, 12)) +>prop : Symbol(prop, Decl(narrowingByDiscriminantInLoop.ts, 55, 15), Decl(narrowingByDiscriminantInLoop.ts, 60, 16)) + + if (x.kind === true) { +>x.kind : Symbol(kind, Decl(narrowingByDiscriminantInLoop.ts, 54, 13), Decl(narrowingByDiscriminantInLoop.ts, 59, 13)) +>x : Symbol(x, Decl(narrowingByDiscriminantInLoop.ts, 64, 12)) +>kind : Symbol(kind, Decl(narrowingByDiscriminantInLoop.ts, 54, 13), Decl(narrowingByDiscriminantInLoop.ts, 59, 13)) + + x.prop.a; +>x.prop.a : Symbol(a, Decl(narrowingByDiscriminantInLoop.ts, 56, 11)) +>x.prop : Symbol(A.prop, Decl(narrowingByDiscriminantInLoop.ts, 55, 15)) +>x : Symbol(x, Decl(narrowingByDiscriminantInLoop.ts, 64, 12)) +>prop : Symbol(A.prop, Decl(narrowingByDiscriminantInLoop.ts, 55, 15)) +>a : Symbol(a, Decl(narrowingByDiscriminantInLoop.ts, 56, 11)) + } + if (x.kind === false) { +>x.kind : Symbol(kind, Decl(narrowingByDiscriminantInLoop.ts, 54, 13), Decl(narrowingByDiscriminantInLoop.ts, 59, 13)) +>x : Symbol(x, Decl(narrowingByDiscriminantInLoop.ts, 64, 12)) +>kind : Symbol(kind, Decl(narrowingByDiscriminantInLoop.ts, 54, 13), Decl(narrowingByDiscriminantInLoop.ts, 59, 13)) + + x.prop.b; +>x.prop.b : Symbol(b, Decl(narrowingByDiscriminantInLoop.ts, 61, 11)) +>x.prop : Symbol(B.prop, Decl(narrowingByDiscriminantInLoop.ts, 60, 16)) +>x : Symbol(x, Decl(narrowingByDiscriminantInLoop.ts, 64, 12)) +>prop : Symbol(B.prop, Decl(narrowingByDiscriminantInLoop.ts, 60, 16)) +>b : Symbol(b, Decl(narrowingByDiscriminantInLoop.ts, 61, 11)) + } + } +} + +function f2(x: A | B) { +>f2 : Symbol(f2, Decl(narrowingByDiscriminantInLoop.ts, 74, 1)) +>x : Symbol(x, Decl(narrowingByDiscriminantInLoop.ts, 76, 12)) +>A : Symbol(A, Decl(narrowingByDiscriminantInLoop.ts, 50, 1)) +>B : Symbol(B, Decl(narrowingByDiscriminantInLoop.ts, 57, 1)) + + while (true) { + if (x.kind) { +>x.kind : Symbol(kind, Decl(narrowingByDiscriminantInLoop.ts, 54, 13), Decl(narrowingByDiscriminantInLoop.ts, 59, 13)) +>x : Symbol(x, Decl(narrowingByDiscriminantInLoop.ts, 76, 12)) +>kind : Symbol(kind, Decl(narrowingByDiscriminantInLoop.ts, 54, 13), Decl(narrowingByDiscriminantInLoop.ts, 59, 13)) + + x.prop.a; +>x.prop.a : Symbol(a, Decl(narrowingByDiscriminantInLoop.ts, 56, 11)) +>x.prop : Symbol(A.prop, Decl(narrowingByDiscriminantInLoop.ts, 55, 15)) +>x : Symbol(x, Decl(narrowingByDiscriminantInLoop.ts, 76, 12)) +>prop : Symbol(A.prop, Decl(narrowingByDiscriminantInLoop.ts, 55, 15)) +>a : Symbol(a, Decl(narrowingByDiscriminantInLoop.ts, 56, 11)) + } + if (!x.kind) { +>x.kind : Symbol(kind, Decl(narrowingByDiscriminantInLoop.ts, 54, 13), Decl(narrowingByDiscriminantInLoop.ts, 59, 13)) +>x : Symbol(x, Decl(narrowingByDiscriminantInLoop.ts, 76, 12)) +>kind : Symbol(kind, Decl(narrowingByDiscriminantInLoop.ts, 54, 13), Decl(narrowingByDiscriminantInLoop.ts, 59, 13)) + + x.prop.b; +>x.prop.b : Symbol(b, Decl(narrowingByDiscriminantInLoop.ts, 61, 11)) +>x.prop : Symbol(B.prop, Decl(narrowingByDiscriminantInLoop.ts, 60, 16)) +>x : Symbol(x, Decl(narrowingByDiscriminantInLoop.ts, 76, 12)) +>prop : Symbol(B.prop, Decl(narrowingByDiscriminantInLoop.ts, 60, 16)) +>b : Symbol(b, Decl(narrowingByDiscriminantInLoop.ts, 61, 11)) + } } } diff --git a/tests/baselines/reference/narrowingByDiscriminantInLoop.types b/tests/baselines/reference/narrowingByDiscriminantInLoop.types index db8cb502a13..50f322636cc 100644 --- a/tests/baselines/reference/narrowingByDiscriminantInLoop.types +++ b/tests/baselines/reference/narrowingByDiscriminantInLoop.types @@ -1,4 +1,5 @@ === tests/cases/compiler/narrowingByDiscriminantInLoop.ts === + // Repro from #9977 type IDLMemberTypes = OperationMemberType | ConstantMemberType; @@ -27,10 +28,9 @@ interface OperationMemberType { type: "operation"; >type : "operation" - idlType: IDLTypeDescription | null; + idlType: IDLTypeDescription; >idlType : IDLTypeDescription >IDLTypeDescription : IDLTypeDescription ->null : null } interface ConstantMemberType { @@ -151,3 +151,111 @@ function foo(memberType: IDLMemberTypes) { >origin : string } } + +// Repro for issue similar to #8383 + +interface A { +>A : A + + kind: true; +>kind : true +>true : true + + prop: { a: string; }; +>prop : { a: string; } +>a : string +} + +interface B { +>B : B + + kind: false; +>kind : false +>false : false + + prop: { b: string; } +>prop : { b: string; } +>b : string +} + +function f1(x: A | B) { +>f1 : (x: A | B) => void +>x : A | B +>A : A +>B : B + + while (true) { +>true : boolean + + x.prop; +>x.prop : { a: string; } | { b: string; } +>x : A | B +>prop : { a: string; } | { b: string; } + + if (x.kind === true) { +>x.kind === true : boolean +>x.kind : boolean +>x : A | B +>kind : boolean +>true : true + + x.prop.a; +>x.prop.a : string +>x.prop : { a: string; } +>x : A +>prop : { a: string; } +>a : string + } + if (x.kind === false) { +>x.kind === false : boolean +>x.kind : boolean +>x : A | B +>kind : boolean +>false : false + + x.prop.b; +>x.prop.b : string +>x.prop : { b: string; } +>x : B +>prop : { b: string; } +>b : string + } + } +} + +function f2(x: A | B) { +>f2 : (x: A | B) => void +>x : A | B +>A : A +>B : B + + while (true) { +>true : boolean + + if (x.kind) { +>x.kind : boolean +>x : A | B +>kind : boolean + + x.prop.a; +>x.prop.a : string +>x.prop : { a: string; } +>x : A +>prop : { a: string; } +>a : string + } + if (!x.kind) { +>!x.kind : boolean +>x.kind : boolean +>x : A | B +>kind : boolean + + x.prop.b; +>x.prop.b : string +>x.prop : { b: string; } +>x : B +>prop : { b: string; } +>b : string + } + } +} From cb1ea7be5d11bd5bafcdd0ca8306ec756b677670 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 1 Aug 2016 06:43:53 -0700 Subject: [PATCH 042/233] Provide `realpath` for module resolution in LSHost --- src/server/editorServices.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 8854ea9f432..6e9adad7f47 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -115,6 +115,9 @@ namespace ts.server { readFile: fileName => this.host.readFile(fileName), directoryExists: directoryName => this.host.directoryExists(directoryName) }; + if (this.host.realpath) { + this.moduleResolutionHost.realpath = path => this.host.realpath(path); + } } private resolveNamesWithLocalCache( From 47f6bb2e26a57e53346412c84b65f01029e6ddef Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 1 Aug 2016 09:49:27 -0700 Subject: [PATCH 043/233] Add test --- tests/cases/compiler/bestChoiceType.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/cases/compiler/bestChoiceType.ts diff --git a/tests/cases/compiler/bestChoiceType.ts b/tests/cases/compiler/bestChoiceType.ts new file mode 100644 index 00000000000..3f8ce4bc868 --- /dev/null +++ b/tests/cases/compiler/bestChoiceType.ts @@ -0,0 +1,19 @@ +// @strictNullChecks: true + +// Repro from #10041 + +(''.match(/ /) || []).map(s => s.toLowerCase()); + +// Similar cases + +function f1() { + let x = ''.match(/ /); + let y = x || []; + let z = y.map(s => s.toLowerCase()); +} + +function f2() { + let x = ''.match(/ /); + let y = x ? x : []; + let z = y.map(s => s.toLowerCase()); +} From aa8c6c8f631b65705ead8019ffe0a955fff5aeab Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 1 Aug 2016 09:50:08 -0700 Subject: [PATCH 044/233] Add test baselines --- tests/baselines/reference/bestChoiceType.js | 35 ++++++++ .../reference/bestChoiceType.symbols | 63 +++++++++++++ .../baselines/reference/bestChoiceType.types | 88 +++++++++++++++++++ 3 files changed, 186 insertions(+) create mode 100644 tests/baselines/reference/bestChoiceType.js create mode 100644 tests/baselines/reference/bestChoiceType.symbols create mode 100644 tests/baselines/reference/bestChoiceType.types diff --git a/tests/baselines/reference/bestChoiceType.js b/tests/baselines/reference/bestChoiceType.js new file mode 100644 index 00000000000..0c1bf5df2d4 --- /dev/null +++ b/tests/baselines/reference/bestChoiceType.js @@ -0,0 +1,35 @@ +//// [bestChoiceType.ts] + +// Repro from #10041 + +(''.match(/ /) || []).map(s => s.toLowerCase()); + +// Similar cases + +function f1() { + let x = ''.match(/ /); + let y = x || []; + let z = y.map(s => s.toLowerCase()); +} + +function f2() { + let x = ''.match(/ /); + let y = x ? x : []; + let z = y.map(s => s.toLowerCase()); +} + + +//// [bestChoiceType.js] +// Repro from #10041 +(''.match(/ /) || []).map(function (s) { return s.toLowerCase(); }); +// Similar cases +function f1() { + var x = ''.match(/ /); + var y = x || []; + var z = y.map(function (s) { return s.toLowerCase(); }); +} +function f2() { + var x = ''.match(/ /); + var y = x ? x : []; + var z = y.map(function (s) { return s.toLowerCase(); }); +} diff --git a/tests/baselines/reference/bestChoiceType.symbols b/tests/baselines/reference/bestChoiceType.symbols new file mode 100644 index 00000000000..25f7de2eeda --- /dev/null +++ b/tests/baselines/reference/bestChoiceType.symbols @@ -0,0 +1,63 @@ +=== tests/cases/compiler/bestChoiceType.ts === + +// Repro from #10041 + +(''.match(/ /) || []).map(s => s.toLowerCase()); +>(''.match(/ /) || []).map : Symbol(Array.map, Decl(lib.d.ts, --, --)) +>''.match : Symbol(String.match, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>match : Symbol(String.match, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>map : Symbol(Array.map, Decl(lib.d.ts, --, --)) +>s : Symbol(s, Decl(bestChoiceType.ts, 3, 26)) +>s.toLowerCase : Symbol(String.toLowerCase, Decl(lib.d.ts, --, --)) +>s : Symbol(s, Decl(bestChoiceType.ts, 3, 26)) +>toLowerCase : Symbol(String.toLowerCase, Decl(lib.d.ts, --, --)) + +// Similar cases + +function f1() { +>f1 : Symbol(f1, Decl(bestChoiceType.ts, 3, 48)) + + let x = ''.match(/ /); +>x : Symbol(x, Decl(bestChoiceType.ts, 8, 7)) +>''.match : Symbol(String.match, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>match : Symbol(String.match, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + let y = x || []; +>y : Symbol(y, Decl(bestChoiceType.ts, 9, 7)) +>x : Symbol(x, Decl(bestChoiceType.ts, 8, 7)) + + let z = y.map(s => s.toLowerCase()); +>z : Symbol(z, Decl(bestChoiceType.ts, 10, 7)) +>y.map : Symbol(Array.map, Decl(lib.d.ts, --, --)) +>y : Symbol(y, Decl(bestChoiceType.ts, 9, 7)) +>map : Symbol(Array.map, Decl(lib.d.ts, --, --)) +>s : Symbol(s, Decl(bestChoiceType.ts, 10, 18)) +>s.toLowerCase : Symbol(String.toLowerCase, Decl(lib.d.ts, --, --)) +>s : Symbol(s, Decl(bestChoiceType.ts, 10, 18)) +>toLowerCase : Symbol(String.toLowerCase, Decl(lib.d.ts, --, --)) +} + +function f2() { +>f2 : Symbol(f2, Decl(bestChoiceType.ts, 11, 1)) + + let x = ''.match(/ /); +>x : Symbol(x, Decl(bestChoiceType.ts, 14, 7)) +>''.match : Symbol(String.match, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>match : Symbol(String.match, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + let y = x ? x : []; +>y : Symbol(y, Decl(bestChoiceType.ts, 15, 7)) +>x : Symbol(x, Decl(bestChoiceType.ts, 14, 7)) +>x : Symbol(x, Decl(bestChoiceType.ts, 14, 7)) + + let z = y.map(s => s.toLowerCase()); +>z : Symbol(z, Decl(bestChoiceType.ts, 16, 7)) +>y.map : Symbol(Array.map, Decl(lib.d.ts, --, --)) +>y : Symbol(y, Decl(bestChoiceType.ts, 15, 7)) +>map : Symbol(Array.map, Decl(lib.d.ts, --, --)) +>s : Symbol(s, Decl(bestChoiceType.ts, 16, 18)) +>s.toLowerCase : Symbol(String.toLowerCase, Decl(lib.d.ts, --, --)) +>s : Symbol(s, Decl(bestChoiceType.ts, 16, 18)) +>toLowerCase : Symbol(String.toLowerCase, Decl(lib.d.ts, --, --)) +} + diff --git a/tests/baselines/reference/bestChoiceType.types b/tests/baselines/reference/bestChoiceType.types new file mode 100644 index 00000000000..f88cf64e5a2 --- /dev/null +++ b/tests/baselines/reference/bestChoiceType.types @@ -0,0 +1,88 @@ +=== tests/cases/compiler/bestChoiceType.ts === + +// Repro from #10041 + +(''.match(/ /) || []).map(s => s.toLowerCase()); +>(''.match(/ /) || []).map(s => s.toLowerCase()) : string[] +>(''.match(/ /) || []).map : (callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[] +>(''.match(/ /) || []) : RegExpMatchArray +>''.match(/ /) || [] : RegExpMatchArray +>''.match(/ /) : RegExpMatchArray | null +>''.match : { (regexp: string): RegExpMatchArray | null; (regexp: RegExp): RegExpMatchArray | null; } +>'' : string +>match : { (regexp: string): RegExpMatchArray | null; (regexp: RegExp): RegExpMatchArray | null; } +>/ / : RegExp +>[] : never[] +>map : (callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[] +>s => s.toLowerCase() : (s: string) => string +>s : string +>s.toLowerCase() : string +>s.toLowerCase : () => string +>s : string +>toLowerCase : () => string + +// Similar cases + +function f1() { +>f1 : () => void + + let x = ''.match(/ /); +>x : RegExpMatchArray | null +>''.match(/ /) : RegExpMatchArray | null +>''.match : { (regexp: string): RegExpMatchArray | null; (regexp: RegExp): RegExpMatchArray | null; } +>'' : string +>match : { (regexp: string): RegExpMatchArray | null; (regexp: RegExp): RegExpMatchArray | null; } +>/ / : RegExp + + let y = x || []; +>y : RegExpMatchArray +>x || [] : RegExpMatchArray +>x : RegExpMatchArray | null +>[] : never[] + + let z = y.map(s => s.toLowerCase()); +>z : string[] +>y.map(s => s.toLowerCase()) : string[] +>y.map : (callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[] +>y : RegExpMatchArray +>map : (callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[] +>s => s.toLowerCase() : (s: string) => string +>s : string +>s.toLowerCase() : string +>s.toLowerCase : () => string +>s : string +>toLowerCase : () => string +} + +function f2() { +>f2 : () => void + + let x = ''.match(/ /); +>x : RegExpMatchArray | null +>''.match(/ /) : RegExpMatchArray | null +>''.match : { (regexp: string): RegExpMatchArray | null; (regexp: RegExp): RegExpMatchArray | null; } +>'' : string +>match : { (regexp: string): RegExpMatchArray | null; (regexp: RegExp): RegExpMatchArray | null; } +>/ / : RegExp + + let y = x ? x : []; +>y : RegExpMatchArray +>x ? x : [] : RegExpMatchArray +>x : RegExpMatchArray | null +>x : RegExpMatchArray +>[] : never[] + + let z = y.map(s => s.toLowerCase()); +>z : string[] +>y.map(s => s.toLowerCase()) : string[] +>y.map : (callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[] +>y : RegExpMatchArray +>map : (callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[] +>s => s.toLowerCase() : (s: string) => string +>s : string +>s.toLowerCase() : string +>s.toLowerCase : () => string +>s : string +>toLowerCase : () => string +} + From c7d2e5975ba21afce30af3cb083bee41e5367f6f Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 1 Aug 2016 09:50:17 -0700 Subject: [PATCH 045/233] Accept new baselines --- .../baselines/reference/nonContextuallyTypedLogicalOr.symbols | 4 ++-- tests/baselines/reference/nonContextuallyTypedLogicalOr.types | 4 ++-- .../reference/subtypingWithObjectMembersOptionality3.types | 4 ++-- .../reference/subtypingWithObjectMembersOptionality4.types | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/baselines/reference/nonContextuallyTypedLogicalOr.symbols b/tests/baselines/reference/nonContextuallyTypedLogicalOr.symbols index 2d921f6631c..43d7539b6b9 100644 --- a/tests/baselines/reference/nonContextuallyTypedLogicalOr.symbols +++ b/tests/baselines/reference/nonContextuallyTypedLogicalOr.symbols @@ -28,8 +28,8 @@ var e: Ellement; >Ellement : Symbol(Ellement, Decl(nonContextuallyTypedLogicalOr.ts, 3, 1)) (c || e).dummy; ->(c || e).dummy : Symbol(dummy, Decl(nonContextuallyTypedLogicalOr.ts, 0, 22), Decl(nonContextuallyTypedLogicalOr.ts, 5, 20)) +>(c || e).dummy : Symbol(Contextual.dummy, Decl(nonContextuallyTypedLogicalOr.ts, 0, 22)) >c : Symbol(c, Decl(nonContextuallyTypedLogicalOr.ts, 10, 3)) >e : Symbol(e, Decl(nonContextuallyTypedLogicalOr.ts, 11, 3)) ->dummy : Symbol(dummy, Decl(nonContextuallyTypedLogicalOr.ts, 0, 22), Decl(nonContextuallyTypedLogicalOr.ts, 5, 20)) +>dummy : Symbol(Contextual.dummy, Decl(nonContextuallyTypedLogicalOr.ts, 0, 22)) diff --git a/tests/baselines/reference/nonContextuallyTypedLogicalOr.types b/tests/baselines/reference/nonContextuallyTypedLogicalOr.types index aeb9d1409c6..5061ad1d975 100644 --- a/tests/baselines/reference/nonContextuallyTypedLogicalOr.types +++ b/tests/baselines/reference/nonContextuallyTypedLogicalOr.types @@ -29,8 +29,8 @@ var e: Ellement; (c || e).dummy; >(c || e).dummy : any ->(c || e) : Contextual | Ellement ->c || e : Contextual | Ellement +>(c || e) : Contextual +>c || e : Contextual >c : Contextual >e : Ellement >dummy : any diff --git a/tests/baselines/reference/subtypingWithObjectMembersOptionality3.types b/tests/baselines/reference/subtypingWithObjectMembersOptionality3.types index 000d1cdc8ed..6490b0abd9b 100644 --- a/tests/baselines/reference/subtypingWithObjectMembersOptionality3.types +++ b/tests/baselines/reference/subtypingWithObjectMembersOptionality3.types @@ -69,8 +69,8 @@ var b: { Foo2: Derived; } >Derived : Derived var r = true ? a : b; // ok ->r : { Foo?: Base; } | { Foo2: Derived; } ->true ? a : b : { Foo?: Base; } | { Foo2: Derived; } +>r : { Foo?: Base; } +>true ? a : b : { Foo?: Base; } >true : boolean >a : { Foo?: Base; } >b : { Foo2: Derived; } diff --git a/tests/baselines/reference/subtypingWithObjectMembersOptionality4.types b/tests/baselines/reference/subtypingWithObjectMembersOptionality4.types index 2dcda59f46f..ee636be020b 100644 --- a/tests/baselines/reference/subtypingWithObjectMembersOptionality4.types +++ b/tests/baselines/reference/subtypingWithObjectMembersOptionality4.types @@ -69,8 +69,8 @@ var b: { Foo2?: Derived; } >Derived : Derived var r = true ? a : b; // ok ->r : { Foo: Base; } | { Foo2?: Derived; } ->true ? a : b : { Foo: Base; } | { Foo2?: Derived; } +>r : { Foo2?: Derived; } +>true ? a : b : { Foo2?: Derived; } >true : boolean >a : { Foo: Base; } >b : { Foo2?: Derived; } From f78b9094be5697d2a14b9e2384c0cc1acfceea47 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Mon, 1 Aug 2016 16:50:16 -0700 Subject: [PATCH 046/233] CR feedback --- src/services/services.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index fd4354923c8..6ec95343e33 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -3090,7 +3090,7 @@ namespace ts { const oldSettings = program && program.getCompilerOptions(); const newSettings = hostCache.compilationSettings(); - const changesInCompilationSettingsAffectSyntax = oldSettings && + const shouldCreateNewSourceFiles = oldSettings && (oldSettings.target !== newSettings.target || oldSettings.module !== newSettings.module || oldSettings.moduleResolution !== newSettings.moduleResolution || @@ -3151,7 +3151,7 @@ namespace ts { const oldSourceFiles = program.getSourceFiles(); const oldSettingsKey = documentRegistry.getKeyForCompilationSettings(oldSettings); for (const oldSourceFile of oldSourceFiles) { - if (!newProgram.getSourceFile(oldSourceFile.fileName) || changesInCompilationSettingsAffectSyntax) { + if (!newProgram.getSourceFile(oldSourceFile.fileName) || shouldCreateNewSourceFiles) { documentRegistry.releaseDocumentWithKey(oldSourceFile.path, oldSettingsKey); } } @@ -3185,7 +3185,7 @@ namespace ts { // Check if the language version has changed since we last created a program; if they are the same, // it is safe to reuse the sourceFiles; if not, then the shape of the AST can change, and the oldSourceFile // can not be reused. we have to dump all syntax trees and create new ones. - if (!changesInCompilationSettingsAffectSyntax) { + if (!shouldCreateNewSourceFiles) { // Check if the old program had this file already const oldSourceFile = program && program.getSourceFileByPath(path); if (oldSourceFile) { From 0f134ed69efb8d59919bcec6a1e702f403665af6 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 2 Aug 2016 06:58:26 -0700 Subject: [PATCH 047/233] Improve error message --- src/compiler/checker.ts | 6 +++-- src/compiler/core.ts | 21 ++++++++++++++--- src/compiler/diagnosticMessages.json | 2 +- src/compiler/utilities.ts | 5 ++-- .../reference/moduleResolutionNoTs.errors.txt | 23 ++++++++++++++----- .../reference/moduleResolutionNoTs.js | 22 ++++++++++++++---- tests/cases/compiler/moduleResolutionNoTs.ts | 14 ++++++++--- 7 files changed, 71 insertions(+), 22 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7eab69f1df6..290fa257daf 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1366,8 +1366,10 @@ namespace ts { if (moduleNotFoundError) { // report errors only if it was requested - if (hasTypeScriptFileExtensionNonDts(moduleName)) { - error(moduleReferenceLiteral, Diagnostics.Module_name_should_not_include_a_ts_extension_Colon_0, moduleName); + const nonDtsExtension = tryExtractTypeScriptExtensionNonDts(moduleName); + if (nonDtsExtension) { + const diag = Diagnostics.An_import_path_should_not_end_with_a_0_extension_Consider_importing_1_instead; + error(moduleReferenceLiteral, diag, nonDtsExtension, removeExtension(moduleName, nonDtsExtension)); } else { error(moduleReferenceLiteral, moduleNotFoundError, moduleName); diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 7d6aea62462..d77036660b0 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -93,6 +93,17 @@ namespace ts { return undefined; } + /** Works like Array.prototype.find. */ + export function find(array: T[], predicate: (element: T, index: number) => boolean): T | undefined { + for (let i = 0, len = array.length; i < len; i++) { + const value = array[i]; + if (predicate(value, i)) { + return value; + } + } + return undefined; + } + export function contains(array: T[], value: T): boolean { if (array) { for (const v of array) { @@ -941,7 +952,7 @@ namespace ts { * [^./] # matches everything up to the first . character (excluding directory seperators) * (\\.(?!min\\.js$))? # matches . characters but not if they are part of the .min.js file extension */ - const singleAsteriskRegexFragmentFiles = "([^./]|(\\.(?!min\\.js$))?)*"; + const singleAsteriskRegexFragmentFiles = "([^./]|(\\.(?!min\\.js$))?)*"; const singleAsteriskRegexFragmentOther = "[^/]*"; export function getRegularExpressionForWildcard(specs: string[], basePath: string, usage: "files" | "directories" | "exclude") { @@ -1271,8 +1282,12 @@ namespace ts { return path; } - export function tryRemoveExtension(path: string, extension: string): string { - return fileExtensionIs(path, extension) ? path.substring(0, path.length - extension.length) : undefined; + export function tryRemoveExtension(path: string, extension: string): string | undefined { + return fileExtensionIs(path, extension) ? removeExtension(path, extension) : undefined; + } + + export function removeExtension(path: string, extension: string): string { + return path.substring(0, path.length - extension.length); } export function isJsxOrTsxExtension(ext: string): boolean { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index ff534847cda..93bd6c608f0 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1951,7 +1951,7 @@ "category": "Error", "code": 2690 }, - "Module name should not include a '.ts' extension: '{0}'.": { + "An import path should not end with a '{0}' extension. Consider importing '{1}' instead.": { "category": "Error", "code": 2691 }, diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index d6667d2b8b2..b92037b7e5b 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2726,8 +2726,9 @@ namespace ts { return forEach(supportedTypeScriptExtensions, extension => fileExtensionIs(fileName, extension)); } - export function hasTypeScriptFileExtensionNonDts(fileName: string) { - return forEach(supportedTypeScriptExtensionsNonDts, extension => fileExtensionIs(fileName, extension)); + /** Return ".ts" or ".tsx" if that is the extension. */ + export function tryExtractTypeScriptExtensionNonDts(fileName: string): string | undefined { + return find(supportedTypeScriptExtensionsNonDts, extension => fileExtensionIs(fileName, extension)); } /** diff --git a/tests/baselines/reference/moduleResolutionNoTs.errors.txt b/tests/baselines/reference/moduleResolutionNoTs.errors.txt index de105f73539..2a9b74a1af6 100644 --- a/tests/baselines/reference/moduleResolutionNoTs.errors.txt +++ b/tests/baselines/reference/moduleResolutionNoTs.errors.txt @@ -1,14 +1,25 @@ -tests/cases/compiler/user.ts(4,15): error TS2690: Module name should not include a '.ts' extension: './m.ts'. +tests/cases/compiler/user.ts(4,15): error TS2691: An import path should not end with a '.ts' extension. Consider importing './x' instead. +tests/cases/compiler/user.ts(5,15): error TS2691: An import path should not end with a '.tsx' extension. Consider importing './y' instead. -==== tests/cases/compiler/user.ts (1 errors) ==== +==== tests/cases/compiler/user.ts (2 errors) ==== // '.ts' extension is OK in a reference - /// + /// - import x from "./m.ts"; + import x from "./x.ts"; ~~~~~~~~ -!!! error TS2690: Module name should not include a '.ts' extension: './m.ts'. +!!! error TS2691: An import path should not end with a '.ts' extension. Consider importing './x' instead. + import y from "./y.tsx"; + ~~~~~~~~~ +!!! error TS2691: An import path should not end with a '.tsx' extension. Consider importing './y' instead. -==== tests/cases/compiler/m.ts (0 errors) ==== + // Making sure the suggested fixes are valid: + import x2 from "./x"; + import y2 from "./y"; + +==== tests/cases/compiler/x.ts (0 errors) ==== + export default 0; + +==== tests/cases/compiler/y.tsx (0 errors) ==== export default 0; \ No newline at end of file diff --git a/tests/baselines/reference/moduleResolutionNoTs.js b/tests/baselines/reference/moduleResolutionNoTs.js index 8a912f33011..d9d918f5600 100644 --- a/tests/baselines/reference/moduleResolutionNoTs.js +++ b/tests/baselines/reference/moduleResolutionNoTs.js @@ -1,20 +1,32 @@ //// [tests/cases/compiler/moduleResolutionNoTs.ts] //// -//// [m.ts] +//// [x.ts] +export default 0; + +//// [y.tsx] export default 0; //// [user.ts] // '.ts' extension is OK in a reference -/// +/// -import x from "./m.ts"; +import x from "./x.ts"; +import y from "./y.tsx"; + +// Making sure the suggested fixes are valid: +import x2 from "./x"; +import y2 from "./y"; -//// [m.js] +//// [x.js] +"use strict"; +exports.__esModule = true; +exports["default"] = 0; +//// [y.js] "use strict"; exports.__esModule = true; exports["default"] = 0; //// [user.js] // '.ts' extension is OK in a reference -/// +/// "use strict"; diff --git a/tests/cases/compiler/moduleResolutionNoTs.ts b/tests/cases/compiler/moduleResolutionNoTs.ts index 29d81589cd9..683f6630d9f 100644 --- a/tests/cases/compiler/moduleResolutionNoTs.ts +++ b/tests/cases/compiler/moduleResolutionNoTs.ts @@ -1,8 +1,16 @@ -// @filename: m.ts +// @filename: x.ts +export default 0; + +// @filename: y.tsx export default 0; // @filename: user.ts // '.ts' extension is OK in a reference -/// +/// -import x from "./m.ts"; +import x from "./x.ts"; +import y from "./y.tsx"; + +// Making sure the suggested fixes are valid: +import x2 from "./x"; +import y2 from "./y"; From 91c9d76f09c173c0480d7faa9fc19624e6ff8bcc Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 2 Aug 2016 10:32:42 -0700 Subject: [PATCH 048/233] Remove `SupportedExpressionWithTypeArguments` type; just check that the expression of each `ExpressionWithTypeArguments` is an `EntityNameExpression`. --- src/compiler/checker.ts | 59 ++++++++++++++---------------- src/compiler/declarationEmitter.ts | 4 +- src/compiler/types.ts | 4 -- src/compiler/utilities.ts | 12 +----- 4 files changed, 32 insertions(+), 47 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ca665346036..d8b3785a89e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -968,37 +968,34 @@ namespace ts { function checkAndReportErrorForExtendingInterface(errorLocation: Node): boolean { - const parentExpression = climbToSupportedExpressionWithTypeArguments(errorLocation); - if (!parentExpression) { - return false; - } - const expression = parentExpression.expression; - - if (resolveEntityName(expression, SymbolFlags.Interface, /*ignoreErrors*/ true)) { + const expression = climbToEntityNameOfExpressionWithTypeArguments(errorLocation); + const isError = !!(expression && resolveEntityName(expression, SymbolFlags.Interface, /*ignoreErrors*/ true)); + if (isError) { error(errorLocation, Diagnostics.Cannot_extend_an_interface_0_Did_you_mean_implements, getTextOfNode(expression)); - return true; } - return false; + return isError; } /** - * Climbs up parents to a SupportedExpressionWIthTypeArguments. - * Does *not* just climb to an ExpressionWithTypeArguments; instead, ensures that this really is supported. + * Climbs up parents to an ExpressionWithTypeArguments, and returns its expression, + * but returns undefined if that expression is not an EntityNameExpression. */ - function climbToSupportedExpressionWithTypeArguments(node: Node): SupportedExpressionWithTypeArguments | undefined { - while (node) { - switch (node.kind) { - case SyntaxKind.Identifier: - case SyntaxKind.PropertyAccessExpression: + function climbToEntityNameOfExpressionWithTypeArguments(node: Node): EntityNameExpression | undefined { + switch (node.kind) { + case SyntaxKind.Identifier: + case SyntaxKind.PropertyAccessExpression: + if (node.parent) { node = node.parent; - break; - case SyntaxKind.ExpressionWithTypeArguments: - Debug.assert(isSupportedExpressionWithTypeArguments(node)); - return node; - default: + } + else { return undefined; - } + } + break; + case SyntaxKind.ExpressionWithTypeArguments: + Debug.assert(isEntityNameExpression((node).expression)); + return (node).expression; + default: + return undefined; } - return undefined; } @@ -3686,7 +3683,7 @@ namespace ts { const baseTypeNodes = getInterfaceBaseTypeNodes(declaration); if (baseTypeNodes) { for (const node of baseTypeNodes) { - if (isSupportedExpressionWithTypeArguments(node)) { + if (isEntityNameExpression(node.expression)) { const baseSymbol = resolveEntityName(node.expression, SymbolFlags.Type, /*ignoreErrors*/ true); if (!baseSymbol || !(baseSymbol.flags & SymbolFlags.Interface) || getDeclaredTypeOfClassOrInterface(baseSymbol).thisType) { return false; @@ -5042,9 +5039,9 @@ namespace ts { case SyntaxKind.ExpressionWithTypeArguments: // We only support expressions that are simple qualified names. For other // expressions this produces undefined. - const expr = node; - if (isSupportedExpressionWithTypeArguments(expr)) { - return expr.expression; + const expr = (node).expression; + if (isEntityNameExpression(expr)) { + return expr; } // fall through; @@ -5101,8 +5098,8 @@ namespace ts { // We only support expressions that are simple qualified names. For other expressions this produces undefined. const typeNameOrExpression: EntityNameOrEntityNameExpression = node.kind === SyntaxKind.TypeReference ? (node).typeName - : isSupportedExpressionWithTypeArguments(node) - ? (node).expression + : isEntityNameExpression((node).expression) + ? (node).expression : undefined; symbol = typeNameOrExpression && resolveEntityName(typeNameOrExpression, SymbolFlags.Type) || unknownSymbol; type = symbol === unknownSymbol ? unknownType : @@ -16255,7 +16252,7 @@ namespace ts { const implementedTypeNodes = getClassImplementsHeritageClauseElements(node); if (implementedTypeNodes) { for (const typeRefNode of implementedTypeNodes) { - if (!isSupportedExpressionWithTypeArguments(typeRefNode)) { + if (!isEntityNameExpression(typeRefNode.expression)) { error(typeRefNode.expression, Diagnostics.A_class_can_only_implement_an_identifier_Slashqualified_name_with_optional_type_arguments); } checkTypeReferenceNode(typeRefNode); @@ -16497,7 +16494,7 @@ namespace ts { checkObjectTypeForDuplicateDeclarations(node); } forEach(getInterfaceBaseTypeNodes(node), heritageElement => { - if (!isSupportedExpressionWithTypeArguments(heritageElement)) { + if (!isEntityNameExpression(heritageElement.expression)) { error(heritageElement.expression, Diagnostics.An_interface_can_only_extend_an_identifier_Slashqualified_name_with_optional_type_arguments); } checkTypeReferenceNode(heritageElement); diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index 220244c55af..c93784cd9e0 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -452,7 +452,7 @@ namespace ts { } function emitExpressionWithTypeArguments(node: ExpressionWithTypeArguments) { - if (isSupportedExpressionWithTypeArguments(node)) { + if (isEntityNameExpression(node.expression)) { Debug.assert(node.expression.kind === SyntaxKind.Identifier || node.expression.kind === SyntaxKind.PropertyAccessExpression); emitEntityName(node.expression); if (node.typeArguments) { @@ -1019,7 +1019,7 @@ namespace ts { } function emitTypeOfTypeReference(node: ExpressionWithTypeArguments) { - if (isSupportedExpressionWithTypeArguments(node)) { + if (isEntityNameExpression(node.expression)) { emitTypeWithNewGetSymbolAccessibilityDiagnostic(node, getHeritageClauseVisibilityError); } else if (!isImplementsList && node.expression.kind === SyntaxKind.NullKeyword) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 983ed25c848..804989570d8 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1014,10 +1014,6 @@ namespace ts { expression: LeftHandSideExpression; typeArguments?: NodeArray; } - export interface SupportedExpressionWithTypeArguments extends ExpressionWithTypeArguments { - _supportedExpressionWithTypeArgumentsBrand?: any; - expression: EntityNameExpression; - } // @kind(SyntaxKind.NewExpression) export interface NewExpression extends CallExpression, PrimaryExpression { } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 466cf8c2b77..f946e4b614f 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1039,8 +1039,8 @@ namespace ts { case SyntaxKind.TypeReference: return (node).typeName; case SyntaxKind.ExpressionWithTypeArguments: - Debug.assert(isSupportedExpressionWithTypeArguments(node)); - return (node).expression; + Debug.assert(isEntityNameExpression((node).expression)); + return (node).expression; case SyntaxKind.Identifier: case SyntaxKind.QualifiedName: return (node); @@ -2684,10 +2684,6 @@ namespace ts { isClassLike(node.parent.parent); } - export function isSupportedExpressionWithTypeArguments(node: ExpressionWithTypeArguments): node is SupportedExpressionWithTypeArguments { - return isEntityNameExpression(node.expression); - } - export function isEntityNameExpression(node: Expression): node is EntityNameExpression { for (; ; ) { switch (node.kind) { @@ -2702,10 +2698,6 @@ namespace ts { } } - export function isPropertyAccessAnEntityNameExpression(node: PropertyAccessExpression): node is PropertyAccessEntityNameExpression { - return isEntityNameExpression(node.expression); - } - export function isRightSideOfQualifiedNameOrPropertyAccess(node: Node) { return (node.parent.kind === SyntaxKind.QualifiedName && (node.parent).right === node) || (node.parent.kind === SyntaxKind.PropertyAccessExpression && (node.parent).name === node); From db44a710058b2340184de90127aed3182ed1c251 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 2 Aug 2016 10:47:06 -0700 Subject: [PATCH 049/233] Fix bug --- src/compiler/checker.ts | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d8b3785a89e..dc0c8a6a415 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -980,21 +980,23 @@ namespace ts { * but returns undefined if that expression is not an EntityNameExpression. */ function climbToEntityNameOfExpressionWithTypeArguments(node: Node): EntityNameExpression | undefined { - switch (node.kind) { - case SyntaxKind.Identifier: - case SyntaxKind.PropertyAccessExpression: - if (node.parent) { - node = node.parent; - } - else { + for (; ; ) { + switch (node.kind) { + case SyntaxKind.Identifier: + case SyntaxKind.PropertyAccessExpression: + if (node.parent) { + node = node.parent; + } + else { + return undefined; + } + break; + case SyntaxKind.ExpressionWithTypeArguments: + Debug.assert(isEntityNameExpression((node).expression)); + return (node).expression; + default: return undefined; - } - break; - case SyntaxKind.ExpressionWithTypeArguments: - Debug.assert(isEntityNameExpression((node).expression)); - return (node).expression; - default: - return undefined; + } } } From 0eeb9cbd0c9d89f0bc5d72ea7ad9248f0302cd37 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 2 Aug 2016 12:34:23 -0700 Subject: [PATCH 050/233] Fix #10083 - allowSyntheticDefaultImports alters getExternalModuleMember (#10096) --- src/compiler/checker.ts | 4 +++ .../allowSyntheticDefaultImports10.errors.txt | 17 +++++++++++ .../allowSyntheticDefaultImports10.js | 17 +++++++++++ .../allowSyntheticDefaultImports7.js | 28 +++++++++++++++++++ .../allowSyntheticDefaultImports7.symbols | 22 +++++++++++++++ .../allowSyntheticDefaultImports7.types | 24 ++++++++++++++++ .../allowSyntheticDefaultImports8.errors.txt | 14 ++++++++++ .../allowSyntheticDefaultImports8.js | 28 +++++++++++++++++++ .../allowSyntheticDefaultImports9.js | 17 +++++++++++ .../allowSyntheticDefaultImports9.symbols | 22 +++++++++++++++ .../allowSyntheticDefaultImports9.types | 24 ++++++++++++++++ .../allowSyntheticDefaultImports10.ts | 11 ++++++++ .../compiler/allowSyntheticDefaultImports7.ts | 10 +++++++ .../compiler/allowSyntheticDefaultImports8.ts | 11 ++++++++ .../compiler/allowSyntheticDefaultImports9.ts | 11 ++++++++ 15 files changed, 260 insertions(+) create mode 100644 tests/baselines/reference/allowSyntheticDefaultImports10.errors.txt create mode 100644 tests/baselines/reference/allowSyntheticDefaultImports10.js create mode 100644 tests/baselines/reference/allowSyntheticDefaultImports7.js create mode 100644 tests/baselines/reference/allowSyntheticDefaultImports7.symbols create mode 100644 tests/baselines/reference/allowSyntheticDefaultImports7.types create mode 100644 tests/baselines/reference/allowSyntheticDefaultImports8.errors.txt create mode 100644 tests/baselines/reference/allowSyntheticDefaultImports8.js create mode 100644 tests/baselines/reference/allowSyntheticDefaultImports9.js create mode 100644 tests/baselines/reference/allowSyntheticDefaultImports9.symbols create mode 100644 tests/baselines/reference/allowSyntheticDefaultImports9.types create mode 100644 tests/cases/compiler/allowSyntheticDefaultImports10.ts create mode 100644 tests/cases/compiler/allowSyntheticDefaultImports7.ts create mode 100644 tests/cases/compiler/allowSyntheticDefaultImports8.ts create mode 100644 tests/cases/compiler/allowSyntheticDefaultImports9.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0a45b61660a..c1626ff0b75 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1135,6 +1135,10 @@ namespace ts { else { symbolFromVariable = getPropertyOfVariable(targetSymbol, name.text); } + // If the export member we're looking for is default, and there is no real default but allowSyntheticDefaultImports is on, return the entire module as the default + if (!symbolFromVariable && allowSyntheticDefaultImports && name.text === "default") { + symbolFromVariable = resolveExternalModuleSymbol(moduleSymbol) || resolveSymbol(moduleSymbol); + } // if symbolFromVariable is export - get its final target symbolFromVariable = resolveSymbol(symbolFromVariable); const symbolFromModule = getExportOfModule(targetSymbol, name.text); diff --git a/tests/baselines/reference/allowSyntheticDefaultImports10.errors.txt b/tests/baselines/reference/allowSyntheticDefaultImports10.errors.txt new file mode 100644 index 00000000000..981f89214e2 --- /dev/null +++ b/tests/baselines/reference/allowSyntheticDefaultImports10.errors.txt @@ -0,0 +1,17 @@ +tests/cases/compiler/a.ts(2,5): error TS2339: Property 'default' does not exist on type 'typeof "tests/cases/compiler/b"'. +tests/cases/compiler/a.ts(3,5): error TS2339: Property 'default' does not exist on type 'typeof "tests/cases/compiler/b"'. + + +==== tests/cases/compiler/a.ts (2 errors) ==== + import Foo = require("./b"); + Foo.default.bar(); + ~~~~~~~ +!!! error TS2339: Property 'default' does not exist on type 'typeof "tests/cases/compiler/b"'. + Foo.default.default.foo(); + ~~~~~~~ +!!! error TS2339: Property 'default' does not exist on type 'typeof "tests/cases/compiler/b"'. +==== tests/cases/compiler/b.d.ts (0 errors) ==== + export function foo(); + + export function bar(); + \ No newline at end of file diff --git a/tests/baselines/reference/allowSyntheticDefaultImports10.js b/tests/baselines/reference/allowSyntheticDefaultImports10.js new file mode 100644 index 00000000000..746997c4c89 --- /dev/null +++ b/tests/baselines/reference/allowSyntheticDefaultImports10.js @@ -0,0 +1,17 @@ +//// [tests/cases/compiler/allowSyntheticDefaultImports10.ts] //// + +//// [b.d.ts] +export function foo(); + +export function bar(); + +//// [a.ts] +import Foo = require("./b"); +Foo.default.bar(); +Foo.default.default.foo(); + +//// [a.js] +"use strict"; +var Foo = require("./b"); +Foo.default.bar(); +Foo.default.default.foo(); diff --git a/tests/baselines/reference/allowSyntheticDefaultImports7.js b/tests/baselines/reference/allowSyntheticDefaultImports7.js new file mode 100644 index 00000000000..51cc63e6e11 --- /dev/null +++ b/tests/baselines/reference/allowSyntheticDefaultImports7.js @@ -0,0 +1,28 @@ +//// [tests/cases/compiler/allowSyntheticDefaultImports7.ts] //// + +//// [b.d.ts] +export function foo(); + +export function bar(); + +//// [a.ts] +import { default as Foo } from "./b"; +Foo.bar(); +Foo.foo(); + +//// [a.js] +System.register(["./b"], function(exports_1, context_1) { + "use strict"; + var __moduleName = context_1 && context_1.id; + var b_1; + return { + setters:[ + function (b_1_1) { + b_1 = b_1_1; + }], + execute: function() { + b_1["default"].bar(); + b_1["default"].foo(); + } + } +}); diff --git a/tests/baselines/reference/allowSyntheticDefaultImports7.symbols b/tests/baselines/reference/allowSyntheticDefaultImports7.symbols new file mode 100644 index 00000000000..51155dd69df --- /dev/null +++ b/tests/baselines/reference/allowSyntheticDefaultImports7.symbols @@ -0,0 +1,22 @@ +=== tests/cases/compiler/b.d.ts === +export function foo(); +>foo : Symbol(foo, Decl(b.d.ts, 0, 0)) + +export function bar(); +>bar : Symbol(bar, Decl(b.d.ts, 0, 22)) + +=== tests/cases/compiler/a.ts === +import { default as Foo } from "./b"; +>default : Symbol(Foo, Decl(a.ts, 0, 8)) +>Foo : Symbol(Foo, Decl(a.ts, 0, 8)) + +Foo.bar(); +>Foo.bar : Symbol(Foo.bar, Decl(b.d.ts, 0, 22)) +>Foo : Symbol(Foo, Decl(a.ts, 0, 8)) +>bar : Symbol(Foo.bar, Decl(b.d.ts, 0, 22)) + +Foo.foo(); +>Foo.foo : Symbol(Foo.foo, Decl(b.d.ts, 0, 0)) +>Foo : Symbol(Foo, Decl(a.ts, 0, 8)) +>foo : Symbol(Foo.foo, Decl(b.d.ts, 0, 0)) + diff --git a/tests/baselines/reference/allowSyntheticDefaultImports7.types b/tests/baselines/reference/allowSyntheticDefaultImports7.types new file mode 100644 index 00000000000..69ce39e6dc6 --- /dev/null +++ b/tests/baselines/reference/allowSyntheticDefaultImports7.types @@ -0,0 +1,24 @@ +=== tests/cases/compiler/b.d.ts === +export function foo(); +>foo : () => any + +export function bar(); +>bar : () => any + +=== tests/cases/compiler/a.ts === +import { default as Foo } from "./b"; +>default : typeof Foo +>Foo : typeof Foo + +Foo.bar(); +>Foo.bar() : any +>Foo.bar : () => any +>Foo : typeof Foo +>bar : () => any + +Foo.foo(); +>Foo.foo() : any +>Foo.foo : () => any +>Foo : typeof Foo +>foo : () => any + diff --git a/tests/baselines/reference/allowSyntheticDefaultImports8.errors.txt b/tests/baselines/reference/allowSyntheticDefaultImports8.errors.txt new file mode 100644 index 00000000000..acb584f8fd9 --- /dev/null +++ b/tests/baselines/reference/allowSyntheticDefaultImports8.errors.txt @@ -0,0 +1,14 @@ +tests/cases/compiler/a.ts(1,10): error TS2305: Module '"tests/cases/compiler/b"' has no exported member 'default'. + + +==== tests/cases/compiler/b.d.ts (0 errors) ==== + export function foo(); + + export function bar(); + +==== tests/cases/compiler/a.ts (1 errors) ==== + import { default as Foo } from "./b"; + ~~~~~~~ +!!! error TS2305: Module '"tests/cases/compiler/b"' has no exported member 'default'. + Foo.bar(); + Foo.foo(); \ No newline at end of file diff --git a/tests/baselines/reference/allowSyntheticDefaultImports8.js b/tests/baselines/reference/allowSyntheticDefaultImports8.js new file mode 100644 index 00000000000..3c3c5fa8d28 --- /dev/null +++ b/tests/baselines/reference/allowSyntheticDefaultImports8.js @@ -0,0 +1,28 @@ +//// [tests/cases/compiler/allowSyntheticDefaultImports8.ts] //// + +//// [b.d.ts] +export function foo(); + +export function bar(); + +//// [a.ts] +import { default as Foo } from "./b"; +Foo.bar(); +Foo.foo(); + +//// [a.js] +System.register(["./b"], function(exports_1, context_1) { + "use strict"; + var __moduleName = context_1 && context_1.id; + var b_1; + return { + setters:[ + function (b_1_1) { + b_1 = b_1_1; + }], + execute: function() { + b_1["default"].bar(); + b_1["default"].foo(); + } + } +}); diff --git a/tests/baselines/reference/allowSyntheticDefaultImports9.js b/tests/baselines/reference/allowSyntheticDefaultImports9.js new file mode 100644 index 00000000000..15810ccaaf7 --- /dev/null +++ b/tests/baselines/reference/allowSyntheticDefaultImports9.js @@ -0,0 +1,17 @@ +//// [tests/cases/compiler/allowSyntheticDefaultImports9.ts] //// + +//// [b.d.ts] +export function foo(); + +export function bar(); + +//// [a.ts] +import { default as Foo } from "./b"; +Foo.bar(); +Foo.foo(); + +//// [a.js] +"use strict"; +var b_1 = require("./b"); +b_1["default"].bar(); +b_1["default"].foo(); diff --git a/tests/baselines/reference/allowSyntheticDefaultImports9.symbols b/tests/baselines/reference/allowSyntheticDefaultImports9.symbols new file mode 100644 index 00000000000..51155dd69df --- /dev/null +++ b/tests/baselines/reference/allowSyntheticDefaultImports9.symbols @@ -0,0 +1,22 @@ +=== tests/cases/compiler/b.d.ts === +export function foo(); +>foo : Symbol(foo, Decl(b.d.ts, 0, 0)) + +export function bar(); +>bar : Symbol(bar, Decl(b.d.ts, 0, 22)) + +=== tests/cases/compiler/a.ts === +import { default as Foo } from "./b"; +>default : Symbol(Foo, Decl(a.ts, 0, 8)) +>Foo : Symbol(Foo, Decl(a.ts, 0, 8)) + +Foo.bar(); +>Foo.bar : Symbol(Foo.bar, Decl(b.d.ts, 0, 22)) +>Foo : Symbol(Foo, Decl(a.ts, 0, 8)) +>bar : Symbol(Foo.bar, Decl(b.d.ts, 0, 22)) + +Foo.foo(); +>Foo.foo : Symbol(Foo.foo, Decl(b.d.ts, 0, 0)) +>Foo : Symbol(Foo, Decl(a.ts, 0, 8)) +>foo : Symbol(Foo.foo, Decl(b.d.ts, 0, 0)) + diff --git a/tests/baselines/reference/allowSyntheticDefaultImports9.types b/tests/baselines/reference/allowSyntheticDefaultImports9.types new file mode 100644 index 00000000000..69ce39e6dc6 --- /dev/null +++ b/tests/baselines/reference/allowSyntheticDefaultImports9.types @@ -0,0 +1,24 @@ +=== tests/cases/compiler/b.d.ts === +export function foo(); +>foo : () => any + +export function bar(); +>bar : () => any + +=== tests/cases/compiler/a.ts === +import { default as Foo } from "./b"; +>default : typeof Foo +>Foo : typeof Foo + +Foo.bar(); +>Foo.bar() : any +>Foo.bar : () => any +>Foo : typeof Foo +>bar : () => any + +Foo.foo(); +>Foo.foo() : any +>Foo.foo : () => any +>Foo : typeof Foo +>foo : () => any + diff --git a/tests/cases/compiler/allowSyntheticDefaultImports10.ts b/tests/cases/compiler/allowSyntheticDefaultImports10.ts new file mode 100644 index 00000000000..6b50bae96d8 --- /dev/null +++ b/tests/cases/compiler/allowSyntheticDefaultImports10.ts @@ -0,0 +1,11 @@ +// @allowSyntheticDefaultImports: true +// @module: commonjs +// @Filename: b.d.ts +export function foo(); + +export function bar(); + +// @Filename: a.ts +import Foo = require("./b"); +Foo.default.bar(); +Foo.default.default.foo(); \ No newline at end of file diff --git a/tests/cases/compiler/allowSyntheticDefaultImports7.ts b/tests/cases/compiler/allowSyntheticDefaultImports7.ts new file mode 100644 index 00000000000..2da05e4678c --- /dev/null +++ b/tests/cases/compiler/allowSyntheticDefaultImports7.ts @@ -0,0 +1,10 @@ +// @module: system +// @Filename: b.d.ts +export function foo(); + +export function bar(); + +// @Filename: a.ts +import { default as Foo } from "./b"; +Foo.bar(); +Foo.foo(); \ No newline at end of file diff --git a/tests/cases/compiler/allowSyntheticDefaultImports8.ts b/tests/cases/compiler/allowSyntheticDefaultImports8.ts new file mode 100644 index 00000000000..3a213ad8023 --- /dev/null +++ b/tests/cases/compiler/allowSyntheticDefaultImports8.ts @@ -0,0 +1,11 @@ +// @allowSyntheticDefaultImports: false +// @module: system +// @Filename: b.d.ts +export function foo(); + +export function bar(); + +// @Filename: a.ts +import { default as Foo } from "./b"; +Foo.bar(); +Foo.foo(); \ No newline at end of file diff --git a/tests/cases/compiler/allowSyntheticDefaultImports9.ts b/tests/cases/compiler/allowSyntheticDefaultImports9.ts new file mode 100644 index 00000000000..8da66f33adf --- /dev/null +++ b/tests/cases/compiler/allowSyntheticDefaultImports9.ts @@ -0,0 +1,11 @@ +// @allowSyntheticDefaultImports: true +// @module: commonjs +// @Filename: b.d.ts +export function foo(); + +export function bar(); + +// @Filename: a.ts +import { default as Foo } from "./b"; +Foo.bar(); +Foo.foo(); \ No newline at end of file From dc192238cca65da38eb321c9e3ed6f7ced643f9a Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 2 Aug 2016 12:37:30 -0700 Subject: [PATCH 051/233] Use recursion, and fix error for undefined node --- src/compiler/checker.ts | 33 +++++++++++++-------------------- src/compiler/utilities.ts | 13 ++----------- 2 files changed, 15 insertions(+), 31 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dc0c8a6a415..ca85a85e279 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -664,7 +664,7 @@ namespace ts { // Resolve a given name for a given meaning at a given location. An error is reported if the name was not found and // the nameNotFoundMessage argument is not undefined. Returns the resolved symbol, or undefined if no symbol with // the given name can be found. - function resolveName(location: Node, name: string, meaning: SymbolFlags, nameNotFoundMessage: DiagnosticMessage, nameArg: string | Identifier): Symbol { + function resolveName(location: Node | undefined, name: string, meaning: SymbolFlags, nameNotFoundMessage: DiagnosticMessage, nameArg: string | Identifier): Symbol { let result: Symbol; let lastLocation: Node; let propertyWithInvalidInitializer: Node; @@ -881,7 +881,8 @@ namespace ts { if (!result) { if (nameNotFoundMessage) { - if (!checkAndReportErrorForMissingPrefix(errorLocation, name, nameArg) && + if (!errorLocation || + !checkAndReportErrorForMissingPrefix(errorLocation, name, nameArg) && !checkAndReportErrorForExtendingInterface(errorLocation)) { error(errorLocation, nameNotFoundMessage, typeof nameArg === "string" ? nameArg : declarationNameToString(nameArg)); } @@ -930,7 +931,7 @@ namespace ts { } function checkAndReportErrorForMissingPrefix(errorLocation: Node, name: string, nameArg: string | Identifier): boolean { - if (!errorLocation || (errorLocation.kind === SyntaxKind.Identifier && (isTypeReferenceIdentifier(errorLocation)) || isInTypeQuery(errorLocation))) { + if ((errorLocation.kind === SyntaxKind.Identifier && (isTypeReferenceIdentifier(errorLocation)) || isInTypeQuery(errorLocation))) { return false; } @@ -980,23 +981,15 @@ namespace ts { * but returns undefined if that expression is not an EntityNameExpression. */ function climbToEntityNameOfExpressionWithTypeArguments(node: Node): EntityNameExpression | undefined { - for (; ; ) { - switch (node.kind) { - case SyntaxKind.Identifier: - case SyntaxKind.PropertyAccessExpression: - if (node.parent) { - node = node.parent; - } - else { - return undefined; - } - break; - case SyntaxKind.ExpressionWithTypeArguments: - Debug.assert(isEntityNameExpression((node).expression)); - return (node).expression; - default: - return undefined; - } + switch (node.kind) { + case SyntaxKind.Identifier: + case SyntaxKind.PropertyAccessExpression: + return node.parent ? climbToEntityNameOfExpressionWithTypeArguments(node.parent) : undefined; + case SyntaxKind.ExpressionWithTypeArguments: + Debug.assert(isEntityNameExpression((node).expression)); + return (node).expression + default: + return undefined; } } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index f946e4b614f..affbcbe7a98 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2685,17 +2685,8 @@ namespace ts { } export function isEntityNameExpression(node: Expression): node is EntityNameExpression { - for (; ; ) { - switch (node.kind) { - case SyntaxKind.Identifier: - return true; - case SyntaxKind.PropertyAccessExpression: - node = (node).expression; - break; - default: - return false; - } - } + return node.kind === SyntaxKind.Identifier || + node.kind === SyntaxKind.PropertyAccessExpression && isEntityNameExpression((node).expression); } export function isRightSideOfQualifiedNameOrPropertyAccess(node: Node) { From 6814a9fac6a218c8a51fd283b1cfd3144cf3d67b Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 2 Aug 2016 12:41:22 -0700 Subject: [PATCH 052/233] Rename function --- src/compiler/checker.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ca85a85e279..dd9b56a5b58 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -969,7 +969,7 @@ namespace ts { function checkAndReportErrorForExtendingInterface(errorLocation: Node): boolean { - const expression = climbToEntityNameOfExpressionWithTypeArguments(errorLocation); + const expression = getEntityNameForExtendingInterface(errorLocation); const isError = !!(expression && resolveEntityName(expression, SymbolFlags.Interface, /*ignoreErrors*/ true)); if (isError) { error(errorLocation, Diagnostics.Cannot_extend_an_interface_0_Did_you_mean_implements, getTextOfNode(expression)); @@ -980,11 +980,11 @@ namespace ts { * Climbs up parents to an ExpressionWithTypeArguments, and returns its expression, * but returns undefined if that expression is not an EntityNameExpression. */ - function climbToEntityNameOfExpressionWithTypeArguments(node: Node): EntityNameExpression | undefined { + function getEntityNameForExtendingInterface(node: Node): EntityNameExpression | undefined { switch (node.kind) { case SyntaxKind.Identifier: case SyntaxKind.PropertyAccessExpression: - return node.parent ? climbToEntityNameOfExpressionWithTypeArguments(node.parent) : undefined; + return node.parent ? getEntityNameForExtendingInterface(node.parent) : undefined; case SyntaxKind.ExpressionWithTypeArguments: Debug.assert(isEntityNameExpression((node).expression)); return (node).expression From 7908257ab7b59b4760e7012c03fcc700e31e7fa2 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 2 Aug 2016 13:18:46 -0700 Subject: [PATCH 053/233] Fix lint error --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dd9b56a5b58..0c65050a327 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -987,7 +987,7 @@ namespace ts { return node.parent ? getEntityNameForExtendingInterface(node.parent) : undefined; case SyntaxKind.ExpressionWithTypeArguments: Debug.assert(isEntityNameExpression((node).expression)); - return (node).expression + return (node).expression; default: return undefined; } From 4189b4d71855dd059b79d075320601cb12f71659 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 2 Aug 2016 16:10:20 -0700 Subject: [PATCH 054/233] Narrowing type parameter intersects w/narrowed types This makes sure that a union type that includes a type parameter is still usable as the actual type that the type guard narrows to. --- src/compiler/checker.ts | 30 +++++++++--- .../reference/controlFlowIfStatement.js | 32 +++++++++++++ .../reference/controlFlowIfStatement.symbols | 38 +++++++++++++++ .../reference/controlFlowIfStatement.types | 47 +++++++++++++++++++ .../controlFlow/controlFlowIfStatement.ts | 16 +++++++ 5 files changed, 156 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 04c12eb613c..2d4ce970172 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7900,13 +7900,17 @@ namespace ts { return TypeFacts.All; } - function getTypeWithFacts(type: Type, include: TypeFacts) { + function getTypeWithFacts(type: Type, include: TypeFacts, intersectForTypeParameters = false) { if (!(type.flags & TypeFlags.Union)) { return getTypeFacts(type) & include ? type : neverType; } let firstType: Type; + let hasTypeParameter = false; let types: Type[]; for (const t of (type as UnionType).types) { + if (t.flags & TypeFlags.TypeParameter) { + hasTypeParameter = true; + } if (getTypeFacts(t) & include) { if (!firstType) { firstType = t; @@ -7919,7 +7923,19 @@ namespace ts { } } } - return firstType ? types ? getUnionType(types) : firstType : neverType; + const narrowed = types ? getUnionType(types) : + firstType ? firstType : neverType; + // if there is a type parameter in the narrowed type, + // add an intersection with the members of the narrowed type so that the shape of the type is correct + if (type.flags & TypeFlags.Union && + narrowed.flags & TypeFlags.Union && + hasTypeParameter && + intersectForTypeParameters) { + return getIntersectionType(types.concat([narrowed])); + } + else { + return narrowed; + } } function getTypeWithDefault(type: Type, defaultExpression: Expression) { @@ -8183,7 +8199,7 @@ namespace ts { let type = getTypeAtFlowNode(flow.antecedent); if (type !== neverType) { // If we have an antecedent type (meaning we're reachable in some way), we first - // attempt to narrow the antecedent type. If that produces the nothing type, then + // attempt to narrow the antecedent type. If that produces the never type, then // we take the type guard as an indication that control could reach here in a // manner not understood by the control flow analyzer (e.g. a function argument // has an invalid type, or a nested function has possibly made an assignment to a @@ -8292,10 +8308,10 @@ namespace ts { function narrowTypeByTruthiness(type: Type, expr: Expression, assumeTrue: boolean): Type { if (isMatchingReference(reference, expr)) { - return getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy); + return getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy, assumeTrue); } if (isMatchingPropertyAccess(expr)) { - return narrowTypeByDiscriminant(type, expr, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy)); + return narrowTypeByDiscriminant(type, expr, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy, assumeTrue)); } return type; } @@ -8353,7 +8369,7 @@ namespace ts { value.kind === SyntaxKind.NullKeyword ? assumeTrue ? TypeFacts.EQNull : TypeFacts.NENull : assumeTrue ? TypeFacts.EQUndefined : TypeFacts.NEUndefined; - return getTypeWithFacts(type, facts); + return getTypeWithFacts(type, facts, assumeTrue); } if (type.flags & TypeFlags.NotUnionOrUnit) { return type; @@ -8391,7 +8407,7 @@ namespace ts { const facts = assumeTrue ? getProperty(typeofEQFacts, literal.text) || TypeFacts.TypeofEQHostObject : getProperty(typeofNEFacts, literal.text) || TypeFacts.TypeofNEHostObject; - return getTypeWithFacts(type, facts); + return getTypeWithFacts(type, facts, assumeTrue); } function narrowTypeBySwitchOnDiscriminant(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number) { diff --git a/tests/baselines/reference/controlFlowIfStatement.js b/tests/baselines/reference/controlFlowIfStatement.js index a70c07d38dc..e40b831552f 100644 --- a/tests/baselines/reference/controlFlowIfStatement.js +++ b/tests/baselines/reference/controlFlowIfStatement.js @@ -35,6 +35,22 @@ function b() { } x; // string } +function c(data: string | T): T { + if (typeof data === 'string') { + return JSON.parse(data); + } + else { + return data; + } +} +function d(data: string | T): never { + if (typeof data === 'string') { + throw new Error('will always happen'); + } + else { + return data; + } +} //// [controlFlowIfStatement.js] @@ -72,3 +88,19 @@ function b() { } x; // string } +function c(data) { + if (typeof data === 'string') { + return JSON.parse(data); + } + else { + return data; + } +} +function d(data) { + if (typeof data === 'string') { + throw new Error('will always happen'); + } + else { + return data; + } +} diff --git a/tests/baselines/reference/controlFlowIfStatement.symbols b/tests/baselines/reference/controlFlowIfStatement.symbols index 1d85b31c998..e4d2bb9f184 100644 --- a/tests/baselines/reference/controlFlowIfStatement.symbols +++ b/tests/baselines/reference/controlFlowIfStatement.symbols @@ -71,4 +71,42 @@ function b() { x; // string >x : Symbol(x, Decl(controlFlowIfStatement.ts, 26, 7)) } +function c(data: string | T): T { +>c : Symbol(c, Decl(controlFlowIfStatement.ts, 35, 1)) +>T : Symbol(T, Decl(controlFlowIfStatement.ts, 36, 11)) +>data : Symbol(data, Decl(controlFlowIfStatement.ts, 36, 14)) +>T : Symbol(T, Decl(controlFlowIfStatement.ts, 36, 11)) +>T : Symbol(T, Decl(controlFlowIfStatement.ts, 36, 11)) + + if (typeof data === 'string') { +>data : Symbol(data, Decl(controlFlowIfStatement.ts, 36, 14)) + + return JSON.parse(data); +>JSON.parse : Symbol(JSON.parse, Decl(lib.d.ts, --, --)) +>JSON : Symbol(JSON, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>parse : Symbol(JSON.parse, Decl(lib.d.ts, --, --)) +>data : Symbol(data, Decl(controlFlowIfStatement.ts, 36, 14)) + } + else { + return data; +>data : Symbol(data, Decl(controlFlowIfStatement.ts, 36, 14)) + } +} +function d(data: string | T): never { +>d : Symbol(d, Decl(controlFlowIfStatement.ts, 43, 1)) +>T : Symbol(T, Decl(controlFlowIfStatement.ts, 44, 11)) +>data : Symbol(data, Decl(controlFlowIfStatement.ts, 44, 29)) +>T : Symbol(T, Decl(controlFlowIfStatement.ts, 44, 11)) + + if (typeof data === 'string') { +>data : Symbol(data, Decl(controlFlowIfStatement.ts, 44, 29)) + + throw new Error('will always happen'); +>Error : Symbol(Error, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + } + else { + return data; +>data : Symbol(data, Decl(controlFlowIfStatement.ts, 44, 29)) + } +} diff --git a/tests/baselines/reference/controlFlowIfStatement.types b/tests/baselines/reference/controlFlowIfStatement.types index 716491ea58b..e706f9f6a4e 100644 --- a/tests/baselines/reference/controlFlowIfStatement.types +++ b/tests/baselines/reference/controlFlowIfStatement.types @@ -90,4 +90,51 @@ function b() { x; // string >x : string } +function c(data: string | T): T { +>c : (data: string | T) => T +>T : T +>data : string | T +>T : T +>T : T + + if (typeof data === 'string') { +>typeof data === 'string' : boolean +>typeof data : string +>data : string | T +>'string' : "string" + + return JSON.parse(data); +>JSON.parse(data) : any +>JSON.parse : (text: string, reviver?: (key: any, value: any) => any) => any +>JSON : JSON +>parse : (text: string, reviver?: (key: any, value: any) => any) => any +>data : string & T & (string | T) + } + else { + return data; +>data : T + } +} +function d(data: string | T): never { +>d : (data: string | T) => never +>T : T +>data : string | T +>T : T + + if (typeof data === 'string') { +>typeof data === 'string' : boolean +>typeof data : string +>data : string | T +>'string' : "string" + + throw new Error('will always happen'); +>new Error('will always happen') : Error +>Error : ErrorConstructor +>'will always happen' : string + } + else { + return data; +>data : never + } +} diff --git a/tests/cases/conformance/controlFlow/controlFlowIfStatement.ts b/tests/cases/conformance/controlFlow/controlFlowIfStatement.ts index c9e9be92f8e..dc1cf97fe59 100644 --- a/tests/cases/conformance/controlFlow/controlFlowIfStatement.ts +++ b/tests/cases/conformance/controlFlow/controlFlowIfStatement.ts @@ -34,3 +34,19 @@ function b() { } x; // string } +function c(data: string | T): T { + if (typeof data === 'string') { + return JSON.parse(data); + } + else { + return data; + } +} +function d(data: string | T): never { + if (typeof data === 'string') { + throw new Error('will always happen'); + } + else { + return data; + } +} From 359c8b12ef3a8339940edd9203dcb2a10c08615b Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 3 Aug 2016 07:12:10 -0700 Subject: [PATCH 055/233] Don't allow ".d.ts" extension in an import either. --- src/compiler/checker.ts | 8 ++-- src/compiler/core.ts | 3 +- src/compiler/diagnosticMessages.json | 2 +- src/compiler/program.ts | 16 ++------ src/compiler/utilities.ts | 6 ++- src/harness/compilerRunner.ts | 2 +- .../reference/moduleResolutionNoTs.errors.txt | 40 +++++++++++-------- .../reference/moduleResolutionNoTs.js | 11 ++--- tests/cases/compiler/moduleResolutionNoTs.ts | 9 +++-- 9 files changed, 50 insertions(+), 47 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 290fa257daf..6dd2747e33f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1366,10 +1366,10 @@ namespace ts { if (moduleNotFoundError) { // report errors only if it was requested - const nonDtsExtension = tryExtractTypeScriptExtensionNonDts(moduleName); - if (nonDtsExtension) { - const diag = Diagnostics.An_import_path_should_not_end_with_a_0_extension_Consider_importing_1_instead; - error(moduleReferenceLiteral, diag, nonDtsExtension, removeExtension(moduleName, nonDtsExtension)); + const tsExtension = tryExtractTypeScriptExtension(moduleName); + if (tsExtension) { + const diag = Diagnostics.An_import_path_cannot_end_with_a_0_extension_Consider_importing_1_instead; + error(moduleReferenceLiteral, diag, tsExtension, removeExtension(moduleName, tsExtension)); } else { error(moduleReferenceLiteral, moduleNotFoundError, moduleName); diff --git a/src/compiler/core.ts b/src/compiler/core.ts index d77036660b0..869cddb2d4d 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1198,8 +1198,7 @@ namespace ts { /** * List of supported extensions in order of file resolution precedence. */ - export const supportedTypeScriptExtensionsNonDts = [".ts", ".tsx"]; - export const supportedTypeScriptExtensions = supportedTypeScriptExtensionsNonDts.concat([".d.ts"]); + export const supportedTypeScriptExtensions = [".ts", ".tsx", ".d.ts"]; export const supportedJavascriptExtensions = [".js", ".jsx"]; const allSupportedExtensions = supportedTypeScriptExtensions.concat(supportedJavascriptExtensions); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 93bd6c608f0..8975e5daa04 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1951,7 +1951,7 @@ "category": "Error", "code": 2690 }, - "An import path should not end with a '{0}' extension. Consider importing '{1}' instead.": { + "An import path cannot end with a '{0}' extension. Consider importing '{1}' instead.": { "category": "Error", "code": 2691 }, diff --git a/src/compiler/program.ts b/src/compiler/program.ts index a2674d79783..d35ce644120 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -669,17 +669,7 @@ namespace ts { * in cases when we know upfront that all load attempts will fail (because containing folder does not exists) however we still need to record all failed lookup locations. */ function loadModuleFromFile(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string | undefined { - // If the candidate already has an extension load that or quit. - if (hasTypeScriptFileExtension(candidate)) { - // Don't allow `.ts` to appear at the end - if (!fileExtensionIs(candidate, ".d.ts")) { - return undefined; - } - return tryFile(candidate, failedLookupLocation, onlyRecordFailures, state); - } - - // Next, try adding an extension. - // We don't allow an import of "foo.ts" to be matched by "foo.ts.ts", but we do allow "foo.js" to be matched by "foo.js.ts". + // First, try adding an extension. An import of "foo" could be matched by a file "foo.ts", or "foo.js" by "foo.js.ts" const resolvedByAddingExtension = tryAddingExtensions(candidate, extensions, failedLookupLocation, onlyRecordFailures, state); if (resolvedByAddingExtension) { return resolvedByAddingExtension; @@ -736,7 +726,9 @@ namespace ts { } const typesFile = tryReadTypesSection(packageJsonPath, candidate, state); if (typesFile) { - const result = loadModuleFromFile(typesFile, extensions, failedLookupLocation, !directoryProbablyExists(getDirectoryPath(typesFile), state.host), state); + const onlyRecordFailures = !directoryProbablyExists(getDirectoryPath(typesFile), state.host); + // The package.json "typings" property must specify the file with extension, so just try that exact filename. + const result = tryFile(typesFile, failedLookupLocation, onlyRecordFailures, state); if (result) { return result; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index b92037b7e5b..22c93550725 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2727,9 +2727,11 @@ namespace ts { } /** Return ".ts" or ".tsx" if that is the extension. */ - export function tryExtractTypeScriptExtensionNonDts(fileName: string): string | undefined { - return find(supportedTypeScriptExtensionsNonDts, extension => fileExtensionIs(fileName, extension)); + export function tryExtractTypeScriptExtension(fileName: string): string | undefined { + return find(supportedTypescriptExtensionsWithDtsFirst, extension => fileExtensionIs(fileName, extension)); } + // Must have '.d.ts' first because if '.ts' goes first, that will be detected as the extension instead of '.d.ts'. + const supportedTypescriptExtensionsWithDtsFirst = supportedTypeScriptExtensions.slice().reverse(); /** * Replace each instance of non-ascii characters by one, two, three, or four escape sequences diff --git a/src/harness/compilerRunner.ts b/src/harness/compilerRunner.ts index ecdc18394b0..b8467adda5c 100644 --- a/src/harness/compilerRunner.ts +++ b/src/harness/compilerRunner.ts @@ -52,7 +52,7 @@ class CompilerBaselineRunner extends RunnerBase { private makeUnitName(name: string, root: string) { const path = ts.toPath(name, root, (fileName) => Harness.Compiler.getCanonicalFileName(fileName)); const pathStart = ts.toPath(Harness.IO.getCurrentDirectory(), "", (fileName) => Harness.Compiler.getCanonicalFileName(fileName)); - return path.replace(pathStart, "/"); + return pathStart === "" ? path : path.replace(pathStart, "/"); }; public checkTestCodeOutput(fileName: string) { diff --git a/tests/baselines/reference/moduleResolutionNoTs.errors.txt b/tests/baselines/reference/moduleResolutionNoTs.errors.txt index 2a9b74a1af6..c0543307311 100644 --- a/tests/baselines/reference/moduleResolutionNoTs.errors.txt +++ b/tests/baselines/reference/moduleResolutionNoTs.errors.txt @@ -1,25 +1,31 @@ -tests/cases/compiler/user.ts(4,15): error TS2691: An import path should not end with a '.ts' extension. Consider importing './x' instead. -tests/cases/compiler/user.ts(5,15): error TS2691: An import path should not end with a '.tsx' extension. Consider importing './y' instead. +tests/cases/compiler/user.ts(1,15): error TS2691: An import path cannot end with a '.ts' extension. Consider importing './x' instead. +tests/cases/compiler/user.ts(2,15): error TS2691: An import path cannot end with a '.tsx' extension. Consider importing './y' instead. +tests/cases/compiler/user.ts(3,15): error TS2691: An import path cannot end with a '.d.ts' extension. Consider importing './z' instead. -==== tests/cases/compiler/user.ts (2 errors) ==== - // '.ts' extension is OK in a reference - /// - - import x from "./x.ts"; - ~~~~~~~~ -!!! error TS2691: An import path should not end with a '.ts' extension. Consider importing './x' instead. - import y from "./y.tsx"; - ~~~~~~~~~ -!!! error TS2691: An import path should not end with a '.tsx' extension. Consider importing './y' instead. - - // Making sure the suggested fixes are valid: - import x2 from "./x"; - import y2 from "./y"; - ==== tests/cases/compiler/x.ts (0 errors) ==== export default 0; ==== tests/cases/compiler/y.tsx (0 errors) ==== export default 0; + +==== tests/cases/compiler/z.d.ts (0 errors) ==== + declare const x: number; + export default x; + +==== tests/cases/compiler/user.ts (3 errors) ==== + import x from "./x.ts"; + ~~~~~~~~ +!!! error TS2691: An import path cannot end with a '.ts' extension. Consider importing './x' instead. + import y from "./y.tsx"; + ~~~~~~~~~ +!!! error TS2691: An import path cannot end with a '.tsx' extension. Consider importing './y' instead. + import z from "./z.d.ts"; + ~~~~~~~~~~ +!!! error TS2691: An import path cannot end with a '.d.ts' extension. Consider importing './z' instead. + + // Making sure the suggested fixes are valid: + import x2 from "./x"; + import y2 from "./y"; + import z2 from "./z"; \ No newline at end of file diff --git a/tests/baselines/reference/moduleResolutionNoTs.js b/tests/baselines/reference/moduleResolutionNoTs.js index d9d918f5600..9ff33d688ea 100644 --- a/tests/baselines/reference/moduleResolutionNoTs.js +++ b/tests/baselines/reference/moduleResolutionNoTs.js @@ -6,16 +6,19 @@ export default 0; //// [y.tsx] export default 0; +//// [z.d.ts] +declare const x: number; +export default x; + //// [user.ts] -// '.ts' extension is OK in a reference -/// - import x from "./x.ts"; import y from "./y.tsx"; +import z from "./z.d.ts"; // Making sure the suggested fixes are valid: import x2 from "./x"; import y2 from "./y"; +import z2 from "./z"; //// [x.js] @@ -27,6 +30,4 @@ exports["default"] = 0; exports.__esModule = true; exports["default"] = 0; //// [user.js] -// '.ts' extension is OK in a reference -/// "use strict"; diff --git a/tests/cases/compiler/moduleResolutionNoTs.ts b/tests/cases/compiler/moduleResolutionNoTs.ts index 683f6630d9f..2051bc259bf 100644 --- a/tests/cases/compiler/moduleResolutionNoTs.ts +++ b/tests/cases/compiler/moduleResolutionNoTs.ts @@ -4,13 +4,16 @@ export default 0; // @filename: y.tsx export default 0; -// @filename: user.ts -// '.ts' extension is OK in a reference -/// +// @filename: z.d.ts +declare const x: number; +export default x; +// @filename: user.ts import x from "./x.ts"; import y from "./y.tsx"; +import z from "./z.d.ts"; // Making sure the suggested fixes are valid: import x2 from "./x"; import y2 from "./y"; +import z2 from "./z"; From 204f2c16c0d6ff851e4798c03a9646b625ac2bd7 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 3 Aug 2016 08:55:55 -0700 Subject: [PATCH 056/233] Add a helper function `getOrUpdateProperty` to prevent unprotected access to Maps. --- src/compiler/core.ts | 10 +++++++--- src/compiler/emitter.ts | 2 +- tests/baselines/reference/exportToString.js | 9 +++++++++ tests/baselines/reference/exportToString.symbols | 7 +++++++ tests/baselines/reference/exportToString.types | 8 ++++++++ tests/cases/compiler/exportToString.ts | 2 ++ 6 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 tests/baselines/reference/exportToString.js create mode 100644 tests/baselines/reference/exportToString.symbols create mode 100644 tests/baselines/reference/exportToString.types create mode 100644 tests/cases/compiler/exportToString.ts diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 709a331e022..6c87ad82955 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -323,8 +323,12 @@ namespace ts { return keys; } - export function getProperty(map: Map, key: string): T { - return hasOwnProperty.call(map, key) ? map[key] : undefined; + export function getProperty(map: Map, key: string): T | undefined { + return hasProperty(map, key) ? map[key] : undefined; + } + + export function getOrUpdateProperty(map: Map, key: string, makeValue: () => T): T { + return hasProperty(map, key) ? map[key] : map[key] = makeValue(); } export function isEmpty(map: Map) { @@ -941,7 +945,7 @@ namespace ts { * [^./] # matches everything up to the first . character (excluding directory seperators) * (\\.(?!min\\.js$))? # matches . characters but not if they are part of the .min.js file extension */ - const singleAsteriskRegexFragmentFiles = "([^./]|(\\.(?!min\\.js$))?)*"; + const singleAsteriskRegexFragmentFiles = "([^./]|(\\.(?!min\\.js$))?)*"; const singleAsteriskRegexFragmentOther = "[^/]*"; export function getRegularExpressionForWildcard(specs: string[], basePath: string, usage: "files" | "directories" | "exclude") { diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 5fe5027978c..c8bd8072b24 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -6842,7 +6842,7 @@ const _super = (function (geti, seti) { // export { x, y } for (const specifier of (node).exportClause.elements) { const name = (specifier.propertyName || specifier.name).text; - (exportSpecifiers[name] || (exportSpecifiers[name] = [])).push(specifier); + getOrUpdateProperty(exportSpecifiers, name, () => []).push(specifier); } } break; diff --git a/tests/baselines/reference/exportToString.js b/tests/baselines/reference/exportToString.js new file mode 100644 index 00000000000..190d1693c24 --- /dev/null +++ b/tests/baselines/reference/exportToString.js @@ -0,0 +1,9 @@ +//// [exportToString.ts] +const toString = 0; +export { toString }; + + +//// [exportToString.js] +"use strict"; +var toString = 0; +exports.toString = toString; diff --git a/tests/baselines/reference/exportToString.symbols b/tests/baselines/reference/exportToString.symbols new file mode 100644 index 00000000000..ce5446317a4 --- /dev/null +++ b/tests/baselines/reference/exportToString.symbols @@ -0,0 +1,7 @@ +=== tests/cases/compiler/exportToString.ts === +const toString = 0; +>toString : Symbol(toString, Decl(exportToString.ts, 0, 5)) + +export { toString }; +>toString : Symbol(toString, Decl(exportToString.ts, 1, 8)) + diff --git a/tests/baselines/reference/exportToString.types b/tests/baselines/reference/exportToString.types new file mode 100644 index 00000000000..17037852f3b --- /dev/null +++ b/tests/baselines/reference/exportToString.types @@ -0,0 +1,8 @@ +=== tests/cases/compiler/exportToString.ts === +const toString = 0; +>toString : number +>0 : number + +export { toString }; +>toString : number + diff --git a/tests/cases/compiler/exportToString.ts b/tests/cases/compiler/exportToString.ts new file mode 100644 index 00000000000..248df036bfd --- /dev/null +++ b/tests/cases/compiler/exportToString.ts @@ -0,0 +1,2 @@ +const toString = 0; +export { toString }; From 7ab6e11aaf5a3c98592555889e34ec4301cd0339 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 3 Aug 2016 10:00:37 -0700 Subject: [PATCH 057/233] Limit type guards as assertions to incomplete types in loops --- src/compiler/checker.ts | 74 +++++++++++++++++++++++++---------------- src/compiler/types.ts | 10 ++++++ 2 files changed, 56 insertions(+), 28 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0a45b61660a..69c93a75323 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -215,7 +215,7 @@ namespace ts { const flowLoopKeys: string[] = []; const flowLoopTypes: Type[][] = []; const visitedFlowNodes: FlowNode[] = []; - const visitedFlowTypes: Type[] = []; + const visitedFlowTypes: FlowType[] = []; const potentialThisCollisions: Node[] = []; const awaitedTypeStack: number[] = []; @@ -8086,6 +8086,18 @@ namespace ts { f(type) ? type : neverType; } + function isIncomplete(flowType: FlowType) { + return flowType.flags === 0; + } + + function getTypeFromFlowType(flowType: FlowType) { + return flowType.flags === 0 ? (flowType).type : flowType; + } + + function createFlowType(type: Type, incomplete: boolean): FlowType { + return incomplete ? { flags: 0, type } : type; + } + function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, includeOuterFunctions: boolean) { let key: string; if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) { @@ -8093,14 +8105,14 @@ namespace ts { } const initialType = assumeInitialized ? declaredType : includeFalsyTypes(declaredType, TypeFlags.Undefined); const visitedFlowStart = visitedFlowCount; - const result = getTypeAtFlowNode(reference.flowNode); + const result = getTypeFromFlowType(getTypeAtFlowNode(reference.flowNode)); visitedFlowCount = visitedFlowStart; if (reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(result, TypeFacts.NEUndefinedOrNull) === neverType) { return declaredType; } return result; - function getTypeAtFlowNode(flow: FlowNode): Type { + function getTypeAtFlowNode(flow: FlowNode): FlowType { while (true) { if (flow.flags & FlowFlags.Shared) { // We cache results of flow type resolution for shared nodes that were previously visited in @@ -8112,7 +8124,7 @@ namespace ts { } } } - let type: Type; + let type: FlowType; if (flow.flags & FlowFlags.Assignment) { type = getTypeAtFlowAssignment(flow); if (!type) { @@ -8180,41 +8192,44 @@ namespace ts { return undefined; } - function getTypeAtFlowCondition(flow: FlowCondition) { - let type = getTypeAtFlowNode(flow.antecedent); + function getTypeAtFlowCondition(flow: FlowCondition): FlowType { + const flowType = getTypeAtFlowNode(flow.antecedent); + let type = getTypeFromFlowType(flowType); if (type !== neverType) { // If we have an antecedent type (meaning we're reachable in some way), we first - // attempt to narrow the antecedent type. If that produces the nothing type, then - // we take the type guard as an indication that control could reach here in a - // manner not understood by the control flow analyzer (e.g. a function argument - // has an invalid type, or a nested function has possibly made an assignment to a - // captured variable). We proceed by reverting to the declared type and then + // attempt to narrow the antecedent type. If that produces the never type, and if + // the antecedent type is incomplete (i.e. a transient type in a loop), then we + // take the type guard as an indication that control *could* reach here once we + // have the complete type. We proceed by reverting to the declared type and then // narrow that. const assumeTrue = (flow.flags & FlowFlags.TrueCondition) !== 0; type = narrowType(type, flow.expression, assumeTrue); - if (type === neverType) { + if (type === neverType && isIncomplete(flowType)) { type = narrowType(declaredType, flow.expression, assumeTrue); } } - return type; + return createFlowType(type, isIncomplete(flowType)); } - function getTypeAtSwitchClause(flow: FlowSwitchClause) { - const type = getTypeAtFlowNode(flow.antecedent); + function getTypeAtSwitchClause(flow: FlowSwitchClause): FlowType { + const flowType = getTypeAtFlowNode(flow.antecedent); + let type = getTypeFromFlowType(flowType); const expr = flow.switchStatement.expression; if (isMatchingReference(reference, expr)) { - return narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd); + type = narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd); } - if (isMatchingPropertyAccess(expr)) { - return narrowTypeByDiscriminant(type, expr, t => narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd)); + else if (isMatchingPropertyAccess(expr)) { + type = narrowTypeByDiscriminant(type, expr, t => narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd)); } - return type; + return createFlowType(type, isIncomplete(flowType)); } - function getTypeAtFlowBranchLabel(flow: FlowLabel) { + function getTypeAtFlowBranchLabel(flow: FlowLabel): FlowType { const antecedentTypes: Type[] = []; + let seenIncomplete = false; for (const antecedent of flow.antecedents) { - const type = getTypeAtFlowNode(antecedent); + const flowType = getTypeAtFlowNode(antecedent); + const type = getTypeFromFlowType(flowType); // If the type at a particular antecedent path is the declared type and the // reference is known to always be assigned (i.e. when declared and initial types // are the same), there is no reason to process more antecedents since the only @@ -8225,11 +8240,14 @@ namespace ts { if (!contains(antecedentTypes, type)) { antecedentTypes.push(type); } + if (isIncomplete(flowType)) { + seenIncomplete = true; + } } - return getUnionType(antecedentTypes); + return createFlowType(getUnionType(antecedentTypes), seenIncomplete); } - function getTypeAtFlowLoopLabel(flow: FlowLabel) { + function getTypeAtFlowLoopLabel(flow: FlowLabel): FlowType { // If we have previously computed the control flow type for the reference at // this flow loop junction, return the cached type. const id = getFlowNodeId(flow); @@ -8241,12 +8259,12 @@ namespace ts { return cache[key]; } // If this flow loop junction and reference are already being processed, return - // the union of the types computed for each branch so far. We should never see - // an empty array here because the first antecedent of a loop junction is always - // the non-looping control flow path that leads to the top. + // the union of the types computed for each branch so far, marked as incomplete. + // We should never see an empty array here because the first antecedent of a loop + // junction is always the non-looping control flow path that leads to the top. for (let i = flowLoopStart; i < flowLoopCount; i++) { if (flowLoopNodes[i] === flow && flowLoopKeys[i] === key) { - return getUnionType(flowLoopTypes[i]); + return createFlowType(getUnionType(flowLoopTypes[i]), true); } } // Add the flow loop junction and reference to the in-process stack and analyze @@ -8257,7 +8275,7 @@ namespace ts { flowLoopTypes[flowLoopCount] = antecedentTypes; for (const antecedent of flow.antecedents) { flowLoopCount++; - const type = getTypeAtFlowNode(antecedent); + const type = getTypeFromFlowType(getTypeAtFlowNode(antecedent)); flowLoopCount--; // If we see a value appear in the cache it is a sign that control flow analysis // was restarted and completed by checkExpressionCached. We can simply pick up diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 072209aa496..b9de9ce1635 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1606,6 +1606,16 @@ namespace ts { antecedent: FlowNode; } + export type FlowType = Type | IncompleteType; + + // Incomplete types occur during control flow analysis of loops. An IncompleteType + // is distinguished from a regular type by a flags value of zero. Incomplete type + // objects are internal to the getFlowTypeOfRefecence function and never escape it. + export interface IncompleteType { + flags: TypeFlags; // No flags set + type: Type; // The type marked incomplete + } + export interface AmdDependency { path: string; name: string; From 917d18a1ca1b490f1d022aba0a736d23d0e0e07b Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 3 Aug 2016 10:05:49 -0700 Subject: [PATCH 058/233] Accept new baselines --- .../reference/stringLiteralTypesAndTuples01.types | 2 +- .../typeGuardTautologicalConsistiency.types | 4 ++-- .../reference/typeGuardTypeOfUndefined.types | 6 +++--- .../baselines/reference/typeGuardsAsAssertions.types | 12 ++++++------ .../reference/typeGuardsInIfStatement.errors.txt | 5 ++++- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/tests/baselines/reference/stringLiteralTypesAndTuples01.types b/tests/baselines/reference/stringLiteralTypesAndTuples01.types index 0900d9fc745..10a30a6d75c 100644 --- a/tests/baselines/reference/stringLiteralTypesAndTuples01.types +++ b/tests/baselines/reference/stringLiteralTypesAndTuples01.types @@ -55,5 +55,5 @@ function rawr(dino: RexOrRaptor) { throw "Unexpected " + dino; >"Unexpected " + dino : string >"Unexpected " : string ->dino : "t-rex" +>dino : never } diff --git a/tests/baselines/reference/typeGuardTautologicalConsistiency.types b/tests/baselines/reference/typeGuardTautologicalConsistiency.types index f86a9e66fa2..0a6524dd26f 100644 --- a/tests/baselines/reference/typeGuardTautologicalConsistiency.types +++ b/tests/baselines/reference/typeGuardTautologicalConsistiency.types @@ -15,7 +15,7 @@ if (typeof stringOrNumber === "number") { >"number" : "number" stringOrNumber; ->stringOrNumber : string +>stringOrNumber : never } } @@ -31,6 +31,6 @@ if (typeof stringOrNumber === "number" && typeof stringOrNumber !== "number") { >"number" : "number" stringOrNumber; ->stringOrNumber : string +>stringOrNumber : never } diff --git a/tests/baselines/reference/typeGuardTypeOfUndefined.types b/tests/baselines/reference/typeGuardTypeOfUndefined.types index 8e9928896a1..520d045d9e0 100644 --- a/tests/baselines/reference/typeGuardTypeOfUndefined.types +++ b/tests/baselines/reference/typeGuardTypeOfUndefined.types @@ -47,7 +47,7 @@ function test2(a: any) { >"boolean" : "boolean" a; ->a : boolean +>a : never } else { a; @@ -129,7 +129,7 @@ function test5(a: boolean | void) { } else { a; ->a : void +>a : never } } else { @@ -188,7 +188,7 @@ function test7(a: boolean | void) { } else { a; ->a : void +>a : never } } diff --git a/tests/baselines/reference/typeGuardsAsAssertions.types b/tests/baselines/reference/typeGuardsAsAssertions.types index 34d3b296a48..661ce9fbc55 100644 --- a/tests/baselines/reference/typeGuardsAsAssertions.types +++ b/tests/baselines/reference/typeGuardsAsAssertions.types @@ -193,10 +193,10 @@ function f1() { >x : undefined x; // string | number (guard as assertion) ->x : string | number +>x : never } x; // string | number | undefined ->x : string | number | undefined +>x : undefined } function f2() { @@ -216,10 +216,10 @@ function f2() { >"string" : "string" x; // string (guard as assertion) ->x : string +>x : never } x; // string | undefined ->x : string | undefined +>x : undefined } function f3() { @@ -239,7 +239,7 @@ function f3() { return; } x; // string | number (guard as assertion) ->x : string | number +>x : never } function f4() { @@ -281,7 +281,7 @@ function f5(x: string | number) { >"number" : "number" x; // number (guard as assertion) ->x : number +>x : never } else { x; // string | number diff --git a/tests/baselines/reference/typeGuardsInIfStatement.errors.txt b/tests/baselines/reference/typeGuardsInIfStatement.errors.txt index 984ca454e76..cd9ae40932a 100644 --- a/tests/baselines/reference/typeGuardsInIfStatement.errors.txt +++ b/tests/baselines/reference/typeGuardsInIfStatement.errors.txt @@ -1,9 +1,10 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts(22,10): error TS2354: No best common type exists among return expressions. tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts(31,10): error TS2354: No best common type exists among return expressions. tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts(49,10): error TS2354: No best common type exists among return expressions. +tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts(139,17): error TS2339: Property 'toString' does not exist on type 'never'. -==== tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts (3 errors) ==== +==== tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts (4 errors) ==== // In the true branch statement of an 'if' statement, // the type of a variable or parameter is narrowed by any type guard in the 'if' condition when true. // In the false branch statement of an 'if' statement, @@ -149,5 +150,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts(49,10) return typeof x === "number" ? x.toString() // number : x.toString(); // boolean | string + ~~~~~~~~ +!!! error TS2339: Property 'toString' does not exist on type 'never'. } } \ No newline at end of file From 12eb57c4d0d9b1082e8f4e7b459685e5829bac78 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 3 Aug 2016 10:15:00 -0700 Subject: [PATCH 059/233] Fix linting error --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 69c93a75323..4e5da63af2f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8264,7 +8264,7 @@ namespace ts { // junction is always the non-looping control flow path that leads to the top. for (let i = flowLoopStart; i < flowLoopCount; i++) { if (flowLoopNodes[i] === flow && flowLoopKeys[i] === key) { - return createFlowType(getUnionType(flowLoopTypes[i]), true); + return createFlowType(getUnionType(flowLoopTypes[i]), /*incomplete*/ true); } } // Add the flow loop junction and reference to the in-process stack and analyze From 8c01efba04a3ee3a1fa0aec6658afb5884b9e3a4 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 3 Aug 2016 10:33:10 -0700 Subject: [PATCH 060/233] Allow JS multiple declarations of ctor properties When a property is declared in the constructor and on the prototype of an ES6 class, the property's symbol is discarded in favour of the method's symbol. That because the usual use for this pattern is to bind an instance function: `this.m = this.m.bind(this)`. In this case the type you want really is the method's type. --- src/compiler/binder.ts | 60 +++++++++++-------- src/compiler/types.ts | 1 + .../reference/multipleDeclarations.js | 32 +++++++++- .../reference/multipleDeclarations.symbols | 47 ++++++++++++++- .../reference/multipleDeclarations.types | 57 +++++++++++++++++- .../conformance/salsa/multipleDeclarations.ts | 17 +++++- 6 files changed, 184 insertions(+), 30 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 8d8666a67ab..6f0def32702 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -298,8 +298,10 @@ namespace ts { const name = isDefaultExport && parent ? "default" : getDeclarationName(node); let symbol: Symbol; - if (name !== undefined) { - + if (name === undefined) { + symbol = createSymbol(SymbolFlags.None, "__missing"); + } + else { // Check and see if the symbol table already has a symbol with this name. If not, // create a new symbol with this name and add it to the table. Note that we don't // give the new symbol any flags *yet*. This ensures that it will not conflict @@ -326,34 +328,38 @@ namespace ts { classifiableNames[name] = name; } - if (symbol.flags & excludes) { - if (node.name) { - node.name.parent = node; + else if (symbol.flags & excludes) { + if (symbol.isDiscardable) { + // Javascript constructor-declared symbols can be discarded in favor of + // prototype symbols like methods. + symbol = symbolTable[name] = createSymbol(SymbolFlags.None, name); } - - // Report errors every position with duplicate declaration - // Report errors on previous encountered declarations - let message = symbol.flags & SymbolFlags.BlockScopedVariable - ? Diagnostics.Cannot_redeclare_block_scoped_variable_0 - : Diagnostics.Duplicate_identifier_0; - - forEach(symbol.declarations, declaration => { - if (declaration.flags & NodeFlags.Default) { - message = Diagnostics.A_module_cannot_have_multiple_default_exports; + else { + if (node.name) { + node.name.parent = node; } - }); - forEach(symbol.declarations, declaration => { - file.bindDiagnostics.push(createDiagnosticForNode(declaration.name || declaration, message, getDisplayName(declaration))); - }); - file.bindDiagnostics.push(createDiagnosticForNode(node.name || node, message, getDisplayName(node))); + // Report errors every position with duplicate declaration + // Report errors on previous encountered declarations + let message = symbol.flags & SymbolFlags.BlockScopedVariable + ? Diagnostics.Cannot_redeclare_block_scoped_variable_0 + : Diagnostics.Duplicate_identifier_0; - symbol = createSymbol(SymbolFlags.None, name); + forEach(symbol.declarations, declaration => { + if (declaration.flags & NodeFlags.Default) { + message = Diagnostics.A_module_cannot_have_multiple_default_exports; + } + }); + + forEach(symbol.declarations, declaration => { + file.bindDiagnostics.push(createDiagnosticForNode(declaration.name || declaration, message, getDisplayName(declaration))); + }); + file.bindDiagnostics.push(createDiagnosticForNode(node.name || node, message, getDisplayName(node))); + + symbol = createSymbol(SymbolFlags.None, name); + } } } - else { - symbol = createSymbol(SymbolFlags.None, "__missing"); - } addDeclarationToSymbol(symbol, node, includes); symbol.parent = parent; @@ -1967,7 +1973,7 @@ namespace ts { } function bindThisPropertyAssignment(node: BinaryExpression) { - // Declare a 'member' in case it turns out the container was an ES5 class or ES6 constructor + // Declare a 'member' if the container is an ES5 class or ES6 constructor let assignee: Node; if (container.kind === SyntaxKind.FunctionDeclaration || container.kind === SyntaxKind.FunctionExpression) { assignee = container; @@ -1980,7 +1986,9 @@ namespace ts { } assignee.symbol.members = assignee.symbol.members || {}; // It's acceptable for multiple 'this' assignments of the same identifier to occur - declareSymbol(assignee.symbol.members, assignee.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property); + // AND it can be overwritten by subsequent method declarations + let symbol = declareSymbol(assignee.symbol.members, assignee.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property); + symbol.isDiscardable = true; } function bindPrototypePropertyAssignment(node: BinaryExpression) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 072209aa496..4c08d7a487c 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2137,6 +2137,7 @@ namespace ts { /* @internal */ exportSymbol?: Symbol; // Exported symbol associated with this symbol /* @internal */ constEnumOnlyModule?: boolean; // True if module contains only const enums or other modules with only const enums /* @internal */ isReferenced?: boolean; // True if the symbol is referenced elsewhere + /* @internal */ isDiscardable?: boolean;// True if a Javascript class property can be overwritten by a method } /* @internal */ diff --git a/tests/baselines/reference/multipleDeclarations.js b/tests/baselines/reference/multipleDeclarations.js index 5f5ac29e909..e5aae416663 100644 --- a/tests/baselines/reference/multipleDeclarations.js +++ b/tests/baselines/reference/multipleDeclarations.js @@ -5,7 +5,22 @@ function C() { } C.prototype.m = function() { this.nothing(); -}; +} + +class X { + constructor() { + this.m = this.m.bind(this); + this.mistake = 'frankly, complete nonsense'; + } + m() { + } + mistake() { + } +} +let x = new X(); +X.prototype.mistake = false; +x.m(); +x.mistake; //// [output.js] @@ -15,3 +30,18 @@ function C() { C.prototype.m = function () { this.nothing(); }; +var X = (function () { + function X() { + this.m = this.m.bind(this); + this.mistake = 'frankly, complete nonsense'; + } + X.prototype.m = function () { + }; + X.prototype.mistake = function () { + }; + return X; +}()); +var x = new X(); +X.prototype.mistake = false; +x.m(); +x.mistake; diff --git a/tests/baselines/reference/multipleDeclarations.symbols b/tests/baselines/reference/multipleDeclarations.symbols index a256943dd50..7e4bcd53c8a 100644 --- a/tests/baselines/reference/multipleDeclarations.symbols +++ b/tests/baselines/reference/multipleDeclarations.symbols @@ -14,6 +14,51 @@ C.prototype.m = function() { this.nothing(); >this : Symbol(C, Decl(input.js, 0, 0)) +} -}; +class X { +>X : Symbol(X, Decl(input.js, 6, 1)) + + constructor() { + this.m = this.m.bind(this); +>this.m : Symbol(X.m, Decl(input.js, 12, 5)) +>this : Symbol(X, Decl(input.js, 6, 1)) +>m : Symbol(X.m, Decl(input.js, 9, 19)) +>this.m.bind : Symbol(Function.bind, Decl(lib.d.ts, --, --)) +>this.m : Symbol(X.m, Decl(input.js, 12, 5)) +>this : Symbol(X, Decl(input.js, 6, 1)) +>m : Symbol(X.m, Decl(input.js, 12, 5)) +>bind : Symbol(Function.bind, Decl(lib.d.ts, --, --)) +>this : Symbol(X, Decl(input.js, 6, 1)) + + this.mistake = 'frankly, complete nonsense'; +>this.mistake : Symbol(X.mistake, Decl(input.js, 14, 5)) +>this : Symbol(X, Decl(input.js, 6, 1)) +>mistake : Symbol(X.mistake, Decl(input.js, 10, 35)) + } + m() { +>m : Symbol(X.m, Decl(input.js, 12, 5)) + } + mistake() { +>mistake : Symbol(X.mistake, Decl(input.js, 14, 5)) + } +} +let x = new X(); +>x : Symbol(x, Decl(input.js, 18, 3)) +>X : Symbol(X, Decl(input.js, 6, 1)) + +X.prototype.mistake = false; +>X.prototype.mistake : Symbol(X.mistake, Decl(input.js, 14, 5)) +>X : Symbol(X, Decl(input.js, 6, 1)) +>prototype : Symbol(X.prototype) + +x.m(); +>x.m : Symbol(X.m, Decl(input.js, 12, 5)) +>x : Symbol(x, Decl(input.js, 18, 3)) +>m : Symbol(X.m, Decl(input.js, 12, 5)) + +x.mistake; +>x.mistake : Symbol(X.mistake, Decl(input.js, 14, 5)) +>x : Symbol(x, Decl(input.js, 18, 3)) +>mistake : Symbol(X.mistake, Decl(input.js, 14, 5)) diff --git a/tests/baselines/reference/multipleDeclarations.types b/tests/baselines/reference/multipleDeclarations.types index 7c0a3de70c9..2a7cac18205 100644 --- a/tests/baselines/reference/multipleDeclarations.types +++ b/tests/baselines/reference/multipleDeclarations.types @@ -24,6 +24,61 @@ C.prototype.m = function() { >this.nothing : any >this : { m: () => void; } >nothing : any +} -}; +class X { +>X : X + + constructor() { + this.m = this.m.bind(this); +>this.m = this.m.bind(this) : any +>this.m : () => void +>this : this +>m : () => void +>this.m.bind(this) : any +>this.m.bind : (this: Function, thisArg: any, ...argArray: any[]) => any +>this.m : () => void +>this : this +>m : () => void +>bind : (this: Function, thisArg: any, ...argArray: any[]) => any +>this : this + + this.mistake = 'frankly, complete nonsense'; +>this.mistake = 'frankly, complete nonsense' : string +>this.mistake : () => void +>this : this +>mistake : () => void +>'frankly, complete nonsense' : string + } + m() { +>m : () => void + } + mistake() { +>mistake : () => void + } +} +let x = new X(); +>x : X +>new X() : X +>X : typeof X + +X.prototype.mistake = false; +>X.prototype.mistake = false : boolean +>X.prototype.mistake : () => void +>X.prototype : X +>X : typeof X +>prototype : X +>mistake : () => void +>false : boolean + +x.m(); +>x.m() : void +>x.m : () => void +>x : X +>m : () => void + +x.mistake; +>x.mistake : () => void +>x : X +>mistake : () => void diff --git a/tests/cases/conformance/salsa/multipleDeclarations.ts b/tests/cases/conformance/salsa/multipleDeclarations.ts index 6899be2ec89..9ead1eeb0a5 100644 --- a/tests/cases/conformance/salsa/multipleDeclarations.ts +++ b/tests/cases/conformance/salsa/multipleDeclarations.ts @@ -7,4 +7,19 @@ function C() { } C.prototype.m = function() { this.nothing(); -}; +} + +class X { + constructor() { + this.m = this.m.bind(this); + this.mistake = 'frankly, complete nonsense'; + } + m() { + } + mistake() { + } +} +let x = new X(); +X.prototype.mistake = false; +x.m(); +x.mistake; From 045b51a8ef0961b3c4420fb1d315e98bde77ece9 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 3 Aug 2016 14:36:05 -0700 Subject: [PATCH 061/233] Use {} type facts for unconstrained type params Previously it was using TypeFacts.All. But the constraint of an unconstrained type parameter is actually {}. --- src/compiler/checker.ts | 33 +++++-------------- .../reference/controlFlowIfStatement.types | 2 +- 2 files changed, 10 insertions(+), 25 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2d4ce970172..278d27a2347 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1187,7 +1187,7 @@ namespace ts { } function resolveSymbol(symbol: Symbol): Symbol { - return symbol && symbol.flags & SymbolFlags.Alias && !(symbol.flags & (SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace)) ? resolveAlias(symbol) : symbol; + return symbol && symbol.flags & SymbolFlags.Alias && !(symbol.flags & (SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace)) ? resolveAlias(symbol) : symbol; } function resolveAlias(symbol: Symbol): Symbol { @@ -7892,7 +7892,7 @@ namespace ts { } if (flags & TypeFlags.TypeParameter) { const constraint = getConstraintOfTypeParameter(type); - return constraint ? getTypeFacts(constraint) : TypeFacts.All; + return getTypeFacts(constraint || emptyObjectType); } if (flags & TypeFlags.UnionOrIntersection) { return getTypeFactsOfTypes((type).types); @@ -7900,17 +7900,13 @@ namespace ts { return TypeFacts.All; } - function getTypeWithFacts(type: Type, include: TypeFacts, intersectForTypeParameters = false) { + function getTypeWithFacts(type: Type, include: TypeFacts) { if (!(type.flags & TypeFlags.Union)) { return getTypeFacts(type) & include ? type : neverType; } let firstType: Type; - let hasTypeParameter = false; let types: Type[]; for (const t of (type as UnionType).types) { - if (t.flags & TypeFlags.TypeParameter) { - hasTypeParameter = true; - } if (getTypeFacts(t) & include) { if (!firstType) { firstType = t; @@ -7923,19 +7919,8 @@ namespace ts { } } } - const narrowed = types ? getUnionType(types) : - firstType ? firstType : neverType; - // if there is a type parameter in the narrowed type, - // add an intersection with the members of the narrowed type so that the shape of the type is correct - if (type.flags & TypeFlags.Union && - narrowed.flags & TypeFlags.Union && - hasTypeParameter && - intersectForTypeParameters) { - return getIntersectionType(types.concat([narrowed])); - } - else { - return narrowed; - } + return types ? getUnionType(types) : + firstType ? firstType : neverType; } function getTypeWithDefault(type: Type, defaultExpression: Expression) { @@ -8308,10 +8293,10 @@ namespace ts { function narrowTypeByTruthiness(type: Type, expr: Expression, assumeTrue: boolean): Type { if (isMatchingReference(reference, expr)) { - return getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy, assumeTrue); + return getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy); } if (isMatchingPropertyAccess(expr)) { - return narrowTypeByDiscriminant(type, expr, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy, assumeTrue)); + return narrowTypeByDiscriminant(type, expr, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy)); } return type; } @@ -8369,7 +8354,7 @@ namespace ts { value.kind === SyntaxKind.NullKeyword ? assumeTrue ? TypeFacts.EQNull : TypeFacts.NENull : assumeTrue ? TypeFacts.EQUndefined : TypeFacts.NEUndefined; - return getTypeWithFacts(type, facts, assumeTrue); + return getTypeWithFacts(type, facts); } if (type.flags & TypeFlags.NotUnionOrUnit) { return type; @@ -8407,7 +8392,7 @@ namespace ts { const facts = assumeTrue ? getProperty(typeofEQFacts, literal.text) || TypeFacts.TypeofEQHostObject : getProperty(typeofNEFacts, literal.text) || TypeFacts.TypeofNEHostObject; - return getTypeWithFacts(type, facts, assumeTrue); + return getTypeWithFacts(type, facts); } function narrowTypeBySwitchOnDiscriminant(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number) { diff --git a/tests/baselines/reference/controlFlowIfStatement.types b/tests/baselines/reference/controlFlowIfStatement.types index e706f9f6a4e..79985378bf5 100644 --- a/tests/baselines/reference/controlFlowIfStatement.types +++ b/tests/baselines/reference/controlFlowIfStatement.types @@ -108,7 +108,7 @@ function c(data: string | T): T { >JSON.parse : (text: string, reviver?: (key: any, value: any) => any) => any >JSON : JSON >parse : (text: string, reviver?: (key: any, value: any) => any) => any ->data : string & T & (string | T) +>data : string } else { return data; From 38ee13cc3215b09ccee398283c936c59a7d8723c Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 3 Aug 2016 14:38:05 -0700 Subject: [PATCH 062/233] Fix newline lint --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 278d27a2347..5e9fb856823 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1187,7 +1187,7 @@ namespace ts { } function resolveSymbol(symbol: Symbol): Symbol { - return symbol && symbol.flags & SymbolFlags.Alias && !(symbol.flags & (SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace)) ? resolveAlias(symbol) : symbol; + return symbol && symbol.flags & SymbolFlags.Alias && !(symbol.flags & (SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace)) ? resolveAlias(symbol) : symbol; } function resolveAlias(symbol: Symbol): Symbol { From 72057500b5220b28e637a6a31acb86f4f4273393 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 3 Aug 2016 16:10:14 -0700 Subject: [PATCH 063/233] Test that declares conflicting method first --- .../conformance/salsa/multipleDeclarations.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tests/cases/conformance/salsa/multipleDeclarations.ts b/tests/cases/conformance/salsa/multipleDeclarations.ts index 9ead1eeb0a5..ba2e14e11e9 100644 --- a/tests/cases/conformance/salsa/multipleDeclarations.ts +++ b/tests/cases/conformance/salsa/multipleDeclarations.ts @@ -1,14 +1,12 @@ // @filename: input.js // @out: output.js // @allowJs: true - function C() { this.m = null; } C.prototype.m = function() { this.nothing(); } - class X { constructor() { this.m = this.m.bind(this); @@ -23,3 +21,17 @@ let x = new X(); X.prototype.mistake = false; x.m(); x.mistake; +class Y { + mistake() { + } + m() { + } + constructor() { + this.m = this.m.bind(this); + this.mistake = 'even more nonsense'; + } +} +Y.prototype.mistake = true; +let y = new Y(); +y.m(); +y.mistake(); From 10b36abc8f85c8a2610fd56f04c81836bcf393a4 Mon Sep 17 00:00:00 2001 From: Yui Date: Thu, 4 Aug 2016 07:43:54 -0700 Subject: [PATCH 064/233] [Release-2.0] Fix 9662: Visual Studio 2015 with TS2.0 gives incorrect @types path resolution errors (#9867) * Change the shape of the shim layer to support getAutomaticTypeDirectives * Change the key for looking up automatic type-directives * Update baselines from change look-up name of type-directives * Add @currentDirectory into the test * Update baselines * Fix linting error * Address PR: fix spelling mistake * Instead of return path of the type directive names just return type directive names --- src/compiler/program.ts | 19 +++++----- src/compiler/types.ts | 1 + src/services/services.ts | 2 +- src/services/shims.ts | 35 ++++++++++++++++--- .../reference/library-reference-13.trace.json | 2 +- .../reference/library-reference-14.trace.json | 2 +- .../reference/library-reference-15.trace.json | 2 +- .../reference/library-reference-2.trace.json | 2 +- .../reference/library-reference-6.trace.json | 2 +- .../reference/typeReferenceDirectives1.js | 1 - .../typeReferenceDirectives1.symbols | 3 +- .../reference/typeReferenceDirectives1.types | 1 - .../compiler/typeReferenceDirectives1.ts | 2 +- .../compiler/typeReferenceDirectives10.ts | 1 + .../compiler/typeReferenceDirectives11.ts | 1 + .../compiler/typeReferenceDirectives12.ts | 1 + .../compiler/typeReferenceDirectives13.ts | 1 + .../compiler/typeReferenceDirectives2.ts | 1 + .../compiler/typeReferenceDirectives3.ts | 1 + .../compiler/typeReferenceDirectives4.ts | 1 + .../compiler/typeReferenceDirectives5.ts | 1 + .../compiler/typeReferenceDirectives6.ts | 1 + .../compiler/typeReferenceDirectives7.ts | 1 + .../compiler/typeReferenceDirectives8.ts | 1 + .../compiler/typeReferenceDirectives9.ts | 1 + .../references/library-reference-13.ts | 1 + .../conformance/typings/typingsLookup1.ts | 1 + 27 files changed, 62 insertions(+), 26 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 4b9948c3ba0..7d40b2f3219 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1055,19 +1055,15 @@ namespace ts { return resolutions; } - function getInferredTypesRoot(options: CompilerOptions, rootFiles: string[], host: CompilerHost) { - return computeCommonSourceDirectoryOfFilenames(rootFiles, host.getCurrentDirectory(), f => host.getCanonicalFileName(f)); - } - /** - * Given a set of options and a set of root files, returns the set of type directive names + * Given a set of options, returns the set of type directive names * that should be included for this program automatically. * This list could either come from the config file, * or from enumerating the types root + initial secondary types lookup location. * More type directives might appear in the program later as a result of loading actual source files; * this list is only the set of defaults that are implicitly included. */ - export function getAutomaticTypeDirectiveNames(options: CompilerOptions, rootFiles: string[], host: CompilerHost): string[] { + export function getAutomaticTypeDirectiveNames(options: CompilerOptions, host: ModuleResolutionHost): string[] { // Use explicit type list from tsconfig.json if (options.types) { return options.types; @@ -1080,7 +1076,10 @@ namespace ts { if (typeRoots) { for (const root of typeRoots) { if (host.directoryExists(root)) { - result = result.concat(host.getDirectories(root)); + for (const typeDirectivePath of host.getDirectories(root)) { + // Return just the type directive names + result = result.concat(getBaseFileName(normalizePath(typeDirectivePath))); + } } } } @@ -1155,11 +1154,11 @@ namespace ts { forEach(rootNames, name => processRootFile(name, /*isDefaultLib*/ false)); // load type declarations specified via 'types' argument or implicitly from types/ and node_modules/@types folders - const typeReferences: string[] = getAutomaticTypeDirectiveNames(options, rootNames, host); + const typeReferences: string[] = getAutomaticTypeDirectiveNames(options, host); if (typeReferences) { - const inferredRoot = getInferredTypesRoot(options, rootNames, host); - const containingFilename = combinePaths(inferredRoot, "__inferred type names__.ts"); + // This containingFilename needs to match with the one used in managed-side + const containingFilename = combinePaths(host.getCurrentDirectory(), "__inferred type names__.ts"); const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeReferences, containingFilename); for (let i = 0; i < typeReferences.length; i++) { processTypeReferenceDirective(typeReferences[i], resolutions[i]); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index b9de9ce1635..28ededebb0d 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2910,6 +2910,7 @@ namespace ts { directoryExists?(directoryName: string): boolean; realpath?(path: string): string; getCurrentDirectory?(): string; + getDirectories?(path: string): string[]; } export interface ResolvedModule { diff --git a/src/services/services.ts b/src/services/services.ts index 6ec95343e33..ab1e30a44a6 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1886,7 +1886,7 @@ namespace ts { }; } - // Cache host information about scrip Should be refreshed + // Cache host information about script should be refreshed // at each language service public entry point, since we don't know when // set of scripts handled by the host changes. class HostCache { diff --git a/src/services/shims.ts b/src/services/shims.ts index 271d6f2d6e2..45c4b284ae7 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -72,8 +72,13 @@ namespace ts { directoryExists(directoryName: string): boolean; } - /** Public interface of the the of a config service shim instance.*/ - export interface CoreServicesShimHost extends Logger, ModuleResolutionHost { + /** Public interface of the core-services host instance used in managed side */ + export interface CoreServicesShimHost extends Logger { + directoryExists(directoryName: string): boolean; + fileExists(fileName: string): boolean; + getCurrentDirectory(): string; + getDirectories(path: string): string; + /** * Returns a JSON-encoded value of the type: string[] * @@ -81,9 +86,14 @@ namespace ts { * when enumerating the directory. */ readDirectory(rootDir: string, extension: string, basePaths?: string, excludeEx?: string, includeFileEx?: string, includeDirEx?: string, depth?: number): string; - useCaseSensitiveFileNames?(): boolean; - getCurrentDirectory(): string; + + /** + * Read arbitary text files on disk, i.e. when resolution procedure needs the content of 'package.json' to determine location of bundled typings for node modules + */ + readFile(fileName: string): string; + realpath?(path: string): string; trace(s: string): void; + useCaseSensitiveFileNames?(): boolean; } /// @@ -240,6 +250,7 @@ namespace ts { } export interface CoreServicesShim extends Shim { + getAutomaticTypeDirectiveNames(compilerOptionsJson: string): string; getPreProcessedFileInfo(fileName: string, sourceText: IScriptSnapshot): string; getTSConfigFileInfo(fileName: string, sourceText: IScriptSnapshot): string; getDefaultCompilationSettings(): string; @@ -492,6 +503,10 @@ namespace ts { private readDirectoryFallback(rootDir: string, extension: string, exclude: string[]) { return JSON.parse(this.shimHost.readDirectory(rootDir, extension, JSON.stringify(exclude))); } + + public getDirectories(path: string): string[] { + return JSON.parse(this.shimHost.getDirectories(path)); + } } function simpleForwardCall(logger: Logger, actionDescription: string, action: () => any, logPerformance: boolean): any { @@ -1003,7 +1018,7 @@ namespace ts { public getPreProcessedFileInfo(fileName: string, sourceTextSnapshot: IScriptSnapshot): string { return this.forwardJSONCall( - "getPreProcessedFileInfo('" + fileName + "')", + `getPreProcessedFileInfo('${fileName}')`, () => { // for now treat files as JavaScript const result = preProcessFile(sourceTextSnapshot.getText(0, sourceTextSnapshot.getLength()), /* readImportFiles */ true, /* detectJavaScriptImports */ true); @@ -1017,6 +1032,16 @@ namespace ts { }); } + public getAutomaticTypeDirectiveNames(compilerOptionsJson: string): string { + return this.forwardJSONCall( + `getAutomaticTypeDirectiveNames('${compilerOptionsJson}')`, + () => { + const compilerOptions = JSON.parse(compilerOptionsJson); + return getAutomaticTypeDirectiveNames(compilerOptions, this.host); + } + ); + } + private convertFileReferences(refs: FileReference[]): IFileReference[] { if (!refs) { return undefined; diff --git a/tests/baselines/reference/library-reference-13.trace.json b/tests/baselines/reference/library-reference-13.trace.json index d8dfb57c2a6..a23f0ef0ca5 100644 --- a/tests/baselines/reference/library-reference-13.trace.json +++ b/tests/baselines/reference/library-reference-13.trace.json @@ -1,5 +1,5 @@ [ - "======== Resolving type reference directive 'jquery', containing file '/a/b/__inferred type names__.ts', root directory '/a/types'. ========", + "======== Resolving type reference directive 'jquery', containing file '/__inferred type names__.ts', root directory '/a/types'. ========", "Resolving with primary search path '/a/types'", "File '/a/types/jquery/package.json' does not exist.", "File '/a/types/jquery/index.d.ts' exist - use it as a name resolution result.", diff --git a/tests/baselines/reference/library-reference-14.trace.json b/tests/baselines/reference/library-reference-14.trace.json index d8dfb57c2a6..fb3a2bb7da4 100644 --- a/tests/baselines/reference/library-reference-14.trace.json +++ b/tests/baselines/reference/library-reference-14.trace.json @@ -1,5 +1,5 @@ [ - "======== Resolving type reference directive 'jquery', containing file '/a/b/__inferred type names__.ts', root directory '/a/types'. ========", + "======== Resolving type reference directive 'jquery', containing file '/a/__inferred type names__.ts', root directory '/a/types'. ========", "Resolving with primary search path '/a/types'", "File '/a/types/jquery/package.json' does not exist.", "File '/a/types/jquery/index.d.ts' exist - use it as a name resolution result.", diff --git a/tests/baselines/reference/library-reference-15.trace.json b/tests/baselines/reference/library-reference-15.trace.json index 3e9d7dba1d2..e23517976b0 100644 --- a/tests/baselines/reference/library-reference-15.trace.json +++ b/tests/baselines/reference/library-reference-15.trace.json @@ -1,5 +1,5 @@ [ - "======== Resolving type reference directive 'jquery', containing file '/a/b/__inferred type names__.ts', root directory 'types'. ========", + "======== Resolving type reference directive 'jquery', containing file '/a/__inferred type names__.ts', root directory 'types'. ========", "Resolving with primary search path 'types'", "File 'types/jquery/package.json' does not exist.", "File 'types/jquery/index.d.ts' exist - use it as a name resolution result.", diff --git a/tests/baselines/reference/library-reference-2.trace.json b/tests/baselines/reference/library-reference-2.trace.json index c26f7d2763d..64cdd809183 100644 --- a/tests/baselines/reference/library-reference-2.trace.json +++ b/tests/baselines/reference/library-reference-2.trace.json @@ -5,7 +5,7 @@ "'package.json' has 'types' field 'jquery.d.ts' that references '/types/jquery/jquery.d.ts'.", "File '/types/jquery/jquery.d.ts' exist - use it as a name resolution result.", "======== Type reference directive 'jquery' was successfully resolved to '/types/jquery/jquery.d.ts', primary: true. ========", - "======== Resolving type reference directive 'jquery', containing file '/__inferred type names__.ts', root directory '/types'. ========", + "======== Resolving type reference directive 'jquery', containing file 'test/__inferred type names__.ts', root directory '/types'. ========", "Resolving with primary search path '/types'", "Found 'package.json' at '/types/jquery/package.json'.", "'package.json' has 'types' field 'jquery.d.ts' that references '/types/jquery/jquery.d.ts'.", diff --git a/tests/baselines/reference/library-reference-6.trace.json b/tests/baselines/reference/library-reference-6.trace.json index 48fb49e6c7f..fd83c1431b4 100644 --- a/tests/baselines/reference/library-reference-6.trace.json +++ b/tests/baselines/reference/library-reference-6.trace.json @@ -4,7 +4,7 @@ "File '/node_modules/@types/alpha/package.json' does not exist.", "File '/node_modules/@types/alpha/index.d.ts' exist - use it as a name resolution result.", "======== Type reference directive 'alpha' was successfully resolved to '/node_modules/@types/alpha/index.d.ts', primary: true. ========", - "======== Resolving type reference directive 'alpha', containing file '/src/__inferred type names__.ts', root directory '/node_modules/@types'. ========", + "======== Resolving type reference directive 'alpha', containing file '/__inferred type names__.ts', root directory '/node_modules/@types'. ========", "Resolving with primary search path '/node_modules/@types'", "File '/node_modules/@types/alpha/package.json' does not exist.", "File '/node_modules/@types/alpha/index.d.ts' exist - use it as a name resolution result.", diff --git a/tests/baselines/reference/typeReferenceDirectives1.js b/tests/baselines/reference/typeReferenceDirectives1.js index 775af9c5283..a6210819931 100644 --- a/tests/baselines/reference/typeReferenceDirectives1.js +++ b/tests/baselines/reference/typeReferenceDirectives1.js @@ -2,7 +2,6 @@ //// [index.d.ts] - interface $ { x } //// [app.ts] diff --git a/tests/baselines/reference/typeReferenceDirectives1.symbols b/tests/baselines/reference/typeReferenceDirectives1.symbols index 55c17b219ec..a33a2aba408 100644 --- a/tests/baselines/reference/typeReferenceDirectives1.symbols +++ b/tests/baselines/reference/typeReferenceDirectives1.symbols @@ -9,8 +9,7 @@ interface A { } === /types/lib/index.d.ts === - interface $ { x } >$ : Symbol($, Decl(index.d.ts, 0, 0)) ->x : Symbol($.x, Decl(index.d.ts, 2, 13)) +>x : Symbol($.x, Decl(index.d.ts, 1, 13)) diff --git a/tests/baselines/reference/typeReferenceDirectives1.types b/tests/baselines/reference/typeReferenceDirectives1.types index 05080e05651..be789a08ddc 100644 --- a/tests/baselines/reference/typeReferenceDirectives1.types +++ b/tests/baselines/reference/typeReferenceDirectives1.types @@ -9,7 +9,6 @@ interface A { } === /types/lib/index.d.ts === - interface $ { x } >$ : $ >x : any diff --git a/tests/cases/compiler/typeReferenceDirectives1.ts b/tests/cases/compiler/typeReferenceDirectives1.ts index e17e498b978..13c84c7ae5e 100644 --- a/tests/cases/compiler/typeReferenceDirectives1.ts +++ b/tests/cases/compiler/typeReferenceDirectives1.ts @@ -2,7 +2,7 @@ // @traceResolution: true // @declaration: true // @typeRoots: /types - +// @currentDirectory: / // @filename: /types/lib/index.d.ts interface $ { x } diff --git a/tests/cases/compiler/typeReferenceDirectives10.ts b/tests/cases/compiler/typeReferenceDirectives10.ts index 61971ba44b2..1eb796d03fd 100644 --- a/tests/cases/compiler/typeReferenceDirectives10.ts +++ b/tests/cases/compiler/typeReferenceDirectives10.ts @@ -2,6 +2,7 @@ // @declaration: true // @typeRoots: /types // @traceResolution: true +// @currentDirectory: / // @filename: /ref.d.ts export interface $ { x } diff --git a/tests/cases/compiler/typeReferenceDirectives11.ts b/tests/cases/compiler/typeReferenceDirectives11.ts index 2d93e22bdcf..8b56459cd69 100644 --- a/tests/cases/compiler/typeReferenceDirectives11.ts +++ b/tests/cases/compiler/typeReferenceDirectives11.ts @@ -4,6 +4,7 @@ // @traceResolution: true // @types: lib // @out: output.js +// @currentDirectory: / // @filename: /types/lib/index.d.ts diff --git a/tests/cases/compiler/typeReferenceDirectives12.ts b/tests/cases/compiler/typeReferenceDirectives12.ts index efdb1e8312b..f1abe27f05f 100644 --- a/tests/cases/compiler/typeReferenceDirectives12.ts +++ b/tests/cases/compiler/typeReferenceDirectives12.ts @@ -3,6 +3,7 @@ // @typeRoots: /types // @traceResolution: true // @out: output.js +// @currentDirectory: / // @filename: /types/lib/index.d.ts diff --git a/tests/cases/compiler/typeReferenceDirectives13.ts b/tests/cases/compiler/typeReferenceDirectives13.ts index 124c31274ac..f9dede73267 100644 --- a/tests/cases/compiler/typeReferenceDirectives13.ts +++ b/tests/cases/compiler/typeReferenceDirectives13.ts @@ -2,6 +2,7 @@ // @declaration: true // @typeRoots: /types // @traceResolution: true +// @currentDirectory: / // @filename: /ref.d.ts export interface $ { x } diff --git a/tests/cases/compiler/typeReferenceDirectives2.ts b/tests/cases/compiler/typeReferenceDirectives2.ts index 31a01a0b8e4..44218683a5a 100644 --- a/tests/cases/compiler/typeReferenceDirectives2.ts +++ b/tests/cases/compiler/typeReferenceDirectives2.ts @@ -3,6 +3,7 @@ // @declaration: true // @typeRoots: /types // @types: lib +// @currentDirectory: / // @filename: /types/lib/index.d.ts interface $ { x } diff --git a/tests/cases/compiler/typeReferenceDirectives3.ts b/tests/cases/compiler/typeReferenceDirectives3.ts index 4c2729ab389..1baf0bdac9d 100644 --- a/tests/cases/compiler/typeReferenceDirectives3.ts +++ b/tests/cases/compiler/typeReferenceDirectives3.ts @@ -2,6 +2,7 @@ // @declaration: true // @typeRoots: /types // @traceResolution: true +// @currentDirectory: / // $ comes from d.ts file - no need to add type reference directive diff --git a/tests/cases/compiler/typeReferenceDirectives4.ts b/tests/cases/compiler/typeReferenceDirectives4.ts index ac7346895ef..dfa87b65138 100644 --- a/tests/cases/compiler/typeReferenceDirectives4.ts +++ b/tests/cases/compiler/typeReferenceDirectives4.ts @@ -2,6 +2,7 @@ // @traceResolution: true // @declaration: true // @typeRoots: /types +// @currentDirectory: / // $ comes from d.ts file - no need to add type reference directive diff --git a/tests/cases/compiler/typeReferenceDirectives5.ts b/tests/cases/compiler/typeReferenceDirectives5.ts index bb24b82b324..e81ae663e24 100644 --- a/tests/cases/compiler/typeReferenceDirectives5.ts +++ b/tests/cases/compiler/typeReferenceDirectives5.ts @@ -2,6 +2,7 @@ // @traceResolution: true // @declaration: true // @typeRoots: /types +// @currentDirectory: / // @filename: /ref.d.ts export interface $ { x } diff --git a/tests/cases/compiler/typeReferenceDirectives6.ts b/tests/cases/compiler/typeReferenceDirectives6.ts index 7154963f1ef..edf2ece7e06 100644 --- a/tests/cases/compiler/typeReferenceDirectives6.ts +++ b/tests/cases/compiler/typeReferenceDirectives6.ts @@ -2,6 +2,7 @@ // @traceResolution: true // @declaration: true // @typeRoots: /types +// @currentDirectory: / // $ comes from type declaration file - type reference directive should be added diff --git a/tests/cases/compiler/typeReferenceDirectives7.ts b/tests/cases/compiler/typeReferenceDirectives7.ts index 79d42fa7018..e9335b07082 100644 --- a/tests/cases/compiler/typeReferenceDirectives7.ts +++ b/tests/cases/compiler/typeReferenceDirectives7.ts @@ -2,6 +2,7 @@ // @traceResolution: true // @declaration: true // @typeRoots: /types +// @currentDirectory: / // local value shadows global - no need to add type reference directive diff --git a/tests/cases/compiler/typeReferenceDirectives8.ts b/tests/cases/compiler/typeReferenceDirectives8.ts index c7725a3aab1..bed69cbf357 100644 --- a/tests/cases/compiler/typeReferenceDirectives8.ts +++ b/tests/cases/compiler/typeReferenceDirectives8.ts @@ -3,6 +3,7 @@ // @typeRoots: /types // @traceResolution: true // @types: lib +// @currentDirectory: / // @filename: /types/lib/index.d.ts diff --git a/tests/cases/compiler/typeReferenceDirectives9.ts b/tests/cases/compiler/typeReferenceDirectives9.ts index 610c7173c89..1ad1aa52288 100644 --- a/tests/cases/compiler/typeReferenceDirectives9.ts +++ b/tests/cases/compiler/typeReferenceDirectives9.ts @@ -2,6 +2,7 @@ // @declaration: true // @typeRoots: /types // @traceResolution: true +// @currentDirectory: / // @filename: /types/lib/index.d.ts diff --git a/tests/cases/conformance/references/library-reference-13.ts b/tests/cases/conformance/references/library-reference-13.ts index 92b4b259ba4..419643d9d0d 100644 --- a/tests/cases/conformance/references/library-reference-13.ts +++ b/tests/cases/conformance/references/library-reference-13.ts @@ -1,5 +1,6 @@ // @noImplicitReferences: true // @traceResolution: true +// @currentDirectory: / // load type declarations from types section of tsconfig diff --git a/tests/cases/conformance/typings/typingsLookup1.ts b/tests/cases/conformance/typings/typingsLookup1.ts index 555d4569af3..150deef992a 100644 --- a/tests/cases/conformance/typings/typingsLookup1.ts +++ b/tests/cases/conformance/typings/typingsLookup1.ts @@ -1,5 +1,6 @@ // @traceResolution: true // @noImplicitReferences: true +// @currentDirectory: / // @filename: /tsconfig.json { "files": "a.ts" } From 18fb33d36fbac6d296b4e7d9a94beb29b57de607 Mon Sep 17 00:00:00 2001 From: Yui Date: Thu, 4 Aug 2016 08:11:42 -0700 Subject: [PATCH 065/233] Remove unused reference files: these tests produce erros so they will not produce these files (#9233) From e5973b8daa325344cf55b4c2840dd235e5e4e4f4 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 4 Aug 2016 09:46:35 -0700 Subject: [PATCH 066/233] Add string-literal completion test for jsdoc --- .../fourslash/completionForStringLiteral4.ts | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tests/cases/fourslash/completionForStringLiteral4.ts diff --git a/tests/cases/fourslash/completionForStringLiteral4.ts b/tests/cases/fourslash/completionForStringLiteral4.ts new file mode 100644 index 00000000000..c240c125dbe --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteral4.ts @@ -0,0 +1,22 @@ +/// +// @allowJs: true +// @Filename: in.js +/////** I am documentation +//// * @param {'literal'} p1 +//// * @param {"literal"} p2 +//// * @param {'other1' | 'other2'} p3 +//// * @param {'literal' | number} p4 +//// */ +////function f(p1, p2, p3, p4) { +//// return p1 + p2 + p3 + p4 + '.'; +////} +////f/*1*/('literal', 'literal', "o/*2*/ther1", 12); + +goTo.marker('1'); +verify.quickInfoExists(); +verify.quickInfoIs('function f(p1: "literal", p2: "literal", p3: "other1" | "other2", p4: "literal" | number): string', 'I am documentation'); + +goTo.marker('2'); +verify.completionListContains("other1"); +verify.completionListContains("other2"); +verify.memberListCount(2); From 3c32478b8fd433b5bdf708b27aef04309a107a3d Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 4 Aug 2016 13:01:17 -0700 Subject: [PATCH 067/233] Support other (new) literal types in jsdoc --- src/compiler/checker.ts | 4 ++-- src/compiler/parser.ts | 13 ++++++---- src/compiler/types.ts | 10 ++++---- ...{jsdocStringLiteral.js => jsdocLiteral.js} | 10 ++++---- .../baselines/reference/jsdocLiteral.symbols | 24 +++++++++++++++++++ ...StringLiteral.types => jsdocLiteral.types} | 16 ++++++++----- .../reference/jsdocStringLiteral.symbols | 21 ---------------- ...{jsdocStringLiteral.ts => jsdocLiteral.ts} | 5 ++-- .../fourslash/completionForStringLiteral4.ts | 7 +++--- 9 files changed, 63 insertions(+), 47 deletions(-) rename tests/baselines/reference/{jsdocStringLiteral.js => jsdocLiteral.js} (55%) create mode 100644 tests/baselines/reference/jsdocLiteral.symbols rename tests/baselines/reference/{jsdocStringLiteral.types => jsdocLiteral.types} (54%) delete mode 100644 tests/baselines/reference/jsdocStringLiteral.symbols rename tests/cases/conformance/jsdoc/{jsdocStringLiteral.ts => jsdocLiteral.ts} (63%) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 01aeb7ca744..ddc188ace92 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5529,8 +5529,8 @@ namespace ts { return getTypeFromThisTypeNode(node); case SyntaxKind.LiteralType: return getTypeFromLiteralTypeNode(node); - case SyntaxKind.JSDocStringLiteralType: - return getTypeFromStringLiteralTypeNode((node).stringLiteral); + case SyntaxKind.JSDocLiteralType: + return getTypeFromLiteralTypeNode((node).literal); case SyntaxKind.TypeReference: case SyntaxKind.JSDocTypeReference: return getTypeFromTypeReference(node); diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 9136105457e..07f013e4f31 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -417,6 +417,8 @@ namespace ts { case SyntaxKind.JSDocPropertyTag: return visitNode(cbNode, (node).typeExpression) || visitNode(cbNode, (node).name); + case SyntaxKind.JSDocLiteralType: + return visitNode(cbNode, (node).literal); } } @@ -5890,7 +5892,10 @@ namespace ts { case SyntaxKind.VoidKeyword: return parseTokenNode(); case SyntaxKind.StringLiteral: - return parseJSDocStringLiteralType(); + case SyntaxKind.NumericLiteral: + case SyntaxKind.TrueKeyword: + case SyntaxKind.FalseKeyword: + return parseJSDocLiteralType(); } return parseJSDocTypeReference(); @@ -6071,9 +6076,9 @@ namespace ts { return finishNode(result); } - function parseJSDocStringLiteralType(): JSDocStringLiteralType { - const result = createNode(SyntaxKind.JSDocStringLiteralType); - result.stringLiteral = parseStringLiteralTypeNode(); + function parseJSDocLiteralType(): JSDocLiteralType { + const result = createNode(SyntaxKind.JSDocLiteralType); + result.literal = parseLiteralTypeNode(); return finishNode(result); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 4f6806d11a5..45187b784b0 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -346,7 +346,7 @@ namespace ts { JSDocTypedefTag, JSDocPropertyTag, JSDocTypeLiteral, - JSDocStringLiteralType, + JSDocLiteralType, // Synthesized list SyntaxList, @@ -377,9 +377,9 @@ namespace ts { LastBinaryOperator = CaretEqualsToken, FirstNode = QualifiedName, FirstJSDocNode = JSDocTypeExpression, - LastJSDocNode = JSDocStringLiteralType, + LastJSDocNode = JSDocLiteralType, FirstJSDocTagNode = JSDocComment, - LastJSDocTagNode = JSDocStringLiteralType + LastJSDocTagNode = JSDocLiteralType } export const enum NodeFlags { @@ -1493,8 +1493,8 @@ namespace ts { type: JSDocType; } - export interface JSDocStringLiteralType extends JSDocType { - stringLiteral: StringLiteralTypeNode; + export interface JSDocLiteralType extends JSDocType { + literal: LiteralTypeNode; } export type JSDocTypeReferencingNode = JSDocThisType | JSDocConstructorType | JSDocVariadicType | JSDocOptionalType | JSDocNullableType | JSDocNonNullableType; diff --git a/tests/baselines/reference/jsdocStringLiteral.js b/tests/baselines/reference/jsdocLiteral.js similarity index 55% rename from tests/baselines/reference/jsdocStringLiteral.js rename to tests/baselines/reference/jsdocLiteral.js index e1210690f91..1a017160302 100644 --- a/tests/baselines/reference/jsdocStringLiteral.js +++ b/tests/baselines/reference/jsdocLiteral.js @@ -4,9 +4,10 @@ * @param {"literal"} p2 * @param {'literal' | 'other'} p3 * @param {'literal' | number} p4 + * @param {12 | true | 'str'} p5 */ -function f(p1, p2, p3, p4) { - return p1 + p2 + p3 + p4 + '.'; +function f(p1, p2, p3, p4, p5) { + return p1 + p2 + p3 + p4 + p5 + '.'; } @@ -16,7 +17,8 @@ function f(p1, p2, p3, p4) { * @param {"literal"} p2 * @param {'literal' | 'other'} p3 * @param {'literal' | number} p4 + * @param {12 | true | 'str'} p5 */ -function f(p1, p2, p3, p4) { - return p1 + p2 + p3 + p4 + '.'; +function f(p1, p2, p3, p4, p5) { + return p1 + p2 + p3 + p4 + p5 + '.'; } diff --git a/tests/baselines/reference/jsdocLiteral.symbols b/tests/baselines/reference/jsdocLiteral.symbols new file mode 100644 index 00000000000..9f01fe782aa --- /dev/null +++ b/tests/baselines/reference/jsdocLiteral.symbols @@ -0,0 +1,24 @@ +=== tests/cases/conformance/jsdoc/in.js === +/** + * @param {'literal'} p1 + * @param {"literal"} p2 + * @param {'literal' | 'other'} p3 + * @param {'literal' | number} p4 + * @param {12 | true | 'str'} p5 + */ +function f(p1, p2, p3, p4, p5) { +>f : Symbol(f, Decl(in.js, 0, 0)) +>p1 : Symbol(p1, Decl(in.js, 7, 11)) +>p2 : Symbol(p2, Decl(in.js, 7, 14)) +>p3 : Symbol(p3, Decl(in.js, 7, 18)) +>p4 : Symbol(p4, Decl(in.js, 7, 22)) +>p5 : Symbol(p5, Decl(in.js, 7, 26)) + + return p1 + p2 + p3 + p4 + p5 + '.'; +>p1 : Symbol(p1, Decl(in.js, 7, 11)) +>p2 : Symbol(p2, Decl(in.js, 7, 14)) +>p3 : Symbol(p3, Decl(in.js, 7, 18)) +>p4 : Symbol(p4, Decl(in.js, 7, 22)) +>p5 : Symbol(p5, Decl(in.js, 7, 26)) +} + diff --git a/tests/baselines/reference/jsdocStringLiteral.types b/tests/baselines/reference/jsdocLiteral.types similarity index 54% rename from tests/baselines/reference/jsdocStringLiteral.types rename to tests/baselines/reference/jsdocLiteral.types index 0400416a259..b6c9e522ac3 100644 --- a/tests/baselines/reference/jsdocStringLiteral.types +++ b/tests/baselines/reference/jsdocLiteral.types @@ -4,23 +4,27 @@ * @param {"literal"} p2 * @param {'literal' | 'other'} p3 * @param {'literal' | number} p4 + * @param {12 | true | 'str'} p5 */ -function f(p1, p2, p3, p4) { ->f : (p1: "literal", p2: "literal", p3: "literal" | "other", p4: "literal" | number) => string +function f(p1, p2, p3, p4, p5) { +>f : (p1: "literal", p2: "literal", p3: "literal" | "other", p4: number | "literal", p5: true | 12 | "str") => string >p1 : "literal" >p2 : "literal" >p3 : "literal" | "other" ->p4 : "literal" | number +>p4 : number | "literal" +>p5 : true | 12 | "str" - return p1 + p2 + p3 + p4 + '.'; ->p1 + p2 + p3 + p4 + '.' : string + return p1 + p2 + p3 + p4 + p5 + '.'; +>p1 + p2 + p3 + p4 + p5 + '.' : string +>p1 + p2 + p3 + p4 + p5 : string >p1 + p2 + p3 + p4 : string >p1 + p2 + p3 : string >p1 + p2 : string >p1 : "literal" >p2 : "literal" >p3 : "literal" | "other" ->p4 : "literal" | number +>p4 : number | "literal" +>p5 : true | 12 | "str" >'.' : string } diff --git a/tests/baselines/reference/jsdocStringLiteral.symbols b/tests/baselines/reference/jsdocStringLiteral.symbols deleted file mode 100644 index 982a1536069..00000000000 --- a/tests/baselines/reference/jsdocStringLiteral.symbols +++ /dev/null @@ -1,21 +0,0 @@ -=== tests/cases/conformance/jsdoc/in.js === -/** - * @param {'literal'} p1 - * @param {"literal"} p2 - * @param {'literal' | 'other'} p3 - * @param {'literal' | number} p4 - */ -function f(p1, p2, p3, p4) { ->f : Symbol(f, Decl(in.js, 0, 0)) ->p1 : Symbol(p1, Decl(in.js, 6, 11)) ->p2 : Symbol(p2, Decl(in.js, 6, 14)) ->p3 : Symbol(p3, Decl(in.js, 6, 18)) ->p4 : Symbol(p4, Decl(in.js, 6, 22)) - - return p1 + p2 + p3 + p4 + '.'; ->p1 : Symbol(p1, Decl(in.js, 6, 11)) ->p2 : Symbol(p2, Decl(in.js, 6, 14)) ->p3 : Symbol(p3, Decl(in.js, 6, 18)) ->p4 : Symbol(p4, Decl(in.js, 6, 22)) -} - diff --git a/tests/cases/conformance/jsdoc/jsdocStringLiteral.ts b/tests/cases/conformance/jsdoc/jsdocLiteral.ts similarity index 63% rename from tests/cases/conformance/jsdoc/jsdocStringLiteral.ts rename to tests/cases/conformance/jsdoc/jsdocLiteral.ts index d2a0d6c644d..bd0d2d562f9 100644 --- a/tests/cases/conformance/jsdoc/jsdocStringLiteral.ts +++ b/tests/cases/conformance/jsdoc/jsdocLiteral.ts @@ -6,7 +6,8 @@ * @param {"literal"} p2 * @param {'literal' | 'other'} p3 * @param {'literal' | number} p4 + * @param {12 | true | 'str'} p5 */ -function f(p1, p2, p3, p4) { - return p1 + p2 + p3 + p4 + '.'; +function f(p1, p2, p3, p4, p5) { + return p1 + p2 + p3 + p4 + p5 + '.'; } diff --git a/tests/cases/fourslash/completionForStringLiteral4.ts b/tests/cases/fourslash/completionForStringLiteral4.ts index c240c125dbe..11ae699eab8 100644 --- a/tests/cases/fourslash/completionForStringLiteral4.ts +++ b/tests/cases/fourslash/completionForStringLiteral4.ts @@ -6,15 +6,16 @@ //// * @param {"literal"} p2 //// * @param {'other1' | 'other2'} p3 //// * @param {'literal' | number} p4 +//// * @param {12 | true} p5 //// */ -////function f(p1, p2, p3, p4) { -//// return p1 + p2 + p3 + p4 + '.'; +////function f(p1, p2, p3, p4, p5) { +//// return p1 + p2 + p3 + p4 + p5 + '.'; ////} ////f/*1*/('literal', 'literal', "o/*2*/ther1", 12); goTo.marker('1'); verify.quickInfoExists(); -verify.quickInfoIs('function f(p1: "literal", p2: "literal", p3: "other1" | "other2", p4: "literal" | number): string', 'I am documentation'); +verify.quickInfoIs('function f(p1: "literal", p2: "literal", p3: "other1" | "other2", p4: number | "literal", p5: true | 12): string', 'I am documentation'); goTo.marker('2'); verify.completionListContains("other1"); From 9947ac2ecefcd343b6ebc40b9614c1dfcf76b666 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 4 Aug 2016 14:13:07 -0700 Subject: [PATCH 068/233] Don't allow properties inherited from Object to be automatically included in TSX attributes --- src/compiler/checker.ts | 4 ++-- .../baselines/reference/tsxAttributeResolution5.errors.txt | 7 +++++-- tests/baselines/reference/tsxAttributeResolution5.js | 4 ++-- tests/cases/conformance/jsx/tsxAttributeResolution5.tsx | 2 +- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0599eb4bbbc..84d5cd08a2b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10060,7 +10060,7 @@ namespace ts { for (const prop of props) { // Is there a corresponding property in the element attributes type? Skip checking of properties // that have already been assigned to, as these are not actually pushed into the resulting type - if (!nameTable[prop.name]) { + if (!hasProperty(nameTable, prop.name)) { const targetPropSym = getPropertyOfType(elementAttributesType, prop.name); if (targetPropSym) { const msg = chainDiagnosticMessages(undefined, Diagnostics.Property_0_of_JSX_spread_attribute_is_not_assignable_to_target_property, prop.name); @@ -10406,7 +10406,7 @@ namespace ts { const targetProperties = getPropertiesOfType(targetAttributesType); for (let i = 0; i < targetProperties.length; i++) { if (!(targetProperties[i].flags & SymbolFlags.Optional) && - nameTable[targetProperties[i].name] === undefined) { + !hasProperty(nameTable, targetProperties[i].name)) { error(node, Diagnostics.Property_0_is_missing_in_type_1, targetProperties[i].name, typeToString(targetAttributesType)); } diff --git a/tests/baselines/reference/tsxAttributeResolution5.errors.txt b/tests/baselines/reference/tsxAttributeResolution5.errors.txt index d7aa46bc05a..42dabc741ab 100644 --- a/tests/baselines/reference/tsxAttributeResolution5.errors.txt +++ b/tests/baselines/reference/tsxAttributeResolution5.errors.txt @@ -2,9 +2,10 @@ tests/cases/conformance/jsx/file.tsx(21,16): error TS2606: Property 'x' of JSX s Type 'number' is not assignable to type 'string'. tests/cases/conformance/jsx/file.tsx(25,9): error TS2324: Property 'x' is missing in type 'Attribs1'. tests/cases/conformance/jsx/file.tsx(29,1): error TS2324: Property 'x' is missing in type 'Attribs1'. +tests/cases/conformance/jsx/file.tsx(30,1): error TS2324: Property 'toString' is missing in type 'Attribs2'. -==== tests/cases/conformance/jsx/file.tsx (3 errors) ==== +==== tests/cases/conformance/jsx/file.tsx (4 errors) ==== declare module JSX { interface Element { } interface IntrinsicElements { @@ -41,5 +42,7 @@ tests/cases/conformance/jsx/file.tsx(29,1): error TS2324: Property 'x' is missin ; // Error, missing x ~~~~~~~~~~~~~~~~~ !!! error TS2324: Property 'x' is missing in type 'Attribs1'. - ; // OK + ; // Error, missing toString + ~~~~~~~~~~~~~~~~~ +!!! error TS2324: Property 'toString' is missing in type 'Attribs2'. \ No newline at end of file diff --git a/tests/baselines/reference/tsxAttributeResolution5.js b/tests/baselines/reference/tsxAttributeResolution5.js index abfb54e000c..4bde09074b5 100644 --- a/tests/baselines/reference/tsxAttributeResolution5.js +++ b/tests/baselines/reference/tsxAttributeResolution5.js @@ -28,7 +28,7 @@ function make3 (obj: T) { ; // Error, missing x -; // OK +; // Error, missing toString //// [file.jsx] @@ -42,4 +42,4 @@ function make3(obj) { return ; // Error, missing x } ; // Error, missing x -; // OK +; // Error, missing toString diff --git a/tests/cases/conformance/jsx/tsxAttributeResolution5.tsx b/tests/cases/conformance/jsx/tsxAttributeResolution5.tsx index dd16ade10e3..83fb7b32f56 100644 --- a/tests/cases/conformance/jsx/tsxAttributeResolution5.tsx +++ b/tests/cases/conformance/jsx/tsxAttributeResolution5.tsx @@ -29,4 +29,4 @@ function make3 (obj: T) { ; // Error, missing x -; // OK +; // Error, missing toString From 798be6f4f977dfbc0e46353e56b28b59c123d1d6 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 4 Aug 2016 15:17:08 -0700 Subject: [PATCH 069/233] Add new test baseline and delete else in binder The extra `else` caused a ton of test failures! --- src/compiler/binder.ts | 2 +- .../reference/multipleDeclarations.js | 31 +++++- .../reference/multipleDeclarations.symbols | 98 +++++++++++++------ .../reference/multipleDeclarations.types | 59 ++++++++++- 4 files changed, 157 insertions(+), 33 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 6f0def32702..5e2cd3f8680 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -328,7 +328,7 @@ namespace ts { classifiableNames[name] = name; } - else if (symbol.flags & excludes) { + if (symbol.flags & excludes) { if (symbol.isDiscardable) { // Javascript constructor-declared symbols can be discarded in favor of // prototype symbols like methods. diff --git a/tests/baselines/reference/multipleDeclarations.js b/tests/baselines/reference/multipleDeclarations.js index e5aae416663..7fb7e0bca02 100644 --- a/tests/baselines/reference/multipleDeclarations.js +++ b/tests/baselines/reference/multipleDeclarations.js @@ -1,12 +1,10 @@ //// [input.js] - function C() { this.m = null; } C.prototype.m = function() { this.nothing(); } - class X { constructor() { this.m = this.m.bind(this); @@ -21,6 +19,20 @@ let x = new X(); X.prototype.mistake = false; x.m(); x.mistake; +class Y { + mistake() { + } + m() { + } + constructor() { + this.m = this.m.bind(this); + this.mistake = 'even more nonsense'; + } +} +Y.prototype.mistake = true; +let y = new Y(); +y.m(); +y.mistake(); //// [output.js] @@ -45,3 +57,18 @@ var x = new X(); X.prototype.mistake = false; x.m(); x.mistake; +var Y = (function () { + function Y() { + this.m = this.m.bind(this); + this.mistake = 'even more nonsense'; + } + Y.prototype.mistake = function () { + }; + Y.prototype.m = function () { + }; + return Y; +}()); +Y.prototype.mistake = true; +var y = new Y(); +y.m(); +y.mistake(); diff --git a/tests/baselines/reference/multipleDeclarations.symbols b/tests/baselines/reference/multipleDeclarations.symbols index 7e4bcd53c8a..9e49c2fc00c 100644 --- a/tests/baselines/reference/multipleDeclarations.symbols +++ b/tests/baselines/reference/multipleDeclarations.symbols @@ -1,64 +1,106 @@ === tests/cases/conformance/salsa/input.js === - function C() { >C : Symbol(C, Decl(input.js, 0, 0)) this.m = null; ->m : Symbol(C.m, Decl(input.js, 1, 14), Decl(input.js, 3, 1)) +>m : Symbol(C.m, Decl(input.js, 0, 14), Decl(input.js, 2, 1)) } C.prototype.m = function() { ->C.prototype : Symbol(C.m, Decl(input.js, 1, 14), Decl(input.js, 3, 1)) +>C.prototype : Symbol(C.m, Decl(input.js, 0, 14), Decl(input.js, 2, 1)) >C : Symbol(C, Decl(input.js, 0, 0)) >prototype : Symbol(Function.prototype, Decl(lib.d.ts, --, --)) ->m : Symbol(C.m, Decl(input.js, 1, 14), Decl(input.js, 3, 1)) +>m : Symbol(C.m, Decl(input.js, 0, 14), Decl(input.js, 2, 1)) this.nothing(); >this : Symbol(C, Decl(input.js, 0, 0)) } - class X { ->X : Symbol(X, Decl(input.js, 6, 1)) +>X : Symbol(X, Decl(input.js, 5, 1)) constructor() { this.m = this.m.bind(this); ->this.m : Symbol(X.m, Decl(input.js, 12, 5)) ->this : Symbol(X, Decl(input.js, 6, 1)) ->m : Symbol(X.m, Decl(input.js, 9, 19)) +>this.m : Symbol(X.m, Decl(input.js, 10, 5)) +>this : Symbol(X, Decl(input.js, 5, 1)) +>m : Symbol(X.m, Decl(input.js, 7, 19)) >this.m.bind : Symbol(Function.bind, Decl(lib.d.ts, --, --)) ->this.m : Symbol(X.m, Decl(input.js, 12, 5)) ->this : Symbol(X, Decl(input.js, 6, 1)) ->m : Symbol(X.m, Decl(input.js, 12, 5)) +>this.m : Symbol(X.m, Decl(input.js, 10, 5)) +>this : Symbol(X, Decl(input.js, 5, 1)) +>m : Symbol(X.m, Decl(input.js, 10, 5)) >bind : Symbol(Function.bind, Decl(lib.d.ts, --, --)) ->this : Symbol(X, Decl(input.js, 6, 1)) +>this : Symbol(X, Decl(input.js, 5, 1)) this.mistake = 'frankly, complete nonsense'; ->this.mistake : Symbol(X.mistake, Decl(input.js, 14, 5)) ->this : Symbol(X, Decl(input.js, 6, 1)) ->mistake : Symbol(X.mistake, Decl(input.js, 10, 35)) +>this.mistake : Symbol(X.mistake, Decl(input.js, 12, 5)) +>this : Symbol(X, Decl(input.js, 5, 1)) +>mistake : Symbol(X.mistake, Decl(input.js, 8, 35)) } m() { ->m : Symbol(X.m, Decl(input.js, 12, 5)) +>m : Symbol(X.m, Decl(input.js, 10, 5)) } mistake() { ->mistake : Symbol(X.mistake, Decl(input.js, 14, 5)) +>mistake : Symbol(X.mistake, Decl(input.js, 12, 5)) } } let x = new X(); ->x : Symbol(x, Decl(input.js, 18, 3)) ->X : Symbol(X, Decl(input.js, 6, 1)) +>x : Symbol(x, Decl(input.js, 16, 3)) +>X : Symbol(X, Decl(input.js, 5, 1)) X.prototype.mistake = false; ->X.prototype.mistake : Symbol(X.mistake, Decl(input.js, 14, 5)) ->X : Symbol(X, Decl(input.js, 6, 1)) +>X.prototype.mistake : Symbol(X.mistake, Decl(input.js, 12, 5)) +>X : Symbol(X, Decl(input.js, 5, 1)) >prototype : Symbol(X.prototype) x.m(); ->x.m : Symbol(X.m, Decl(input.js, 12, 5)) ->x : Symbol(x, Decl(input.js, 18, 3)) ->m : Symbol(X.m, Decl(input.js, 12, 5)) +>x.m : Symbol(X.m, Decl(input.js, 10, 5)) +>x : Symbol(x, Decl(input.js, 16, 3)) +>m : Symbol(X.m, Decl(input.js, 10, 5)) x.mistake; ->x.mistake : Symbol(X.mistake, Decl(input.js, 14, 5)) ->x : Symbol(x, Decl(input.js, 18, 3)) ->mistake : Symbol(X.mistake, Decl(input.js, 14, 5)) +>x.mistake : Symbol(X.mistake, Decl(input.js, 12, 5)) +>x : Symbol(x, Decl(input.js, 16, 3)) +>mistake : Symbol(X.mistake, Decl(input.js, 12, 5)) + +class Y { +>Y : Symbol(Y, Decl(input.js, 19, 10)) + + mistake() { +>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35)) + } + m() { +>m : Symbol(Y.m, Decl(input.js, 22, 5), Decl(input.js, 25, 19)) + } + constructor() { + this.m = this.m.bind(this); +>this.m : Symbol(Y.m, Decl(input.js, 22, 5), Decl(input.js, 25, 19)) +>this : Symbol(Y, Decl(input.js, 19, 10)) +>m : Symbol(Y.m, Decl(input.js, 22, 5), Decl(input.js, 25, 19)) +>this.m : Symbol(Y.m, Decl(input.js, 22, 5), Decl(input.js, 25, 19)) +>this : Symbol(Y, Decl(input.js, 19, 10)) +>m : Symbol(Y.m, Decl(input.js, 22, 5), Decl(input.js, 25, 19)) +>this : Symbol(Y, Decl(input.js, 19, 10)) + + this.mistake = 'even more nonsense'; +>this.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35)) +>this : Symbol(Y, Decl(input.js, 19, 10)) +>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35)) + } +} +Y.prototype.mistake = true; +>Y.prototype.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35)) +>Y : Symbol(Y, Decl(input.js, 19, 10)) +>prototype : Symbol(Y.prototype) + +let y = new Y(); +>y : Symbol(y, Decl(input.js, 31, 3)) +>Y : Symbol(Y, Decl(input.js, 19, 10)) + +y.m(); +>y.m : Symbol(Y.m, Decl(input.js, 22, 5), Decl(input.js, 25, 19)) +>y : Symbol(y, Decl(input.js, 31, 3)) +>m : Symbol(Y.m, Decl(input.js, 22, 5), Decl(input.js, 25, 19)) + +y.mistake(); +>y.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35)) +>y : Symbol(y, Decl(input.js, 31, 3)) +>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35)) diff --git a/tests/baselines/reference/multipleDeclarations.types b/tests/baselines/reference/multipleDeclarations.types index 2a7cac18205..900d03195d4 100644 --- a/tests/baselines/reference/multipleDeclarations.types +++ b/tests/baselines/reference/multipleDeclarations.types @@ -1,5 +1,4 @@ === tests/cases/conformance/salsa/input.js === - function C() { >C : () => void @@ -25,7 +24,6 @@ C.prototype.m = function() { >this : { m: () => void; } >nothing : any } - class X { >X : X @@ -82,3 +80,60 @@ x.mistake; >x : X >mistake : () => void +class Y { +>Y : Y + + mistake() { +>mistake : any + } + m() { +>m : any + } + constructor() { + this.m = this.m.bind(this); +>this.m = this.m.bind(this) : any +>this.m : any +>this : this +>m : any +>this.m.bind(this) : any +>this.m.bind : any +>this.m : any +>this : this +>m : any +>bind : any +>this : this + + this.mistake = 'even more nonsense'; +>this.mistake = 'even more nonsense' : string +>this.mistake : any +>this : this +>mistake : any +>'even more nonsense' : string + } +} +Y.prototype.mistake = true; +>Y.prototype.mistake = true : boolean +>Y.prototype.mistake : any +>Y.prototype : Y +>Y : typeof Y +>prototype : Y +>mistake : any +>true : boolean + +let y = new Y(); +>y : Y +>new Y() : Y +>Y : typeof Y + +y.m(); +>y.m() : any +>y.m : any +>y : Y +>m : any + +y.mistake(); +>y.mistake() : any +>y.mistake : any +>y : Y +>mistake : any + From 8f638f7ecdc154c00c2be11a55971a7d13ee5757 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Fri, 5 Aug 2016 09:58:30 -0700 Subject: [PATCH 070/233] Fix lint --- src/compiler/binder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 5e2cd3f8680..b771a3b67be 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1987,7 +1987,7 @@ namespace ts { assignee.symbol.members = assignee.symbol.members || {}; // It's acceptable for multiple 'this' assignments of the same identifier to occur // AND it can be overwritten by subsequent method declarations - let symbol = declareSymbol(assignee.symbol.members, assignee.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property); + const symbol = declareSymbol(assignee.symbol.members, assignee.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property); symbol.isDiscardable = true; } From ceab31cf0db3eb0b5e96559c1f5e77c9328e58aa Mon Sep 17 00:00:00 2001 From: Yui Date: Fri, 5 Aug 2016 10:12:01 -0700 Subject: [PATCH 071/233] Port PR #10016 to Master (#10100) * Treat namespaceExportDeclaration as declaration * Update baselines * wip - add tests * Add tests * Show "export namespace" for quick-info --- src/compiler/checker.ts | 3 ++- src/compiler/utilities.ts | 1 + src/services/services.ts | 9 ++++++++- .../reference/umd-augmentation-1.symbols | 1 + .../reference/umd-augmentation-1.types | 2 +- .../reference/umd-augmentation-2.symbols | 1 + .../reference/umd-augmentation-2.types | 2 +- .../reference/umd-augmentation-3.symbols | 1 + .../reference/umd-augmentation-3.types | 2 +- .../reference/umd-augmentation-4.symbols | 1 + .../reference/umd-augmentation-4.types | 2 +- tests/baselines/reference/umd1.symbols | 1 + tests/baselines/reference/umd1.types | 2 +- tests/baselines/reference/umd3.symbols | 1 + tests/baselines/reference/umd3.types | 2 +- tests/baselines/reference/umd4.symbols | 1 + tests/baselines/reference/umd4.types | 2 +- tests/baselines/reference/umd6.symbols | 1 + tests/baselines/reference/umd6.types | 2 +- tests/baselines/reference/umd7.symbols | 1 + tests/baselines/reference/umd7.types | 2 +- tests/baselines/reference/umd8.symbols | 1 + tests/baselines/reference/umd8.types | 2 +- .../fourslash/findAllRefsForUMDModuleAlias1.ts | 13 +++++++++++++ .../fourslash/quickInfoForUMDModuleAlias.ts | 17 +++++++++++++++++ tests/cases/fourslash/renameAlias.ts | 8 ++++---- tests/cases/fourslash/renameUMDModuleAlias1.ts | 17 +++++++++++++++++ tests/cases/fourslash/renameUMDModuleAlias2.ts | 14 ++++++++++++++ 28 files changed, 96 insertions(+), 16 deletions(-) create mode 100644 tests/cases/fourslash/findAllRefsForUMDModuleAlias1.ts create mode 100644 tests/cases/fourslash/quickInfoForUMDModuleAlias.ts create mode 100644 tests/cases/fourslash/renameUMDModuleAlias1.ts create mode 100644 tests/cases/fourslash/renameUMDModuleAlias2.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 84d5cd08a2b..9583fadba62 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19691,7 +19691,7 @@ namespace ts { } function checkGrammarTopLevelElementForRequiredDeclareModifier(node: Node): boolean { - // A declare modifier is required for any top level .d.ts declaration except export=, export default, + // A declare modifier is required for any top level .d.ts declaration except export=, export default, export as namespace // interfaces and imports categories: // // DeclarationElement: @@ -19709,6 +19709,7 @@ namespace ts { node.kind === SyntaxKind.ImportEqualsDeclaration || node.kind === SyntaxKind.ExportDeclaration || node.kind === SyntaxKind.ExportAssignment || + node.kind === SyntaxKind.NamespaceExportDeclaration || (node.flags & NodeFlags.Ambient) || (node.flags & (NodeFlags.Export | NodeFlags.Default))) { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 4ae0005e9c5..2cfaf1f5cfb 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1569,6 +1569,7 @@ namespace ts { case SyntaxKind.MethodSignature: case SyntaxKind.ModuleDeclaration: case SyntaxKind.NamespaceImport: + case SyntaxKind.NamespaceExportDeclaration: case SyntaxKind.Parameter: case SyntaxKind.PropertyAssignment: case SyntaxKind.PropertyDeclaration: diff --git a/src/services/services.ts b/src/services/services.ts index ab1e30a44a6..aea4d5f872e 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4830,7 +4830,14 @@ namespace ts { } if (symbolFlags & SymbolFlags.Alias) { addNewLineIfDisplayPartsExist(); - displayParts.push(keywordPart(SyntaxKind.ImportKeyword)); + if (symbol.declarations[0].kind === SyntaxKind.NamespaceExportDeclaration) { + displayParts.push(keywordPart(SyntaxKind.ExportKeyword)); + displayParts.push(spacePart()); + displayParts.push(keywordPart(SyntaxKind.NamespaceKeyword)); + } + else { + displayParts.push(keywordPart(SyntaxKind.ImportKeyword)); + } displayParts.push(spacePart()); addFullSymbolName(symbol); ts.forEach(symbol.declarations, declaration => { diff --git a/tests/baselines/reference/umd-augmentation-1.symbols b/tests/baselines/reference/umd-augmentation-1.symbols index 645511350a9..70715649894 100644 --- a/tests/baselines/reference/umd-augmentation-1.symbols +++ b/tests/baselines/reference/umd-augmentation-1.symbols @@ -39,6 +39,7 @@ var t = p.x; === tests/cases/conformance/externalModules/node_modules/math2d/index.d.ts === export as namespace Math2d; +>Math2d : Symbol(Math2d, Decl(index.d.ts, 0, 0)) export interface Point { >Point : Symbol(Point, Decl(index.d.ts, 1, 27)) diff --git a/tests/baselines/reference/umd-augmentation-1.types b/tests/baselines/reference/umd-augmentation-1.types index 31ac43fe855..59b577141e0 100644 --- a/tests/baselines/reference/umd-augmentation-1.types +++ b/tests/baselines/reference/umd-augmentation-1.types @@ -48,7 +48,7 @@ var t = p.x; === tests/cases/conformance/externalModules/node_modules/math2d/index.d.ts === export as namespace Math2d; ->Math2d : any +>Math2d : typeof Math2d export interface Point { >Point : Point diff --git a/tests/baselines/reference/umd-augmentation-2.symbols b/tests/baselines/reference/umd-augmentation-2.symbols index bd6584d3d74..a31ed61d531 100644 --- a/tests/baselines/reference/umd-augmentation-2.symbols +++ b/tests/baselines/reference/umd-augmentation-2.symbols @@ -37,6 +37,7 @@ var t = p.x; === tests/cases/conformance/externalModules/node_modules/math2d/index.d.ts === export as namespace Math2d; +>Math2d : Symbol(Math2d, Decl(index.d.ts, 0, 0)) export interface Point { >Point : Symbol(Point, Decl(index.d.ts, 1, 27)) diff --git a/tests/baselines/reference/umd-augmentation-2.types b/tests/baselines/reference/umd-augmentation-2.types index 20bba091903..24fe6df3c3c 100644 --- a/tests/baselines/reference/umd-augmentation-2.types +++ b/tests/baselines/reference/umd-augmentation-2.types @@ -46,7 +46,7 @@ var t = p.x; === tests/cases/conformance/externalModules/node_modules/math2d/index.d.ts === export as namespace Math2d; ->Math2d : any +>Math2d : typeof Math2d export interface Point { >Point : Point diff --git a/tests/baselines/reference/umd-augmentation-3.symbols b/tests/baselines/reference/umd-augmentation-3.symbols index 3193e83cd19..3e2df7c8a89 100644 --- a/tests/baselines/reference/umd-augmentation-3.symbols +++ b/tests/baselines/reference/umd-augmentation-3.symbols @@ -39,6 +39,7 @@ var t = p.x; === tests/cases/conformance/externalModules/node_modules/math2d/index.d.ts === export as namespace Math2d; +>Math2d : Symbol(Math2d, Decl(index.d.ts, 0, 0)) export = M2D; >M2D : Symbol(M2D, Decl(index.d.ts, 3, 13)) diff --git a/tests/baselines/reference/umd-augmentation-3.types b/tests/baselines/reference/umd-augmentation-3.types index 854ebceff37..39bc28cbd24 100644 --- a/tests/baselines/reference/umd-augmentation-3.types +++ b/tests/baselines/reference/umd-augmentation-3.types @@ -48,7 +48,7 @@ var t = p.x; === tests/cases/conformance/externalModules/node_modules/math2d/index.d.ts === export as namespace Math2d; ->Math2d : any +>Math2d : typeof Math2d export = M2D; >M2D : typeof M2D diff --git a/tests/baselines/reference/umd-augmentation-4.symbols b/tests/baselines/reference/umd-augmentation-4.symbols index 3f2cc913d86..7be5d278bf2 100644 --- a/tests/baselines/reference/umd-augmentation-4.symbols +++ b/tests/baselines/reference/umd-augmentation-4.symbols @@ -37,6 +37,7 @@ var t = p.x; === tests/cases/conformance/externalModules/node_modules/math2d/index.d.ts === export as namespace Math2d; +>Math2d : Symbol(Math2d, Decl(index.d.ts, 0, 0)) export = M2D; >M2D : Symbol(M2D, Decl(index.d.ts, 3, 13)) diff --git a/tests/baselines/reference/umd-augmentation-4.types b/tests/baselines/reference/umd-augmentation-4.types index 71783d03012..246270aa5a6 100644 --- a/tests/baselines/reference/umd-augmentation-4.types +++ b/tests/baselines/reference/umd-augmentation-4.types @@ -46,7 +46,7 @@ var t = p.x; === tests/cases/conformance/externalModules/node_modules/math2d/index.d.ts === export as namespace Math2d; ->Math2d : any +>Math2d : typeof Math2d export = M2D; >M2D : typeof M2D diff --git a/tests/baselines/reference/umd1.symbols b/tests/baselines/reference/umd1.symbols index 9b964456bcf..0307a9b78d3 100644 --- a/tests/baselines/reference/umd1.symbols +++ b/tests/baselines/reference/umd1.symbols @@ -30,4 +30,5 @@ export interface Thing { n: typeof x } >x : Symbol(x, Decl(foo.d.ts, 1, 10)) export as namespace Foo; +>Foo : Symbol(Foo, Decl(foo.d.ts, 3, 38)) diff --git a/tests/baselines/reference/umd1.types b/tests/baselines/reference/umd1.types index 1767f3b5a89..1379bba8d5d 100644 --- a/tests/baselines/reference/umd1.types +++ b/tests/baselines/reference/umd1.types @@ -31,5 +31,5 @@ export interface Thing { n: typeof x } >x : number export as namespace Foo; ->Foo : any +>Foo : typeof Foo diff --git a/tests/baselines/reference/umd3.symbols b/tests/baselines/reference/umd3.symbols index 165fd81597a..c1fd3d6d74f 100644 --- a/tests/baselines/reference/umd3.symbols +++ b/tests/baselines/reference/umd3.symbols @@ -32,4 +32,5 @@ export interface Thing { n: typeof x } >x : Symbol(x, Decl(foo.d.ts, 1, 10)) export as namespace Foo; +>Foo : Symbol(Foo, Decl(foo.d.ts, 3, 38)) diff --git a/tests/baselines/reference/umd3.types b/tests/baselines/reference/umd3.types index 85ee6bafe5e..23149e58d76 100644 --- a/tests/baselines/reference/umd3.types +++ b/tests/baselines/reference/umd3.types @@ -33,5 +33,5 @@ export interface Thing { n: typeof x } >x : number export as namespace Foo; ->Foo : any +>Foo : typeof Foo diff --git a/tests/baselines/reference/umd4.symbols b/tests/baselines/reference/umd4.symbols index 8403187198b..d266997770b 100644 --- a/tests/baselines/reference/umd4.symbols +++ b/tests/baselines/reference/umd4.symbols @@ -32,4 +32,5 @@ export interface Thing { n: typeof x } >x : Symbol(x, Decl(foo.d.ts, 1, 10)) export as namespace Foo; +>Foo : Symbol(Foo, Decl(foo.d.ts, 3, 38)) diff --git a/tests/baselines/reference/umd4.types b/tests/baselines/reference/umd4.types index 579599f5661..18794258517 100644 --- a/tests/baselines/reference/umd4.types +++ b/tests/baselines/reference/umd4.types @@ -33,5 +33,5 @@ export interface Thing { n: typeof x } >x : number export as namespace Foo; ->Foo : any +>Foo : typeof Foo diff --git a/tests/baselines/reference/umd6.symbols b/tests/baselines/reference/umd6.symbols index d08507f1ea2..958875b79eb 100644 --- a/tests/baselines/reference/umd6.symbols +++ b/tests/baselines/reference/umd6.symbols @@ -18,4 +18,5 @@ export = Thing; >Thing : Symbol(Thing, Decl(foo.d.ts, 0, 0)) export as namespace Foo; +>Foo : Symbol(Foo, Decl(foo.d.ts, 4, 15)) diff --git a/tests/baselines/reference/umd6.types b/tests/baselines/reference/umd6.types index 7318a43057a..5b1469cbd6c 100644 --- a/tests/baselines/reference/umd6.types +++ b/tests/baselines/reference/umd6.types @@ -19,5 +19,5 @@ export = Thing; >Thing : typeof Thing export as namespace Foo; ->Foo : any +>Foo : typeof Thing diff --git a/tests/baselines/reference/umd7.symbols b/tests/baselines/reference/umd7.symbols index 0b3ef17fb7b..19b56ed30f3 100644 --- a/tests/baselines/reference/umd7.symbols +++ b/tests/baselines/reference/umd7.symbols @@ -13,4 +13,5 @@ export = Thing; >Thing : Symbol(Thing, Decl(foo.d.ts, 0, 0)) export as namespace Foo; +>Foo : Symbol(Foo, Decl(foo.d.ts, 2, 15)) diff --git a/tests/baselines/reference/umd7.types b/tests/baselines/reference/umd7.types index 60782543710..d83e039b674 100644 --- a/tests/baselines/reference/umd7.types +++ b/tests/baselines/reference/umd7.types @@ -14,5 +14,5 @@ export = Thing; >Thing : () => number export as namespace Foo; ->Foo : any +>Foo : () => number diff --git a/tests/baselines/reference/umd8.symbols b/tests/baselines/reference/umd8.symbols index 8c38f267a2a..97da693b38f 100644 --- a/tests/baselines/reference/umd8.symbols +++ b/tests/baselines/reference/umd8.symbols @@ -22,4 +22,5 @@ export = Thing; >Thing : Symbol(Thing, Decl(foo.d.ts, 0, 0)) export as namespace Foo; +>Foo : Symbol(Foo, Decl(foo.d.ts, 4, 15)) diff --git a/tests/baselines/reference/umd8.types b/tests/baselines/reference/umd8.types index 0e66a49b963..ae3bdfd5fc8 100644 --- a/tests/baselines/reference/umd8.types +++ b/tests/baselines/reference/umd8.types @@ -23,5 +23,5 @@ export = Thing; >Thing : Thing export as namespace Foo; ->Foo : any +>Foo : typeof Thing diff --git a/tests/cases/fourslash/findAllRefsForUMDModuleAlias1.ts b/tests/cases/fourslash/findAllRefsForUMDModuleAlias1.ts new file mode 100644 index 00000000000..4177e154532 --- /dev/null +++ b/tests/cases/fourslash/findAllRefsForUMDModuleAlias1.ts @@ -0,0 +1,13 @@ +/// + +// @Filename: 0.d.ts +//// export function doThing(): string; +//// export function doTheOtherThing(): void; + +//// export as namespace [|myLib|]; + +// @Filename: 1.ts +//// /// +//// [|myLib|].doThing(); + +verify.rangesReferenceEachOther(); diff --git a/tests/cases/fourslash/quickInfoForUMDModuleAlias.ts b/tests/cases/fourslash/quickInfoForUMDModuleAlias.ts new file mode 100644 index 00000000000..085ea33b874 --- /dev/null +++ b/tests/cases/fourslash/quickInfoForUMDModuleAlias.ts @@ -0,0 +1,17 @@ +/// + +// @Filename: 0.d.ts +//// export function doThing(): string; +//// export function doTheOtherThing(): void; + +//// export as namespace /*0*/myLib; + +// @Filename: 1.ts +//// /// +//// /*1*/myLib.doThing(); + +goTo.marker("0"); +verify.quickInfoIs("export namespace myLib"); + +goTo.marker("1"); +verify.quickInfoIs("export namespace myLib"); diff --git a/tests/cases/fourslash/renameAlias.ts b/tests/cases/fourslash/renameAlias.ts index e3f57ac7b41..e0408af656f 100644 --- a/tests/cases/fourslash/renameAlias.ts +++ b/tests/cases/fourslash/renameAlias.ts @@ -4,8 +4,8 @@ ////import [|M|] = SomeModule; ////import C = [|M|].SomeClass; -let ranges = test.ranges() -for (let range of ranges) { - goTo.position(range.start); - verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false); +let ranges = test.ranges() +for (let range of ranges) { + goTo.position(range.start); + verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false); } \ No newline at end of file diff --git a/tests/cases/fourslash/renameUMDModuleAlias1.ts b/tests/cases/fourslash/renameUMDModuleAlias1.ts new file mode 100644 index 00000000000..94aabcdbde5 --- /dev/null +++ b/tests/cases/fourslash/renameUMDModuleAlias1.ts @@ -0,0 +1,17 @@ +/// + +// @Filename: 0.d.ts +//// export function doThing(): string; +//// export function doTheOtherThing(): void; + +//// export as namespace [|myLib|]; + +// @Filename: 1.ts +//// /// +//// [|myLib|].doThing(); + +const ranges = test.ranges() +for (const range of ranges) { + goTo.position(range.start); + verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false); +} \ No newline at end of file diff --git a/tests/cases/fourslash/renameUMDModuleAlias2.ts b/tests/cases/fourslash/renameUMDModuleAlias2.ts new file mode 100644 index 00000000000..0daa4087b0d --- /dev/null +++ b/tests/cases/fourslash/renameUMDModuleAlias2.ts @@ -0,0 +1,14 @@ +/// + +// @Filename: 0.d.ts +//// export function doThing(): string; +//// export function doTheOtherThing(): void; + +//// export as namespace /**/[|myLib|]; + +// @Filename: 1.ts +//// /// +//// myLib.doThing(); + +goTo.marker(); +verify.renameInfoSucceeded("myLib"); \ No newline at end of file From cabd276ddcfe849f59b1ccfebc24b0d798dee062 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Fri, 5 Aug 2016 10:28:03 -0700 Subject: [PATCH 072/233] Fix more lint --- src/compiler/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 4c08d7a487c..f5cee2f8f8f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2137,7 +2137,7 @@ namespace ts { /* @internal */ exportSymbol?: Symbol; // Exported symbol associated with this symbol /* @internal */ constEnumOnlyModule?: boolean; // True if module contains only const enums or other modules with only const enums /* @internal */ isReferenced?: boolean; // True if the symbol is referenced elsewhere - /* @internal */ isDiscardable?: boolean;// True if a Javascript class property can be overwritten by a method + /* @internal */ isDiscardable?: boolean; // True if a Javascript class property can be overwritten by a method } /* @internal */ From 02a79e3f81ce7e09ab41050cf4bb3068c1cfeae6 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 5 Aug 2016 13:50:21 -0700 Subject: [PATCH 073/233] Try using runtests-parallel for CI (#9970) * Try using runtests-parallel for CI * Put worker count setting into .travis.yml * Reduce worker count to 4 - 8 wasnt much different from 4-6 but had contention issues causing timeouts --- .travis.yml | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 478e31c4398..cb9bf42225a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,12 +7,16 @@ node_js: sudo: false +env: + - workerCount=4 + matrix: fast_finish: true include: - os: osx node_js: stable osx_image: xcode7.3 + env: workerCount=2 branches: only: diff --git a/package.json b/package.json index 5606a423a0e..9f3122c3374 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ }, "scripts": { "pretest": "jake tests", - "test": "jake runtests", + "test": "jake runtests-parallel", "build": "npm run build:compiler && npm run build:tests", "build:compiler": "jake local", "build:tests": "jake tests", From 269b8285387f5a7fdf1a2d2aafa40e96a425576d Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 5 Aug 2016 14:16:29 -0700 Subject: [PATCH 074/233] Fix lssl task (#9967) --- Gulpfile.ts | 4 ++-- Jakefile.js | 6 +++--- src/server/tsconfig.library.json | 2 ++ 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Gulpfile.ts b/Gulpfile.ts index 6c91ef52cbb..b9b5d2068b2 100644 --- a/Gulpfile.ts +++ b/Gulpfile.ts @@ -449,7 +449,7 @@ gulp.task(tsserverLibraryFile, false, [servicesFile], (done) => { }); gulp.task("lssl", "Builds language service server library", [tsserverLibraryFile]); -gulp.task("local", "Builds the full compiler and services", [builtLocalCompiler, servicesFile, serverFile, builtGeneratedDiagnosticMessagesJSON]); +gulp.task("local", "Builds the full compiler and services", [builtLocalCompiler, servicesFile, serverFile, builtGeneratedDiagnosticMessagesJSON, tsserverLibraryFile]); gulp.task("tsc", "Builds only the compiler", [builtLocalCompiler]); @@ -503,7 +503,7 @@ gulp.task("VerifyLKG", false, [], () => { return gulp.src(expectedFiles).pipe(gulp.dest(LKGDirectory)); }); -gulp.task("LKGInternal", false, ["lib", "local", "lssl"]); +gulp.task("LKGInternal", false, ["lib", "local"]); gulp.task("LKG", "Makes a new LKG out of the built js files", ["clean", "dontUseDebugMode"], () => { return runSequence("LKGInternal", "VerifyLKG"); diff --git a/Jakefile.js b/Jakefile.js index 174be5e702f..a5650a56b16 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -551,7 +551,7 @@ var tsserverLibraryDefinitionFile = path.join(builtLocalDirectory, "tsserverlibr compileFile( tsserverLibraryFile, languageServiceLibrarySources, - [builtLocalDirectory, copyright].concat(languageServiceLibrarySources), + [builtLocalDirectory, copyright, builtLocalCompiler].concat(languageServiceLibrarySources).concat(libraryTargets), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true, { noOutFile: false, generateDeclarations: true }); @@ -562,7 +562,7 @@ task("lssl", [tsserverLibraryFile, tsserverLibraryDefinitionFile]); // Local target to build the compiler and services desc("Builds the full compiler and services"); -task("local", ["generate-diagnostics", "lib", tscFile, servicesFile, nodeDefinitionsFile, serverFile, builtGeneratedDiagnosticMessagesJSON]); +task("local", ["generate-diagnostics", "lib", tscFile, servicesFile, nodeDefinitionsFile, serverFile, builtGeneratedDiagnosticMessagesJSON, "lssl"]); // Local target to build only tsc.js desc("Builds only the compiler"); @@ -617,7 +617,7 @@ task("generate-spec", [specMd]); // Makes a new LKG. This target does not build anything, but errors if not all the outputs are present in the built/local directory desc("Makes a new LKG out of the built js files"); -task("LKG", ["clean", "release", "local", "lssl"].concat(libraryTargets), function() { +task("LKG", ["clean", "release", "local"].concat(libraryTargets), function() { var expectedFiles = [tscFile, servicesFile, serverFile, nodePackageFile, nodeDefinitionsFile, standaloneDefinitionsFile, tsserverLibraryFile, tsserverLibraryDefinitionFile].concat(libraryTargets); var missingFiles = expectedFiles.filter(function (f) { return !fs.existsSync(f); diff --git a/src/server/tsconfig.library.json b/src/server/tsconfig.library.json index 746283720bf..af04a74d557 100644 --- a/src/server/tsconfig.library.json +++ b/src/server/tsconfig.library.json @@ -10,6 +10,8 @@ "types": [] }, "files": [ + "../services/shims.ts", + "../services/utilities.ts", "editorServices.ts", "protocol.d.ts", "session.ts" From 46f5e5fad14785023e022c88ef09c1637ad5f5f8 Mon Sep 17 00:00:00 2001 From: gcnew Date: Sat, 6 Aug 2016 01:10:02 +0300 Subject: [PATCH 075/233] Surface noErrorTruncation option --- src/compiler/commandLineParser.ts | 5 +++++ src/compiler/diagnosticMessages.json | 4 ++++ .../reference/noErrorTruncation.errors.txt | 22 +++++++++++++++++++ .../baselines/reference/noErrorTruncation.js | 21 ++++++++++++++++++ tests/cases/compiler/noErrorTruncation.ts | 15 +++++++++++++ 5 files changed, 67 insertions(+) create mode 100644 tests/baselines/reference/noErrorTruncation.errors.txt create mode 100644 tests/baselines/reference/noErrorTruncation.js create mode 100644 tests/cases/compiler/noErrorTruncation.ts diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 7e2c6eb8d33..c1408187d88 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -126,6 +126,11 @@ namespace ts { type: "boolean", description: Diagnostics.Do_not_emit_outputs_if_any_errors_were_reported, }, + { + name: "noErrorTruncation", + type: "boolean", + description: Diagnostics.Do_not_truncate_verbose_types, + }, { name: "noImplicitAny", type: "boolean", diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 8126d5c605e..e3ac2d78c63 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2828,6 +2828,10 @@ "category": "Message", "code": 6137 }, + "Do not truncate verbose types.": { + "category": "Message", + "code": 6138 + }, "Variable '{0}' implicitly has an '{1}' type.": { "category": "Error", "code": 7005 diff --git a/tests/baselines/reference/noErrorTruncation.errors.txt b/tests/baselines/reference/noErrorTruncation.errors.txt new file mode 100644 index 00000000000..d27fad84e50 --- /dev/null +++ b/tests/baselines/reference/noErrorTruncation.errors.txt @@ -0,0 +1,22 @@ +tests/cases/compiler/noErrorTruncation.ts(10,7): error TS2322: Type 'number' is not assignable to type '{ someLongOptionA: string; } | { someLongOptionB: string; } | { someLongOptionC: string; } | { someLongOptionD: string; } | { someLongOptionE: string; } | { someLongOptionF: string; }'. + + +==== tests/cases/compiler/noErrorTruncation.ts (1 errors) ==== + // @noErrorTruncation + + type SomeLongOptionA = { someLongOptionA: string } + type SomeLongOptionB = { someLongOptionB: string } + type SomeLongOptionC = { someLongOptionC: string } + type SomeLongOptionD = { someLongOptionD: string } + type SomeLongOptionE = { someLongOptionE: string } + type SomeLongOptionF = { someLongOptionF: string } + + const x: SomeLongOptionA + ~ +!!! error TS2322: Type 'number' is not assignable to type '{ someLongOptionA: string; } | { someLongOptionB: string; } | { someLongOptionC: string; } | { someLongOptionD: string; } | { someLongOptionE: string; } | { someLongOptionF: string; }'. + | SomeLongOptionB + | SomeLongOptionC + | SomeLongOptionD + | SomeLongOptionE + | SomeLongOptionF = 42; + \ No newline at end of file diff --git a/tests/baselines/reference/noErrorTruncation.js b/tests/baselines/reference/noErrorTruncation.js new file mode 100644 index 00000000000..70bbc6bd8dc --- /dev/null +++ b/tests/baselines/reference/noErrorTruncation.js @@ -0,0 +1,21 @@ +//// [noErrorTruncation.ts] +// @noErrorTruncation + +type SomeLongOptionA = { someLongOptionA: string } +type SomeLongOptionB = { someLongOptionB: string } +type SomeLongOptionC = { someLongOptionC: string } +type SomeLongOptionD = { someLongOptionD: string } +type SomeLongOptionE = { someLongOptionE: string } +type SomeLongOptionF = { someLongOptionF: string } + +const x: SomeLongOptionA + | SomeLongOptionB + | SomeLongOptionC + | SomeLongOptionD + | SomeLongOptionE + | SomeLongOptionF = 42; + + +//// [noErrorTruncation.js] +// @noErrorTruncation +var x = 42; diff --git a/tests/cases/compiler/noErrorTruncation.ts b/tests/cases/compiler/noErrorTruncation.ts new file mode 100644 index 00000000000..5bd6ecf1d74 --- /dev/null +++ b/tests/cases/compiler/noErrorTruncation.ts @@ -0,0 +1,15 @@ +// @noErrorTruncation + +type SomeLongOptionA = { someLongOptionA: string } +type SomeLongOptionB = { someLongOptionB: string } +type SomeLongOptionC = { someLongOptionC: string } +type SomeLongOptionD = { someLongOptionD: string } +type SomeLongOptionE = { someLongOptionE: string } +type SomeLongOptionF = { someLongOptionF: string } + +const x: SomeLongOptionA + | SomeLongOptionB + | SomeLongOptionC + | SomeLongOptionD + | SomeLongOptionE + | SomeLongOptionF = 42; From 37e96b3a06c99b6f22bf0221cf63e7057c86dc05 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 6 Aug 2016 09:01:35 -0700 Subject: [PATCH 076/233] Stricter check for discriminant properties in type guards --- src/compiler/checker.ts | 62 ++++++++++++++++++++++++++++++++++------- src/compiler/types.ts | 2 ++ 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 84d5cd08a2b..3158a190244 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4416,10 +4416,19 @@ namespace ts { } const propTypes: Type[] = []; const declarations: Declaration[] = []; + let commonType: Type = undefined; + let hasCommonType = true; for (const prop of props) { if (prop.declarations) { addRange(declarations, prop.declarations); } + const type = getTypeOfSymbol(prop); + if (!commonType) { + commonType = type; + } + else if (type !== commonType) { + hasCommonType = false; + } propTypes.push(getTypeOfSymbol(prop)); } const result = createSymbol( @@ -4429,6 +4438,7 @@ namespace ts { commonFlags, name); result.containingType = containingType; + result.hasCommonType = hasCommonType; result.declarations = declarations; result.isReadonly = isReadonly; result.type = containingType.flags & TypeFlags.Union ? getUnionType(propTypes) : getIntersectionType(propTypes); @@ -7793,8 +7803,39 @@ namespace ts { return false; } - function rootContainsMatchingReference(source: Node, target: Node) { - return target.kind === SyntaxKind.PropertyAccessExpression && containsMatchingReference(source, (target).expression); + // Return true if target is a property access xxx.yyy, source is a property access xxx.zzz, the declared + // type of xxx is a union type, and yyy is a property that is possibly a discriminant. We consider a property + // a possible discriminant if its type differs in the constituents of containing union type, and if every + // choice is a unit type or a union of unit types. + function containsMatchingReferenceDiscriminant(source: Node, target: Node) { + return target.kind === SyntaxKind.PropertyAccessExpression && + containsMatchingReference(source, (target).expression) && + isDiscriminantProperty(getDeclaredTypeOfReference((target).expression), (target).name.text); + } + + function getDeclaredTypeOfReference(expr: Node): Type { + if (expr.kind === SyntaxKind.Identifier) { + return getTypeOfSymbol(getResolvedSymbol(expr)); + } + if (expr.kind === SyntaxKind.PropertyAccessExpression) { + const type = getDeclaredTypeOfReference((expr).expression); + return type && getTypeOfPropertyOfType(type, (expr).name.text); + } + return undefined; + } + + function isDiscriminantProperty(type: Type, name: string) { + if (type && type.flags & TypeFlags.Union) { + const prop = getPropertyOfType(type, name); + if (prop && prop.flags & SymbolFlags.SyntheticProperty) { + if ((prop).isDiscriminantProperty === undefined) { + (prop).isDiscriminantProperty = !(prop).hasCommonType && + isUnitUnionType(getTypeOfSymbol(prop)); + } + return (prop).isDiscriminantProperty; + } + } + return false; } function isOrContainsMatchingReference(source: Node, target: Node) { @@ -8222,7 +8263,7 @@ namespace ts { if (isMatchingReference(reference, expr)) { type = narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd); } - else if (isMatchingPropertyAccess(expr)) { + else if (isMatchingReferenceDiscriminant(expr)) { type = narrowTypeByDiscriminant(type, expr, t => narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd)); } return createFlowType(type, isIncomplete(flowType)); @@ -8300,10 +8341,11 @@ namespace ts { return cache[key] = getUnionType(antecedentTypes); } - function isMatchingPropertyAccess(expr: Expression) { + function isMatchingReferenceDiscriminant(expr: Expression) { return expr.kind === SyntaxKind.PropertyAccessExpression && + declaredType.flags & TypeFlags.Union && isMatchingReference(reference, (expr).expression) && - (declaredType.flags & TypeFlags.Union) !== 0; + isDiscriminantProperty(declaredType, (expr).name.text); } function narrowTypeByDiscriminant(type: Type, propAccess: PropertyAccessExpression, narrowType: (t: Type) => Type): Type { @@ -8317,10 +8359,10 @@ namespace ts { if (isMatchingReference(reference, expr)) { return getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy); } - if (isMatchingPropertyAccess(expr)) { + if (isMatchingReferenceDiscriminant(expr)) { return narrowTypeByDiscriminant(type, expr, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy)); } - if (rootContainsMatchingReference(reference, expr)) { + if (containsMatchingReferenceDiscriminant(reference, expr)) { return declaredType; } return type; @@ -8349,13 +8391,13 @@ namespace ts { if (isMatchingReference(reference, right)) { return narrowTypeByEquality(type, operator, left, assumeTrue); } - if (isMatchingPropertyAccess(left)) { + if (isMatchingReferenceDiscriminant(left)) { return narrowTypeByDiscriminant(type, left, t => narrowTypeByEquality(t, operator, right, assumeTrue)); } - if (isMatchingPropertyAccess(right)) { + if (isMatchingReferenceDiscriminant(right)) { return narrowTypeByDiscriminant(type, right, t => narrowTypeByEquality(t, operator, left, assumeTrue)); } - if (rootContainsMatchingReference(reference, left) || rootContainsMatchingReference(reference, right)) { + if (containsMatchingReferenceDiscriminant(reference, left) || containsMatchingReferenceDiscriminant(reference, right)) { return declaredType; } break; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 28ededebb0d..a6e860450c6 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2160,6 +2160,8 @@ namespace ts { mapper?: TypeMapper; // Type mapper for instantiation alias referenced?: boolean; // True if alias symbol has been referenced as a value containingType?: UnionOrIntersectionType; // Containing union or intersection type for synthetic property + hasCommonType?: boolean; // True if constituents of synthetic property all have same type + isDiscriminantProperty?: boolean; // True if discriminant synthetic property resolvedExports?: SymbolTable; // Resolved exports of module exportsChecked?: boolean; // True if exports of external module have been checked isDeclarationWithCollidingName?: boolean; // True if symbol is block scoped redeclaration From 1375505a1f58a623c0c5142046de4c54a4402f93 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 6 Aug 2016 09:06:56 -0700 Subject: [PATCH 077/233] Add tests --- .../discriminantPropertyCheck.errors.txt | 77 ++++++++++++ .../reference/discriminantPropertyCheck.js | 111 ++++++++++++++++++ .../compiler/discriminantPropertyCheck.ts | 69 +++++++++++ 3 files changed, 257 insertions(+) create mode 100644 tests/baselines/reference/discriminantPropertyCheck.errors.txt create mode 100644 tests/baselines/reference/discriminantPropertyCheck.js create mode 100644 tests/cases/compiler/discriminantPropertyCheck.ts diff --git a/tests/baselines/reference/discriminantPropertyCheck.errors.txt b/tests/baselines/reference/discriminantPropertyCheck.errors.txt new file mode 100644 index 00000000000..41593c4b224 --- /dev/null +++ b/tests/baselines/reference/discriminantPropertyCheck.errors.txt @@ -0,0 +1,77 @@ +tests/cases/compiler/discriminantPropertyCheck.ts(30,9): error TS2532: Object is possibly 'undefined'. +tests/cases/compiler/discriminantPropertyCheck.ts(66,9): error TS2532: Object is possibly 'undefined'. + + +==== tests/cases/compiler/discriminantPropertyCheck.ts (2 errors) ==== + + type Item = Item1 | Item2; + + interface Base { + bar: boolean; + } + + interface Item1 extends Base { + kind: "A"; + foo: string | undefined; + baz: boolean; + qux: true; + } + + interface Item2 extends Base { + kind: "B"; + foo: string | undefined; + baz: boolean; + qux: false; + } + + function goo1(x: Item) { + if (x.kind === "A" && x.foo !== undefined) { + x.foo.length; + } + } + + function goo2(x: Item) { + if (x.foo !== undefined && x.kind === "A") { + x.foo.length; // Error, intervening discriminant guard + ~~~~~ +!!! error TS2532: Object is possibly 'undefined'. + } + } + + function foo1(x: Item) { + if (x.bar && x.foo !== undefined) { + x.foo.length; + } + } + + function foo2(x: Item) { + if (x.foo !== undefined && x.bar) { + x.foo.length; + } + } + + function foo3(x: Item) { + if (x.baz && x.foo !== undefined) { + x.foo.length; + } + } + + function foo4(x: Item) { + if (x.foo !== undefined && x.baz) { + x.foo.length; + } + } + + function foo5(x: Item) { + if (x.qux && x.foo !== undefined) { + x.foo.length; + } + } + + function foo6(x: Item) { + if (x.foo !== undefined && x.qux) { + x.foo.length; // Error, intervening discriminant guard + ~~~~~ +!!! error TS2532: Object is possibly 'undefined'. + } + } \ No newline at end of file diff --git a/tests/baselines/reference/discriminantPropertyCheck.js b/tests/baselines/reference/discriminantPropertyCheck.js new file mode 100644 index 00000000000..b75a6277735 --- /dev/null +++ b/tests/baselines/reference/discriminantPropertyCheck.js @@ -0,0 +1,111 @@ +//// [discriminantPropertyCheck.ts] + +type Item = Item1 | Item2; + +interface Base { + bar: boolean; +} + +interface Item1 extends Base { + kind: "A"; + foo: string | undefined; + baz: boolean; + qux: true; +} + +interface Item2 extends Base { + kind: "B"; + foo: string | undefined; + baz: boolean; + qux: false; +} + +function goo1(x: Item) { + if (x.kind === "A" && x.foo !== undefined) { + x.foo.length; + } +} + +function goo2(x: Item) { + if (x.foo !== undefined && x.kind === "A") { + x.foo.length; // Error, intervening discriminant guard + } +} + +function foo1(x: Item) { + if (x.bar && x.foo !== undefined) { + x.foo.length; + } +} + +function foo2(x: Item) { + if (x.foo !== undefined && x.bar) { + x.foo.length; + } +} + +function foo3(x: Item) { + if (x.baz && x.foo !== undefined) { + x.foo.length; + } +} + +function foo4(x: Item) { + if (x.foo !== undefined && x.baz) { + x.foo.length; + } +} + +function foo5(x: Item) { + if (x.qux && x.foo !== undefined) { + x.foo.length; + } +} + +function foo6(x: Item) { + if (x.foo !== undefined && x.qux) { + x.foo.length; // Error, intervening discriminant guard + } +} + +//// [discriminantPropertyCheck.js] +function goo1(x) { + if (x.kind === "A" && x.foo !== undefined) { + x.foo.length; + } +} +function goo2(x) { + if (x.foo !== undefined && x.kind === "A") { + x.foo.length; // Error, intervening discriminant guard + } +} +function foo1(x) { + if (x.bar && x.foo !== undefined) { + x.foo.length; + } +} +function foo2(x) { + if (x.foo !== undefined && x.bar) { + x.foo.length; + } +} +function foo3(x) { + if (x.baz && x.foo !== undefined) { + x.foo.length; + } +} +function foo4(x) { + if (x.foo !== undefined && x.baz) { + x.foo.length; + } +} +function foo5(x) { + if (x.qux && x.foo !== undefined) { + x.foo.length; + } +} +function foo6(x) { + if (x.foo !== undefined && x.qux) { + x.foo.length; // Error, intervening discriminant guard + } +} diff --git a/tests/cases/compiler/discriminantPropertyCheck.ts b/tests/cases/compiler/discriminantPropertyCheck.ts new file mode 100644 index 00000000000..e493f6bf770 --- /dev/null +++ b/tests/cases/compiler/discriminantPropertyCheck.ts @@ -0,0 +1,69 @@ +// @strictNullChecks: true + +type Item = Item1 | Item2; + +interface Base { + bar: boolean; +} + +interface Item1 extends Base { + kind: "A"; + foo: string | undefined; + baz: boolean; + qux: true; +} + +interface Item2 extends Base { + kind: "B"; + foo: string | undefined; + baz: boolean; + qux: false; +} + +function goo1(x: Item) { + if (x.kind === "A" && x.foo !== undefined) { + x.foo.length; + } +} + +function goo2(x: Item) { + if (x.foo !== undefined && x.kind === "A") { + x.foo.length; // Error, intervening discriminant guard + } +} + +function foo1(x: Item) { + if (x.bar && x.foo !== undefined) { + x.foo.length; + } +} + +function foo2(x: Item) { + if (x.foo !== undefined && x.bar) { + x.foo.length; + } +} + +function foo3(x: Item) { + if (x.baz && x.foo !== undefined) { + x.foo.length; + } +} + +function foo4(x: Item) { + if (x.foo !== undefined && x.baz) { + x.foo.length; + } +} + +function foo5(x: Item) { + if (x.qux && x.foo !== undefined) { + x.foo.length; + } +} + +function foo6(x: Item) { + if (x.foo !== undefined && x.qux) { + x.foo.length; // Error, intervening discriminant guard + } +} \ No newline at end of file From cc2dc3acb0a3c654ca6ca26cad906af5585fcadf Mon Sep 17 00:00:00 2001 From: Godfrey Chan Date: Sat, 6 Aug 2016 10:36:17 -0700 Subject: [PATCH 078/233] Emit more efficient/concise "empty" ES6 ctor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When there are property assignments in a the class body of an inheriting class, tsc current emit the following compilation: ```ts class Foo extends Bar { public foo = 1; } ``` ```js class Foo extends Bar { constructor(…args) { super(…args); this.foo = 1; } } ``` This introduces an unneeded local variable and might force a reification of the `arguments` object (or otherwise reify the arguments into an array). This is particularly bad when that output is fed into another transpiler like Babel. In Babel, you get something like this today: ```js var Foo = (function (_Bar) { _inherits(Foo, _Bar); function Foo() { _classCallCheck(this, Foo); for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } _Bar.call.apply(_Bar, [this].concat(args)); this.foo = 1; } return Foo; })(Bar); ``` This causes a lot of needless work/allocations and some very strange code (`.call.apply` o_0). Admittedly, this is not strictly tsc’s problem; it could have done a deeper analysis of the code and optimized out the extra dance. However, tsc could also have emitted this simpler, more concise and semantically equivalent code in the first place: ```js class Foo extends Bar { constructor() { super(…arguments); this.foo = 1; } } ``` Which compiles into the following in Babel: ```js var Foo = (function (_Bar) { _inherits(Foo, _Bar); function Foo() { _classCallCheck(this, Foo); _Bar.apply(this, arguments); this.foo = 1; } return Foo; })(Bar); ``` Which is well-optimized (today) in most engines and much less confusing to read. As far as I can tell, the proposed compilation has exactly the same semantics as before. Fixes #10175 --- src/compiler/emitter.ts | 15 ++------------- tests/baselines/reference/classExpressionES63.js | 8 ++++---- ...ClassDeclarationWithPropertyAssignmentInES6.js | 4 ++-- 3 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index c8bd8072b24..6666b7df235 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -5311,18 +5311,7 @@ const _super = (function (geti, seti) { emitSignatureParameters(ctor); } else { - // Based on EcmaScript6 section 14.5.14: Runtime Semantics: ClassDefinitionEvaluation. - // If constructor is empty, then, - // If ClassHeritageopt is present, then - // Let constructor be the result of parsing the String "constructor(... args){ super (...args);}" using the syntactic grammar with the goal symbol MethodDefinition. - // Else, - // Let constructor be the result of parsing the String "constructor( ){ }" using the syntactic grammar with the goal symbol MethodDefinition - if (baseTypeElement) { - write("(...args)"); - } - else { - write("()"); - } + write("()"); } } @@ -5360,7 +5349,7 @@ const _super = (function (geti, seti) { write("_super.apply(this, arguments);"); } else { - write("super(...args);"); + write("super(...arguments);"); } emitEnd(baseTypeElement); } diff --git a/tests/baselines/reference/classExpressionES63.js b/tests/baselines/reference/classExpressionES63.js index 6b3a06cf7c3..5a72e173475 100644 --- a/tests/baselines/reference/classExpressionES63.js +++ b/tests/baselines/reference/classExpressionES63.js @@ -13,14 +13,14 @@ let C = class extends class extends class { } } { - constructor(...args) { - super(...args); + constructor() { + super(...arguments); this.b = 2; } } { - constructor(...args) { - super(...args); + constructor() { + super(...arguments); this.c = 3; } } diff --git a/tests/baselines/reference/emitClassDeclarationWithPropertyAssignmentInES6.js b/tests/baselines/reference/emitClassDeclarationWithPropertyAssignmentInES6.js index 03fb45b806e..91e6c506c2b 100644 --- a/tests/baselines/reference/emitClassDeclarationWithPropertyAssignmentInES6.js +++ b/tests/baselines/reference/emitClassDeclarationWithPropertyAssignmentInES6.js @@ -37,8 +37,8 @@ class D { } } class E extends D { - constructor(...args) { - super(...args); + constructor() { + super(...arguments); this.z = true; } } From 01f865dee7f36f00ad79877b09661e6b8de08b21 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 7 Aug 2016 07:48:40 -0700 Subject: [PATCH 079/233] Fix instanceof operator narrowing issues --- src/compiler/checker.ts | 53 +++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9583fadba62..7dbd6c1ab8b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8084,6 +8084,25 @@ namespace ts { return source.flags & TypeFlags.Union ? !forEach((source).types, t => !contains(types, t)) : contains(types, source); } + function isTypeSubsetOf(source: Type, target: Type) { + return source === target || target.flags & TypeFlags.Union && isTypeSubsetOfUnion(source, target); + } + + function isTypeSubsetOfUnion(source: Type, target: UnionType) { + if (source.flags & TypeFlags.Union) { + for (const t of (source).types) { + if (!containsType(target.types, t)) { + return false; + } + } + return true; + } + if (source.flags & TypeFlags.EnumLiteral && target.flags & TypeFlags.Enum && (source).baseType === target) { + return true; + } + return containsType(target.types, source); + } + function filterType(type: Type, f: (t: Type) => boolean): Type { return type.flags & TypeFlags.Union ? getUnionType(filter((type).types, f)) : @@ -8230,6 +8249,7 @@ namespace ts { function getTypeAtFlowBranchLabel(flow: FlowLabel): FlowType { const antecedentTypes: Type[] = []; + let subtypeReduction = false; let seenIncomplete = false; for (const antecedent of flow.antecedents) { const flowType = getTypeAtFlowNode(antecedent); @@ -8244,11 +8264,17 @@ namespace ts { if (!contains(antecedentTypes, type)) { antecedentTypes.push(type); } + // If an antecedent type is not a subset of the declared type, we need to perform + // subtype reduction. This happens when a "foreign" type is injected into the control + // flow using the instanceof operator or a user defined type predicate. + if (!isTypeSubsetOf(type, declaredType)) { + subtypeReduction = true; + } if (isIncomplete(flowType)) { seenIncomplete = true; } } - return createFlowType(getUnionType(antecedentTypes), seenIncomplete); + return createFlowType(getUnionType(antecedentTypes, subtypeReduction), seenIncomplete); } function getTypeAtFlowLoopLabel(flow: FlowLabel): FlowType { @@ -8274,6 +8300,7 @@ namespace ts { // Add the flow loop junction and reference to the in-process stack and analyze // each antecedent code path. const antecedentTypes: Type[] = []; + let subtypeReduction = false; flowLoopNodes[flowLoopCount] = flow; flowLoopKeys[flowLoopCount] = key; flowLoopTypes[flowLoopCount] = antecedentTypes; @@ -8290,6 +8317,12 @@ namespace ts { if (!contains(antecedentTypes, type)) { antecedentTypes.push(type); } + // If an antecedent type is not a subset of the declared type, we need to perform + // subtype reduction. This happens when a "foreign" type is injected into the control + // flow using the instanceof operator or a user defined type predicate. + if (!isTypeSubsetOf(type, declaredType)) { + subtypeReduction = true; + } // If the type at a particular antecedent path is the declared type there is no // reason to process more antecedents since the only possible outcome is subtypes // that will be removed in the final union type anyway. @@ -8297,7 +8330,7 @@ namespace ts { break; } } - return cache[key] = getUnionType(antecedentTypes); + return cache[key] = getUnionType(antecedentTypes, subtypeReduction); } function isMatchingPropertyAccess(expr: Expression) { @@ -8494,9 +8527,7 @@ namespace ts { function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean) { if (!assumeTrue) { - return type.flags & TypeFlags.Union ? - getUnionType(filter((type).types, t => !isTypeSubtypeOf(t, candidate))) : - type; + return filterType(type, t => !isTypeSubtypeOf(t, candidate)); } // If the current type is a union type, remove all constituents that aren't assignable to // the candidate type. If one or more constituents remain, return a union of those. @@ -8506,13 +8537,15 @@ namespace ts { return getUnionType(assignableConstituents); } } - // If the candidate type is assignable to the target type, narrow to the candidate type. - // Otherwise, if the current type is assignable to the candidate, keep the current type. - // Otherwise, the types are completely unrelated, so narrow to the empty type. + // If the candidate type is a subtype of the target type, narrow to the candidate type. + // Otherwise, narrow to whichever of the target type or the candidate type that is assignable + // to the other. Otherwise, the types are completely unrelated, so narrow to an intersection + // of the two types. const targetType = type.flags & TypeFlags.TypeParameter ? getApparentType(type) : type; - return isTypeAssignableTo(candidate, targetType) ? candidate : + return isTypeSubtypeOf(candidate, targetType) ? candidate : isTypeAssignableTo(type, candidate) ? type : - getIntersectionType([type, candidate]); + isTypeAssignableTo(candidate, targetType) ? candidate : + getIntersectionType([type, candidate]); } function narrowTypeByTypePredicate(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type { From f50226b481900af204ed9f8d0c29e32a56827c54 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 7 Aug 2016 07:53:36 -0700 Subject: [PATCH 080/233] Accept new baselines --- .../baselines/reference/controlFlowBinaryOrExpression.symbols | 4 ++-- tests/baselines/reference/controlFlowBinaryOrExpression.types | 2 +- tests/baselines/reference/stringLiteralTypesAsTags01.types | 4 ++-- tests/baselines/reference/stringLiteralTypesAsTags02.types | 4 ++-- tests/baselines/reference/stringLiteralTypesAsTags03.types | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/baselines/reference/controlFlowBinaryOrExpression.symbols b/tests/baselines/reference/controlFlowBinaryOrExpression.symbols index e76cfbf4cab..5251973005f 100644 --- a/tests/baselines/reference/controlFlowBinaryOrExpression.symbols +++ b/tests/baselines/reference/controlFlowBinaryOrExpression.symbols @@ -86,8 +86,8 @@ if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) { >sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3)) sourceObj.length; ->sourceObj.length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33)) +>sourceObj.length : Symbol(NodeList.length, Decl(controlFlowBinaryOrExpression.ts, 10, 27)) >sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3)) ->length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33)) +>length : Symbol(NodeList.length, Decl(controlFlowBinaryOrExpression.ts, 10, 27)) } diff --git a/tests/baselines/reference/controlFlowBinaryOrExpression.types b/tests/baselines/reference/controlFlowBinaryOrExpression.types index 0bf5ab524b9..d24462316b4 100644 --- a/tests/baselines/reference/controlFlowBinaryOrExpression.types +++ b/tests/baselines/reference/controlFlowBinaryOrExpression.types @@ -106,7 +106,7 @@ if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) { sourceObj.length; >sourceObj.length : number ->sourceObj : NodeList | HTMLCollection | ({ a: string; } & HTMLCollection) +>sourceObj : NodeList >length : number } diff --git a/tests/baselines/reference/stringLiteralTypesAsTags01.types b/tests/baselines/reference/stringLiteralTypesAsTags01.types index e00fd18163d..e95a7364f93 100644 --- a/tests/baselines/reference/stringLiteralTypesAsTags01.types +++ b/tests/baselines/reference/stringLiteralTypesAsTags01.types @@ -99,8 +99,8 @@ if (hasKind(x, "A")) { } else { let b = x; ->b : A ->x : A +>b : never +>x : never } if (!hasKind(x, "B")) { diff --git a/tests/baselines/reference/stringLiteralTypesAsTags02.types b/tests/baselines/reference/stringLiteralTypesAsTags02.types index fb1632559ea..c984b8a78f9 100644 --- a/tests/baselines/reference/stringLiteralTypesAsTags02.types +++ b/tests/baselines/reference/stringLiteralTypesAsTags02.types @@ -93,8 +93,8 @@ if (hasKind(x, "A")) { } else { let b = x; ->b : A ->x : A +>b : never +>x : never } if (!hasKind(x, "B")) { diff --git a/tests/baselines/reference/stringLiteralTypesAsTags03.types b/tests/baselines/reference/stringLiteralTypesAsTags03.types index 05be633813b..fbe71ff07c1 100644 --- a/tests/baselines/reference/stringLiteralTypesAsTags03.types +++ b/tests/baselines/reference/stringLiteralTypesAsTags03.types @@ -96,8 +96,8 @@ if (hasKind(x, "A")) { } else { let b = x; ->b : A ->x : A +>b : never +>x : never } if (!hasKind(x, "B")) { From 67b3fe58fa9b0761da93d9ea6ab2e8019b6a4b22 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 7 Aug 2016 08:53:36 -0700 Subject: [PATCH 081/233] Add regression test --- .../reference/controlFlowInstanceof.js | 156 +++++++++++++ .../reference/controlFlowInstanceof.symbols | 194 ++++++++++++++++ .../reference/controlFlowInstanceof.types | 217 ++++++++++++++++++ tests/cases/compiler/controlFlowInstanceof.ts | 80 +++++++ 4 files changed, 647 insertions(+) create mode 100644 tests/baselines/reference/controlFlowInstanceof.js create mode 100644 tests/baselines/reference/controlFlowInstanceof.symbols create mode 100644 tests/baselines/reference/controlFlowInstanceof.types create mode 100644 tests/cases/compiler/controlFlowInstanceof.ts diff --git a/tests/baselines/reference/controlFlowInstanceof.js b/tests/baselines/reference/controlFlowInstanceof.js new file mode 100644 index 00000000000..7c7833927ca --- /dev/null +++ b/tests/baselines/reference/controlFlowInstanceof.js @@ -0,0 +1,156 @@ +//// [controlFlowInstanceof.ts] + +// Repros from #10167 + +function f1(s: Set | Set) { + s = new Set(); + s; // Set + if (s instanceof Set) { + s; // Set + } + s; // Set + s.add(42); +} + +function f2(s: Set | Set) { + s = new Set(); + s; // Set + if (s instanceof Promise) { + s; // Set & Promise + } + s; // Set + s.add(42); +} + +function f3(s: Set | Set) { + s; // Set | Set + if (s instanceof Set) { + s; // Set | Set + } + else { + s; // never + } +} + +function f4(s: Set | Set) { + s = new Set(); + s; // Set + if (s instanceof Set) { + s; // Set + } + else { + s; // never + } +} + +// More tests + +class A { a: string } +class B extends A { b: string } +class C extends A { c: string } + +function foo(x: A | undefined) { + x; // A | undefined + if (x instanceof B || x instanceof C) { + x; // B | C + } + x; // A | undefined + if (x instanceof B && x instanceof C) { + x; // B & C + } + x; // A | undefined + if (!x) { + return; + } + x; // A + if (x instanceof B) { + x; // B + if (x instanceof C) { + x; // B & C + } + else { + x; // B + } + x; // B + } + else { + x; // A + } + x; // A +} + +//// [controlFlowInstanceof.js] +// Repros from #10167 +function f1(s) { + s = new Set(); + s; // Set + if (s instanceof Set) { + s; // Set + } + s; // Set + s.add(42); +} +function f2(s) { + s = new Set(); + s; // Set + if (s instanceof Promise) { + s; // Set & Promise + } + s; // Set + s.add(42); +} +function f3(s) { + s; // Set | Set + if (s instanceof Set) { + s; // Set | Set + } + else { + s; // never + } +} +function f4(s) { + s = new Set(); + s; // Set + if (s instanceof Set) { + s; // Set + } + else { + s; // never + } +} +// More tests +class A { +} +class B extends A { +} +class C extends A { +} +function foo(x) { + x; // A | undefined + if (x instanceof B || x instanceof C) { + x; // B | C + } + x; // A | undefined + if (x instanceof B && x instanceof C) { + x; // B & C + } + x; // A | undefined + if (!x) { + return; + } + x; // A + if (x instanceof B) { + x; // B + if (x instanceof C) { + x; // B & C + } + else { + x; // B + } + x; // B + } + else { + x; // A + } + x; // A +} diff --git a/tests/baselines/reference/controlFlowInstanceof.symbols b/tests/baselines/reference/controlFlowInstanceof.symbols new file mode 100644 index 00000000000..64b567c1548 --- /dev/null +++ b/tests/baselines/reference/controlFlowInstanceof.symbols @@ -0,0 +1,194 @@ +=== tests/cases/compiler/controlFlowInstanceof.ts === + +// Repros from #10167 + +function f1(s: Set | Set) { +>f1 : Symbol(f1, Decl(controlFlowInstanceof.ts, 0, 0)) +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 3, 12)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) + + s = new Set(); +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 3, 12)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) + + s; // Set +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 3, 12)) + + if (s instanceof Set) { +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 3, 12)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) + + s; // Set +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 3, 12)) + } + s; // Set +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 3, 12)) + + s.add(42); +>s.add : Symbol(Set.add, Decl(lib.es2015.collection.d.ts, --, --)) +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 3, 12)) +>add : Symbol(Set.add, Decl(lib.es2015.collection.d.ts, --, --)) +} + +function f2(s: Set | Set) { +>f2 : Symbol(f2, Decl(controlFlowInstanceof.ts, 11, 1)) +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 13, 12)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) + + s = new Set(); +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 13, 12)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) + + s; // Set +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 13, 12)) + + if (s instanceof Promise) { +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 13, 12)) +>Promise : Symbol(Promise, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --)) + + s; // Set & Promise +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 13, 12)) + } + s; // Set +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 13, 12)) + + s.add(42); +>s.add : Symbol(Set.add, Decl(lib.es2015.collection.d.ts, --, --)) +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 13, 12)) +>add : Symbol(Set.add, Decl(lib.es2015.collection.d.ts, --, --)) +} + +function f3(s: Set | Set) { +>f3 : Symbol(f3, Decl(controlFlowInstanceof.ts, 21, 1)) +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 23, 12)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) + + s; // Set | Set +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 23, 12)) + + if (s instanceof Set) { +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 23, 12)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) + + s; // Set | Set +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 23, 12)) + } + else { + s; // never +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 23, 12)) + } +} + +function f4(s: Set | Set) { +>f4 : Symbol(f4, Decl(controlFlowInstanceof.ts, 31, 1)) +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 33, 12)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) + + s = new Set(); +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 33, 12)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) + + s; // Set +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 33, 12)) + + if (s instanceof Set) { +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 33, 12)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) + + s; // Set +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 33, 12)) + } + else { + s; // never +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 33, 12)) + } +} + +// More tests + +class A { a: string } +>A : Symbol(A, Decl(controlFlowInstanceof.ts, 42, 1)) +>a : Symbol(A.a, Decl(controlFlowInstanceof.ts, 46, 9)) + +class B extends A { b: string } +>B : Symbol(B, Decl(controlFlowInstanceof.ts, 46, 21)) +>A : Symbol(A, Decl(controlFlowInstanceof.ts, 42, 1)) +>b : Symbol(B.b, Decl(controlFlowInstanceof.ts, 47, 19)) + +class C extends A { c: string } +>C : Symbol(C, Decl(controlFlowInstanceof.ts, 47, 31)) +>A : Symbol(A, Decl(controlFlowInstanceof.ts, 42, 1)) +>c : Symbol(C.c, Decl(controlFlowInstanceof.ts, 48, 19)) + +function foo(x: A | undefined) { +>foo : Symbol(foo, Decl(controlFlowInstanceof.ts, 48, 31)) +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) +>A : Symbol(A, Decl(controlFlowInstanceof.ts, 42, 1)) + + x; // A | undefined +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) + + if (x instanceof B || x instanceof C) { +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) +>B : Symbol(B, Decl(controlFlowInstanceof.ts, 46, 21)) +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) +>C : Symbol(C, Decl(controlFlowInstanceof.ts, 47, 31)) + + x; // B | C +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) + } + x; // A | undefined +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) + + if (x instanceof B && x instanceof C) { +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) +>B : Symbol(B, Decl(controlFlowInstanceof.ts, 46, 21)) +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) +>C : Symbol(C, Decl(controlFlowInstanceof.ts, 47, 31)) + + x; // B & C +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) + } + x; // A | undefined +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) + + if (!x) { +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) + + return; + } + x; // A +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) + + if (x instanceof B) { +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) +>B : Symbol(B, Decl(controlFlowInstanceof.ts, 46, 21)) + + x; // B +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) + + if (x instanceof C) { +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) +>C : Symbol(C, Decl(controlFlowInstanceof.ts, 47, 31)) + + x; // B & C +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) + } + else { + x; // B +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) + } + x; // B +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) + } + else { + x; // A +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) + } + x; // A +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) +} diff --git a/tests/baselines/reference/controlFlowInstanceof.types b/tests/baselines/reference/controlFlowInstanceof.types new file mode 100644 index 00000000000..362715914f9 --- /dev/null +++ b/tests/baselines/reference/controlFlowInstanceof.types @@ -0,0 +1,217 @@ +=== tests/cases/compiler/controlFlowInstanceof.ts === + +// Repros from #10167 + +function f1(s: Set | Set) { +>f1 : (s: Set | Set) => void +>s : Set | Set +>Set : Set +>Set : Set + + s = new Set(); +>s = new Set() : Set +>s : Set | Set +>new Set() : Set +>Set : SetConstructor + + s; // Set +>s : Set + + if (s instanceof Set) { +>s instanceof Set : boolean +>s : Set +>Set : SetConstructor + + s; // Set +>s : Set + } + s; // Set +>s : Set + + s.add(42); +>s.add(42) : Set +>s.add : (value: number) => Set +>s : Set +>add : (value: number) => Set +>42 : number +} + +function f2(s: Set | Set) { +>f2 : (s: Set | Set) => void +>s : Set | Set +>Set : Set +>Set : Set + + s = new Set(); +>s = new Set() : Set +>s : Set | Set +>new Set() : Set +>Set : SetConstructor + + s; // Set +>s : Set + + if (s instanceof Promise) { +>s instanceof Promise : boolean +>s : Set +>Promise : PromiseConstructor + + s; // Set & Promise +>s : Set & Promise + } + s; // Set +>s : Set + + s.add(42); +>s.add(42) : Set +>s.add : (value: number) => Set +>s : Set +>add : (value: number) => Set +>42 : number +} + +function f3(s: Set | Set) { +>f3 : (s: Set | Set) => void +>s : Set | Set +>Set : Set +>Set : Set + + s; // Set | Set +>s : Set | Set + + if (s instanceof Set) { +>s instanceof Set : boolean +>s : Set | Set +>Set : SetConstructor + + s; // Set | Set +>s : Set | Set + } + else { + s; // never +>s : never + } +} + +function f4(s: Set | Set) { +>f4 : (s: Set | Set) => void +>s : Set | Set +>Set : Set +>Set : Set + + s = new Set(); +>s = new Set() : Set +>s : Set | Set +>new Set() : Set +>Set : SetConstructor + + s; // Set +>s : Set + + if (s instanceof Set) { +>s instanceof Set : boolean +>s : Set +>Set : SetConstructor + + s; // Set +>s : Set + } + else { + s; // never +>s : never + } +} + +// More tests + +class A { a: string } +>A : A +>a : string + +class B extends A { b: string } +>B : B +>A : A +>b : string + +class C extends A { c: string } +>C : C +>A : A +>c : string + +function foo(x: A | undefined) { +>foo : (x: A) => void +>x : A +>A : A + + x; // A | undefined +>x : A + + if (x instanceof B || x instanceof C) { +>x instanceof B || x instanceof C : boolean +>x instanceof B : boolean +>x : A +>B : typeof B +>x instanceof C : boolean +>x : A +>C : typeof C + + x; // B | C +>x : B | C + } + x; // A | undefined +>x : A + + if (x instanceof B && x instanceof C) { +>x instanceof B && x instanceof C : boolean +>x instanceof B : boolean +>x : A +>B : typeof B +>x instanceof C : boolean +>x : B +>C : typeof C + + x; // B & C +>x : B & C + } + x; // A | undefined +>x : A + + if (!x) { +>!x : boolean +>x : A + + return; + } + x; // A +>x : A + + if (x instanceof B) { +>x instanceof B : boolean +>x : A +>B : typeof B + + x; // B +>x : B + + if (x instanceof C) { +>x instanceof C : boolean +>x : B +>C : typeof C + + x; // B & C +>x : B & C + } + else { + x; // B +>x : B + } + x; // B +>x : B + } + else { + x; // A +>x : A + } + x; // A +>x : A +} diff --git a/tests/cases/compiler/controlFlowInstanceof.ts b/tests/cases/compiler/controlFlowInstanceof.ts new file mode 100644 index 00000000000..229a00236ba --- /dev/null +++ b/tests/cases/compiler/controlFlowInstanceof.ts @@ -0,0 +1,80 @@ +// @target: es6 + +// Repros from #10167 + +function f1(s: Set | Set) { + s = new Set(); + s; // Set + if (s instanceof Set) { + s; // Set + } + s; // Set + s.add(42); +} + +function f2(s: Set | Set) { + s = new Set(); + s; // Set + if (s instanceof Promise) { + s; // Set & Promise + } + s; // Set + s.add(42); +} + +function f3(s: Set | Set) { + s; // Set | Set + if (s instanceof Set) { + s; // Set | Set + } + else { + s; // never + } +} + +function f4(s: Set | Set) { + s = new Set(); + s; // Set + if (s instanceof Set) { + s; // Set + } + else { + s; // never + } +} + +// More tests + +class A { a: string } +class B extends A { b: string } +class C extends A { c: string } + +function foo(x: A | undefined) { + x; // A | undefined + if (x instanceof B || x instanceof C) { + x; // B | C + } + x; // A | undefined + if (x instanceof B && x instanceof C) { + x; // B & C + } + x; // A | undefined + if (!x) { + return; + } + x; // A + if (x instanceof B) { + x; // B + if (x instanceof C) { + x; // B & C + } + else { + x; // B + } + x; // B + } + else { + x; // A + } + x; // A +} \ No newline at end of file From 2845d2f8b8c2c476085eda9abe083000047d70c4 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 8 Aug 2016 09:04:46 -0700 Subject: [PATCH 082/233] Improve naming and documentation from PR --- src/compiler/binder.ts | 9 +++++++-- src/compiler/types.ts | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index b771a3b67be..849f503ef85 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -313,6 +313,11 @@ namespace ts { // declaration we have for this symbol, and then create a new symbol for this // declaration. // + // Note that when properties declared in Javascript constructors + // (marked by isReplaceableByMethod) conflict with another symbol, the property loses. + // Always. This allows the common Javascript pattern of overwriting a prototype method + // with an bound instance method of the same type: `this.method = this.method.bind(this)` + // // If we created a new symbol, either because we didn't have a symbol with this name // in the symbol table, or we conflicted with an existing symbol, then just add this // node as the sole declaration of the new symbol. @@ -329,7 +334,7 @@ namespace ts { } if (symbol.flags & excludes) { - if (symbol.isDiscardable) { + if (symbol.isReplaceableByMethod) { // Javascript constructor-declared symbols can be discarded in favor of // prototype symbols like methods. symbol = symbolTable[name] = createSymbol(SymbolFlags.None, name); @@ -1988,7 +1993,7 @@ namespace ts { // It's acceptable for multiple 'this' assignments of the same identifier to occur // AND it can be overwritten by subsequent method declarations const symbol = declareSymbol(assignee.symbol.members, assignee.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property); - symbol.isDiscardable = true; + symbol.isReplaceableByMethod = true; } function bindPrototypePropertyAssignment(node: BinaryExpression) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f5cee2f8f8f..70bd3c3c070 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2137,7 +2137,7 @@ namespace ts { /* @internal */ exportSymbol?: Symbol; // Exported symbol associated with this symbol /* @internal */ constEnumOnlyModule?: boolean; // True if module contains only const enums or other modules with only const enums /* @internal */ isReferenced?: boolean; // True if the symbol is referenced elsewhere - /* @internal */ isDiscardable?: boolean; // True if a Javascript class property can be overwritten by a method + /* @internal */ isReplaceableByMethod?: boolean; // Can this Javascript class property be replaced by a method symbol? } /* @internal */ From a0c5608770b911d357c745f4842e03b408a135fe Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 8 Aug 2016 09:44:24 -0700 Subject: [PATCH 083/233] Update comment --- src/compiler/checker.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7dbd6c1ab8b..09402b94119 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8538,9 +8538,10 @@ namespace ts { } } // If the candidate type is a subtype of the target type, narrow to the candidate type. - // Otherwise, narrow to whichever of the target type or the candidate type that is assignable - // to the other. Otherwise, the types are completely unrelated, so narrow to an intersection - // of the two types. + // Otherwise, if the target type is assignable to the candidate type, keep the target type. + // Otherwise, if the candidate type is assignable to the target type, narrow to the candidate + // type. Otherwise, the types are completely unrelated, so narrow to an intersection of the + // two types. const targetType = type.flags & TypeFlags.TypeParameter ? getApparentType(type) : type; return isTypeSubtypeOf(candidate, targetType) ? candidate : isTypeAssignableTo(type, candidate) ? type : From ce5a3f466d281695d700fbd08dcb92a685340809 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 8 Aug 2016 09:44:43 -0700 Subject: [PATCH 084/233] Add more tests --- tests/cases/compiler/controlFlowInstanceof.ts | 19 ++++++++++++ .../discriminantsAndTypePredicates.ts | 31 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 tests/cases/compiler/discriminantsAndTypePredicates.ts diff --git a/tests/cases/compiler/controlFlowInstanceof.ts b/tests/cases/compiler/controlFlowInstanceof.ts index 229a00236ba..56f3ff97e4c 100644 --- a/tests/cases/compiler/controlFlowInstanceof.ts +++ b/tests/cases/compiler/controlFlowInstanceof.ts @@ -77,4 +77,23 @@ function foo(x: A | undefined) { x; // A } x; // A +} + +// X is neither assignable to Y nor a subtype of Y +// Y is assignable to X, but not a subtype of X + +interface X { + x?: string; +} + +class Y { + y: string; +} + +function goo(x: X) { + x; + if (x instanceof Y) { + x.y; + } + x; } \ No newline at end of file diff --git a/tests/cases/compiler/discriminantsAndTypePredicates.ts b/tests/cases/compiler/discriminantsAndTypePredicates.ts new file mode 100644 index 00000000000..c21ab7ec8f4 --- /dev/null +++ b/tests/cases/compiler/discriminantsAndTypePredicates.ts @@ -0,0 +1,31 @@ +// Repro from #10145 + +interface A { type: 'A' } +interface B { type: 'B' } + +function isA(x: A | B): x is A { return x.type === 'A'; } +function isB(x: A | B): x is B { return x.type === 'B'; } + +function foo1(x: A | B): any { + x; // A | B + if (isA(x)) { + return x; // A + } + x; // B + if (isB(x)) { + return x; // B + } + x; // never +} + +function foo2(x: A | B): any { + x; // A | B + if (x.type === 'A') { + return x; // A + } + x; // B + if (x.type === 'B') { + return x; // B + } + x; // never +} \ No newline at end of file From ba521de66d8f654784be4f011814a5ca17b413a2 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 8 Aug 2016 09:45:12 -0700 Subject: [PATCH 085/233] Accept new baselines --- .../reference/controlFlowInstanceof.js | 28 +++++ .../reference/controlFlowInstanceof.symbols | 38 +++++++ .../reference/controlFlowInstanceof.types | 39 +++++++ .../discriminantsAndTypePredicates.js | 59 ++++++++++ .../discriminantsAndTypePredicates.symbols | 94 ++++++++++++++++ .../discriminantsAndTypePredicates.types | 104 ++++++++++++++++++ 6 files changed, 362 insertions(+) create mode 100644 tests/baselines/reference/discriminantsAndTypePredicates.js create mode 100644 tests/baselines/reference/discriminantsAndTypePredicates.symbols create mode 100644 tests/baselines/reference/discriminantsAndTypePredicates.types diff --git a/tests/baselines/reference/controlFlowInstanceof.js b/tests/baselines/reference/controlFlowInstanceof.js index 7c7833927ca..3f2287b4628 100644 --- a/tests/baselines/reference/controlFlowInstanceof.js +++ b/tests/baselines/reference/controlFlowInstanceof.js @@ -77,6 +77,25 @@ function foo(x: A | undefined) { x; // A } x; // A +} + +// X is neither assignable to Y nor a subtype of Y +// Y is assignable to X, but not a subtype of X + +interface X { + x?: string; +} + +class Y { + y: string; +} + +function goo(x: X) { + x; + if (x instanceof Y) { + x.y; + } + x; } //// [controlFlowInstanceof.js] @@ -154,3 +173,12 @@ function foo(x) { } x; // A } +class Y { +} +function goo(x) { + x; + if (x instanceof Y) { + x.y; + } + x; +} diff --git a/tests/baselines/reference/controlFlowInstanceof.symbols b/tests/baselines/reference/controlFlowInstanceof.symbols index 64b567c1548..31894827990 100644 --- a/tests/baselines/reference/controlFlowInstanceof.symbols +++ b/tests/baselines/reference/controlFlowInstanceof.symbols @@ -192,3 +192,41 @@ function foo(x: A | undefined) { x; // A >x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) } + +// X is neither assignable to Y nor a subtype of Y +// Y is assignable to X, but not a subtype of X + +interface X { +>X : Symbol(X, Decl(controlFlowInstanceof.ts, 78, 1)) + + x?: string; +>x : Symbol(X.x, Decl(controlFlowInstanceof.ts, 83, 13)) +} + +class Y { +>Y : Symbol(Y, Decl(controlFlowInstanceof.ts, 85, 1)) + + y: string; +>y : Symbol(Y.y, Decl(controlFlowInstanceof.ts, 87, 9)) +} + +function goo(x: X) { +>goo : Symbol(goo, Decl(controlFlowInstanceof.ts, 89, 1)) +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 91, 13)) +>X : Symbol(X, Decl(controlFlowInstanceof.ts, 78, 1)) + + x; +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 91, 13)) + + if (x instanceof Y) { +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 91, 13)) +>Y : Symbol(Y, Decl(controlFlowInstanceof.ts, 85, 1)) + + x.y; +>x.y : Symbol(Y.y, Decl(controlFlowInstanceof.ts, 87, 9)) +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 91, 13)) +>y : Symbol(Y.y, Decl(controlFlowInstanceof.ts, 87, 9)) + } + x; +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 91, 13)) +} diff --git a/tests/baselines/reference/controlFlowInstanceof.types b/tests/baselines/reference/controlFlowInstanceof.types index 362715914f9..e52d1a25b1d 100644 --- a/tests/baselines/reference/controlFlowInstanceof.types +++ b/tests/baselines/reference/controlFlowInstanceof.types @@ -215,3 +215,42 @@ function foo(x: A | undefined) { x; // A >x : A } + +// X is neither assignable to Y nor a subtype of Y +// Y is assignable to X, but not a subtype of X + +interface X { +>X : X + + x?: string; +>x : string +} + +class Y { +>Y : Y + + y: string; +>y : string +} + +function goo(x: X) { +>goo : (x: X) => void +>x : X +>X : X + + x; +>x : X + + if (x instanceof Y) { +>x instanceof Y : boolean +>x : X +>Y : typeof Y + + x.y; +>x.y : string +>x : Y +>y : string + } + x; +>x : X +} diff --git a/tests/baselines/reference/discriminantsAndTypePredicates.js b/tests/baselines/reference/discriminantsAndTypePredicates.js new file mode 100644 index 00000000000..66cfe056ab7 --- /dev/null +++ b/tests/baselines/reference/discriminantsAndTypePredicates.js @@ -0,0 +1,59 @@ +//// [discriminantsAndTypePredicates.ts] +// Repro from #10145 + +interface A { type: 'A' } +interface B { type: 'B' } + +function isA(x: A | B): x is A { return x.type === 'A'; } +function isB(x: A | B): x is B { return x.type === 'B'; } + +function foo1(x: A | B): any { + x; // A | B + if (isA(x)) { + return x; // A + } + x; // B + if (isB(x)) { + return x; // B + } + x; // never +} + +function foo2(x: A | B): any { + x; // A | B + if (x.type === 'A') { + return x; // A + } + x; // B + if (x.type === 'B') { + return x; // B + } + x; // never +} + +//// [discriminantsAndTypePredicates.js] +// Repro from #10145 +function isA(x) { return x.type === 'A'; } +function isB(x) { return x.type === 'B'; } +function foo1(x) { + x; // A | B + if (isA(x)) { + return x; // A + } + x; // B + if (isB(x)) { + return x; // B + } + x; // never +} +function foo2(x) { + x; // A | B + if (x.type === 'A') { + return x; // A + } + x; // B + if (x.type === 'B') { + return x; // B + } + x; // never +} diff --git a/tests/baselines/reference/discriminantsAndTypePredicates.symbols b/tests/baselines/reference/discriminantsAndTypePredicates.symbols new file mode 100644 index 00000000000..4f412823936 --- /dev/null +++ b/tests/baselines/reference/discriminantsAndTypePredicates.symbols @@ -0,0 +1,94 @@ +=== tests/cases/compiler/discriminantsAndTypePredicates.ts === +// Repro from #10145 + +interface A { type: 'A' } +>A : Symbol(A, Decl(discriminantsAndTypePredicates.ts, 0, 0)) +>type : Symbol(A.type, Decl(discriminantsAndTypePredicates.ts, 2, 13)) + +interface B { type: 'B' } +>B : Symbol(B, Decl(discriminantsAndTypePredicates.ts, 2, 25)) +>type : Symbol(B.type, Decl(discriminantsAndTypePredicates.ts, 3, 13)) + +function isA(x: A | B): x is A { return x.type === 'A'; } +>isA : Symbol(isA, Decl(discriminantsAndTypePredicates.ts, 3, 25)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 5, 13)) +>A : Symbol(A, Decl(discriminantsAndTypePredicates.ts, 0, 0)) +>B : Symbol(B, Decl(discriminantsAndTypePredicates.ts, 2, 25)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 5, 13)) +>A : Symbol(A, Decl(discriminantsAndTypePredicates.ts, 0, 0)) +>x.type : Symbol(type, Decl(discriminantsAndTypePredicates.ts, 2, 13), Decl(discriminantsAndTypePredicates.ts, 3, 13)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 5, 13)) +>type : Symbol(type, Decl(discriminantsAndTypePredicates.ts, 2, 13), Decl(discriminantsAndTypePredicates.ts, 3, 13)) + +function isB(x: A | B): x is B { return x.type === 'B'; } +>isB : Symbol(isB, Decl(discriminantsAndTypePredicates.ts, 5, 57)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 6, 13)) +>A : Symbol(A, Decl(discriminantsAndTypePredicates.ts, 0, 0)) +>B : Symbol(B, Decl(discriminantsAndTypePredicates.ts, 2, 25)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 6, 13)) +>B : Symbol(B, Decl(discriminantsAndTypePredicates.ts, 2, 25)) +>x.type : Symbol(type, Decl(discriminantsAndTypePredicates.ts, 2, 13), Decl(discriminantsAndTypePredicates.ts, 3, 13)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 6, 13)) +>type : Symbol(type, Decl(discriminantsAndTypePredicates.ts, 2, 13), Decl(discriminantsAndTypePredicates.ts, 3, 13)) + +function foo1(x: A | B): any { +>foo1 : Symbol(foo1, Decl(discriminantsAndTypePredicates.ts, 6, 57)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 8, 14)) +>A : Symbol(A, Decl(discriminantsAndTypePredicates.ts, 0, 0)) +>B : Symbol(B, Decl(discriminantsAndTypePredicates.ts, 2, 25)) + + x; // A | B +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 8, 14)) + + if (isA(x)) { +>isA : Symbol(isA, Decl(discriminantsAndTypePredicates.ts, 3, 25)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 8, 14)) + + return x; // A +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 8, 14)) + } + x; // B +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 8, 14)) + + if (isB(x)) { +>isB : Symbol(isB, Decl(discriminantsAndTypePredicates.ts, 5, 57)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 8, 14)) + + return x; // B +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 8, 14)) + } + x; // never +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 8, 14)) +} + +function foo2(x: A | B): any { +>foo2 : Symbol(foo2, Decl(discriminantsAndTypePredicates.ts, 18, 1)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 20, 14)) +>A : Symbol(A, Decl(discriminantsAndTypePredicates.ts, 0, 0)) +>B : Symbol(B, Decl(discriminantsAndTypePredicates.ts, 2, 25)) + + x; // A | B +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 20, 14)) + + if (x.type === 'A') { +>x.type : Symbol(type, Decl(discriminantsAndTypePredicates.ts, 2, 13), Decl(discriminantsAndTypePredicates.ts, 3, 13)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 20, 14)) +>type : Symbol(type, Decl(discriminantsAndTypePredicates.ts, 2, 13), Decl(discriminantsAndTypePredicates.ts, 3, 13)) + + return x; // A +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 20, 14)) + } + x; // B +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 20, 14)) + + if (x.type === 'B') { +>x.type : Symbol(B.type, Decl(discriminantsAndTypePredicates.ts, 3, 13)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 20, 14)) +>type : Symbol(B.type, Decl(discriminantsAndTypePredicates.ts, 3, 13)) + + return x; // B +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 20, 14)) + } + x; // never +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 20, 14)) +} diff --git a/tests/baselines/reference/discriminantsAndTypePredicates.types b/tests/baselines/reference/discriminantsAndTypePredicates.types new file mode 100644 index 00000000000..f7675b14756 --- /dev/null +++ b/tests/baselines/reference/discriminantsAndTypePredicates.types @@ -0,0 +1,104 @@ +=== tests/cases/compiler/discriminantsAndTypePredicates.ts === +// Repro from #10145 + +interface A { type: 'A' } +>A : A +>type : "A" + +interface B { type: 'B' } +>B : B +>type : "B" + +function isA(x: A | B): x is A { return x.type === 'A'; } +>isA : (x: A | B) => x is A +>x : A | B +>A : A +>B : B +>x : any +>A : A +>x.type === 'A' : boolean +>x.type : "A" | "B" +>x : A | B +>type : "A" | "B" +>'A' : "A" + +function isB(x: A | B): x is B { return x.type === 'B'; } +>isB : (x: A | B) => x is B +>x : A | B +>A : A +>B : B +>x : any +>B : B +>x.type === 'B' : boolean +>x.type : "A" | "B" +>x : A | B +>type : "A" | "B" +>'B' : "B" + +function foo1(x: A | B): any { +>foo1 : (x: A | B) => any +>x : A | B +>A : A +>B : B + + x; // A | B +>x : A | B + + if (isA(x)) { +>isA(x) : boolean +>isA : (x: A | B) => x is A +>x : A | B + + return x; // A +>x : A + } + x; // B +>x : B + + if (isB(x)) { +>isB(x) : boolean +>isB : (x: A | B) => x is B +>x : B + + return x; // B +>x : B + } + x; // never +>x : never +} + +function foo2(x: A | B): any { +>foo2 : (x: A | B) => any +>x : A | B +>A : A +>B : B + + x; // A | B +>x : A | B + + if (x.type === 'A') { +>x.type === 'A' : boolean +>x.type : "A" | "B" +>x : A | B +>type : "A" | "B" +>'A' : "A" + + return x; // A +>x : A + } + x; // B +>x : B + + if (x.type === 'B') { +>x.type === 'B' : boolean +>x.type : "B" +>x : B +>type : "B" +>'B' : "B" + + return x; // B +>x : B + } + x; // never +>x : never +} From 30e95df91e7a5ad8a927abdee6e8f77628ed5da7 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Mon, 8 Aug 2016 13:11:02 -0700 Subject: [PATCH 086/233] Reduce worker count to 3 (#10210) Since we saw a starvation issue on one of @sandersn's PRs. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index cb9bf42225a..4126683eb62 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ node_js: sudo: false env: - - workerCount=4 + - workerCount=3 matrix: fast_finish: true From ce7f554090c68ad9c897ea4928596a8c64d6e934 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Mon, 8 Aug 2016 13:15:25 -0700 Subject: [PATCH 087/233] Speed up fourslash tests --- src/harness/fourslash.ts | 107 +++++++++++++-------------------------- 1 file changed, 35 insertions(+), 72 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index a42abbbc609..366891c0d30 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -249,6 +249,7 @@ namespace FourSlash { if (compilationOptions.typeRoots) { compilationOptions.typeRoots = compilationOptions.typeRoots.map(p => ts.getNormalizedAbsolutePath(p, this.basePath)); } + compilationOptions.skipDefaultLibCheck = true; const languageServiceAdapter = this.getLanguageServiceAdapter(testType, this.cancellationToken, compilationOptions); this.languageServiceAdapterHost = languageServiceAdapter.getHost(); @@ -376,7 +377,7 @@ namespace FourSlash { public verifyErrorExistsBetweenMarkers(startMarkerName: string, endMarkerName: string, negative: boolean) { const startMarker = this.getMarkerByName(startMarkerName); const endMarker = this.getMarkerByName(endMarkerName); - const predicate = function (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) { + const predicate = function(errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) { return ((errorMinChar === startPos) && (errorLimChar === endPos)) ? true : false; }; @@ -428,12 +429,12 @@ namespace FourSlash { let predicate: (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) => boolean; if (after) { - predicate = function (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) { + predicate = function(errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) { return ((errorMinChar >= startPos) && (errorLimChar >= startPos)) ? true : false; }; } else { - predicate = function (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) { + predicate = function(errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) { return ((errorMinChar <= startPos) && (errorLimChar <= startPos)) ? true : false; }; } @@ -458,7 +459,7 @@ namespace FourSlash { endPos = endMarker.position; } - errors.forEach(function (error: ts.Diagnostic) { + errors.forEach(function(error: ts.Diagnostic) { if (predicate(error.start, error.start + error.length, startPos, endPos)) { exists = true; } @@ -475,7 +476,7 @@ namespace FourSlash { Harness.IO.log("Unexpected error(s) found. Error list is:"); } - errors.forEach(function (error: ts.Diagnostic) { + errors.forEach(function(error: ts.Diagnostic) { Harness.IO.log(" minChar: " + error.start + ", limChar: " + (error.start + error.length) + ", message: " + ts.flattenDiagnosticMessageText(error.messageText, Harness.IO.newLine()) + "\n"); @@ -1348,14 +1349,7 @@ namespace FourSlash { } // Enters lines of text at the current caret position - public type(text: string) { - return this.typeHighFidelity(text); - } - - // Enters lines of text at the current caret position, invoking - // language service APIs to mimic Visual Studio's behavior - // as much as possible - private typeHighFidelity(text: string) { + public type(text: string, highFidelity = false) { let offset = this.currentCaretPosition; const prevChar = " "; const checkCadence = (text.length >> 2) + 1; @@ -1364,24 +1358,26 @@ namespace FourSlash { // Make the edit const ch = text.charAt(i); this.languageServiceAdapterHost.editScript(this.activeFile.fileName, offset, offset, ch); - this.languageService.getBraceMatchingAtPosition(this.activeFile.fileName, offset); + if (highFidelity) { + this.languageService.getBraceMatchingAtPosition(this.activeFile.fileName, offset); + } this.updateMarkersForEdit(this.activeFile.fileName, offset, offset, ch); offset++; - if (ch === "(" || ch === ",") { - /* Signature help*/ - this.languageService.getSignatureHelpItems(this.activeFile.fileName, offset); - } - else if (prevChar === " " && /A-Za-z_/.test(ch)) { - /* Completions */ - this.languageService.getCompletionsAtPosition(this.activeFile.fileName, offset); - } + if (highFidelity) { + if (ch === "(" || ch === ",") { + /* Signature help*/ + this.languageService.getSignatureHelpItems(this.activeFile.fileName, offset); + } + else if (prevChar === " " && /A-Za-z_/.test(ch)) { + /* Completions */ + this.languageService.getCompletionsAtPosition(this.activeFile.fileName, offset); + } - if (i % checkCadence === 0) { - this.checkPostEditInvariants(); - // this.languageService.getSyntacticDiagnostics(this.activeFile.fileName); - // this.languageService.getSemanticDiagnostics(this.activeFile.fileName); + if (i % checkCadence === 0) { + this.checkPostEditInvariants(); + } } // Handle post-keystroke formatting @@ -1389,14 +1385,12 @@ namespace FourSlash { const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, offset, ch, this.formatCodeOptions); if (edits.length) { offset += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true); - // this.checkPostEditInvariants(); } } } // Move the caret to wherever we ended up this.currentCaretPosition = offset; - this.fixCaretPosition(); this.checkPostEditInvariants(); } @@ -1415,7 +1409,6 @@ namespace FourSlash { const edits = this.languageService.getFormattingEditsForRange(this.activeFile.fileName, start, offset, this.formatCodeOptions); if (edits.length) { offset += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true); - this.checkPostEditInvariants(); } } @@ -1433,20 +1426,22 @@ namespace FourSlash { return; } - const incrementalSourceFile = this.languageService.getNonBoundSourceFile(this.activeFile.fileName); - Utils.assertInvariants(incrementalSourceFile, /*parent:*/ undefined); + if (1 + 1 === 2) { + const incrementalSourceFile = this.languageService.getNonBoundSourceFile(this.activeFile.fileName); + Utils.assertInvariants(incrementalSourceFile, /*parent:*/ undefined); - const incrementalSyntaxDiagnostics = incrementalSourceFile.parseDiagnostics; + const incrementalSyntaxDiagnostics = incrementalSourceFile.parseDiagnostics; - // Check syntactic structure - const content = this.getFileContent(this.activeFile.fileName); + // Check syntactic structure + const content = this.getFileContent(this.activeFile.fileName); - const referenceSourceFile = ts.createLanguageServiceSourceFile( - this.activeFile.fileName, createScriptSnapShot(content), ts.ScriptTarget.Latest, /*version:*/ "0", /*setNodeParents:*/ false); - const referenceSyntaxDiagnostics = referenceSourceFile.parseDiagnostics; + const referenceSourceFile = ts.createLanguageServiceSourceFile( + this.activeFile.fileName, createScriptSnapShot(content), ts.ScriptTarget.Latest, /*version:*/ "0", /*setNodeParents:*/ false); + const referenceSyntaxDiagnostics = referenceSourceFile.parseDiagnostics; - Utils.assertDiagnosticsEquals(incrementalSyntaxDiagnostics, referenceSyntaxDiagnostics); - Utils.assertStructuralEquals(incrementalSourceFile, referenceSourceFile); + Utils.assertDiagnosticsEquals(incrementalSyntaxDiagnostics, referenceSyntaxDiagnostics); + Utils.assertStructuralEquals(incrementalSourceFile, referenceSourceFile); + } } private fixCaretPosition() { @@ -2269,40 +2264,8 @@ namespace FourSlash { export function runFourSlashTestContent(basePath: string, testType: FourSlashTestType, content: string, fileName: string): void { // Parse out the files and their metadata const testData = parseTestData(basePath, content, fileName); - const state = new TestState(basePath, testType, testData); - - let result = ""; - const fourslashFile: Harness.Compiler.TestFile = { - unitName: Harness.Compiler.fourslashFileName, - content: undefined, - }; - const testFile: Harness.Compiler.TestFile = { - unitName: fileName, - content: content - }; - - const host = Harness.Compiler.createCompilerHost( - [fourslashFile, testFile], - (fn, contents) => result = contents, - ts.ScriptTarget.Latest, - Harness.IO.useCaseSensitiveFileNames(), - Harness.IO.getCurrentDirectory()); - - const program = ts.createProgram([Harness.Compiler.fourslashFileName, fileName], { outFile: "fourslashTestOutput.js", noResolve: true, target: ts.ScriptTarget.ES3 }, host); - - const sourceFile = host.getSourceFile(fileName, ts.ScriptTarget.ES3); - - const diagnostics = ts.getPreEmitDiagnostics(program, sourceFile); - if (diagnostics.length > 0) { - throw new Error(`Error compiling ${fileName}: ` + - diagnostics.map(e => ts.flattenDiagnosticMessageText(e.messageText, Harness.IO.newLine())).join("\r\n")); - } - - program.emit(sourceFile); - - ts.Debug.assert(!!result); - runCode(result, state); + runCode(ts.transpile(content), state); } function runCode(code: string, state: TestState): void { From e724e883e6dd21226c8399893e5b49a9266f5797 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Mon, 8 Aug 2016 13:31:45 -0700 Subject: [PATCH 088/233] Duh --- src/harness/fourslash.ts | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 366891c0d30..751db7b971a 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1426,22 +1426,20 @@ namespace FourSlash { return; } - if (1 + 1 === 2) { - const incrementalSourceFile = this.languageService.getNonBoundSourceFile(this.activeFile.fileName); - Utils.assertInvariants(incrementalSourceFile, /*parent:*/ undefined); + const incrementalSourceFile = this.languageService.getNonBoundSourceFile(this.activeFile.fileName); + Utils.assertInvariants(incrementalSourceFile, /*parent:*/ undefined); - const incrementalSyntaxDiagnostics = incrementalSourceFile.parseDiagnostics; + const incrementalSyntaxDiagnostics = incrementalSourceFile.parseDiagnostics; - // Check syntactic structure - const content = this.getFileContent(this.activeFile.fileName); + // Check syntactic structure + const content = this.getFileContent(this.activeFile.fileName); - const referenceSourceFile = ts.createLanguageServiceSourceFile( - this.activeFile.fileName, createScriptSnapShot(content), ts.ScriptTarget.Latest, /*version:*/ "0", /*setNodeParents:*/ false); - const referenceSyntaxDiagnostics = referenceSourceFile.parseDiagnostics; + const referenceSourceFile = ts.createLanguageServiceSourceFile( + this.activeFile.fileName, createScriptSnapShot(content), ts.ScriptTarget.Latest, /*version:*/ "0", /*setNodeParents:*/ false); + const referenceSyntaxDiagnostics = referenceSourceFile.parseDiagnostics; - Utils.assertDiagnosticsEquals(incrementalSyntaxDiagnostics, referenceSyntaxDiagnostics); - Utils.assertStructuralEquals(incrementalSourceFile, referenceSourceFile); - } + Utils.assertDiagnosticsEquals(incrementalSyntaxDiagnostics, referenceSyntaxDiagnostics); + Utils.assertStructuralEquals(incrementalSourceFile, referenceSourceFile); } private fixCaretPosition() { From 53d54c2b282d7bc4808941292fd91c1d41f5e4cc Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Mon, 8 Aug 2016 14:30:27 -0700 Subject: [PATCH 089/233] Make baselines faster by not writing out unneeded files --- Gulpfile.ts | 111 ++++++++++++++++++++----------------- Jakefile.js | 17 +++--- package.json | 1 + scripts/types/ambient.d.ts | 2 +- src/harness/harness.ts | 19 +++---- 5 files changed, 79 insertions(+), 71 deletions(-) diff --git a/Gulpfile.ts b/Gulpfile.ts index b9b5d2068b2..e2d37277bcf 100644 --- a/Gulpfile.ts +++ b/Gulpfile.ts @@ -17,7 +17,7 @@ declare module "gulp-typescript" { stripInternal?: boolean; types?: string[]; } - interface CompileStream extends NodeJS.ReadWriteStream {} // Either gulp or gulp-typescript has some odd typings which don't reflect reality, making this required + interface CompileStream extends NodeJS.ReadWriteStream { } // Either gulp or gulp-typescript has some odd typings which don't reflect reality, making this required } import * as insert from "gulp-insert"; import * as sourcemaps from "gulp-sourcemaps"; @@ -65,7 +65,7 @@ const cmdLineOptions = minimist(process.argv.slice(2), { } }); -function exec(cmd: string, args: string[], complete: () => void = (() => {}), error: (e: any, status: number) => void = (() => {})) { +function exec(cmd: string, args: string[], complete: () => void = (() => { }), error: (e: any, status: number) => void = (() => { })) { console.log(`${cmd} ${args.join(" ")}`); // TODO (weswig): Update child_process types to add windowsVerbatimArguments to the type definition const subshellFlag = isWin ? "/c" : "-c"; @@ -116,12 +116,12 @@ const es2015LibrarySources = [ ]; const es2015LibrarySourceMap = es2015LibrarySources.map(function(source) { - return { target: "lib." + source, sources: ["header.d.ts", source] }; + return { target: "lib." + source, sources: ["header.d.ts", source] }; }); -const es2016LibrarySource = [ "es2016.array.include.d.ts" ]; +const es2016LibrarySource = ["es2016.array.include.d.ts"]; -const es2016LibrarySourceMap = es2016LibrarySource.map(function (source) { +const es2016LibrarySourceMap = es2016LibrarySource.map(function(source) { return { target: "lib." + source, sources: ["header.d.ts", source] }; }); @@ -130,38 +130,38 @@ const es2017LibrarySource = [ "es2017.sharedmemory.d.ts" ]; -const es2017LibrarySourceMap = es2017LibrarySource.map(function (source) { +const es2017LibrarySourceMap = es2017LibrarySource.map(function(source) { return { target: "lib." + source, sources: ["header.d.ts", source] }; }); const hostsLibrarySources = ["dom.generated.d.ts", "webworker.importscripts.d.ts", "scripthost.d.ts"]; const librarySourceMap = [ - // Host library - { target: "lib.dom.d.ts", sources: ["header.d.ts", "dom.generated.d.ts"] }, - { target: "lib.dom.iterable.d.ts", sources: ["header.d.ts", "dom.iterable.d.ts"] }, - { target: "lib.webworker.d.ts", sources: ["header.d.ts", "webworker.generated.d.ts"] }, - { target: "lib.scripthost.d.ts", sources: ["header.d.ts", "scripthost.d.ts"] }, + // Host library + { target: "lib.dom.d.ts", sources: ["header.d.ts", "dom.generated.d.ts"] }, + { target: "lib.dom.iterable.d.ts", sources: ["header.d.ts", "dom.iterable.d.ts"] }, + { target: "lib.webworker.d.ts", sources: ["header.d.ts", "webworker.generated.d.ts"] }, + { target: "lib.scripthost.d.ts", sources: ["header.d.ts", "scripthost.d.ts"] }, - // JavaScript library - { target: "lib.es5.d.ts", sources: ["header.d.ts", "es5.d.ts"] }, - { target: "lib.es2015.d.ts", sources: ["header.d.ts", "es2015.d.ts"] }, - { target: "lib.es2016.d.ts", sources: ["header.d.ts", "es2016.d.ts"] }, - { target: "lib.es2017.d.ts", sources: ["header.d.ts", "es2017.d.ts"] }, + // JavaScript library + { target: "lib.es5.d.ts", sources: ["header.d.ts", "es5.d.ts"] }, + { target: "lib.es2015.d.ts", sources: ["header.d.ts", "es2015.d.ts"] }, + { target: "lib.es2016.d.ts", sources: ["header.d.ts", "es2016.d.ts"] }, + { target: "lib.es2017.d.ts", sources: ["header.d.ts", "es2017.d.ts"] }, - // JavaScript + all host library - { target: "lib.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(hostsLibrarySources) }, - { target: "lib.es6.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(es2015LibrarySources, hostsLibrarySources, "dom.iterable.d.ts") } + // JavaScript + all host library + { target: "lib.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(hostsLibrarySources) }, + { target: "lib.es6.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(es2015LibrarySources, hostsLibrarySources, "dom.iterable.d.ts") } ].concat(es2015LibrarySourceMap, es2016LibrarySourceMap, es2017LibrarySourceMap); -const libraryTargets = librarySourceMap.map(function (f) { +const libraryTargets = librarySourceMap.map(function(f) { return path.join(builtLocalDirectory, f.target); }); for (const i in libraryTargets) { const entry = librarySourceMap[i]; const target = libraryTargets[i]; - const sources = [copyright].concat(entry.sources.map(function (s) { + const sources = [copyright].concat(entry.sources.map(function(s) { return path.join(libraryDirectory, s); })); gulp.task(target, false, [], function() { @@ -391,7 +391,7 @@ gulp.task(servicesFile, false, ["lib", "generate-diagnostics"], () => { .pipe(sourcemaps.init()) .pipe(tsc(servicesProject)); const completedJs = js.pipe(prependCopyright()) - .pipe(sourcemaps.write(".")); + .pipe(sourcemaps.write(".")); const completedDts = dts.pipe(prependCopyright(/*outputCopyright*/true)) .pipe(insert.transform((contents, file) => { file.path = standaloneDefinitionsFile; @@ -434,17 +434,17 @@ const tsserverLibraryDefinitionFile = path.join(builtLocalDirectory, "tsserverli gulp.task(tsserverLibraryFile, false, [servicesFile], (done) => { const serverLibraryProject = tsc.createProject("src/server/tsconfig.library.json", getCompilerSettings({}, /*useBuiltCompiler*/ true)); - const {js, dts}: {js: NodeJS.ReadableStream, dts: NodeJS.ReadableStream} = serverLibraryProject.src() + const {js, dts}: { js: NodeJS.ReadableStream, dts: NodeJS.ReadableStream } = serverLibraryProject.src() .pipe(sourcemaps.init()) .pipe(newer(tsserverLibraryFile)) .pipe(tsc(serverLibraryProject)); return merge2([ js.pipe(prependCopyright()) - .pipe(sourcemaps.write(".")) - .pipe(gulp.dest(builtLocalDirectory)), + .pipe(sourcemaps.write(".")) + .pipe(gulp.dest(builtLocalDirectory)), dts.pipe(prependCopyright()) - .pipe(gulp.dest(builtLocalDirectory)) + .pipe(gulp.dest(builtLocalDirectory)) ]); }); @@ -476,7 +476,7 @@ gulp.task(specMd, false, [word2mdJs], (done) => { const specMDFullPath = path.resolve(specMd); const cmd = "cscript //nologo " + word2mdJs + " \"" + specWordFullPath + "\" " + "\"" + specMDFullPath + "\""; console.log(cmd); - cp.exec(cmd, function () { + cp.exec(cmd, function() { done(); }); }); @@ -492,12 +492,12 @@ gulp.task("dontUseDebugMode", false, [], (done) => { useDebugMode = false; done( gulp.task("VerifyLKG", false, [], () => { const expectedFiles = [builtLocalCompiler, servicesFile, serverFile, nodePackageFile, nodeDefinitionsFile, standaloneDefinitionsFile, tsserverLibraryFile, tsserverLibraryDefinitionFile].concat(libraryTargets); - const missingFiles = expectedFiles.filter(function (f) { + const missingFiles = expectedFiles.filter(function(f) { return !fs.existsSync(f); }); if (missingFiles.length > 0) { throw new Error("Cannot replace the LKG unless all built targets are present in directory " + builtLocalDirectory + - ". The following files are missing:\n" + missingFiles.join("\n")); + ". The following files are missing:\n" + missingFiles.join("\n")); } // Copy all the targets into the LKG directory return gulp.src(expectedFiles).pipe(gulp.dest(LKGDirectory)); @@ -627,7 +627,7 @@ function runConsoleTests(defaultReporter: string, runInParallel: boolean, done: } args.push(run); setNodeEnvToDevelopment(); - runTestsInParallel(taskConfigsFolder, run, { testTimeout: testTimeout, noColors: colors === " --no-colors " }, function (err) { + runTestsInParallel(taskConfigsFolder, run, { testTimeout: testTimeout, noColors: colors === " --no-colors " }, function(err) { // last worker clean everything and runs linter in case if there were no errors del(taskConfigsFolder).then(() => { if (!err) { @@ -679,7 +679,7 @@ gulp.task("runtests", ["build-rules", "tests"], (done) => { runConsoleTests("mocha-fivemat-progress-reporter", /*runInParallel*/ false, done); -}); + }); const nodeServerOutFile = "tests/webTestServer.js"; const nodeServerInFile = "tests/webTestServer.ts"; @@ -812,31 +812,38 @@ gulp.task("diff-rwc", "Diffs the RWC baselines using the diff tool specified by }); -gulp.task("baseline-accept", "Makes the most recent test results the new baseline, overwriting the old baseline", (done) => { - const softAccept = cmdLineOptions["soft"]; - if (!softAccept) { - del(refBaseline).then(() => { - fs.renameSync(localBaseline, refBaseline); - done(); - }, done); - } - else { - gulp.src(localBaseline) - .pipe(gulp.dest(refBaseline)) - .on("end", () => { - del(path.join(refBaseline, "local")).then(() => done(), done); - }); - } +const deleteEnding = '.delete'; +gulp.task("baseline-accept", "Makes the most recent test results the new baseline, overwriting the old baseline", () => { + return baselineAccept(""); }); + +function baselineAccept(subfolder = "") { + return merge2(baselineCopy(subfolder), baselineDelete(subfolder)); +} + +function baselineCopy(subfolder = "") { + return gulp.src([`tests/baselines/local/${subfolder}/**`, `!tests/baselines/local/${subfolder}/**/*.delete`]) + .pipe(gulp.dest(refBaseline)); +} + +function baselineDelete(subfolder = "") { + return gulp.src(['tests/baselines/local/**/*.delete']) + .pipe(insert.transform((content, fileObj) => { + const target = path.join(refBaseline, fileObj.relative.substr(0, fileObj.relative.length - '.delete'.length)); + console.log(target); + del.sync(target); + del.sync(fileObj.path); + return ''; + })); +} + gulp.task("baseline-accept-rwc", "Makes the most recent rwc test results the new baseline, overwriting the old baseline", () => { - return del(refRwcBaseline).then(() => { - fs.renameSync(localRwcBaseline, refRwcBaseline); - }); + return baselineAccept("rwc"); }); + + gulp.task("baseline-accept-test262", "Makes the most recent test262 test results the new baseline, overwriting the old baseline", () => { - return del(refTest262Baseline).then(() => { - fs.renameSync(localTest262Baseline, refTest262Baseline); - }); + return baselineAccept("test262"); }); diff --git a/Jakefile.js b/Jakefile.js index a5650a56b16..8062d8a2928 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -1,3 +1,4 @@ +"use strict"; // This file contains the build logic for the public repo var fs = require("fs"); @@ -898,16 +899,16 @@ task("tests-debug", ["setDebugMode", "tests"]); // Makes the test results the new baseline desc("Makes the most recent test results the new baseline, overwriting the old baseline"); task("baseline-accept", function(hardOrSoft) { - if (!hardOrSoft || hardOrSoft === "hard") { - jake.rmRf(refBaseline); - fs.renameSync(localBaseline, refBaseline); - } - else if (hardOrSoft === "soft") { - var files = jake.readdirR(localBaseline); - for (var i in files) { + var files = jake.readdirR(localBaseline); + var deleteEnding = '.delete'; + for (var i in files) { + if (files[i].substr(files[i].length - deleteEnding.length) === deleteEnding) { + var filename = path.basename(files[i]); + filename = filename.substr(0, filename.length - deleteEnding.length); + fs.unlink(path.join(refBaseline, filename)); + } else { jake.cpR(files[i], refBaseline); } - jake.rmRf(path.join(refBaseline, "local")); } }); diff --git a/package.json b/package.json index 9f3122c3374..1f1c52b42ed 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "mkdirp": "latest", "mocha": "latest", "mocha-fivemat-progress-reporter": "latest", + "q": "latest", "run-sequence": "latest", "sorcery": "latest", "through2": "latest", diff --git a/scripts/types/ambient.d.ts b/scripts/types/ambient.d.ts index e77e3fe8c5a..16ec9385cb1 100644 --- a/scripts/types/ambient.d.ts +++ b/scripts/types/ambient.d.ts @@ -10,7 +10,7 @@ declare module "gulp-insert" { export function append(text: string | Buffer): NodeJS.ReadWriteStream; export function prepend(text: string | Buffer): NodeJS.ReadWriteStream; export function wrap(text: string | Buffer, tail: string | Buffer): NodeJS.ReadWriteStream; - export function transform(cb: (contents: string, file: {path: string}) => string): NodeJS.ReadWriteStream; // file is a vinyl file + export function transform(cb: (contents: string, file: {path: string, relative: string}) => string): NodeJS.ReadWriteStream; // file is a vinyl file } declare module "into-stream" { diff --git a/src/harness/harness.ts b/src/harness/harness.ts index f27e7e1c174..18a440c789b 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1569,6 +1569,7 @@ namespace Harness { /** Support class for baseline files */ export namespace Baseline { + const NoContent = ""; export interface BaselineOptions { Subfolder?: string; @@ -1635,14 +1636,6 @@ namespace Harness { throw new Error("The generated content was \"undefined\". Return \"null\" if no baselining is required.\""); } - // Store the content in the 'local' folder so we - // can accept it later (manually) - /* tslint:disable:no-null-keyword */ - if (actual !== null) { - /* tslint:enable:no-null-keyword */ - IO.writeFile(actualFileName, actual); - } - return actual; } @@ -1659,7 +1652,7 @@ namespace Harness { /* tslint:disable:no-null-keyword */ if (actual === null) { /* tslint:enable:no-null-keyword */ - actual = ""; + actual = NoContent; } let expected = ""; @@ -1672,7 +1665,13 @@ namespace Harness { function writeComparison(expected: string, actual: string, relativeFileName: string, actualFileName: string, descriptionForDescribe: string) { const encoded_actual = Utils.encodeString(actual); - if (expected != encoded_actual) { + if (expected !== encoded_actual) { + if (actual === NoContent) { + IO.writeFile(relativeFileName + '.delete', ''); + } + else { + IO.writeFile(relativeFileName, actual); + } // Overwrite & issue error const errMsg = "The baseline file " + relativeFileName + " has changed."; throw new Error(errMsg); From eac7a48c5f03248bf6c597aacf7a1216e20f4ba9 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Mon, 8 Aug 2016 14:39:31 -0700 Subject: [PATCH 090/233] Fix non-strict-compliant test --- tests/cases/fourslash/localGetReferences.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/cases/fourslash/localGetReferences.ts b/tests/cases/fourslash/localGetReferences.ts index ada4947acde..b05346a366a 100644 --- a/tests/cases/fourslash/localGetReferences.ts +++ b/tests/cases/fourslash/localGetReferences.ts @@ -205,12 +205,13 @@ const rangesByText = test.rangesByText(); for (const text in rangesByText) { const ranges = rangesByText[text]; if (text === "globalVar") { - function isShadow(r) { - return r.marker && r.marker.data && r.marker.data.shadow; - } verify.rangesReferenceEachOther(ranges.filter(isShadow)); verify.rangesReferenceEachOther(ranges.filter(r => !isShadow(r))); } else { verify.rangesReferenceEachOther(ranges); } } + +function isShadow(r) { + return r.marker && r.marker.data && r.marker.data.shadow; +} From 3f6aa3f3f0705cfc6bfe59920d49e76c12023953 Mon Sep 17 00:00:00 2001 From: Yui Date: Mon, 8 Aug 2016 14:45:29 -0700 Subject: [PATCH 091/233] Fix 10076: Fix Tuple Destructing with "this" (#10208) * Call checkExpression eventhough there is no appropriate type from destructuring of array * Add tests and baselines --- src/compiler/checker.ts | 14 ++++++++--- ...turingThisInTupleDestructuring1.errors.txt | 13 +++++++++++ .../emitCapturingThisInTupleDestructuring1.js | 11 +++++++++ ...turingThisInTupleDestructuring2.errors.txt | 16 +++++++++++++ .../emitCapturingThisInTupleDestructuring2.js | 23 +++++++++++++++++++ .../emitCapturingThisInTupleDestructuring1.ts | 4 ++++ .../emitCapturingThisInTupleDestructuring2.ts | 10 ++++++++ 7 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/emitCapturingThisInTupleDestructuring1.errors.txt create mode 100644 tests/baselines/reference/emitCapturingThisInTupleDestructuring1.js create mode 100644 tests/baselines/reference/emitCapturingThisInTupleDestructuring2.errors.txt create mode 100644 tests/baselines/reference/emitCapturingThisInTupleDestructuring2.js create mode 100644 tests/cases/compiler/emitCapturingThisInTupleDestructuring1.ts create mode 100644 tests/cases/compiler/emitCapturingThisInTupleDestructuring2.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index fb60852faf3..bec8686ab93 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4457,9 +4457,14 @@ namespace ts { return property; } - // Return the symbol for the property with the given name in the given type. Creates synthetic union properties when - // necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from - // Object and Function as appropriate. + /** + * Return the symbol for the property with the given name in the given type. Creates synthetic union properties when + * necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from + * Object and Function as appropriate. + * + * @param type a type to look up property from + * @param name a name of property to look up in a given type + */ function getPropertyOfType(type: Type, name: string): Symbol { type = getApparentType(type); if (type.flags & TypeFlags.ObjectType) { @@ -12932,6 +12937,9 @@ namespace ts { return checkDestructuringAssignment(element, type, contextualMapper); } else { + // We still need to check element expression here because we may need to set appropriate flag on the expression + // such as NodeCheckFlags.LexicalThis on "this"expression. + checkExpression(element); if (isTupleType(sourceType)) { error(element, Diagnostics.Tuple_type_0_with_length_1_cannot_be_assigned_to_tuple_with_length_2, typeToString(sourceType), (sourceType).elementTypes.length, elements.length); } diff --git a/tests/baselines/reference/emitCapturingThisInTupleDestructuring1.errors.txt b/tests/baselines/reference/emitCapturingThisInTupleDestructuring1.errors.txt new file mode 100644 index 00000000000..d0f11d056ca --- /dev/null +++ b/tests/baselines/reference/emitCapturingThisInTupleDestructuring1.errors.txt @@ -0,0 +1,13 @@ +tests/cases/compiler/emitCapturingThisInTupleDestructuring1.ts(3,17): error TS2493: Tuple type '[any]' with length '1' cannot be assigned to tuple with length '3'. +tests/cases/compiler/emitCapturingThisInTupleDestructuring1.ts(3,29): error TS2493: Tuple type '[any]' with length '1' cannot be assigned to tuple with length '3'. + + +==== tests/cases/compiler/emitCapturingThisInTupleDestructuring1.ts (2 errors) ==== + declare function wrapper(x: any); + wrapper((array: [any]) => { + [this.test, this.test1, this.test2] = array; // even though there is a compiler error, we should still emit lexical capture for "this" + ~~~~~~~~~~ +!!! error TS2493: Tuple type '[any]' with length '1' cannot be assigned to tuple with length '3'. + ~~~~~~~~~~ +!!! error TS2493: Tuple type '[any]' with length '1' cannot be assigned to tuple with length '3'. + }); \ No newline at end of file diff --git a/tests/baselines/reference/emitCapturingThisInTupleDestructuring1.js b/tests/baselines/reference/emitCapturingThisInTupleDestructuring1.js new file mode 100644 index 00000000000..9dd7453ba4f --- /dev/null +++ b/tests/baselines/reference/emitCapturingThisInTupleDestructuring1.js @@ -0,0 +1,11 @@ +//// [emitCapturingThisInTupleDestructuring1.ts] +declare function wrapper(x: any); +wrapper((array: [any]) => { + [this.test, this.test1, this.test2] = array; // even though there is a compiler error, we should still emit lexical capture for "this" +}); + +//// [emitCapturingThisInTupleDestructuring1.js] +var _this = this; +wrapper(function (array) { + _this.test = array[0], _this.test1 = array[1], _this.test2 = array[2]; // even though there is a compiler error, we should still emit lexical capture for "this" +}); diff --git a/tests/baselines/reference/emitCapturingThisInTupleDestructuring2.errors.txt b/tests/baselines/reference/emitCapturingThisInTupleDestructuring2.errors.txt new file mode 100644 index 00000000000..25207e28c79 --- /dev/null +++ b/tests/baselines/reference/emitCapturingThisInTupleDestructuring2.errors.txt @@ -0,0 +1,16 @@ +tests/cases/compiler/emitCapturingThisInTupleDestructuring2.ts(8,39): error TS2493: Tuple type '[number, number]' with length '2' cannot be assigned to tuple with length '3'. + + +==== tests/cases/compiler/emitCapturingThisInTupleDestructuring2.ts (1 errors) ==== + var array1: [number, number] = [1, 2]; + + class B { + test: number; + test1: any; + test2: any; + method() { + () => [this.test, this.test1, this.test2] = array1; // even though there is a compiler error, we should still emit lexical capture for "this" + ~~~~~~~~~~ +!!! error TS2493: Tuple type '[number, number]' with length '2' cannot be assigned to tuple with length '3'. + } + } \ No newline at end of file diff --git a/tests/baselines/reference/emitCapturingThisInTupleDestructuring2.js b/tests/baselines/reference/emitCapturingThisInTupleDestructuring2.js new file mode 100644 index 00000000000..f999cfe70f0 --- /dev/null +++ b/tests/baselines/reference/emitCapturingThisInTupleDestructuring2.js @@ -0,0 +1,23 @@ +//// [emitCapturingThisInTupleDestructuring2.ts] +var array1: [number, number] = [1, 2]; + +class B { + test: number; + test1: any; + test2: any; + method() { + () => [this.test, this.test1, this.test2] = array1; // even though there is a compiler error, we should still emit lexical capture for "this" + } +} + +//// [emitCapturingThisInTupleDestructuring2.js] +var array1 = [1, 2]; +var B = (function () { + function B() { + } + B.prototype.method = function () { + var _this = this; + (function () { return (_this.test = array1[0], _this.test1 = array1[1], _this.test2 = array1[2], array1); }); // even though there is a compiler error, we should still emit lexical capture for "this" + }; + return B; +}()); diff --git a/tests/cases/compiler/emitCapturingThisInTupleDestructuring1.ts b/tests/cases/compiler/emitCapturingThisInTupleDestructuring1.ts new file mode 100644 index 00000000000..e369cd9fb50 --- /dev/null +++ b/tests/cases/compiler/emitCapturingThisInTupleDestructuring1.ts @@ -0,0 +1,4 @@ +declare function wrapper(x: any); +wrapper((array: [any]) => { + [this.test, this.test1, this.test2] = array; // even though there is a compiler error, we should still emit lexical capture for "this" +}); \ No newline at end of file diff --git a/tests/cases/compiler/emitCapturingThisInTupleDestructuring2.ts b/tests/cases/compiler/emitCapturingThisInTupleDestructuring2.ts new file mode 100644 index 00000000000..2bb931c4f4f --- /dev/null +++ b/tests/cases/compiler/emitCapturingThisInTupleDestructuring2.ts @@ -0,0 +1,10 @@ +var array1: [number, number] = [1, 2]; + +class B { + test: number; + test1: any; + test2: any; + method() { + () => [this.test, this.test1, this.test2] = array1; // even though there is a compiler error, we should still emit lexical capture for "this" + } +} \ No newline at end of file From 40f0c6018dce1e1aeadec37020827ef6a7b318a3 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Mon, 8 Aug 2016 15:36:38 -0700 Subject: [PATCH 092/233] use transpileModule --- src/harness/fourslash.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 751db7b971a..d0643db4967 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2263,7 +2263,11 @@ namespace FourSlash { // Parse out the files and their metadata const testData = parseTestData(basePath, content, fileName); const state = new TestState(basePath, testType, testData); - runCode(ts.transpile(content), state); + const output = ts.transpileModule(content, { reportDiagnostics: true }); + if (output.diagnostics.length > 0) { + throw new Error(`Syntax error in ${basePath}: ${output.diagnostics[0].messageText}`); + } + runCode(output.outputText, state); } function runCode(code: string, state: TestState): void { From 5adceb92a73c93dbc4dd642dae7398e6b1a4ad7b Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Mon, 8 Aug 2016 15:40:25 -0700 Subject: [PATCH 093/233] Remove use strict --- Jakefile.js | 1 - 1 file changed, 1 deletion(-) diff --git a/Jakefile.js b/Jakefile.js index 8062d8a2928..0c08c0faf4c 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -1,4 +1,3 @@ -"use strict"; // This file contains the build logic for the public repo var fs = require("fs"); From 624e52e8342947f49f9744de8904bad7917175c8 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 8 Aug 2016 16:40:59 -0700 Subject: [PATCH 094/233] Improve instanceof for structurally identical types --- src/compiler/checker.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index fb60852faf3..e2e09b80a72 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8569,8 +8569,12 @@ namespace ts { } function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean) { + // In the false branch we keep types that aren't subtypes of the candidate type and we keep structurally + // identical types except for the current type itself. The latter rule means that given two structurally + // identical classes A and B and a variable x of type A, in the false branch of an 'x instanceof B' type + // guard x keeps type A and is not narrowed to type never. if (!assumeTrue) { - return filterType(type, t => !isTypeSubtypeOf(t, candidate)); + return filterType(type, t => !isTypeSubtypeOf(t, candidate) || isTypeIdenticalTo(t, candidate) && t !== candidate); } // If the current type is a union type, remove all constituents that aren't assignable to // the candidate type. If one or more constituents remain, return a union of those. From 5ebfad6e4aecc6bdea21d0c63e5769ae57caaecb Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 8 Aug 2016 17:40:27 -0700 Subject: [PATCH 095/233] Introduce isTypeInstanceOf function --- src/compiler/checker.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e2e09b80a72..d4599a47f4f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5919,6 +5919,13 @@ namespace ts { return isTypeRelatedTo(source, target, assignableRelation); } + // A type S is considered to be an instance of a type T if S and T are the same type or if S is a + // subtype of T but not structurally identical to T. This specifically means that two distinct but + // structurally identical types (such as two classes) are not considered instances of each other. + function isTypeInstanceOf(source: Type, target: Type): boolean { + return source === target || isTypeSubtypeOf(source, target) && !isTypeIdenticalTo(source, target); + } + /** * This is *not* a bi-directional relationship. * If one needs to check both directions for comparability, use a second call to this function or 'checkTypeComparableTo'. @@ -8569,17 +8576,13 @@ namespace ts { } function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean) { - // In the false branch we keep types that aren't subtypes of the candidate type and we keep structurally - // identical types except for the current type itself. The latter rule means that given two structurally - // identical classes A and B and a variable x of type A, in the false branch of an 'x instanceof B' type - // guard x keeps type A and is not narrowed to type never. if (!assumeTrue) { - return filterType(type, t => !isTypeSubtypeOf(t, candidate) || isTypeIdenticalTo(t, candidate) && t !== candidate); + return filterType(type, t => !isTypeInstanceOf(t, candidate)); } - // If the current type is a union type, remove all constituents that aren't assignable to + // If the current type is a union type, remove all constituents that couldn't be instances of // the candidate type. If one or more constituents remain, return a union of those. if (type.flags & TypeFlags.Union) { - const assignableConstituents = filter((type).types, t => isTypeAssignableTo(t, candidate)); + const assignableConstituents = filter((type).types, t => isTypeInstanceOf(t, candidate)); if (assignableConstituents.length) { return getUnionType(assignableConstituents); } From 9277b3f5addac8380a6b6eb2c501d22d82554bf2 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 8 Aug 2016 17:40:53 -0700 Subject: [PATCH 096/233] Add test --- ...nstanceofWithStructurallyIdenticalTypes.ts | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 tests/cases/compiler/instanceofWithStructurallyIdenticalTypes.ts diff --git a/tests/cases/compiler/instanceofWithStructurallyIdenticalTypes.ts b/tests/cases/compiler/instanceofWithStructurallyIdenticalTypes.ts new file mode 100644 index 00000000000..564f7a9c22e --- /dev/null +++ b/tests/cases/compiler/instanceofWithStructurallyIdenticalTypes.ts @@ -0,0 +1,69 @@ +// Repro from #7271 + +class C1 { item: string } +class C2 { item: string[] } +class C3 { item: string } + +function foo1(x: C1 | C2 | C3): string { + if (x instanceof C1) { + return x.item; + } + else if (x instanceof C2) { + return x.item[0]; + } + else if (x instanceof C3) { + return x.item; + } + return "error"; +} + +function isC1(c: C1 | C2 | C3): c is C1 { return c instanceof C1 } +function isC2(c: C1 | C2 | C3): c is C2 { return c instanceof C2 } +function isC3(c: C1 | C2 | C3): c is C3 { return c instanceof C3 } + +function foo2(x: C1 | C2 | C3): string { + if (isC1(x)) { + return x.item; + } + else if (isC2(x)) { + return x.item[0]; + } + else if (isC3(x)) { + return x.item; + } + return "error"; +} + +// More tests + +class A { a: string } +class A1 extends A { } +class A2 { a: string } +class B extends A { b: string } + +function goo(x: A) { + if (x instanceof A) { + x; // A + } + else { + x; // never + } + if (x instanceof A1) { + x; // A1 + } + else { + x; // A + } + if (x instanceof A2) { + x; // A2 + } + else { + x; // A + } + if (x instanceof B) { + x; // B + } + else { + x; // A + } +} From fe1854e4410d342d5e18f7c7d8a285941604743c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 8 Aug 2016 17:43:41 -0700 Subject: [PATCH 097/233] Accept new baselines --- .../controlFlowBinaryOrExpression.symbols | 12 +- .../controlFlowBinaryOrExpression.types | 8 +- ...nstanceofWithStructurallyIdenticalTypes.js | 172 ++++++++++++++ ...ceofWithStructurallyIdenticalTypes.symbols | 192 ++++++++++++++++ ...anceofWithStructurallyIdenticalTypes.types | 211 ++++++++++++++++++ 5 files changed, 585 insertions(+), 10 deletions(-) create mode 100644 tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.js create mode 100644 tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.symbols create mode 100644 tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.types diff --git a/tests/baselines/reference/controlFlowBinaryOrExpression.symbols b/tests/baselines/reference/controlFlowBinaryOrExpression.symbols index 5251973005f..217e11dc95d 100644 --- a/tests/baselines/reference/controlFlowBinaryOrExpression.symbols +++ b/tests/baselines/reference/controlFlowBinaryOrExpression.symbols @@ -64,9 +64,9 @@ if (isNodeList(sourceObj)) { >sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3)) sourceObj.length; ->sourceObj.length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33)) +>sourceObj.length : Symbol(NodeList.length, Decl(controlFlowBinaryOrExpression.ts, 10, 27)) >sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3)) ->length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33)) +>length : Symbol(NodeList.length, Decl(controlFlowBinaryOrExpression.ts, 10, 27)) } if (isHTMLCollection(sourceObj)) { @@ -74,9 +74,9 @@ if (isHTMLCollection(sourceObj)) { >sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3)) sourceObj.length; ->sourceObj.length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33)) +>sourceObj.length : Symbol(HTMLCollection.length, Decl(controlFlowBinaryOrExpression.ts, 14, 33)) >sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3)) ->length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33)) +>length : Symbol(HTMLCollection.length, Decl(controlFlowBinaryOrExpression.ts, 14, 33)) } if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) { @@ -86,8 +86,8 @@ if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) { >sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3)) sourceObj.length; ->sourceObj.length : Symbol(NodeList.length, Decl(controlFlowBinaryOrExpression.ts, 10, 27)) +>sourceObj.length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33)) >sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3)) ->length : Symbol(NodeList.length, Decl(controlFlowBinaryOrExpression.ts, 10, 27)) +>length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33)) } diff --git a/tests/baselines/reference/controlFlowBinaryOrExpression.types b/tests/baselines/reference/controlFlowBinaryOrExpression.types index d24462316b4..7e92fcdbbd1 100644 --- a/tests/baselines/reference/controlFlowBinaryOrExpression.types +++ b/tests/baselines/reference/controlFlowBinaryOrExpression.types @@ -80,7 +80,7 @@ if (isNodeList(sourceObj)) { sourceObj.length; >sourceObj.length : number ->sourceObj : NodeList | HTMLCollection +>sourceObj : NodeList >length : number } @@ -91,7 +91,7 @@ if (isHTMLCollection(sourceObj)) { sourceObj.length; >sourceObj.length : number ->sourceObj : NodeList | HTMLCollection +>sourceObj : HTMLCollection >length : number } @@ -102,11 +102,11 @@ if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) { >sourceObj : EventTargetLike >isHTMLCollection(sourceObj) : boolean >isHTMLCollection : (sourceObj: any) => sourceObj is HTMLCollection ->sourceObj : { a: string; } +>sourceObj : HTMLCollection | { a: string; } sourceObj.length; >sourceObj.length : number ->sourceObj : NodeList +>sourceObj : NodeList | HTMLCollection >length : number } diff --git a/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.js b/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.js new file mode 100644 index 00000000000..97be2137722 --- /dev/null +++ b/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.js @@ -0,0 +1,172 @@ +//// [instanceofWithStructurallyIdenticalTypes.ts] +// Repro from #7271 + +class C1 { item: string } +class C2 { item: string[] } +class C3 { item: string } + +function foo1(x: C1 | C2 | C3): string { + if (x instanceof C1) { + return x.item; + } + else if (x instanceof C2) { + return x.item[0]; + } + else if (x instanceof C3) { + return x.item; + } + return "error"; +} + +function isC1(c: C1 | C2 | C3): c is C1 { return c instanceof C1 } +function isC2(c: C1 | C2 | C3): c is C2 { return c instanceof C2 } +function isC3(c: C1 | C2 | C3): c is C3 { return c instanceof C3 } + +function foo2(x: C1 | C2 | C3): string { + if (isC1(x)) { + return x.item; + } + else if (isC2(x)) { + return x.item[0]; + } + else if (isC3(x)) { + return x.item; + } + return "error"; +} + +// More tests + +class A { a: string } +class A1 extends A { } +class A2 { a: string } +class B extends A { b: string } + +function goo(x: A) { + if (x instanceof A) { + x; // A + } + else { + x; // never + } + if (x instanceof A1) { + x; // A1 + } + else { + x; // A + } + if (x instanceof A2) { + x; // A2 + } + else { + x; // A + } + if (x instanceof B) { + x; // B + } + else { + x; // A + } +} + + +//// [instanceofWithStructurallyIdenticalTypes.js] +// Repro from #7271 +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var C1 = (function () { + function C1() { + } + return C1; +}()); +var C2 = (function () { + function C2() { + } + return C2; +}()); +var C3 = (function () { + function C3() { + } + return C3; +}()); +function foo1(x) { + if (x instanceof C1) { + return x.item; + } + else if (x instanceof C2) { + return x.item[0]; + } + else if (x instanceof C3) { + return x.item; + } + return "error"; +} +function isC1(c) { return c instanceof C1; } +function isC2(c) { return c instanceof C2; } +function isC3(c) { return c instanceof C3; } +function foo2(x) { + if (isC1(x)) { + return x.item; + } + else if (isC2(x)) { + return x.item[0]; + } + else if (isC3(x)) { + return x.item; + } + return "error"; +} +// More tests +var A = (function () { + function A() { + } + return A; +}()); +var A1 = (function (_super) { + __extends(A1, _super); + function A1() { + _super.apply(this, arguments); + } + return A1; +}(A)); +var A2 = (function () { + function A2() { + } + return A2; +}()); +var B = (function (_super) { + __extends(B, _super); + function B() { + _super.apply(this, arguments); + } + return B; +}(A)); +function goo(x) { + if (x instanceof A) { + x; // A + } + else { + x; // never + } + if (x instanceof A1) { + x; // A1 + } + else { + x; // A + } + if (x instanceof A2) { + x; // A2 + } + else { + x; // A + } + if (x instanceof B) { + x; // B + } + else { + x; // A + } +} diff --git a/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.symbols b/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.symbols new file mode 100644 index 00000000000..f2eb40eea47 --- /dev/null +++ b/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.symbols @@ -0,0 +1,192 @@ +=== tests/cases/compiler/instanceofWithStructurallyIdenticalTypes.ts === +// Repro from #7271 + +class C1 { item: string } +>C1 : Symbol(C1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 0, 0)) +>item : Symbol(C1.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 10)) + +class C2 { item: string[] } +>C2 : Symbol(C2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 25)) +>item : Symbol(C2.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 10)) + +class C3 { item: string } +>C3 : Symbol(C3, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 27)) +>item : Symbol(C3.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 4, 10)) + +function foo1(x: C1 | C2 | C3): string { +>foo1 : Symbol(foo1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 4, 25)) +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 6, 14)) +>C1 : Symbol(C1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 0, 0)) +>C2 : Symbol(C2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 25)) +>C3 : Symbol(C3, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 27)) + + if (x instanceof C1) { +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 6, 14)) +>C1 : Symbol(C1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 0, 0)) + + return x.item; +>x.item : Symbol(C1.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 10)) +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 6, 14)) +>item : Symbol(C1.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 10)) + } + else if (x instanceof C2) { +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 6, 14)) +>C2 : Symbol(C2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 25)) + + return x.item[0]; +>x.item : Symbol(C2.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 10)) +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 6, 14)) +>item : Symbol(C2.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 10)) + } + else if (x instanceof C3) { +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 6, 14)) +>C3 : Symbol(C3, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 27)) + + return x.item; +>x.item : Symbol(C3.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 4, 10)) +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 6, 14)) +>item : Symbol(C3.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 4, 10)) + } + return "error"; +} + +function isC1(c: C1 | C2 | C3): c is C1 { return c instanceof C1 } +>isC1 : Symbol(isC1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 17, 1)) +>c : Symbol(c, Decl(instanceofWithStructurallyIdenticalTypes.ts, 19, 14)) +>C1 : Symbol(C1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 0, 0)) +>C2 : Symbol(C2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 25)) +>C3 : Symbol(C3, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 27)) +>c : Symbol(c, Decl(instanceofWithStructurallyIdenticalTypes.ts, 19, 14)) +>C1 : Symbol(C1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 0, 0)) +>c : Symbol(c, Decl(instanceofWithStructurallyIdenticalTypes.ts, 19, 14)) +>C1 : Symbol(C1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 0, 0)) + +function isC2(c: C1 | C2 | C3): c is C2 { return c instanceof C2 } +>isC2 : Symbol(isC2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 19, 66)) +>c : Symbol(c, Decl(instanceofWithStructurallyIdenticalTypes.ts, 20, 14)) +>C1 : Symbol(C1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 0, 0)) +>C2 : Symbol(C2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 25)) +>C3 : Symbol(C3, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 27)) +>c : Symbol(c, Decl(instanceofWithStructurallyIdenticalTypes.ts, 20, 14)) +>C2 : Symbol(C2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 25)) +>c : Symbol(c, Decl(instanceofWithStructurallyIdenticalTypes.ts, 20, 14)) +>C2 : Symbol(C2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 25)) + +function isC3(c: C1 | C2 | C3): c is C3 { return c instanceof C3 } +>isC3 : Symbol(isC3, Decl(instanceofWithStructurallyIdenticalTypes.ts, 20, 66)) +>c : Symbol(c, Decl(instanceofWithStructurallyIdenticalTypes.ts, 21, 14)) +>C1 : Symbol(C1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 0, 0)) +>C2 : Symbol(C2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 25)) +>C3 : Symbol(C3, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 27)) +>c : Symbol(c, Decl(instanceofWithStructurallyIdenticalTypes.ts, 21, 14)) +>C3 : Symbol(C3, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 27)) +>c : Symbol(c, Decl(instanceofWithStructurallyIdenticalTypes.ts, 21, 14)) +>C3 : Symbol(C3, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 27)) + +function foo2(x: C1 | C2 | C3): string { +>foo2 : Symbol(foo2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 21, 66)) +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 23, 14)) +>C1 : Symbol(C1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 0, 0)) +>C2 : Symbol(C2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 25)) +>C3 : Symbol(C3, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 27)) + + if (isC1(x)) { +>isC1 : Symbol(isC1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 17, 1)) +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 23, 14)) + + return x.item; +>x.item : Symbol(C1.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 10)) +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 23, 14)) +>item : Symbol(C1.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 10)) + } + else if (isC2(x)) { +>isC2 : Symbol(isC2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 19, 66)) +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 23, 14)) + + return x.item[0]; +>x.item : Symbol(C2.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 10)) +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 23, 14)) +>item : Symbol(C2.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 10)) + } + else if (isC3(x)) { +>isC3 : Symbol(isC3, Decl(instanceofWithStructurallyIdenticalTypes.ts, 20, 66)) +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 23, 14)) + + return x.item; +>x.item : Symbol(C3.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 4, 10)) +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 23, 14)) +>item : Symbol(C3.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 4, 10)) + } + return "error"; +} + +// More tests + +class A { a: string } +>A : Symbol(A, Decl(instanceofWithStructurallyIdenticalTypes.ts, 34, 1)) +>a : Symbol(A.a, Decl(instanceofWithStructurallyIdenticalTypes.ts, 38, 9)) + +class A1 extends A { } +>A1 : Symbol(A1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 38, 21)) +>A : Symbol(A, Decl(instanceofWithStructurallyIdenticalTypes.ts, 34, 1)) + +class A2 { a: string } +>A2 : Symbol(A2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 39, 22)) +>a : Symbol(A2.a, Decl(instanceofWithStructurallyIdenticalTypes.ts, 40, 10)) + +class B extends A { b: string } +>B : Symbol(B, Decl(instanceofWithStructurallyIdenticalTypes.ts, 40, 22)) +>A : Symbol(A, Decl(instanceofWithStructurallyIdenticalTypes.ts, 34, 1)) +>b : Symbol(B.b, Decl(instanceofWithStructurallyIdenticalTypes.ts, 41, 19)) + +function goo(x: A) { +>goo : Symbol(goo, Decl(instanceofWithStructurallyIdenticalTypes.ts, 41, 31)) +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 43, 13)) +>A : Symbol(A, Decl(instanceofWithStructurallyIdenticalTypes.ts, 34, 1)) + + if (x instanceof A) { +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 43, 13)) +>A : Symbol(A, Decl(instanceofWithStructurallyIdenticalTypes.ts, 34, 1)) + + x; // A +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 43, 13)) + } + else { + x; // never +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 43, 13)) + } + if (x instanceof A1) { +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 43, 13)) +>A1 : Symbol(A1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 38, 21)) + + x; // A1 +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 43, 13)) + } + else { + x; // A +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 43, 13)) + } + if (x instanceof A2) { +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 43, 13)) +>A2 : Symbol(A2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 39, 22)) + + x; // A2 +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 43, 13)) + } + else { + x; // A +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 43, 13)) + } + if (x instanceof B) { +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 43, 13)) +>B : Symbol(B, Decl(instanceofWithStructurallyIdenticalTypes.ts, 40, 22)) + + x; // B +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 43, 13)) + } + else { + x; // A +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 43, 13)) + } +} + diff --git a/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.types b/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.types new file mode 100644 index 00000000000..d98a6025796 --- /dev/null +++ b/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.types @@ -0,0 +1,211 @@ +=== tests/cases/compiler/instanceofWithStructurallyIdenticalTypes.ts === +// Repro from #7271 + +class C1 { item: string } +>C1 : C1 +>item : string + +class C2 { item: string[] } +>C2 : C2 +>item : string[] + +class C3 { item: string } +>C3 : C3 +>item : string + +function foo1(x: C1 | C2 | C3): string { +>foo1 : (x: C1 | C2 | C3) => string +>x : C1 | C2 | C3 +>C1 : C1 +>C2 : C2 +>C3 : C3 + + if (x instanceof C1) { +>x instanceof C1 : boolean +>x : C1 | C2 | C3 +>C1 : typeof C1 + + return x.item; +>x.item : string +>x : C1 +>item : string + } + else if (x instanceof C2) { +>x instanceof C2 : boolean +>x : C2 | C3 +>C2 : typeof C2 + + return x.item[0]; +>x.item[0] : string +>x.item : string[] +>x : C2 +>item : string[] +>0 : number + } + else if (x instanceof C3) { +>x instanceof C3 : boolean +>x : C3 +>C3 : typeof C3 + + return x.item; +>x.item : string +>x : C3 +>item : string + } + return "error"; +>"error" : string +} + +function isC1(c: C1 | C2 | C3): c is C1 { return c instanceof C1 } +>isC1 : (c: C1 | C2 | C3) => c is C1 +>c : C1 | C2 | C3 +>C1 : C1 +>C2 : C2 +>C3 : C3 +>c : any +>C1 : C1 +>c instanceof C1 : boolean +>c : C1 | C2 | C3 +>C1 : typeof C1 + +function isC2(c: C1 | C2 | C3): c is C2 { return c instanceof C2 } +>isC2 : (c: C1 | C2 | C3) => c is C2 +>c : C1 | C2 | C3 +>C1 : C1 +>C2 : C2 +>C3 : C3 +>c : any +>C2 : C2 +>c instanceof C2 : boolean +>c : C1 | C2 | C3 +>C2 : typeof C2 + +function isC3(c: C1 | C2 | C3): c is C3 { return c instanceof C3 } +>isC3 : (c: C1 | C2 | C3) => c is C3 +>c : C1 | C2 | C3 +>C1 : C1 +>C2 : C2 +>C3 : C3 +>c : any +>C3 : C3 +>c instanceof C3 : boolean +>c : C1 | C2 | C3 +>C3 : typeof C3 + +function foo2(x: C1 | C2 | C3): string { +>foo2 : (x: C1 | C2 | C3) => string +>x : C1 | C2 | C3 +>C1 : C1 +>C2 : C2 +>C3 : C3 + + if (isC1(x)) { +>isC1(x) : boolean +>isC1 : (c: C1 | C2 | C3) => c is C1 +>x : C1 | C2 | C3 + + return x.item; +>x.item : string +>x : C1 +>item : string + } + else if (isC2(x)) { +>isC2(x) : boolean +>isC2 : (c: C1 | C2 | C3) => c is C2 +>x : C2 | C3 + + return x.item[0]; +>x.item[0] : string +>x.item : string[] +>x : C2 +>item : string[] +>0 : number + } + else if (isC3(x)) { +>isC3(x) : boolean +>isC3 : (c: C1 | C2 | C3) => c is C3 +>x : C3 + + return x.item; +>x.item : string +>x : C3 +>item : string + } + return "error"; +>"error" : string +} + +// More tests + +class A { a: string } +>A : A +>a : string + +class A1 extends A { } +>A1 : A1 +>A : A + +class A2 { a: string } +>A2 : A2 +>a : string + +class B extends A { b: string } +>B : B +>A : A +>b : string + +function goo(x: A) { +>goo : (x: A) => void +>x : A +>A : A + + if (x instanceof A) { +>x instanceof A : boolean +>x : A +>A : typeof A + + x; // A +>x : A + } + else { + x; // never +>x : never + } + if (x instanceof A1) { +>x instanceof A1 : boolean +>x : A +>A1 : typeof A1 + + x; // A1 +>x : A1 + } + else { + x; // A +>x : A + } + if (x instanceof A2) { +>x instanceof A2 : boolean +>x : A +>A2 : typeof A2 + + x; // A2 +>x : A2 + } + else { + x; // A +>x : A + } + if (x instanceof B) { +>x instanceof B : boolean +>x : A +>B : typeof B + + x; // B +>x : B + } + else { + x; // A +>x : A + } +} + From 4f54c6c228c5e3a7e9fd1115de81fa49327b974f Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 9 Aug 2016 06:42:14 -0700 Subject: [PATCH 098/233] Fix loop over array to use for-of instead of for-in --- src/harness/harness.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/harness/harness.ts b/src/harness/harness.ts index f27e7e1c174..e8e0a11f615 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -750,7 +750,7 @@ namespace Harness { export function readDirectory(path: string, extension?: string[], exclude?: string[], include?: string[]) { const fs = new Utils.VirtualFileSystem(path, useCaseSensitiveFileNames()); - for (const file in listFiles(path)) { + for (const file of listFiles(path)) { fs.addFile(file); } return ts.matchFiles(path, extension, exclude, include, useCaseSensitiveFileNames(), getCurrentDirectory(), path => { From 7e115bbbef0e6e9cfe71ca63373d0881dd48e3be Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 9 Aug 2016 12:44:08 -0700 Subject: [PATCH 099/233] Use correct this in tuple type parameter constraints Instantiate this in tuple types used as type parameter constraints --- src/compiler/checker.ts | 8 ++- .../thisInTupleTypeParameterConstraints.js | 29 ++++++++ ...hisInTupleTypeParameterConstraints.symbols | 66 ++++++++++++++++++ .../thisInTupleTypeParameterConstraints.types | 67 +++++++++++++++++++ .../thisInTupleTypeParameterConstraints.ts | 22 ++++++ 5 files changed, 190 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/thisInTupleTypeParameterConstraints.js create mode 100644 tests/baselines/reference/thisInTupleTypeParameterConstraints.symbols create mode 100644 tests/baselines/reference/thisInTupleTypeParameterConstraints.types create mode 100644 tests/cases/compiler/thisInTupleTypeParameterConstraints.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1f2eaea833d..8b31ef91930 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4000,6 +4000,10 @@ namespace ts { return createTypeReference((type).target, concatenate((type).typeArguments, [thisArgument || (type).target.thisType])); } + if (type.flags & TypeFlags.Tuple) { + resolveTupleTypeMembers(type as TupleType, thisArgument); + return type; + } return type; } @@ -4100,10 +4104,10 @@ namespace ts { return members; } - function resolveTupleTypeMembers(type: TupleType) { + function resolveTupleTypeMembers(type: TupleType, thisArgument?: Type) { const arrayElementType = getUnionType(type.elementTypes); // Make the tuple type itself the 'this' type by including an extra type argument - const arrayType = resolveStructuredTypeMembers(createTypeFromGenericGlobalType(globalArrayType, [arrayElementType, type])); + const arrayType = resolveStructuredTypeMembers(createTypeFromGenericGlobalType(globalArrayType, [arrayElementType, thisArgument || type])); const members = createTupleTypeMemberSymbols(type.elementTypes); addInheritedMembers(members, arrayType.properties); setObjectTypeMembers(type, members, arrayType.callSignatures, arrayType.constructSignatures, arrayType.stringIndexInfo, arrayType.numberIndexInfo); diff --git a/tests/baselines/reference/thisInTupleTypeParameterConstraints.js b/tests/baselines/reference/thisInTupleTypeParameterConstraints.js new file mode 100644 index 00000000000..c63fbf61991 --- /dev/null +++ b/tests/baselines/reference/thisInTupleTypeParameterConstraints.js @@ -0,0 +1,29 @@ +//// [thisInTupleTypeParameterConstraints.ts] +/// + +interface Boolean {} +interface IArguments {} +interface Function {} +interface Number {} +interface RegExp {} +interface Object {} +interface String {} + +interface Array { + // 4 methods will run out of memory if this-types are not instantiated + // correctly for tuple types that are type parameter constraints + map(arg: this): void; + reduceRight(arg: this): void; + reduce(arg: this): void; + reduce2(arg: this): void; +} + +declare function f number]>(a: T): void; +let x: [(x: number) => number]; +f(x); + + +//// [thisInTupleTypeParameterConstraints.js] +/// +var x; +f(x); diff --git a/tests/baselines/reference/thisInTupleTypeParameterConstraints.symbols b/tests/baselines/reference/thisInTupleTypeParameterConstraints.symbols new file mode 100644 index 00000000000..4d2bef22c55 --- /dev/null +++ b/tests/baselines/reference/thisInTupleTypeParameterConstraints.symbols @@ -0,0 +1,66 @@ +=== tests/cases/compiler/thisInTupleTypeParameterConstraints.ts === +/// + +interface Boolean {} +>Boolean : Symbol(Boolean, Decl(thisInTupleTypeParameterConstraints.ts, 0, 0)) + +interface IArguments {} +>IArguments : Symbol(IArguments, Decl(thisInTupleTypeParameterConstraints.ts, 2, 20)) + +interface Function {} +>Function : Symbol(Function, Decl(thisInTupleTypeParameterConstraints.ts, 3, 23)) + +interface Number {} +>Number : Symbol(Number, Decl(thisInTupleTypeParameterConstraints.ts, 4, 21)) + +interface RegExp {} +>RegExp : Symbol(RegExp, Decl(thisInTupleTypeParameterConstraints.ts, 5, 19)) + +interface Object {} +>Object : Symbol(Object, Decl(thisInTupleTypeParameterConstraints.ts, 6, 19)) + +interface String {} +>String : Symbol(String, Decl(thisInTupleTypeParameterConstraints.ts, 7, 19)) + +interface Array { +>Array : Symbol(Array, Decl(thisInTupleTypeParameterConstraints.ts, 8, 19)) +>T : Symbol(T, Decl(thisInTupleTypeParameterConstraints.ts, 10, 16)) + + // 4 methods will run out of memory if this-types are not instantiated + // correctly for tuple types that are type parameter constraints + map(arg: this): void; +>map : Symbol(Array.map, Decl(thisInTupleTypeParameterConstraints.ts, 10, 20)) +>U : Symbol(U, Decl(thisInTupleTypeParameterConstraints.ts, 13, 8)) +>arg : Symbol(arg, Decl(thisInTupleTypeParameterConstraints.ts, 13, 11)) + + reduceRight(arg: this): void; +>reduceRight : Symbol(Array.reduceRight, Decl(thisInTupleTypeParameterConstraints.ts, 13, 28)) +>U : Symbol(U, Decl(thisInTupleTypeParameterConstraints.ts, 14, 16)) +>arg : Symbol(arg, Decl(thisInTupleTypeParameterConstraints.ts, 14, 19)) + + reduce(arg: this): void; +>reduce : Symbol(Array.reduce, Decl(thisInTupleTypeParameterConstraints.ts, 14, 36)) +>U : Symbol(U, Decl(thisInTupleTypeParameterConstraints.ts, 15, 11)) +>arg : Symbol(arg, Decl(thisInTupleTypeParameterConstraints.ts, 15, 14)) + + reduce2(arg: this): void; +>reduce2 : Symbol(Array.reduce2, Decl(thisInTupleTypeParameterConstraints.ts, 15, 31)) +>U : Symbol(U, Decl(thisInTupleTypeParameterConstraints.ts, 16, 12)) +>arg : Symbol(arg, Decl(thisInTupleTypeParameterConstraints.ts, 16, 15)) +} + +declare function f number]>(a: T): void; +>f : Symbol(f, Decl(thisInTupleTypeParameterConstraints.ts, 17, 1)) +>T : Symbol(T, Decl(thisInTupleTypeParameterConstraints.ts, 19, 19)) +>x : Symbol(x, Decl(thisInTupleTypeParameterConstraints.ts, 19, 31)) +>a : Symbol(a, Decl(thisInTupleTypeParameterConstraints.ts, 19, 54)) +>T : Symbol(T, Decl(thisInTupleTypeParameterConstraints.ts, 19, 19)) + +let x: [(x: number) => number]; +>x : Symbol(x, Decl(thisInTupleTypeParameterConstraints.ts, 20, 3)) +>x : Symbol(x, Decl(thisInTupleTypeParameterConstraints.ts, 20, 9)) + +f(x); +>f : Symbol(f, Decl(thisInTupleTypeParameterConstraints.ts, 17, 1)) +>x : Symbol(x, Decl(thisInTupleTypeParameterConstraints.ts, 20, 3)) + diff --git a/tests/baselines/reference/thisInTupleTypeParameterConstraints.types b/tests/baselines/reference/thisInTupleTypeParameterConstraints.types new file mode 100644 index 00000000000..7daafe02bfc --- /dev/null +++ b/tests/baselines/reference/thisInTupleTypeParameterConstraints.types @@ -0,0 +1,67 @@ +=== tests/cases/compiler/thisInTupleTypeParameterConstraints.ts === +/// + +interface Boolean {} +>Boolean : Boolean + +interface IArguments {} +>IArguments : IArguments + +interface Function {} +>Function : Function + +interface Number {} +>Number : Number + +interface RegExp {} +>RegExp : RegExp + +interface Object {} +>Object : Object + +interface String {} +>String : String + +interface Array { +>Array : T[] +>T : T + + // 4 methods will run out of memory if this-types are not instantiated + // correctly for tuple types that are type parameter constraints + map(arg: this): void; +>map : (arg: this) => void +>U : U +>arg : this + + reduceRight(arg: this): void; +>reduceRight : (arg: this) => void +>U : U +>arg : this + + reduce(arg: this): void; +>reduce : (arg: this) => void +>U : U +>arg : this + + reduce2(arg: this): void; +>reduce2 : (arg: this) => void +>U : U +>arg : this +} + +declare function f number]>(a: T): void; +>f : number]>(a: T) => void +>T : T +>x : number +>a : T +>T : T + +let x: [(x: number) => number]; +>x : [(x: number) => number] +>x : number + +f(x); +>f(x) : void +>f : number]>(a: T) => void +>x : [(x: number) => number] + diff --git a/tests/cases/compiler/thisInTupleTypeParameterConstraints.ts b/tests/cases/compiler/thisInTupleTypeParameterConstraints.ts new file mode 100644 index 00000000000..b6d0d338d85 --- /dev/null +++ b/tests/cases/compiler/thisInTupleTypeParameterConstraints.ts @@ -0,0 +1,22 @@ +/// + +interface Boolean {} +interface IArguments {} +interface Function {} +interface Number {} +interface RegExp {} +interface Object {} +interface String {} + +interface Array { + // 4 methods will run out of memory if this-types are not instantiated + // correctly for tuple types that are type parameter constraints + map(arg: this): void; + reduceRight(arg: this): void; + reduce(arg: this): void; + reduce2(arg: this): void; +} + +declare function f number]>(a: T): void; +let x: [(x: number) => number]; +f(x); From d34bbe5f5880170066a55301a576f6372634d47c Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 9 Aug 2016 12:47:43 -0700 Subject: [PATCH 100/233] Add explanatory comment to resolveTupleTypeMembers --- src/compiler/checker.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8b31ef91930..546d8412c98 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4107,6 +4107,7 @@ namespace ts { function resolveTupleTypeMembers(type: TupleType, thisArgument?: Type) { const arrayElementType = getUnionType(type.elementTypes); // Make the tuple type itself the 'this' type by including an extra type argument + // (Unless it's provided in the case that the tuple is a type parameter constraint) const arrayType = resolveStructuredTypeMembers(createTypeFromGenericGlobalType(globalArrayType, [arrayElementType, thisArgument || type])); const members = createTupleTypeMemberSymbols(type.elementTypes); addInheritedMembers(members, arrayType.properties); From a5ea55ab6561b932a57d18593f89c81ad1d0f9f0 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 9 Aug 2016 13:39:00 -0700 Subject: [PATCH 101/233] Ignore null, undefined, void when checking for discriminant property --- src/compiler/checker.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1f2eaea833d..c88ab16acd7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7837,14 +7837,17 @@ namespace ts { } function isDiscriminantProperty(type: Type, name: string) { - if (type && type.flags & TypeFlags.Union) { - const prop = getPropertyOfType(type, name); - if (prop && prop.flags & SymbolFlags.SyntheticProperty) { - if ((prop).isDiscriminantProperty === undefined) { - (prop).isDiscriminantProperty = !(prop).hasCommonType && - isUnitUnionType(getTypeOfSymbol(prop)); + if (type) { + const nonNullType = getNonNullableType(type); + if (nonNullType.flags & TypeFlags.Union) { + const prop = getPropertyOfType(nonNullType, name); + if (prop && prop.flags & SymbolFlags.SyntheticProperty) { + if ((prop).isDiscriminantProperty === undefined) { + (prop).isDiscriminantProperty = !(prop).hasCommonType && + isUnitUnionType(getTypeOfSymbol(prop)); + } + return (prop).isDiscriminantProperty; } - return (prop).isDiscriminantProperty; } } return false; From 6c0bca0ae583c6b68ae9e007c081338ce9047d51 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 9 Aug 2016 13:39:12 -0700 Subject: [PATCH 102/233] Add regression test --- .../discriminantsAndNullOrUndefined.js | 44 ++++++++++++ .../discriminantsAndNullOrUndefined.symbols | 61 +++++++++++++++++ .../discriminantsAndNullOrUndefined.types | 68 +++++++++++++++++++ .../discriminantsAndNullOrUndefined.ts | 25 +++++++ 4 files changed, 198 insertions(+) create mode 100644 tests/baselines/reference/discriminantsAndNullOrUndefined.js create mode 100644 tests/baselines/reference/discriminantsAndNullOrUndefined.symbols create mode 100644 tests/baselines/reference/discriminantsAndNullOrUndefined.types create mode 100644 tests/cases/compiler/discriminantsAndNullOrUndefined.ts diff --git a/tests/baselines/reference/discriminantsAndNullOrUndefined.js b/tests/baselines/reference/discriminantsAndNullOrUndefined.js new file mode 100644 index 00000000000..153950f8ab5 --- /dev/null +++ b/tests/baselines/reference/discriminantsAndNullOrUndefined.js @@ -0,0 +1,44 @@ +//// [discriminantsAndNullOrUndefined.ts] + +// Repro from #10228 + +interface A { kind: 'A'; } +interface B { kind: 'B'; } + +type C = A | B | undefined; + +function never(_: never): never { + throw new Error(); +} + +function useA(_: A): void { } +function useB(_: B): void { } + +declare var c: C; + +if (c !== undefined) { + switch (c.kind) { + case 'A': useA(c); break; + case 'B': useB(c); break; + default: never(c); + } +} + +//// [discriminantsAndNullOrUndefined.js] +// Repro from #10228 +function never(_) { + throw new Error(); +} +function useA(_) { } +function useB(_) { } +if (c !== undefined) { + switch (c.kind) { + case 'A': + useA(c); + break; + case 'B': + useB(c); + break; + default: never(c); + } +} diff --git a/tests/baselines/reference/discriminantsAndNullOrUndefined.symbols b/tests/baselines/reference/discriminantsAndNullOrUndefined.symbols new file mode 100644 index 00000000000..3f95fcf3a3f --- /dev/null +++ b/tests/baselines/reference/discriminantsAndNullOrUndefined.symbols @@ -0,0 +1,61 @@ +=== tests/cases/compiler/discriminantsAndNullOrUndefined.ts === + +// Repro from #10228 + +interface A { kind: 'A'; } +>A : Symbol(A, Decl(discriminantsAndNullOrUndefined.ts, 0, 0)) +>kind : Symbol(A.kind, Decl(discriminantsAndNullOrUndefined.ts, 3, 13)) + +interface B { kind: 'B'; } +>B : Symbol(B, Decl(discriminantsAndNullOrUndefined.ts, 3, 26)) +>kind : Symbol(B.kind, Decl(discriminantsAndNullOrUndefined.ts, 4, 13)) + +type C = A | B | undefined; +>C : Symbol(C, Decl(discriminantsAndNullOrUndefined.ts, 4, 26)) +>A : Symbol(A, Decl(discriminantsAndNullOrUndefined.ts, 0, 0)) +>B : Symbol(B, Decl(discriminantsAndNullOrUndefined.ts, 3, 26)) + +function never(_: never): never { +>never : Symbol(never, Decl(discriminantsAndNullOrUndefined.ts, 6, 27)) +>_ : Symbol(_, Decl(discriminantsAndNullOrUndefined.ts, 8, 15)) + + throw new Error(); +>Error : Symbol(Error, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +} + +function useA(_: A): void { } +>useA : Symbol(useA, Decl(discriminantsAndNullOrUndefined.ts, 10, 1)) +>_ : Symbol(_, Decl(discriminantsAndNullOrUndefined.ts, 12, 14)) +>A : Symbol(A, Decl(discriminantsAndNullOrUndefined.ts, 0, 0)) + +function useB(_: B): void { } +>useB : Symbol(useB, Decl(discriminantsAndNullOrUndefined.ts, 12, 29)) +>_ : Symbol(_, Decl(discriminantsAndNullOrUndefined.ts, 13, 14)) +>B : Symbol(B, Decl(discriminantsAndNullOrUndefined.ts, 3, 26)) + +declare var c: C; +>c : Symbol(c, Decl(discriminantsAndNullOrUndefined.ts, 15, 11)) +>C : Symbol(C, Decl(discriminantsAndNullOrUndefined.ts, 4, 26)) + +if (c !== undefined) { +>c : Symbol(c, Decl(discriminantsAndNullOrUndefined.ts, 15, 11)) +>undefined : Symbol(undefined) + + switch (c.kind) { +>c.kind : Symbol(kind, Decl(discriminantsAndNullOrUndefined.ts, 3, 13), Decl(discriminantsAndNullOrUndefined.ts, 4, 13)) +>c : Symbol(c, Decl(discriminantsAndNullOrUndefined.ts, 15, 11)) +>kind : Symbol(kind, Decl(discriminantsAndNullOrUndefined.ts, 3, 13), Decl(discriminantsAndNullOrUndefined.ts, 4, 13)) + + case 'A': useA(c); break; +>useA : Symbol(useA, Decl(discriminantsAndNullOrUndefined.ts, 10, 1)) +>c : Symbol(c, Decl(discriminantsAndNullOrUndefined.ts, 15, 11)) + + case 'B': useB(c); break; +>useB : Symbol(useB, Decl(discriminantsAndNullOrUndefined.ts, 12, 29)) +>c : Symbol(c, Decl(discriminantsAndNullOrUndefined.ts, 15, 11)) + + default: never(c); +>never : Symbol(never, Decl(discriminantsAndNullOrUndefined.ts, 6, 27)) +>c : Symbol(c, Decl(discriminantsAndNullOrUndefined.ts, 15, 11)) + } +} diff --git a/tests/baselines/reference/discriminantsAndNullOrUndefined.types b/tests/baselines/reference/discriminantsAndNullOrUndefined.types new file mode 100644 index 00000000000..7a2918ab83b --- /dev/null +++ b/tests/baselines/reference/discriminantsAndNullOrUndefined.types @@ -0,0 +1,68 @@ +=== tests/cases/compiler/discriminantsAndNullOrUndefined.ts === + +// Repro from #10228 + +interface A { kind: 'A'; } +>A : A +>kind : "A" + +interface B { kind: 'B'; } +>B : B +>kind : "B" + +type C = A | B | undefined; +>C : C +>A : A +>B : B + +function never(_: never): never { +>never : (_: never) => never +>_ : never + + throw new Error(); +>new Error() : Error +>Error : ErrorConstructor +} + +function useA(_: A): void { } +>useA : (_: A) => void +>_ : A +>A : A + +function useB(_: B): void { } +>useB : (_: B) => void +>_ : B +>B : B + +declare var c: C; +>c : C +>C : C + +if (c !== undefined) { +>c !== undefined : boolean +>c : C +>undefined : undefined + + switch (c.kind) { +>c.kind : "A" | "B" +>c : A | B +>kind : "A" | "B" + + case 'A': useA(c); break; +>'A' : "A" +>useA(c) : void +>useA : (_: A) => void +>c : A + + case 'B': useB(c); break; +>'B' : "B" +>useB(c) : void +>useB : (_: B) => void +>c : B + + default: never(c); +>never(c) : never +>never : (_: never) => never +>c : never + } +} diff --git a/tests/cases/compiler/discriminantsAndNullOrUndefined.ts b/tests/cases/compiler/discriminantsAndNullOrUndefined.ts new file mode 100644 index 00000000000..8346fa8adea --- /dev/null +++ b/tests/cases/compiler/discriminantsAndNullOrUndefined.ts @@ -0,0 +1,25 @@ +// @strictNullChecks: true + +// Repro from #10228 + +interface A { kind: 'A'; } +interface B { kind: 'B'; } + +type C = A | B | undefined; + +function never(_: never): never { + throw new Error(); +} + +function useA(_: A): void { } +function useB(_: B): void { } + +declare var c: C; + +if (c !== undefined) { + switch (c.kind) { + case 'A': useA(c); break; + case 'B': useB(c); break; + default: never(c); + } +} \ No newline at end of file From 6a8f4cb676f0df1ce6fd84c0766643b8ca42f98b Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 9 Aug 2016 14:48:11 -0700 Subject: [PATCH 103/233] Delay tuple type constraint resolution Create a new tuple that stores the this-type. --- src/compiler/checker.ts | 20 ++++++++++++-------- src/compiler/types.ts | 1 + 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 546d8412c98..1ed8cb8c6ee 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4001,8 +4001,7 @@ namespace ts { concatenate((type).typeArguments, [thisArgument || (type).target.thisType])); } if (type.flags & TypeFlags.Tuple) { - resolveTupleTypeMembers(type as TupleType, thisArgument); - return type; + return createTupleType((type as TupleType).elementTypes, thisArgument); } return type; } @@ -4104,11 +4103,11 @@ namespace ts { return members; } - function resolveTupleTypeMembers(type: TupleType, thisArgument?: Type) { + function resolveTupleTypeMembers(type: TupleType) { const arrayElementType = getUnionType(type.elementTypes); // Make the tuple type itself the 'this' type by including an extra type argument // (Unless it's provided in the case that the tuple is a type parameter constraint) - const arrayType = resolveStructuredTypeMembers(createTypeFromGenericGlobalType(globalArrayType, [arrayElementType, thisArgument || type])); + const arrayType = resolveStructuredTypeMembers(createTypeFromGenericGlobalType(globalArrayType, [arrayElementType, type.thisType || type])); const members = createTupleTypeMemberSymbols(type.elementTypes); addInheritedMembers(members, arrayType.properties); setObjectTypeMembers(type, members, arrayType.callSignatures, arrayType.constructSignatures, arrayType.stringIndexInfo, arrayType.numberIndexInfo); @@ -5235,15 +5234,20 @@ namespace ts { return links.resolvedType; } - function createTupleType(elementTypes: Type[]) { - const id = getTypeListId(elementTypes); - return tupleTypes[id] || (tupleTypes[id] = createNewTupleType(elementTypes)); + function createTupleType(elementTypes: Type[], thisType?: Type) { + let id = getTypeListId(elementTypes); + if (thisType) { + id += ',' + thisType.id; + } + + return tupleTypes[id] || (tupleTypes[id] = createNewTupleType(elementTypes, thisType)); } - function createNewTupleType(elementTypes: Type[]) { + function createNewTupleType(elementTypes: Type[], thisType?: Type) { const propagatedFlags = getPropagatingFlagsOfTypes(elementTypes, /*excludeKinds*/ 0); const type = createObjectType(TypeFlags.Tuple | propagatedFlags); type.elementTypes = elementTypes; + type.thisType = thisType; return type; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index a6e860450c6..25630059bf0 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2371,6 +2371,7 @@ namespace ts { export interface TupleType extends ObjectType { elementTypes: Type[]; // Element types + thisType?: Type; // This-type of tuple (only needed for tuples that are constraints of type parameters) } export interface UnionOrIntersectionType extends Type { From 80963baf509fd7f22b81f13da3aa6b3840312c97 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 9 Aug 2016 15:37:15 -0700 Subject: [PATCH 104/233] Always use thisType when generating tuple id --- src/compiler/checker.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1ed8cb8c6ee..8fcd22f0ffb 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1538,8 +1538,8 @@ namespace ts { function createType(flags: TypeFlags): Type { const result = new Type(checker, flags); - result.id = typeCount; typeCount++; + result.id = typeCount; return result; } @@ -5235,11 +5235,7 @@ namespace ts { } function createTupleType(elementTypes: Type[], thisType?: Type) { - let id = getTypeListId(elementTypes); - if (thisType) { - id += ',' + thisType.id; - } - + const id = getTypeListId(elementTypes) + ',' + (thisType ? thisType.id : 0); return tupleTypes[id] || (tupleTypes[id] = createNewTupleType(elementTypes, thisType)); } From 9d4547c5d08430a87304caac6a8fe164baed4c19 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 9 Aug 2016 16:33:45 -0700 Subject: [PATCH 105/233] Optimize format of type list id strings used in maps --- src/compiler/checker.ts | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1f2eaea833d..ac4ac3d506d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4941,24 +4941,27 @@ namespace ts { } function getTypeListId(types: Type[]) { + let result = ""; if (types) { - switch (types.length) { - case 1: - return "" + types[0].id; - case 2: - return types[0].id + "," + types[1].id; - default: - let result = ""; - for (let i = 0; i < types.length; i++) { - if (i > 0) { - result += ","; - } - result += types[i].id; - } - return result; + const length = types.length; + let i = 0; + while (i < length) { + const startId = types[i].id; + let count = 1; + while (i + count < length && types[i + count].id === startId + count) { + count++; + } + if (result.length) { + result += ","; + } + result += startId; + if (count > 1) { + result += ":" + count; + } + i += count; } } - return ""; + return result; } // This function is used to propagate certain flags when creating new object type references and union types. From 8975cd7024dc1057794af93fcb74f36844f8e1a5 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 9 Aug 2016 23:00:47 -0400 Subject: [PATCH 106/233] Make ReadonlyArray iterable. --- src/lib/es2015.iterable.d.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/lib/es2015.iterable.d.ts b/src/lib/es2015.iterable.d.ts index e1b9d99762b..de339e2bf2c 100644 --- a/src/lib/es2015.iterable.d.ts +++ b/src/lib/es2015.iterable.d.ts @@ -63,6 +63,26 @@ interface ArrayConstructor { from(iterable: Iterable): Array; } +interface ReadonlyArray { + /** Iterator */ + [Symbol.iterator](): IterableIterator; + + /** + * Returns an array of key, value pairs for every entry in the array + */ + entries(): IterableIterator<[number, T]>; + + /** + * Returns an list of keys in the array + */ + keys(): IterableIterator; + + /** + * Returns an list of values in the array + */ + values(): IterableIterator; +} + interface IArguments { /** Iterator */ [Symbol.iterator](): IterableIterator; From ecc51af1ec712447be88fa97c4ba38612bd98f42 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 10 Aug 2016 10:01:16 -0700 Subject: [PATCH 107/233] Allow OSX to fail while we investigate (#10255) The random test timeouts are an issue. --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 4126683eb62..97c772f45ac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,8 @@ matrix: node_js: stable osx_image: xcode7.3 env: workerCount=2 + allow_failures: + - os: osx branches: only: From 56cb07ae919372d96e98d9195375b7264555380e Mon Sep 17 00:00:00 2001 From: zhengbli Date: Wed, 10 Aug 2016 12:34:27 -0700 Subject: [PATCH 108/233] avoid using the global name --- src/harness/unittests/session.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/harness/unittests/session.ts b/src/harness/unittests/session.ts index c5285544329..d5e6daa5d60 100644 --- a/src/harness/unittests/session.ts +++ b/src/harness/unittests/session.ts @@ -106,7 +106,7 @@ namespace ts.server { describe("onMessage", () => { it("should not throw when commands are executed with invalid arguments", () => { let i = 0; - for (name in CommandNames) { + for (const name in CommandNames) { if (!Object.prototype.hasOwnProperty.call(CommandNames, name)) { continue; } From 408780864c1199899ed5277aa0ed893a95528671 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 10 Aug 2016 14:09:52 -0700 Subject: [PATCH 109/233] Fix single-quote lint --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8fcd22f0ffb..b8d7146efb7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5235,7 +5235,7 @@ namespace ts { } function createTupleType(elementTypes: Type[], thisType?: Type) { - const id = getTypeListId(elementTypes) + ',' + (thisType ? thisType.id : 0); + const id = getTypeListId(elementTypes) + "," + (thisType ? thisType.id : 0); return tupleTypes[id] || (tupleTypes[id] = createNewTupleType(elementTypes, thisType)); } From 65e1293b2e542165f668191a26dae2d446c54b74 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 10 Aug 2016 16:47:06 -0700 Subject: [PATCH 110/233] Optimize performance of maps --- src/compiler/binder.ts | 27 +- src/compiler/checker.ts | 280 ++++++++---------- src/compiler/commandLineParser.ts | 10 +- src/compiler/core.ts | 48 +-- src/compiler/declarationEmitter.ts | 2 +- src/compiler/emitter.ts | 24 +- src/compiler/parser.ts | 4 +- src/compiler/performance.ts | 4 +- src/compiler/program.ts | 16 +- src/compiler/scanner.ts | 4 +- src/compiler/sys.ts | 29 +- src/compiler/tsc.ts | 8 +- src/compiler/types.ts | 24 +- src/compiler/utilities.ts | 35 +-- src/harness/compilerRunner.ts | 6 +- src/harness/fourslash.ts | 14 +- src/harness/harness.ts | 4 +- src/harness/harnessLanguageService.ts | 6 +- src/harness/projectsRunner.ts | 2 +- .../unittests/cachingInServerLSHost.ts | 6 +- src/harness/unittests/moduleResolution.ts | 30 +- .../unittests/reuseProgramStructure.ts | 10 +- src/harness/unittests/session.ts | 4 +- .../unittests/tsserverProjectSystem.ts | 8 +- src/server/client.ts | 2 +- src/server/editorServices.ts | 16 +- src/server/session.ts | 2 +- src/services/jsTyping.ts | 4 +- src/services/navigationBar.ts | 2 +- src/services/patternMatcher.ts | 2 +- src/services/services.ts | 22 +- 31 files changed, 321 insertions(+), 334 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 8d8666a67ab..c7450ac88fb 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -135,7 +135,7 @@ namespace ts { options = opts; languageVersion = getEmitScriptTarget(options); inStrictMode = !!file.externalModuleIndicator; - classifiableNames = {}; + classifiableNames = createMap(); symbolCount = 0; Symbol = objectAllocator.getSymbolConstructor(); @@ -183,11 +183,11 @@ namespace ts { symbol.declarations.push(node); if (symbolFlags & SymbolFlags.HasExports && !symbol.exports) { - symbol.exports = {}; + symbol.exports = createMap(); } if (symbolFlags & SymbolFlags.HasMembers && !symbol.members) { - symbol.members = {}; + symbol.members = createMap(); } if (symbolFlags & SymbolFlags.Value) { @@ -318,9 +318,7 @@ namespace ts { // Otherwise, we'll be merging into a compatible existing symbol (for example when // you have multiple 'vars' with the same name in the same container). In this case // just add this node into the declarations list of the symbol. - symbol = hasProperty(symbolTable, name) - ? symbolTable[name] - : (symbolTable[name] = createSymbol(SymbolFlags.None, name)); + symbol = symbolTable[name] || (symbolTable[name] = createSymbol(SymbolFlags.None, name)); if (name && (includes & SymbolFlags.Classifiable)) { classifiableNames[name] = name; @@ -434,7 +432,7 @@ namespace ts { if (containerFlags & ContainerFlags.IsContainer) { container = blockScopeContainer = node; if (containerFlags & ContainerFlags.HasLocals) { - container.locals = {}; + container.locals = createMap(); } addToContainerChain(container); } @@ -1399,7 +1397,8 @@ namespace ts { const typeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, "__type"); addDeclarationToSymbol(typeLiteralSymbol, node, SymbolFlags.TypeLiteral); - typeLiteralSymbol.members = { [symbol.name]: symbol }; + typeLiteralSymbol.members = createMap(); + typeLiteralSymbol.members[symbol.name] = symbol; } function bindObjectLiteralExpression(node: ObjectLiteralExpression) { @@ -1409,7 +1408,7 @@ namespace ts { } if (inStrictMode) { - const seen: Map = {}; + const seen = createMap(); for (const prop of node.properties) { if (prop.name.kind !== SyntaxKind.Identifier) { @@ -1465,7 +1464,7 @@ namespace ts { // fall through. default: if (!blockScopeContainer.locals) { - blockScopeContainer.locals = {}; + blockScopeContainer.locals = createMap(); addToContainerChain(blockScopeContainer); } declareSymbol(blockScopeContainer.locals, undefined, node, symbolFlags, symbolExcludes); @@ -1925,7 +1924,7 @@ namespace ts { } } - file.symbol.globalExports = file.symbol.globalExports || {}; + file.symbol.globalExports = file.symbol.globalExports || createMap(); declareSymbol(file.symbol.globalExports, file.symbol, node, SymbolFlags.Alias, SymbolFlags.AliasExcludes); } @@ -1978,7 +1977,7 @@ namespace ts { else { return; } - assignee.symbol.members = assignee.symbol.members || {}; + assignee.symbol.members = assignee.symbol.members || createMap(); // It's acceptable for multiple 'this' assignments of the same identifier to occur declareSymbol(assignee.symbol.members, assignee.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property); } @@ -2004,7 +2003,7 @@ namespace ts { // Set up the members collection if it doesn't exist already if (!funcSymbol.members) { - funcSymbol.members = {}; + funcSymbol.members = createMap(); } // Declare the method/property @@ -2053,7 +2052,7 @@ namespace ts { // module might have an exported variable called 'prototype'. We can't allow that as // that would clash with the built-in 'prototype' for the class. const prototypeSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Prototype, "prototype"); - if (hasProperty(symbol.exports, prototypeSymbol.name)) { + if (symbol.exports[prototypeSymbol.name]) { if (node.name) { node.name.parent = node; } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c88ab16acd7..6b3fb1076a1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -44,7 +44,7 @@ namespace ts { let symbolCount = 0; const emptyArray: any[] = []; - const emptySymbols: SymbolTable = {}; + const emptySymbols = createMap(); const compilerOptions = host.getCompilerOptions(); const languageVersion = compilerOptions.target || ScriptTarget.ES3; @@ -106,11 +106,11 @@ namespace ts { isOptionalParameter }; - const tupleTypes: Map = {}; - const unionTypes: Map = {}; - const intersectionTypes: Map = {}; - const stringLiteralTypes: Map = {}; - const numericLiteralTypes: Map = {}; + const tupleTypes = createMap(); + const unionTypes = createMap(); + const intersectionTypes = createMap(); + const stringLiteralTypes = createMap(); + const numericLiteralTypes = createMap(); const unknownSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "unknown"); const resolvingSymbol = createSymbol(SymbolFlags.Transient, "__resolving__"); @@ -132,7 +132,7 @@ namespace ts { const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); const emptyGenericType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); - emptyGenericType.instantiations = {}; + emptyGenericType.instantiations = createMap(); const anyFunctionType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); // The anyFunctionType contains the anyFunctionType by definition. The flag is further propagated @@ -146,7 +146,7 @@ namespace ts { const enumNumberIndexInfo = createIndexInfo(stringType, /*isReadonly*/ true); - const globals: SymbolTable = {}; + const globals = createMap(); /** * List of every ambient module with a "*" wildcard. * Unlike other ambient modules, these can't be stored in `globals` because symbol tables only deal with exact matches. @@ -283,7 +283,7 @@ namespace ts { NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy, } - const typeofEQFacts: Map = { + const typeofEQFacts: MapLike = { "string": TypeFacts.TypeofEQString, "number": TypeFacts.TypeofEQNumber, "boolean": TypeFacts.TypeofEQBoolean, @@ -293,7 +293,7 @@ namespace ts { "function": TypeFacts.TypeofEQFunction }; - const typeofNEFacts: Map = { + const typeofNEFacts: MapLike = { "string": TypeFacts.TypeofNEString, "number": TypeFacts.TypeofNENumber, "boolean": TypeFacts.TypeofNEBoolean, @@ -303,7 +303,7 @@ namespace ts { "function": TypeFacts.TypeofNEFunction }; - const typeofTypesByName: Map = { + const typeofTypesByName: MapLike = { "string": stringType, "number": numberType, "boolean": booleanType, @@ -313,7 +313,7 @@ namespace ts { let jsxElementType: ObjectType; /** Things we lazy load from the JSX namespace */ - const jsxTypes: Map = {}; + const jsxTypes = createMap(); const JsxNames = { JSX: "JSX", IntrinsicElements: "IntrinsicElements", @@ -324,10 +324,10 @@ namespace ts { IntrinsicClassAttributes: "IntrinsicClassAttributes" }; - const subtypeRelation: Map = {}; - const assignableRelation: Map = {}; - const comparableRelation: Map = {}; - const identityRelation: Map = {}; + const subtypeRelation = createMap(); + const assignableRelation = createMap(); + const comparableRelation = createMap(); + const identityRelation = createMap(); // This is for caching the result of getSymbolDisplayBuilder. Do not access directly. let _displayBuilder: SymbolDisplayBuilder; @@ -341,9 +341,8 @@ namespace ts { ResolvedReturnType } - const builtinGlobals: SymbolTable = { - [undefinedSymbol.name]: undefinedSymbol - }; + const builtinGlobals = createMap(); + builtinGlobals[undefinedSymbol.name] = undefinedSymbol; initializeTypeChecker(); @@ -426,11 +425,11 @@ namespace ts { target.declarations.push(node); }); if (source.members) { - if (!target.members) target.members = {}; + if (!target.members) target.members = createMap(); mergeSymbolTable(target.members, source.members); } if (source.exports) { - if (!target.exports) target.exports = {}; + if (!target.exports) target.exports = createMap(); mergeSymbolTable(target.exports, source.exports); } recordMergedSymbol(target, source); @@ -448,28 +447,24 @@ namespace ts { } function cloneSymbolTable(symbolTable: SymbolTable): SymbolTable { - const result: SymbolTable = {}; + const result = createMap(); for (const id in symbolTable) { - if (hasProperty(symbolTable, id)) { - result[id] = symbolTable[id]; - } + result[id] = symbolTable[id]; } return result; } function mergeSymbolTable(target: SymbolTable, source: SymbolTable) { for (const id in source) { - if (hasProperty(source, id)) { - if (!hasProperty(target, id)) { - target[id] = source[id]; - } - else { - let symbol = target[id]; - if (!(symbol.flags & SymbolFlags.Merged)) { - target[id] = symbol = cloneSymbol(symbol); - } - mergeSymbol(symbol, source[id]); + let targetSymbol = target[id]; + if (!targetSymbol) { + target[id] = source[id]; + } + else { + if (!(targetSymbol.flags & SymbolFlags.Merged)) { + target[id] = targetSymbol = cloneSymbol(targetSymbol); } + mergeSymbol(targetSymbol, source[id]); } } } @@ -513,14 +508,12 @@ namespace ts { function addToSymbolTable(target: SymbolTable, source: SymbolTable, message: DiagnosticMessage) { for (const id in source) { - if (hasProperty(source, id)) { - if (hasProperty(target, id)) { - // Error on redeclarations - forEach(target[id].declarations, addDeclarationDiagnostic(id, message)); - } - else { - target[id] = source[id]; - } + if (target[id]) { + // Error on redeclarations + forEach(target[id].declarations, addDeclarationDiagnostic(id, message)); + } + else { + target[id] = source[id]; } } @@ -545,18 +538,20 @@ namespace ts { } function getSymbol(symbols: SymbolTable, name: string, meaning: SymbolFlags): Symbol { - if (meaning && hasProperty(symbols, name)) { + if (meaning) { const symbol = symbols[name]; - Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0, "Should never get an instantiated symbol here."); - if (symbol.flags & meaning) { - return symbol; - } - if (symbol.flags & SymbolFlags.Alias) { - const target = resolveAlias(symbol); - // Unknown symbol means an error occurred in alias resolution, treat it as positive answer to avoid cascading errors - if (target === unknownSymbol || target.flags & meaning) { + if (symbol) { + Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0, "Should never get an instantiated symbol here."); + if (symbol.flags & meaning) { return symbol; } + if (symbol.flags & SymbolFlags.Alias) { + const target = resolveAlias(symbol); + // Unknown symbol means an error occurred in alias resolution, treat it as positive answer to avoid cascading errors + if (target === unknownSymbol || target.flags & meaning) { + return symbol; + } + } } } // return undefined if we can't find a symbol. @@ -744,7 +739,7 @@ namespace ts { // 2. We check === SymbolFlags.Alias in order to check that the symbol is *purely* // an alias. If we used &, we'd be throwing out symbols that have non alias aspects, // which is not the desired behavior. - if (hasProperty(moduleExports, name) && + if (moduleExports[name] && moduleExports[name].flags === SymbolFlags.Alias && getDeclarationOfKind(moduleExports[name], SyntaxKind.ExportSpecifier)) { break; @@ -1101,9 +1096,9 @@ namespace ts { function getExportOfModule(symbol: Symbol, name: string): Symbol { if (symbol.flags & SymbolFlags.Module) { - const exports = getExportsOfSymbol(symbol); - if (hasProperty(exports, name)) { - return resolveSymbol(exports[name]); + const exportedSymbol = getExportsOfSymbol(symbol)[name]; + if (exportedSymbol) { + return resolveSymbol(exportedSymbol); } } } @@ -1421,7 +1416,7 @@ namespace ts { */ function extendExportSymbols(target: SymbolTable, source: SymbolTable, lookupTable?: Map, exportNode?: ExportDeclaration) { for (const id in source) { - if (id !== "default" && !hasProperty(target, id)) { + if (id !== "default" && !target[id]) { target[id] = source[id]; if (lookupTable && exportNode) { lookupTable[id] = { @@ -1429,7 +1424,7 @@ namespace ts { } as ExportCollisionTracker; } } - else if (lookupTable && exportNode && id !== "default" && hasProperty(target, id) && resolveSymbol(target[id]) !== resolveSymbol(source[id])) { + else if (lookupTable && exportNode && id !== "default" && target[id] && resolveSymbol(target[id]) !== resolveSymbol(source[id])) { if (!lookupTable[id].exportsWithDuplicate) { lookupTable[id].exportsWithDuplicate = [exportNode]; } @@ -1455,8 +1450,8 @@ namespace ts { // All export * declarations are collected in an __export symbol by the binder const exportStars = symbol.exports["__export"]; if (exportStars) { - const nestedSymbols: SymbolTable = {}; - const lookupTable: Map = {}; + const nestedSymbols = createMap(); + const lookupTable = createMap(); for (const node of exportStars.declarations) { const resolvedModule = resolveExternalModuleName(node, (node as ExportDeclaration).moduleSpecifier); const exportedSymbols = visit(resolvedModule); @@ -1470,7 +1465,7 @@ namespace ts { for (const id in lookupTable) { const { exportsWithDuplicate } = lookupTable[id]; // It's not an error if the file with multiple `export *`s with duplicate names exports a member with that name itself - if (id === "export=" || !(exportsWithDuplicate && exportsWithDuplicate.length) || hasProperty(symbols, id)) { + if (id === "export=" || !(exportsWithDuplicate && exportsWithDuplicate.length) || symbols[id]) { continue; } for (const node of exportsWithDuplicate) { @@ -1576,13 +1571,11 @@ namespace ts { function getNamedMembers(members: SymbolTable): Symbol[] { let result: Symbol[]; for (const id in members) { - if (hasProperty(members, id)) { - if (!isReservedMemberName(id)) { - if (!result) result = []; - const symbol = members[id]; - if (symbolIsValue(symbol)) { - result.push(symbol); - } + if (!isReservedMemberName(id)) { + if (!result) result = []; + const symbol = members[id]; + if (symbolIsValue(symbol)) { + result.push(symbol); } } } @@ -1698,12 +1691,12 @@ namespace ts { let qualify = false; forEachSymbolTableInScope(enclosingDeclaration, symbolTable => { // If symbol of this name is not available in the symbol table we are ok - if (!hasProperty(symbolTable, symbol.name)) { + let symbolFromSymbolTable = symbolTable[symbol.name]; + if (!symbolFromSymbolTable) { // Continue to the next symbol table return false; } // If the symbol with this name is present it should refer to the symbol - let symbolFromSymbolTable = symbolTable[symbol.name]; if (symbolFromSymbolTable === symbol) { // No need to qualify return true; @@ -3121,7 +3114,7 @@ namespace ts { // Return the type implied by an object binding pattern function getTypeFromObjectBindingPattern(pattern: BindingPattern, includePatternInType: boolean): Type { - const members: SymbolTable = {}; + const members = createMap(); let hasComputedProperties = false; forEach(pattern.elements, e => { const name = e.propertyName || e.name; @@ -3709,7 +3702,7 @@ namespace ts { type.typeParameters = concatenate(outerTypeParameters, localTypeParameters); type.outerTypeParameters = outerTypeParameters; type.localTypeParameters = localTypeParameters; - (type).instantiations = {}; + (type).instantiations = createMap(); (type).instantiations[getTypeListId(type.typeParameters)] = type; (type).target = type; (type).typeArguments = type.typeParameters; @@ -3751,7 +3744,7 @@ namespace ts { if (typeParameters) { // Initialize the instantiation cache for generic type aliases. The declared type corresponds to // an instantiation of the type alias with the type parameters supplied as type arguments. - links.instantiations = {}; + links.instantiations = createMap(); links.instantiations[getTypeListId(links.typeParameters)] = type; } } @@ -3772,7 +3765,7 @@ namespace ts { return expr.kind === SyntaxKind.NumericLiteral || expr.kind === SyntaxKind.PrefixUnaryExpression && (expr).operator === SyntaxKind.MinusToken && (expr).operand.kind === SyntaxKind.NumericLiteral || - expr.kind === SyntaxKind.Identifier && hasProperty(symbol.exports, (expr).text); + expr.kind === SyntaxKind.Identifier && !!symbol.exports[(expr).text]; } function enumHasLiteralMembers(symbol: Symbol) { @@ -3795,7 +3788,7 @@ namespace ts { enumType.symbol = symbol; if (enumHasLiteralMembers(symbol)) { const memberTypeList: Type[] = []; - const memberTypes: Map = {}; + const memberTypes = createMap(); for (const declaration of enumType.symbol.declarations) { if (declaration.kind === SyntaxKind.EnumDeclaration) { computeEnumMemberValues(declaration); @@ -3958,7 +3951,7 @@ namespace ts { } function createSymbolTable(symbols: Symbol[]): SymbolTable { - const result: SymbolTable = {}; + const result = createMap(); for (const symbol of symbols) { result[symbol.name] = symbol; } @@ -3968,7 +3961,7 @@ namespace ts { // The mappingThisOnly flag indicates that the only type parameter being mapped is "this". When the flag is true, // we check symbols to see if we can quickly conclude they are free of "this" references, thus needing no instantiation. function createInstantiatedSymbolTable(symbols: Symbol[], mapper: TypeMapper, mappingThisOnly: boolean): SymbolTable { - const result: SymbolTable = {}; + const result = createMap(); for (const symbol of symbols) { result[symbol.name] = mappingThisOnly && isIndependentMember(symbol) ? symbol : instantiateSymbol(symbol, mapper); } @@ -3977,7 +3970,7 @@ namespace ts { function addInheritedMembers(symbols: SymbolTable, baseSymbols: Symbol[]) { for (const s of baseSymbols) { - if (!hasProperty(symbols, s.name)) { + if (!symbols[s.name]) { symbols[s.name] = s; } } @@ -4091,7 +4084,7 @@ namespace ts { } function createTupleTypeMemberSymbols(memberTypes: Type[]): SymbolTable { - const members: SymbolTable = {}; + const members = createMap(); for (let i = 0; i < memberTypes.length; i++) { const symbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "" + i); symbol.type = memberTypes[i]; @@ -4313,11 +4306,9 @@ namespace ts { function getPropertyOfObjectType(type: Type, name: string): Symbol { if (type.flags & TypeFlags.ObjectType) { const resolved = resolveStructuredTypeMembers(type); - if (hasProperty(resolved.members, name)) { - const symbol = resolved.members[name]; - if (symbolIsValue(symbol)) { - return symbol; - } + const symbol = resolved.members[name]; + if (symbol && symbolIsValue(symbol)) { + return symbol; } } } @@ -4446,13 +4437,13 @@ namespace ts { } function getPropertyOfUnionOrIntersectionType(type: UnionOrIntersectionType, name: string): Symbol { - const properties = type.resolvedProperties || (type.resolvedProperties = {}); - if (hasProperty(properties, name)) { - return properties[name]; - } - const property = createUnionOrIntersectionProperty(type, name); - if (property) { - properties[name] = property; + const properties = type.resolvedProperties || (type.resolvedProperties = createMap()); + let property = properties[name]; + if (!property) { + property = createUnionOrIntersectionProperty(type, name); + if (property) { + properties[name] = property; + } } return property; } @@ -4469,11 +4460,9 @@ namespace ts { type = getApparentType(type); if (type.flags & TypeFlags.ObjectType) { const resolved = resolveStructuredTypeMembers(type); - if (hasProperty(resolved.members, name)) { - const symbol = resolved.members[name]; - if (symbolIsValue(symbol)) { - return symbol; - } + const symbol = resolved.members[name]; + if (symbol && symbolIsValue(symbol)) { + return symbol; } if (resolved === anyFunctionType || resolved.callSignatures.length || resolved.constructSignatures.length) { const symbol = getPropertyOfObjectType(globalFunctionType, name); @@ -5467,7 +5456,7 @@ namespace ts { function getLiteralTypeForText(flags: TypeFlags, text: string) { const map = flags & TypeFlags.StringLiteral ? stringLiteralTypes : numericLiteralTypes; - return hasProperty(map, text) ? map[text] : map[text] = createLiteralType(flags, text); + return map[text] || (map[text] = createLiteralType(flags, text)); } function getTypeFromLiteralTypeNode(node: LiteralTypeNode): Type { @@ -6582,7 +6571,7 @@ namespace ts { } sourceStack[depth] = source; targetStack[depth] = target; - maybeStack[depth] = {}; + maybeStack[depth] = createMap(); maybeStack[depth][id] = RelationComparisonResult.Succeeded; depth++; const saveExpandingFlags = expandingFlags; @@ -7223,7 +7212,7 @@ namespace ts { } function transformTypeOfMembers(type: Type, f: (propertyType: Type) => Type) { - const members: SymbolTable = {}; + const members = createMap(); for (const property of getPropertiesOfObjectType(type)) { const original = getTypeOfSymbol(property); const updated = f(original); @@ -7447,7 +7436,7 @@ namespace ts { let targetStack: Type[]; let depth = 0; let inferiority = 0; - const visited: Map = {}; + const visited = createMap(); inferFromTypes(source, target); function isInProcess(source: Type, target: Type) { @@ -7581,7 +7570,7 @@ namespace ts { return; } const key = source.id + "," + target.id; - if (hasProperty(visited, key)) { + if (visited[key]) { return; } visited[key] = true; @@ -8338,7 +8327,7 @@ namespace ts { // If we have previously computed the control flow type for the reference at // this flow loop junction, return the cached type. const id = getFlowNodeId(flow); - const cache = flowLoopCaches[id] || (flowLoopCaches[id] = {}); + const cache = flowLoopCaches[id] || (flowLoopCaches[id] = createMap()); if (!key) { key = getFlowCacheKey(reference); } @@ -9933,7 +9922,7 @@ namespace ts { // Grammar checking checkGrammarObjectLiteralExpression(node, inDestructuringPattern); - const propertiesTable: SymbolTable = {}; + const propertiesTable = createMap(); const propertiesArray: Symbol[] = []; const contextualType = getApparentTypeOfContextualType(node); const contextualTypeHasPattern = contextualType && contextualType.pattern && @@ -10024,7 +10013,7 @@ namespace ts { // type with those properties for which the binding pattern specifies a default value. if (contextualTypeHasPattern) { for (const prop of getPropertiesOfType(contextualType)) { - if (!hasProperty(propertiesTable, prop.name)) { + if (!propertiesTable[prop.name]) { if (!(prop.flags & SymbolFlags.Optional)) { error(prop.valueDeclaration || (prop).bindingElement, Diagnostics.Initializer_provides_no_value_for_this_binding_element_and_the_binding_element_has_no_default_value); @@ -10152,7 +10141,7 @@ namespace ts { for (const prop of props) { // Is there a corresponding property in the element attributes type? Skip checking of properties // that have already been assigned to, as these are not actually pushed into the resulting type - if (!hasProperty(nameTable, prop.name)) { + if (!nameTable[prop.name]) { const targetPropSym = getPropertyOfType(elementAttributesType, prop.name); if (targetPropSym) { const msg = chainDiagnosticMessages(undefined, Diagnostics.Property_0_of_JSX_spread_attribute_is_not_assignable_to_target_property, prop.name); @@ -10474,7 +10463,7 @@ namespace ts { const targetAttributesType = getJsxElementAttributesType(node); - const nameTable: Map = {}; + const nameTable = createMap(); // Process this array in right-to-left order so we know which // attributes (mostly from spreads) are being overwritten and // thus should have their types ignored @@ -10498,7 +10487,7 @@ namespace ts { const targetProperties = getPropertiesOfType(targetAttributesType); for (let i = 0; i < targetProperties.length; i++) { if (!(targetProperties[i].flags & SymbolFlags.Optional) && - !hasProperty(nameTable, targetProperties[i].name)) { + !nameTable[targetProperties[i].name]) { error(node, Diagnostics.Property_0_is_missing_in_type_1, targetProperties[i].name, typeToString(targetAttributesType)); } @@ -13782,8 +13771,8 @@ namespace ts { function checkClassForDuplicateDeclarations(node: ClassLikeDeclaration) { const getter = 1, setter = 2, property = getter | setter; - const instanceNames: Map = {}; - const staticNames: Map = {}; + const instanceNames = createMap(); + const staticNames = createMap(); for (const member of node.members) { if (member.kind === SyntaxKind.Constructor) { for (const param of (member as ConstructorDeclaration).parameters) { @@ -13816,8 +13805,8 @@ namespace ts { } function addName(names: Map, location: Node, name: string, meaning: number) { - if (hasProperty(names, name)) { - const prev = names[name]; + const prev = names[name]; + if (prev) { if (prev & meaning) { error(location, Diagnostics.Duplicate_identifier_0, getTextOfNode(location)); } @@ -13832,7 +13821,7 @@ namespace ts { } function checkObjectTypeForDuplicateDeclarations(node: TypeLiteralNode | InterfaceDeclaration) { - const names: Map = {}; + const names = createMap(); for (const member of node.members) { if (member.kind == SyntaxKind.PropertySignature) { let memberName: string; @@ -13846,7 +13835,7 @@ namespace ts { continue; } - if (hasProperty(names, memberName)) { + if (names[memberName]) { error(member.symbol.valueDeclaration.name, Diagnostics.Duplicate_identifier_0, memberName); error(member.name, Diagnostics.Duplicate_identifier_0, memberName); } @@ -15058,22 +15047,20 @@ namespace ts { function checkUnusedLocalsAndParameters(node: Node): void { if (node.parent.kind !== SyntaxKind.InterfaceDeclaration && noUnusedIdentifiers && !isInAmbientContext(node)) { for (const key in node.locals) { - if (hasProperty(node.locals, key)) { - const local = node.locals[key]; - if (!local.isReferenced) { - if (local.valueDeclaration && local.valueDeclaration.kind === SyntaxKind.Parameter) { - const parameter = local.valueDeclaration; - if (compilerOptions.noUnusedParameters && - !isParameterPropertyDeclaration(parameter) && - !parameterIsThisKeyword(parameter) && - !parameterNameStartsWithUnderscore(parameter)) { - error(local.valueDeclaration.name, Diagnostics._0_is_declared_but_never_used, local.name); - } - } - else if (compilerOptions.noUnusedLocals) { - forEach(local.declarations, d => error(d.name || d, Diagnostics._0_is_declared_but_never_used, local.name)); + const local = node.locals[key]; + if (!local.isReferenced) { + if (local.valueDeclaration && local.valueDeclaration.kind === SyntaxKind.Parameter) { + const parameter = local.valueDeclaration; + if (compilerOptions.noUnusedParameters && + !isParameterPropertyDeclaration(parameter) && + !parameterIsThisKeyword(parameter) && + !parameterNameStartsWithUnderscore(parameter)) { + error(local.valueDeclaration.name, Diagnostics._0_is_declared_but_never_used, local.name); } } + else if (compilerOptions.noUnusedLocals) { + forEach(local.declarations, d => error(d.name || d, Diagnostics._0_is_declared_but_never_used, local.name)); + } } } } @@ -15130,13 +15117,11 @@ namespace ts { function checkUnusedModuleMembers(node: ModuleDeclaration | SourceFile): void { if (compilerOptions.noUnusedLocals && !isInAmbientContext(node)) { for (const key in node.locals) { - if (hasProperty(node.locals, key)) { - const local = node.locals[key]; - if (!local.isReferenced && !local.exportSymbol) { - for (const declaration of local.declarations) { - if (!isAmbientModule(declaration)) { - error(declaration.name, Diagnostics._0_is_declared_but_never_used, local.name); - } + const local = node.locals[key]; + if (!local.isReferenced && !local.exportSymbol) { + for (const declaration of local.declarations) { + if (!isAmbientModule(declaration)) { + error(declaration.name, Diagnostics._0_is_declared_but_never_used, local.name); } } } @@ -16145,7 +16130,7 @@ namespace ts { else { const identifierName = (catchClause.variableDeclaration.name).text; const locals = catchClause.block.locals; - if (locals && hasProperty(locals, identifierName)) { + if (locals) { const localSymbol = locals[identifierName]; if (localSymbol && (localSymbol.flags & SymbolFlags.BlockScopedVariable) !== 0) { grammarErrorOnNode(localSymbol.valueDeclaration, Diagnostics.Cannot_redeclare_identifier_0_in_catch_clause, identifierName); @@ -16566,18 +16551,18 @@ namespace ts { return true; } - const seen: Map<{ prop: Symbol; containingType: Type }> = {}; + const seen = createMap<{ prop: Symbol; containingType: Type }>(); forEach(resolveDeclaredMembers(type).declaredProperties, p => { seen[p.name] = { prop: p, containingType: type }; }); let ok = true; for (const base of baseTypes) { const properties = getPropertiesOfObjectType(getTypeWithThisArgument(base, type.thisType)); for (const prop of properties) { - if (!hasProperty(seen, prop.name)) { + const existing = seen[prop.name]; + if (!existing) { seen[prop.name] = { prop: prop, containingType: base }; } else { - const existing = seen[prop.name]; const isInheritedProperty = existing.containingType !== type; if (isInheritedProperty && !isPropertyIdenticalTo(existing.prop, prop)) { ok = false; @@ -17635,7 +17620,7 @@ namespace ts { } function getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[] { - const symbols: SymbolTable = {}; + const symbols = createMap(); let memberFlags: NodeFlags = 0; if (isInsideWithStatementBody(location)) { @@ -17713,7 +17698,7 @@ namespace ts { // We will copy all symbol regardless of its reserved name because // symbolsToArray will check whether the key is a reserved name and // it will not copy symbol with reserved name to the array - if (!hasProperty(symbols, id)) { + if (!symbols[id]) { symbols[id] = symbol; } } @@ -18134,7 +18119,7 @@ namespace ts { const propsByName = createSymbolTable(getPropertiesOfType(type)); if (getSignaturesOfType(type, SignatureKind.Call).length || getSignaturesOfType(type, SignatureKind.Construct).length) { forEach(getPropertiesOfType(globalFunctionType), p => { - if (!hasProperty(propsByName, p.name)) { + if (!propsByName[p.name]) { propsByName[p.name] = p; } }); @@ -18482,7 +18467,7 @@ namespace ts { } function hasGlobalName(name: string): boolean { - return hasProperty(globals, name); + return !!globals[name]; } function getReferencedValueSymbol(reference: Identifier): Symbol { @@ -18506,9 +18491,6 @@ namespace ts { // populate reverse mapping: file path -> type reference directive that was resolved to this file fileToDirective = createFileMap(); for (const key in resolvedTypeReferenceDirectives) { - if (!hasProperty(resolvedTypeReferenceDirectives, key)) { - continue; - } const resolvedDirective = resolvedTypeReferenceDirectives[key]; if (!resolvedDirective) { continue; @@ -19297,7 +19279,7 @@ namespace ts { } function checkGrammarObjectLiteralExpression(node: ObjectLiteralExpression, inDestructuring: boolean) { - const seen: Map = {}; + const seen = createMap(); const Property = 1; const GetAccessor = 2; const SetAccessor = 4; @@ -19359,7 +19341,7 @@ namespace ts { continue; } - if (!hasProperty(seen, effectiveName)) { + if (!seen[effectiveName]) { seen[effectiveName] = currentKind; } else { @@ -19383,7 +19365,7 @@ namespace ts { } function checkGrammarJsxElement(node: JsxOpeningLikeElement) { - const seen: Map = {}; + const seen = createMap(); for (const attr of node.attributes) { if (attr.kind === SyntaxKind.JsxSpreadAttribute) { continue; @@ -19391,7 +19373,7 @@ namespace ts { const jsxAttr = (attr); const name = jsxAttr.name; - if (!hasProperty(seen, name.text)) { + if (!seen[name.text]) { seen[name.text] = true; } else { diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 7e2c6eb8d33..eaf17f00dd0 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -470,8 +470,8 @@ namespace ts { return optionNameMapCache; } - const optionNameMap: Map = {}; - const shortOptionNames: Map = {}; + const optionNameMap = createMap(); + const shortOptionNames = createMap(); forEach(optionDeclarations, option => { optionNameMap[option.name.toLowerCase()] = option; if (option.shortName) { @@ -958,12 +958,12 @@ namespace ts { // Literal file names (provided via the "files" array in tsconfig.json) are stored in a // file map with a possibly case insensitive key. We use this map later when when including // wildcard paths. - const literalFileMap: Map = {}; + const literalFileMap = createMap(); // Wildcard paths (provided via the "includes" array in tsconfig.json) are stored in a // file map with a possibly case insensitive key. We use this map to store paths matched // via wildcard, and to handle extension priority. - const wildcardFileMap: Map = {}; + const wildcardFileMap = createMap(); if (include) { include = validateSpecs(include, errors, /*allowTrailingRecursion*/ false); @@ -1063,7 +1063,7 @@ namespace ts { // /a/b/a?z - Watch /a/b directly to catch any new file matching a?z const rawExcludeRegex = getRegularExpressionForWildcard(exclude, path, "exclude"); const excludeRegex = rawExcludeRegex && new RegExp(rawExcludeRegex, useCaseSensitiveFileNames ? "" : "i"); - const wildcardDirectories: Map = {}; + const wildcardDirectories = createMap(); if (include !== undefined) { const recursiveKeys: string[] = []; for (const file of include) { diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 6c87ad82955..db88305d2c5 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -19,8 +19,24 @@ namespace ts { True = -1 } + const createObject = Object.create; + + export function createMap(): Map { + /* tslint:disable:no-null-keyword */ + const map: Map = createObject(null); + /* tslint:enable:no-null-keyword */ + + // Using 'delete' on an object causes V8 to put the object in dictionary mode. + // This disables creation of hidden classes, which are expensive when an object is + // constantly changing shape. + map["__"] = undefined; + delete map["__"]; + + return map; + } + export function createFileMap(keyMapper?: (key: string) => string): FileMap { - let files: Map = {}; + let files = createMap(); return { get, set, @@ -55,7 +71,7 @@ namespace ts { } function clear() { - files = {}; + files = createMap(); } function toKey(path: Path): string { @@ -311,11 +327,11 @@ namespace ts { const hasOwnProperty = Object.prototype.hasOwnProperty; - export function hasProperty(map: Map, key: string): boolean { + export function hasProperty(map: MapLike, key: string): boolean { return hasOwnProperty.call(map, key); } - export function getKeys(map: Map): string[] { + export function getKeys(map: MapLike): string[] { const keys: string[] = []; for (const key in map) { keys.push(key); @@ -323,15 +339,15 @@ namespace ts { return keys; } - export function getProperty(map: Map, key: string): T | undefined { + export function getProperty(map: MapLike, key: string): T | undefined { return hasProperty(map, key) ? map[key] : undefined; } - export function getOrUpdateProperty(map: Map, key: string, makeValue: () => T): T { + export function getOrUpdateProperty(map: MapLike, key: string, makeValue: () => T): T { return hasProperty(map, key) ? map[key] : map[key] = makeValue(); } - export function isEmpty(map: Map) { + export function isEmpty(map: MapLike) { for (const id in map) { if (hasProperty(map, id)) { return false; @@ -348,7 +364,7 @@ namespace ts { return result; } - export function extend, T2 extends Map<{}>>(first: T1 , second: T2): T1 & T2 { + export function extend, T2 extends MapLike<{}>>(first: T1 , second: T2): T1 & T2 { const result: T1 & T2 = {}; for (const id in first) { (result as any)[id] = first[id]; @@ -361,7 +377,7 @@ namespace ts { return result; } - export function forEachValue(map: Map, callback: (value: T) => U): U { + export function forEachValue(map: MapLike, callback: (value: T) => U): U { let result: U; for (const id in map) { if (result = callback(map[id])) break; @@ -369,7 +385,7 @@ namespace ts { return result; } - export function forEachKey(map: Map, callback: (key: string) => U): U { + export function forEachKey(map: MapLike, callback: (key: string) => U): U { let result: U; for (const id in map) { if (result = callback(id)) break; @@ -377,11 +393,11 @@ namespace ts { return result; } - export function lookUp(map: Map, key: string): T { + export function lookUp(map: MapLike, key: string): T { return hasProperty(map, key) ? map[key] : undefined; } - export function copyMap(source: Map, target: Map): void { + export function copyMap(source: MapLike, target: MapLike): void { for (const p in source) { target[p] = source[p]; } @@ -398,7 +414,7 @@ namespace ts { * index in the array will be the one associated with the produced key. */ export function arrayToMap(array: T[], makeKey: (value: T) => string): Map { - const result: Map = {}; + const result = createMap(); forEach(array, value => { result[makeKey(value)] = value; @@ -414,7 +430,7 @@ namespace ts { * @param callback An aggregation function that is called for each entry in the map * @param initial The initial value for the reduction. */ - export function reduceProperties(map: Map, callback: (aggregate: U, value: T, key: string) => U, initial: U): U { + export function reduceProperties(map: MapLike, callback: (aggregate: U, value: T, key: string) => U, initial: U): U { let result = initial; if (map) { for (const key in map) { @@ -454,9 +470,7 @@ namespace ts { export let localizedDiagnosticMessages: Map = undefined; export function getLocaleSpecificMessage(message: DiagnosticMessage) { - return localizedDiagnosticMessages && localizedDiagnosticMessages[message.key] - ? localizedDiagnosticMessages[message.key] - : message.message; + return localizedDiagnosticMessages && localizedDiagnosticMessages[message.key] || message.message; } export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage, ...args: any[]): Diagnostic; diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index d93a8a0aed0..113b79f33d4 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -269,7 +269,7 @@ namespace ts { } if (!usedTypeDirectiveReferences) { - usedTypeDirectiveReferences = {}; + usedTypeDirectiveReferences = createMap(); } for (const directive of typeReferenceDirectives) { if (!hasProperty(usedTypeDirectiveReferences, directive)) { diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index c8bd8072b24..1d62a4a4f46 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -24,7 +24,7 @@ namespace ts { Return = 1 << 3 } - const entities: Map = { + const entities: MapLike = { "quot": 0x0022, "amp": 0x0026, "apos": 0x0027, @@ -489,13 +489,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge function setLabeledJump(state: ConvertedLoopState, isBreak: boolean, labelText: string, labelMarker: string): void { if (isBreak) { if (!state.labeledNonLocalBreaks) { - state.labeledNonLocalBreaks = {}; + state.labeledNonLocalBreaks = createMap(); } state.labeledNonLocalBreaks[labelText] = labelMarker; } else { if (!state.labeledNonLocalContinues) { - state.labeledNonLocalContinues = {}; + state.labeledNonLocalContinues = createMap(); } state.labeledNonLocalContinues[labelText] = labelMarker; } @@ -531,7 +531,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge let currentText: string; let currentLineMap: number[]; let currentFileIdentifiers: Map; - let renamedDependencies: Map; + let renamedDependencies: MapLike; let isEs6Module: boolean; let isCurrentFileExternalModule: boolean; @@ -577,7 +577,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge const setSourceMapWriterEmit = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? changeSourceMapEmit : function (writer: SourceMapWriter) { }; - const moduleEmitDelegates: Map<(node: SourceFile, emitRelativePathAsModuleName?: boolean) => void> = { + const moduleEmitDelegates: MapLike<(node: SourceFile, emitRelativePathAsModuleName?: boolean) => void> = { [ModuleKind.ES6]: emitES6Module, [ModuleKind.AMD]: emitAMDModule, [ModuleKind.System]: emitSystemModule, @@ -585,7 +585,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge [ModuleKind.CommonJS]: emitCommonJSModule, }; - const bundleEmitDelegates: Map<(node: SourceFile, emitRelativePathAsModuleName?: boolean) => void> = { + const bundleEmitDelegates: MapLike<(node: SourceFile, emitRelativePathAsModuleName?: boolean) => void> = { [ModuleKind.ES6]() {}, [ModuleKind.AMD]: emitAMDModule, [ModuleKind.System]: emitSystemModule, @@ -597,7 +597,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge function doEmit(jsFilePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) { sourceMap.initialize(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit); - generatedNameSet = {}; + generatedNameSet = createMap(); nodeToGeneratedName = []; decoratedClassAliases = []; isOwnFileEmit = !isBundledEmit; @@ -3257,7 +3257,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge // Don't initialize seen unless we have at least one element. // Emit a comma to separate for all but the first element. if (!seen) { - seen = {}; + seen = createMap(); } else { write(", "); @@ -3856,7 +3856,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge if (convertedLoopState) { if (!convertedLoopState.labels) { - convertedLoopState.labels = {}; + convertedLoopState.labels = createMap(); } convertedLoopState.labels[node.label.text] = node.label.text; } @@ -6803,7 +6803,7 @@ const _super = (function (geti, seti) { function collectExternalModuleInfo(sourceFile: SourceFile) { externalImports = []; - exportSpecifiers = {}; + exportSpecifiers = createMap(); exportEquals = undefined; hasExportStarsToExportValues = false; for (const node of sourceFile.statements) { @@ -7081,7 +7081,7 @@ const _super = (function (geti, seti) { if (hoistedVars) { writeLine(); write("var "); - const seen: Map = {}; + const seen = createMap(); for (let i = 0; i < hoistedVars.length; i++) { const local = hoistedVars[i]; const name = local.kind === SyntaxKind.Identifier @@ -7447,7 +7447,7 @@ const _super = (function (geti, seti) { writeModuleName(node, emitRelativePathAsModuleName); write("["); - const groupIndices: Map = {}; + const groupIndices = createMap(); const dependencyGroups: DependencyGroup[] = []; for (let i = 0; i < externalImports.length; i++) { diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 014d0926213..6f38783d5ed 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -595,7 +595,7 @@ namespace ts { parseDiagnostics = []; parsingContext = 0; - identifiers = {}; + identifiers = createMap(); identifierCount = 0; nodeCount = 0; @@ -1084,7 +1084,7 @@ namespace ts { function internIdentifier(text: string): string { text = escapeIdentifier(text); - return hasProperty(identifiers, text) ? identifiers[text] : (identifiers[text] = text); + return identifiers[text] || (identifiers[text] = text); } // An identifier that starts with two underscores has an extra underscore character prepended to it to avoid issues diff --git a/src/compiler/performance.ts b/src/compiler/performance.ts index 89db876ae5e..e496353fde8 100644 --- a/src/compiler/performance.ts +++ b/src/compiler/performance.ts @@ -10,8 +10,8 @@ namespace ts.performance { /** Performance measurements for the compiler. */ declare const onProfilerEvent: { (markName: string): void; profiler: boolean; }; let profilerEvent: (markName: string) => void; - let counters: Map; - let measures: Map; + let counters: MapLike; + let measures: MapLike; /** * Emit a performance event if ts-profiler is connected. This is primarily used diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 7d40b2f3219..c26e748da9a 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -846,7 +846,7 @@ namespace ts { } export function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost { - const existingDirectories: Map = {}; + const existingDirectories = createMap(); function getCanonicalFileName(fileName: string): string { // if underlying system can distinguish between two files whose names differs only in cases then file name already in canonical form. @@ -899,7 +899,7 @@ namespace ts { function writeFileIfUpdated(fileName: string, data: string, writeByteOrderMark: boolean): void { if (!outputFingerprints) { - outputFingerprints = {}; + outputFingerprints = createMap(); } const hash = sys.createHash(data); @@ -1040,7 +1040,7 @@ namespace ts { return []; } const resolutions: T[] = []; - const cache: Map = {}; + const cache = createMap(); for (const name of names) { let result: T; if (hasProperty(cache, name)) { @@ -1095,7 +1095,7 @@ namespace ts { let noDiagnosticsTypeChecker: TypeChecker; let classifiableNames: Map; - let resolvedTypeReferenceDirectives: Map = {}; + let resolvedTypeReferenceDirectives = createMap(); let fileProcessingDiagnostics = createDiagnosticCollection(); // The below settings are to track if a .js file should be add to the program if loaded via searching under node_modules. @@ -1110,10 +1110,10 @@ namespace ts { // If a module has some of its imports skipped due to being at the depth limit under node_modules, then track // this, as it may be imported at a shallower depth later, and then it will need its skipped imports processed. - const modulesWithElidedImports: Map = {}; + const modulesWithElidedImports = createMap(); // Track source files that are source files found by searching under node_modules, as these shouldn't be compiled. - const sourceFilesFoundSearchingNodeModules: Map = {}; + const sourceFilesFoundSearchingNodeModules = createMap(); const start = performance.mark(); @@ -1241,7 +1241,7 @@ namespace ts { if (!classifiableNames) { // Initialize a checker so that all our files are bound. getTypeChecker(); - classifiableNames = {}; + classifiableNames = createMap(); for (const sourceFile of files) { copyMap(sourceFile.classifiableNames, classifiableNames); @@ -2082,7 +2082,7 @@ namespace ts { function processImportedModules(file: SourceFile, basePath: string) { collectExternalModuleReferences(file); if (file.imports.length || file.moduleAugmentations.length) { - file.resolvedModules = {}; + file.resolvedModules = createMap(); const moduleNames = map(concatenate(file.imports, file.moduleAugmentations), getTextOfLiteral); const resolutions = resolveModuleNamesWorker(moduleNames, getNormalizedAbsolutePath(file.fileName, currentDirectory)); for (let i = 0; i < moduleNames.length; i++) { diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 6dd84298008..134dc489b2a 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -55,7 +55,7 @@ namespace ts { tryScan(callback: () => T): T; } - const textToToken: Map = { + const textToToken: MapLike = { "abstract": SyntaxKind.AbstractKeyword, "any": SyntaxKind.AnyKeyword, "as": SyntaxKind.AsKeyword, @@ -271,7 +271,7 @@ namespace ts { lookupInUnicodeMap(code, unicodeES3IdentifierPart); } - function makeReverseMap(source: Map): string[] { + function makeReverseMap(source: MapLike): string[] { const result: string[] = []; for (const name in source) { if (source.hasOwnProperty(name)) { diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 29ae2c60af1..350d75429b7 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -233,15 +233,15 @@ namespace ts { const useNonPollingWatchers = process.env["TSC_NONPOLLING_WATCHER"]; function createWatchedFileSet() { - const dirWatchers: Map = {}; + const dirWatchers = createMap(); // One file can have multiple watchers - const fileWatcherCallbacks: Map = {}; + const fileWatcherCallbacks = createMap(); return { addFile, removeFile }; function reduceDirWatcherRefCountForFile(fileName: string) { const dirName = getDirectoryPath(fileName); - if (hasProperty(dirWatchers, dirName)) { - const watcher = dirWatchers[dirName]; + const watcher = dirWatchers[dirName]; + if (watcher) { watcher.referenceCount -= 1; if (watcher.referenceCount <= 0) { watcher.close(); @@ -251,13 +251,12 @@ namespace ts { } function addDirWatcher(dirPath: string): void { - if (hasProperty(dirWatchers, dirPath)) { - const watcher = dirWatchers[dirPath]; + let watcher = dirWatchers[dirPath]; + if (watcher) { watcher.referenceCount += 1; return; } - - const watcher: DirectoryWatcher = _fs.watch( + watcher = _fs.watch( dirPath, { persistent: true }, (eventName: string, relativeFileName: string) => fileEventHandler(eventName, relativeFileName, dirPath) @@ -268,12 +267,7 @@ namespace ts { } function addFileWatcherCallback(filePath: string, callback: FileWatcherCallback): void { - if (hasProperty(fileWatcherCallbacks, filePath)) { - fileWatcherCallbacks[filePath].push(callback); - } - else { - fileWatcherCallbacks[filePath] = [callback]; - } + (fileWatcherCallbacks[filePath] || (fileWatcherCallbacks[filePath] = [])).push(callback); } function addFile(fileName: string, callback: FileWatcherCallback): WatchedFile { @@ -289,8 +283,9 @@ namespace ts { } function removeFileWatcherCallback(filePath: string, callback: FileWatcherCallback) { - if (hasProperty(fileWatcherCallbacks, filePath)) { - const newCallbacks = copyListRemovingItem(callback, fileWatcherCallbacks[filePath]); + const callbacks = fileWatcherCallbacks[filePath]; + if (callbacks) { + const newCallbacks = copyListRemovingItem(callback, callbacks); if (newCallbacks.length === 0) { delete fileWatcherCallbacks[filePath]; } @@ -306,7 +301,7 @@ namespace ts { ? undefined : ts.getNormalizedAbsolutePath(relativeFileName, baseDirPath); // Some applications save a working file via rename operations - if ((eventName === "change" || eventName === "rename") && hasProperty(fileWatcherCallbacks, fileName)) { + if ((eventName === "change" || eventName === "rename") && fileWatcherCallbacks[fileName]) { for (const fileCallback of fileWatcherCallbacks[fileName]) { fileCallback(fileName); } diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 10538d0c009..3b588edd101 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -122,7 +122,7 @@ namespace ts { const gutterSeparator = " "; const resetEscapeSequence = "\u001b[0m"; const ellipsis = "..."; - const categoryFormatMap: Map = { + const categoryFormatMap: MapLike = { [DiagnosticCategory.Warning]: yellowForegroundEscapeSequence, [DiagnosticCategory.Error]: redForegroundEscapeSequence, [DiagnosticCategory.Message]: blueForegroundEscapeSequence, @@ -432,7 +432,7 @@ namespace ts { } // reset the cache of existing files - cachedExistingFiles = {}; + cachedExistingFiles = createMap(); const compileResult = compile(rootFileNames, compilerOptions, compilerHost); @@ -676,7 +676,7 @@ namespace ts { const usageColumn: string[] = []; // Things like "-d, --declaration" go in here. const descriptionColumn: string[] = []; - const optionsDescriptionMap: Map = {}; // Map between option.description and list of option.type if it is a kind + const optionsDescriptionMap = createMap(); // Map between option.description and list of option.type if it is a kind for (let i = 0; i < optsList.length; i++) { const option = optsList[i]; @@ -786,7 +786,7 @@ namespace ts { return; function serializeCompilerOptions(options: CompilerOptions): Map { - const result: Map = {}; + const result = createMap(); const optionsNameMap = getOptionNameMap().optionNameMap; for (const name in options) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index a6e860450c6..dad60775baf 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1,9 +1,13 @@ - namespace ts { - export interface Map { + + export interface MapLike { [index: string]: T; } + export interface Map extends MapLike { + __mapBrand: any; + } + // branded string type used to store absolute, normalized and canonicalized paths // arbitrary file name can be converted to Path via toPath function export type Path = string & { __pathBrand: any }; @@ -1640,7 +1644,7 @@ namespace ts { // this map is used by transpiler to supply alternative names for dependencies (i.e. in case of bundling) /* @internal */ - renamedDependencies?: Map; + renamedDependencies?: MapLike; /** * lib.d.ts should have a reference comment like @@ -2172,9 +2176,7 @@ namespace ts { /* @internal */ export interface TransientSymbol extends Symbol, SymbolLinks { } - export interface SymbolTable { - [index: string]: Symbol; - } + export type SymbolTable = Map; /** Represents a "prefix*suffix" pattern. */ /* @internal */ @@ -2557,7 +2559,7 @@ namespace ts { } export type RootPaths = string[]; - export type PathSubstitutions = Map; + export type PathSubstitutions = MapLike; export type TsConfigOnlyOptions = RootPaths | PathSubstitutions; export type CompilerOptionsValue = string | number | boolean | (string | number)[] | TsConfigOnlyOptions; @@ -2717,7 +2719,7 @@ namespace ts { fileNames: string[]; raw?: any; errors: Diagnostic[]; - wildcardDirectories?: Map; + wildcardDirectories?: MapLike; } export const enum WatchDirectoryFlags { @@ -2727,13 +2729,13 @@ namespace ts { export interface ExpandResult { fileNames: string[]; - wildcardDirectories: Map; + wildcardDirectories: MapLike; } /* @internal */ export interface CommandLineOptionBase { name: string; - type: "string" | "number" | "boolean" | "object" | "list" | Map; // a value of a primitive type, or an object literal mapping named values to actual values + type: "string" | "number" | "boolean" | "object" | "list" | MapLike; // a value of a primitive type, or an object literal mapping named values to actual values isFilePath?: boolean; // True if option value is a path or fileName shortName?: string; // A short mnemonic for convenience - for instance, 'h' can be used in place of 'help' description?: DiagnosticMessage; // The message describing what the command line switch does @@ -2749,7 +2751,7 @@ namespace ts { /* @internal */ export interface CommandLineOptionOfCustomType extends CommandLineOptionBase { - type: Map; // an object literal mapping named values to actual values + type: MapLike; // an object literal mapping named values to actual values } /* @internal */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 2cfaf1f5cfb..e0b3700c6f1 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -87,14 +87,14 @@ namespace ts { return node.end - node.pos; } - export function mapIsEqualTo(map1: Map, map2: Map): boolean { + export function mapIsEqualTo(map1: MapLike, map2: MapLike): boolean { if (!map1 || !map2) { return map1 === map2; } return containsAll(map1, map2) && containsAll(map2, map1); } - function containsAll(map: Map, other: Map): boolean { + function containsAll(map: MapLike, other: MapLike): boolean { for (const key in map) { if (!hasProperty(map, key)) { continue; @@ -126,7 +126,7 @@ namespace ts { } export function hasResolvedModule(sourceFile: SourceFile, moduleNameText: string): boolean { - return sourceFile.resolvedModules && hasProperty(sourceFile.resolvedModules, moduleNameText); + return !!(sourceFile.resolvedModules && sourceFile.resolvedModules[moduleNameText]); } export function getResolvedModule(sourceFile: SourceFile, moduleNameText: string): ResolvedModule { @@ -135,7 +135,7 @@ namespace ts { export function setResolvedModule(sourceFile: SourceFile, moduleNameText: string, resolvedModule: ResolvedModule): void { if (!sourceFile.resolvedModules) { - sourceFile.resolvedModules = {}; + sourceFile.resolvedModules = createMap(); } sourceFile.resolvedModules[moduleNameText] = resolvedModule; @@ -143,7 +143,7 @@ namespace ts { export function setResolvedTypeReferenceDirective(sourceFile: SourceFile, typeReferenceDirectiveName: string, resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective): void { if (!sourceFile.resolvedTypeReferenceDirectiveNames) { - sourceFile.resolvedTypeReferenceDirectiveNames = {}; + sourceFile.resolvedTypeReferenceDirectiveNames = createMap(); } sourceFile.resolvedTypeReferenceDirectiveNames[typeReferenceDirectiveName] = resolvedTypeReferenceDirective; @@ -166,7 +166,7 @@ namespace ts { } for (let i = 0; i < names.length; i++) { const newResolution = newResolutions[i]; - const oldResolution = oldResolutions && hasProperty(oldResolutions, names[i]) ? oldResolutions[names[i]] : undefined; + const oldResolution = oldResolutions && oldResolutions[names[i]]; const changed = oldResolution ? !newResolution || !comparer(oldResolution, newResolution) @@ -1966,7 +1966,7 @@ namespace ts { export function createDiagnosticCollection(): DiagnosticCollection { let nonFileDiagnostics: Diagnostic[] = []; - const fileDiagnostics: Map = {}; + const fileDiagnostics = createMap(); let diagnosticsModified = false; let modificationCount = 0; @@ -1984,12 +1984,11 @@ namespace ts { } function reattachFileDiagnostics(newFile: SourceFile): void { - if (!hasProperty(fileDiagnostics, newFile.fileName)) { - return; - } - - for (const diagnostic of fileDiagnostics[newFile.fileName]) { - diagnostic.file = newFile; + const diagnostics = fileDiagnostics[newFile.fileName]; + if (diagnostics) { + for (const diagnostic of diagnostics) { + diagnostic.file = newFile; + } } } @@ -2030,9 +2029,7 @@ namespace ts { forEach(nonFileDiagnostics, pushDiagnostic); for (const key in fileDiagnostics) { - if (hasProperty(fileDiagnostics, key)) { - forEach(fileDiagnostics[key], pushDiagnostic); - } + forEach(fileDiagnostics[key], pushDiagnostic); } return sortAndDeduplicateDiagnostics(allDiagnostics); @@ -2047,9 +2044,7 @@ namespace ts { nonFileDiagnostics = sortAndDeduplicateDiagnostics(nonFileDiagnostics); for (const key in fileDiagnostics) { - if (hasProperty(fileDiagnostics, key)) { - fileDiagnostics[key] = sortAndDeduplicateDiagnostics(fileDiagnostics[key]); - } + fileDiagnostics[key] = sortAndDeduplicateDiagnostics(fileDiagnostics[key]); } } } @@ -2060,7 +2055,7 @@ namespace ts { // the map below must be updated. Note that this regexp *does not* include the 'delete' character. // There is no reason for this other than that JSON.stringify does not handle it either. const escapedCharsRegExp = /[\\\"\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g; - const escapedCharsMap: Map = { + const escapedCharsMap: MapLike = { "\0": "\\0", "\t": "\\t", "\v": "\\v", diff --git a/src/harness/compilerRunner.ts b/src/harness/compilerRunner.ts index ecdc18394b0..5d4bdf7db32 100644 --- a/src/harness/compilerRunner.ts +++ b/src/harness/compilerRunner.ts @@ -291,8 +291,8 @@ class CompilerBaselineRunner extends RunnerBase { const fullWalker = new TypeWriterWalker(program, /*fullTypeCheck*/ true); - const fullResults: ts.Map = {}; - const pullResults: ts.Map = {}; + const fullResults: ts.MapLike = {}; + const pullResults: ts.MapLike = {}; for (const sourceFile of allFiles) { fullResults[sourceFile.unitName] = fullWalker.getTypeAndSymbols(sourceFile.unitName); @@ -338,7 +338,7 @@ class CompilerBaselineRunner extends RunnerBase { } } - function generateBaseLine(typeWriterResults: ts.Map, isSymbolBaseline: boolean): string { + function generateBaseLine(typeWriterResults: ts.MapLike, isSymbolBaseline: boolean): string { const typeLines: string[] = []; const typeMap: { [fileName: string]: { [lineNum: number]: string[]; } } = {}; diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index a42abbbc609..fed7751eb4a 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -95,7 +95,7 @@ namespace FourSlash { export import IndentStyle = ts.IndentStyle; - const entityMap: ts.Map = { + const entityMap: ts.MapLike = { "&": "&", "\"": """, "'": "'", @@ -204,7 +204,7 @@ namespace FourSlash { public formatCodeOptions: ts.FormatCodeOptions; - private inputFiles: ts.Map = {}; // Map between inputFile's fileName and its content for easily looking up when resolving references + private inputFiles: ts.MapLike = {}; // Map between inputFile's fileName and its content for easily looking up when resolving references // Add input file which has matched file name with the given reference-file path. // This is necessary when resolveReference flag is specified @@ -593,7 +593,7 @@ namespace FourSlash { public noItemsWithSameNameButDifferentKind(): void { const completions = this.getCompletionListAtCaret(); - const uniqueItems: ts.Map = {}; + const uniqueItems: ts.MapLike = {}; for (const item of completions.entries) { if (!ts.hasProperty(uniqueItems, item.name)) { uniqueItems[item.name] = item.kind; @@ -1638,8 +1638,8 @@ namespace FourSlash { return this.testData.ranges; } - public rangesByText(): ts.Map { - const result: ts.Map = {}; + public rangesByText(): ts.MapLike { + const result: ts.MapLike = {}; for (const range of this.getRanges()) { const text = this.rangeText(range); (ts.getProperty(result, text) || (result[text] = [])).push(range); @@ -1897,7 +1897,7 @@ namespace FourSlash { public verifyBraceCompletionAtPosition(negative: boolean, openingBrace: string) { - const openBraceMap: ts.Map = { + const openBraceMap: ts.MapLike = { "(": ts.CharacterCodes.openParen, "{": ts.CharacterCodes.openBrace, "[": ts.CharacterCodes.openBracket, @@ -2772,7 +2772,7 @@ namespace FourSlashInterface { return this.state.getRanges(); } - public rangesByText(): ts.Map { + public rangesByText(): ts.MapLike { return this.state.rangesByText(); } diff --git a/src/harness/harness.ts b/src/harness/harness.ts index e8e0a11f615..5ae33303d55 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -848,7 +848,7 @@ namespace Harness { export const defaultLibFileName = "lib.d.ts"; export const es2015DefaultLibFileName = "lib.es2015.d.ts"; - const libFileNameSourceFileMap: ts.Map = { + const libFileNameSourceFileMap: ts.MapLike = { [defaultLibFileName]: createSourceFileAndAssertInvariants(defaultLibFileName, IO.readFile(libFolder + "lib.es5.d.ts"), /*languageVersion*/ ts.ScriptTarget.Latest) }; @@ -1002,7 +1002,7 @@ namespace Harness { { name: "symlink", type: "string" } ]; - let optionsIndex: ts.Map; + let optionsIndex: ts.MapLike; function getCommandLineOption(name: string): ts.CommandLineOption { if (!optionsIndex) { optionsIndex = {}; diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index d7ed04b627f..4f95f19e144 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -123,7 +123,7 @@ namespace Harness.LanguageService { } export class LanguageServiceAdapterHost { - protected fileNameToScript: ts.Map = {}; + protected fileNameToScript: ts.MapLike = {}; constructor(protected cancellationToken = DefaultHostCancellationToken.Instance, protected settings = ts.getDefaultCompilerOptions()) { @@ -235,7 +235,7 @@ namespace Harness.LanguageService { this.getModuleResolutionsForFile = (fileName) => { const scriptInfo = this.getScriptInfo(fileName); const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ true); - const imports: ts.Map = {}; + const imports: ts.MapLike = {}; for (const module of preprocessInfo.importedFiles) { const resolutionInfo = ts.resolveModuleName(module.fileName, fileName, compilerOptions, moduleResolutionHost); if (resolutionInfo.resolvedModule) { @@ -248,7 +248,7 @@ namespace Harness.LanguageService { const scriptInfo = this.getScriptInfo(fileName); if (scriptInfo) { const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ false); - const resolutions: ts.Map = {}; + const resolutions: ts.MapLike = {}; const settings = this.nativeHost.getCompilationSettings(); for (const typeReferenceDirective of preprocessInfo.typeReferenceDirectives) { const resolutionInfo = ts.resolveTypeReferenceDirective(typeReferenceDirective.fileName, fileName, settings, moduleResolutionHost); diff --git a/src/harness/projectsRunner.ts b/src/harness/projectsRunner.ts index 038b2dbe359..be3030a6dd6 100644 --- a/src/harness/projectsRunner.ts +++ b/src/harness/projectsRunner.ts @@ -253,7 +253,7 @@ class ProjectRunner extends RunnerBase { moduleResolution: ts.ModuleResolutionKind.Classic, // currently all tests use classic module resolution kind, this will change in the future }; // Set the values specified using json - const optionNameMap: ts.Map = {}; + const optionNameMap: ts.MapLike = {}; ts.forEach(ts.optionDeclarations, option => { optionNameMap[option.name] = option; }); diff --git a/src/harness/unittests/cachingInServerLSHost.ts b/src/harness/unittests/cachingInServerLSHost.ts index e0ce09391fb..a1e010b97df 100644 --- a/src/harness/unittests/cachingInServerLSHost.ts +++ b/src/harness/unittests/cachingInServerLSHost.ts @@ -6,8 +6,8 @@ namespace ts { content: string; } - function createDefaultServerHost(fileMap: Map): server.ServerHost { - const existingDirectories: Map = {}; + function createDefaultServerHost(fileMap: MapLike): server.ServerHost { + const existingDirectories: MapLike = {}; forEachValue(fileMap, v => { let dir = getDirectoryPath(v.name); let previous: string; @@ -193,7 +193,7 @@ namespace ts { content: `export var y = 1` }; - const fileMap: Map = { [root.name]: root }; + const fileMap: MapLike = { [root.name]: root }; const serverHost = createDefaultServerHost(fileMap); const originalFileExists = serverHost.fileExists; diff --git a/src/harness/unittests/moduleResolution.ts b/src/harness/unittests/moduleResolution.ts index 5f63889b084..4b5ef9f24f1 100644 --- a/src/harness/unittests/moduleResolution.ts +++ b/src/harness/unittests/moduleResolution.ts @@ -10,7 +10,7 @@ namespace ts { const map = arrayToMap(files, f => f.name); if (hasDirectoryExists) { - const directories: Map = {}; + const directories: MapLike = {}; for (const f of files) { let name = getDirectoryPath(f.name); while (true) { @@ -282,7 +282,7 @@ namespace ts { }); describe("Module resolution - relative imports", () => { - function test(files: Map, currentDirectory: string, rootFiles: string[], expectedFilesCount: number, relativeNamesToCheck: string[]) { + function test(files: MapLike, currentDirectory: string, rootFiles: string[], expectedFilesCount: number, relativeNamesToCheck: string[]) { const options: CompilerOptions = { module: ModuleKind.CommonJS }; const host: CompilerHost = { getSourceFile: (fileName: string, languageVersion: ScriptTarget) => { @@ -318,7 +318,7 @@ namespace ts { } it("should find all modules", () => { - const files: Map = { + const files: MapLike = { "/a/b/c/first/shared.ts": ` class A {} export = A`, @@ -337,7 +337,7 @@ export = C; }); it("should find modules in node_modules", () => { - const files: Map = { + const files: MapLike = { "/parent/node_modules/mod/index.d.ts": "export var x", "/parent/app/myapp.ts": `import {x} from "mod"` }; @@ -345,7 +345,7 @@ export = C; }); it("should find file referenced via absolute and relative names", () => { - const files: Map = { + const files: MapLike = { "/a/b/c.ts": `/// `, "/a/b/b.ts": "var x" }; @@ -355,10 +355,10 @@ export = C; describe("Files with different casing", () => { const library = createSourceFile("lib.d.ts", "", ScriptTarget.ES5); - function test(files: Map, options: CompilerOptions, currentDirectory: string, useCaseSensitiveFileNames: boolean, rootFiles: string[], diagnosticCodes: number[]): void { + function test(files: MapLike, options: CompilerOptions, currentDirectory: string, useCaseSensitiveFileNames: boolean, rootFiles: string[], diagnosticCodes: number[]): void { const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); if (!useCaseSensitiveFileNames) { - const f: Map = {}; + const f: MapLike = {}; for (const fileName in files) { f[getCanonicalFileName(fileName)] = files[fileName]; } @@ -395,7 +395,7 @@ export = C; } it("should succeed when the same file is referenced using absolute and relative names", () => { - const files: Map = { + const files: MapLike = { "/a/b/c.ts": `/// `, "/a/b/d.ts": "var x" }; @@ -403,7 +403,7 @@ export = C; }); it("should fail when two files used in program differ only in casing (tripleslash references)", () => { - const files: Map = { + const files: MapLike = { "/a/b/c.ts": `/// `, "/a/b/d.ts": "var x" }; @@ -411,7 +411,7 @@ export = C; }); it("should fail when two files used in program differ only in casing (imports)", () => { - const files: Map = { + const files: MapLike = { "/a/b/c.ts": `import {x} from "D"`, "/a/b/d.ts": "export var x" }; @@ -419,7 +419,7 @@ export = C; }); it("should fail when two files used in program differ only in casing (imports, relative module names)", () => { - const files: Map = { + const files: MapLike = { "moduleA.ts": `import {x} from "./ModuleB"`, "moduleB.ts": "export var x" }; @@ -427,7 +427,7 @@ export = C; }); it("should fail when two files exist on disk that differs only in casing", () => { - const files: Map = { + const files: MapLike = { "/a/b/c.ts": `import {x} from "D"`, "/a/b/D.ts": "export var x", "/a/b/d.ts": "export var y" @@ -436,7 +436,7 @@ export = C; }); it("should fail when module name in 'require' calls has inconsistent casing", () => { - const files: Map = { + const files: MapLike = { "moduleA.ts": `import a = require("./ModuleC")`, "moduleB.ts": `import a = require("./moduleC")`, "moduleC.ts": "export var x" @@ -445,7 +445,7 @@ export = C; }); it("should fail when module names in 'require' calls has inconsistent casing and current directory has uppercase chars", () => { - const files: Map = { + const files: MapLike = { "/a/B/c/moduleA.ts": `import a = require("./ModuleC")`, "/a/B/c/moduleB.ts": `import a = require("./moduleC")`, "/a/B/c/moduleC.ts": "export var x", @@ -457,7 +457,7 @@ import b = require("./moduleB.ts"); test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "/a/B/c", /*useCaseSensitiveFileNames*/ false, ["moduleD.ts"], [1149]); }); it("should not fail when module names in 'require' calls has consistent casing and current directory has uppercase chars", () => { - const files: Map = { + const files: MapLike = { "/a/B/c/moduleA.ts": `import a = require("./moduleC")`, "/a/B/c/moduleB.ts": `import a = require("./moduleC")`, "/a/B/c/moduleC.ts": "export var x", diff --git a/src/harness/unittests/reuseProgramStructure.ts b/src/harness/unittests/reuseProgramStructure.ts index 8b2b15c15b3..76015667232 100644 --- a/src/harness/unittests/reuseProgramStructure.ts +++ b/src/harness/unittests/reuseProgramStructure.ts @@ -96,7 +96,7 @@ namespace ts { } function createTestCompilerHost(texts: NamedSourceText[], target: ScriptTarget): CompilerHost { - const files: Map = {}; + const files: MapLike = {}; for (const t of texts) { const file = createSourceFile(t.name, t.text.getFullText(), target); file.sourceText = t.text; @@ -152,7 +152,7 @@ namespace ts { return program; } - function getSizeOfMap(map: Map): number { + function getSizeOfMap(map: MapLike): number { let size = 0; for (const id in map) { if (hasProperty(map, id)) { @@ -174,7 +174,7 @@ namespace ts { assert.isTrue(expected.primary === actual.primary, `'primary': expected '${expected.primary}' to be equal to '${actual.primary}'`); } - function checkCache(caption: string, program: Program, fileName: string, expectedContent: Map, getCache: (f: SourceFile) => Map, entryChecker: (expected: T, original: T) => void): void { + function checkCache(caption: string, program: Program, fileName: string, expectedContent: MapLike, getCache: (f: SourceFile) => MapLike, entryChecker: (expected: T, original: T) => void): void { const file = program.getSourceFile(fileName); assert.isTrue(file !== undefined, `cannot find file ${fileName}`); const cache = getCache(file); @@ -203,11 +203,11 @@ namespace ts { } } - function checkResolvedModulesCache(program: Program, fileName: string, expectedContent: Map): void { + function checkResolvedModulesCache(program: Program, fileName: string, expectedContent: MapLike): void { checkCache("resolved modules", program, fileName, expectedContent, f => f.resolvedModules, checkResolvedModule); } - function checkResolvedTypeDirectivesCache(program: Program, fileName: string, expectedContent: Map): void { + function checkResolvedTypeDirectivesCache(program: Program, fileName: string, expectedContent: MapLike): void { checkCache("resolved type directives", program, fileName, expectedContent, f => f.resolvedTypeReferenceDirectiveNames, checkResolvedTypeDirective); } diff --git a/src/harness/unittests/session.ts b/src/harness/unittests/session.ts index c5285544329..8e7faa94a7a 100644 --- a/src/harness/unittests/session.ts +++ b/src/harness/unittests/session.ts @@ -362,8 +362,8 @@ namespace ts.server { class InProcClient { private server: InProcSession; private seq = 0; - private callbacks: ts.Map<(resp: protocol.Response) => void> = {}; - private eventHandlers: ts.Map<(args: any) => void> = {}; + private callbacks: ts.MapLike<(resp: protocol.Response) => void> = {}; + private eventHandlers: ts.MapLike<(args: any) => void> = {}; handle(msg: protocol.Message): void { if (msg.type === "response") { diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 7a375c50b8a..74b9e303cf7 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -68,7 +68,7 @@ namespace ts { return entry; } - function sizeOfMap(map: Map): number { + function sizeOfMap(map: MapLike): number { let n = 0; for (const name in map) { if (hasProperty(map, name)) { @@ -78,7 +78,7 @@ namespace ts { return n; } - function checkMapKeys(caption: string, map: Map, expectedKeys: string[]) { + function checkMapKeys(caption: string, map: MapLike, expectedKeys: string[]) { assert.equal(sizeOfMap(map), expectedKeys.length, `${caption}: incorrect size of map`); for (const name of expectedKeys) { assert.isTrue(hasProperty(map, name), `${caption} is expected to contain ${name}, actual keys: ${getKeys(map)}`); @@ -126,8 +126,8 @@ namespace ts { private getCanonicalFileName: (s: string) => string; private toPath: (f: string) => Path; private callbackQueue: TimeOutCallback[] = []; - readonly watchedDirectories: Map<{ cb: DirectoryWatcherCallback, recursive: boolean }[]> = {}; - readonly watchedFiles: Map = {}; + readonly watchedDirectories: MapLike<{ cb: DirectoryWatcherCallback, recursive: boolean }[]> = {}; + readonly watchedFiles: MapLike = {}; constructor(public useCaseSensitiveFileNames: boolean, private executingFilePath: string, private currentDirectory: string, fileOrFolderList: FileOrFolder[]) { this.getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); diff --git a/src/server/client.ts b/src/server/client.ts index f04dbd8dc02..3547ac52602 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -21,7 +21,7 @@ namespace ts.server { export class SessionClient implements LanguageService { private sequence: number = 0; - private lineMaps: ts.Map = {}; + private lineMaps: ts.Map = ts.createMap(); private messages: string[] = []; private lastRenameEntry: RenameEntry; diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 6e9adad7f47..a14c42f471e 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -130,7 +130,7 @@ namespace ts.server { const path = toPath(containingFile, this.host.getCurrentDirectory(), this.getCanonicalFileName); const currentResolutionsInFile = cache.get(path); - const newResolutions: Map = {}; + const newResolutions = createMap(); const resolvedModules: R[] = []; const compilerOptions = this.getCompilationSettings(); @@ -378,7 +378,7 @@ namespace ts.server { export interface ProjectOptions { // these fields can be present in the project file files?: string[]; - wildcardDirectories?: ts.Map; + wildcardDirectories?: ts.MapLike; compilerOptions?: ts.CompilerOptions; } @@ -391,7 +391,7 @@ namespace ts.server { // Used to keep track of what directories are watched for this project directoriesWatchedForTsconfig: string[] = []; program: ts.Program; - filenameToSourceFile: ts.Map = {}; + filenameToSourceFile = ts.createMap(); updateGraphSeq = 0; /** Used for configured projects which may have multiple open roots */ openRefCount = 0; @@ -504,7 +504,7 @@ namespace ts.server { return; } - this.filenameToSourceFile = {}; + this.filenameToSourceFile = createMap(); const sourceFiles = this.program.getSourceFiles(); for (let i = 0, len = sourceFiles.length; i < len; i++) { const normFilename = ts.normalizePath(sourceFiles[i].fileName); @@ -613,7 +613,7 @@ namespace ts.server { } export class ProjectService { - filenameToScriptInfo: ts.Map = {}; + filenameToScriptInfo = ts.createMap(); // open, non-configured root files openFileRoots: ScriptInfo[] = []; // projects built from openFileRoots @@ -625,12 +625,12 @@ namespace ts.server { // open files that are roots of a configured project openFileRootsConfigured: ScriptInfo[] = []; // a path to directory watcher map that detects added tsconfig files - directoryWatchersForTsconfig: ts.Map = {}; + directoryWatchersForTsconfig = ts.createMap(); // count of how many projects are using the directory watcher. If the // number becomes 0 for a watcher, then we should close it. - directoryWatchersRefCount: ts.Map = {}; + directoryWatchersRefCount = ts.createMap(); hostConfiguration: HostConfiguration; - timerForDetectingProjectFileListChanges: Map = {}; + timerForDetectingProjectFileListChanges = createMap(); constructor(public host: ServerHost, public psLogger: Logger, public eventHandler?: ProjectServiceEventHandler) { // ts.disableIncrementalParsing = true; diff --git a/src/server/session.ts b/src/server/session.ts index 7e1ca81e2af..13b0e6d6ce4 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1061,7 +1061,7 @@ namespace ts.server { return { response, responseRequired: true }; } - private handlers: Map<(request: protocol.Request) => { response?: any, responseRequired?: boolean }> = { + private handlers: MapLike<(request: protocol.Request) => { response?: any, responseRequired?: boolean }> = { [CommandNames.Exit]: () => { this.exit(); return { responseRequired: false }; diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index 3b013a4a924..05de0061d39 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -47,7 +47,7 @@ namespace ts.JsTyping { { cachedTypingPaths: string[], newTypingNames: string[], filesToWatch: string[] } { // A typing name to typing file path mapping - const inferredTypings: Map = {}; + const inferredTypings = createMap(); if (!typingOptions || !typingOptions.enableAutoDiscovery) { return { cachedTypingPaths: [], newTypingNames: [], filesToWatch: [] }; @@ -62,7 +62,7 @@ namespace ts.JsTyping { safeList = result.config; } else { - safeList = {}; + safeList = createMap(); }; } diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index f105a9fa6e2..ebd6936ea85 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -234,7 +234,7 @@ namespace ts.NavigationBar { /** Merge declarations of the same kind. */ function mergeChildren(children: NavigationBarNode[]): void { - const nameToItems: Map = {}; + const nameToItems = createMap(); filterMutate(children, child => { const decl = child.node; const name = decl.name && nodeText(decl.name); diff --git a/src/services/patternMatcher.ts b/src/services/patternMatcher.ts index 3d20337eca7..cff3f591f8a 100644 --- a/src/services/patternMatcher.ts +++ b/src/services/patternMatcher.ts @@ -113,7 +113,7 @@ namespace ts { // we see the name of a module that is used everywhere, or the name of an overload). As // such, we cache the information we compute about the candidate for the life of this // pattern matcher so we don't have to compute it multiple times. - const stringToWordSpans: Map = {}; + const stringToWordSpans = createMap(); pattern = pattern.trim(); diff --git a/src/services/services.ts b/src/services/services.ts index aea4d5f872e..850e29030d8 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -975,7 +975,7 @@ namespace ts { } private computeNamedDeclarations(): Map { - const result: Map = {}; + const result = createMap(); forEachChild(this, visit); @@ -2025,7 +2025,7 @@ namespace ts { fileName?: string; reportDiagnostics?: boolean; moduleName?: string; - renamedDependencies?: Map; + renamedDependencies?: MapLike; } export interface TranspileOutput { @@ -2243,7 +2243,7 @@ namespace ts { export function createDocumentRegistry(useCaseSensitiveFileNames?: boolean, currentDirectory = ""): DocumentRegistry { // Maps from compiler setting target (ES3, ES5, etc.) to all the cached documents we have // for those settings. - const buckets: Map> = {}; + const buckets = createMap>(); const getCanonicalFileName = createGetCanonicalFileName(!!useCaseSensitiveFileNames); function getKeyForCompilationSettings(settings: CompilerOptions): DocumentRegistryBucketKey { @@ -4102,7 +4102,7 @@ namespace ts { * do not occur at the current position and have not otherwise been typed. */ function filterNamedImportOrExportCompletionItems(exportsOfModule: Symbol[], namedImportsOrExports: ImportOrExportSpecifier[]): Symbol[] { - const existingImportsOrExports: Map = {}; + const existingImportsOrExports = createMap(); for (const element of namedImportsOrExports) { // If this is the current item we are editing right now, do not filter it out @@ -4132,7 +4132,7 @@ namespace ts { return contextualMemberSymbols; } - const existingMemberNames: Map = {}; + const existingMemberNames = createMap(); for (const m of existingMembers) { // Ignore omitted expressions for missing members if (m.kind !== SyntaxKind.PropertyAssignment && @@ -4175,7 +4175,7 @@ namespace ts { * do not occur at the current position and have not otherwise been typed. */ function filterJsxAttributes(symbols: Symbol[], attributes: NodeArray): Symbol[] { - const seenNames: Map = {}; + const seenNames = createMap(); for (const attr of attributes) { // If this is the current item we are editing right now, do not filter it out if (attr.getStart() <= position && position <= attr.getEnd()) { @@ -4317,7 +4317,7 @@ namespace ts { function getCompletionEntriesFromSymbols(symbols: Symbol[], entries: CompletionEntry[], location: Node, performCharacterChecks: boolean): Map { const start = timestamp(); - const uniqueNames: Map = {}; + const uniqueNames = createMap(); if (symbols) { for (const symbol of symbols) { const entry = createCompletionEntry(symbol, location, performCharacterChecks); @@ -5318,7 +5318,7 @@ namespace ts { return undefined; } - const fileNameToDocumentHighlights: Map = {}; + const fileNameToDocumentHighlights = createMap(); const result: DocumentHighlights[] = []; for (const referencedSymbol of referencedSymbols) { for (const referenceEntry of referencedSymbol.references) { @@ -6713,7 +6713,7 @@ namespace ts { // Add symbol of properties/methods of the same name in base classes and implemented interfaces definitions if (rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { - getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.getName(), result, /*previousIterationSymbolsCache*/ {}); + getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.getName(), result, /*previousIterationSymbolsCache*/ createMap()); } }); @@ -6834,7 +6834,7 @@ namespace ts { // see if any is in the list if (rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { const result: Symbol[] = []; - getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.getName(), result, /*previousIterationSymbolsCache*/ {}); + getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.getName(), result, /*previousIterationSymbolsCache*/ createMap()); return forEach(result, s => searchSymbols.indexOf(s) >= 0 ? s : undefined); } @@ -8318,7 +8318,7 @@ namespace ts { } function initializeNameTable(sourceFile: SourceFile): void { - const nameTable: Map = {}; + const nameTable = createMap(); walk(sourceFile); sourceFile.nameTable = nameTable; From cba2e1aacb6c8544629a4114fd4316d85fa1dcb3 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 10 Aug 2016 16:47:35 -0700 Subject: [PATCH 111/233] Update API sample --- tests/baselines/reference/APISample_watcher.js | 2 +- tests/cases/compiler/APISample_watcher.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/APISample_watcher.js b/tests/baselines/reference/APISample_watcher.js index 7573f2ba99e..cb7fc3a4c5c 100644 --- a/tests/baselines/reference/APISample_watcher.js +++ b/tests/baselines/reference/APISample_watcher.js @@ -20,7 +20,7 @@ declare var path: any; import * as ts from "typescript"; function watch(rootFileNames: string[], options: ts.CompilerOptions) { - const files: ts.Map<{ version: number }> = {}; + const files: ts.MapLike<{ version: number }> = {}; // initialize the list of files rootFileNames.forEach(fileName => { diff --git a/tests/cases/compiler/APISample_watcher.ts b/tests/cases/compiler/APISample_watcher.ts index 34baa04c850..07922bd35c7 100644 --- a/tests/cases/compiler/APISample_watcher.ts +++ b/tests/cases/compiler/APISample_watcher.ts @@ -23,7 +23,7 @@ declare var path: any; import * as ts from "typescript"; function watch(rootFileNames: string[], options: ts.CompilerOptions) { - const files: ts.Map<{ version: number }> = {}; + const files: ts.MapLike<{ version: number }> = {}; // initialize the list of files rootFileNames.forEach(fileName => { From 3ff89f794f8707e9b4a8dd8219b6b5d1f8e43149 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 10 Aug 2016 17:28:52 -0700 Subject: [PATCH 112/233] Fix processDiagnosticMessages script --- scripts/processDiagnosticMessages.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/processDiagnosticMessages.ts b/scripts/processDiagnosticMessages.ts index 26632ba6bab..431cf460180 100644 --- a/scripts/processDiagnosticMessages.ts +++ b/scripts/processDiagnosticMessages.ts @@ -69,7 +69,7 @@ function checkForUniqueCodes(messages: string[], diagnosticTable: InputDiagnosti } function buildUniqueNameMap(names: string[]): ts.Map { - var nameMap: ts.Map = {}; + var nameMap = ts.createMap(); var uniqueNames = NameGenerator.ensureUniqueness(names, /* isCaseSensitive */ false, /* isFixed */ undefined); From fa991b51750d7b6951733bf521d0f18ef888fc9e Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 10 Aug 2016 23:45:24 -0700 Subject: [PATCH 113/233] Have travis take shallow clones of the repo (#10275) Just cloning TS on travis takes 23 seconds on linux (68 seconds on mac), hopefully having it do a shallow clone will help. We don't rely on any tagging/artifacts from the travis servers which clone depth could impact, so this shouldn't impact anything other than build speed. --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 97c772f45ac..bfc07e2b510 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,3 +34,6 @@ install: cache: directories: - node_modules + +git: + depth: 1 From c9f62f33d4d87dbb4a40ba5a28bb1085c683def2 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 11 Aug 2016 09:53:38 -0700 Subject: [PATCH 114/233] Add folds to travis log (#10269) --- Gulpfile.ts | 3 +++ Jakefile.js | 27 +++++++++++++++++++++++++-- package.json | 3 ++- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/Gulpfile.ts b/Gulpfile.ts index b9b5d2068b2..d677b042250 100644 --- a/Gulpfile.ts +++ b/Gulpfile.ts @@ -35,6 +35,7 @@ import merge2 = require("merge2"); import intoStream = require("into-stream"); import * as os from "os"; import Linter = require("tslint"); +import fold = require("travis-fold"); const gulp = helpMaker(originalGulp); const mochaParallel = require("./scripts/mocha-parallel.js"); const {runTestsInParallel} = mochaParallel; @@ -964,6 +965,7 @@ gulp.task("lint", "Runs tslint on the compiler sources. Optional arguments are: const fileMatcher = RegExp(cmdLineOptions["files"]); const lintOptions = getLinterOptions(); let failed = 0; + if (fold.isTravis()) console.log(fold.start("lint")); return gulp.src(lintTargets) .pipe(insert.transform((contents, file) => { if (!fileMatcher.test(file.path)) return contents; @@ -975,6 +977,7 @@ gulp.task("lint", "Runs tslint on the compiler sources. Optional arguments are: return contents; // TODO (weswig): Automatically apply fixes? :3 })) .on("end", () => { + if (fold.isTravis()) console.log(fold.end("lint")); if (failed > 0) { console.error("Linter errors."); process.exit(1); diff --git a/Jakefile.js b/Jakefile.js index a5650a56b16..0624c92f162 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -5,6 +5,7 @@ var os = require("os"); var path = require("path"); var child_process = require("child_process"); var Linter = require("tslint"); +var fold = require("travis-fold"); var runTestsInParallel = require("./scripts/mocha-parallel").runTestsInParallel; // Variables @@ -560,9 +561,19 @@ compileFile( desc("Builds language service server library"); task("lssl", [tsserverLibraryFile, tsserverLibraryDefinitionFile]); +desc("Emit the start of the build fold"); +task("build-fold-start", [] , function() { + if (fold.isTravis()) console.log(fold.start("build")); +}); + +desc("Emit the end of the build fold"); +task("build-fold-end", [] , function() { + if (fold.isTravis()) console.log(fold.end("build")); +}); + // Local target to build the compiler and services desc("Builds the full compiler and services"); -task("local", ["generate-diagnostics", "lib", tscFile, servicesFile, nodeDefinitionsFile, serverFile, builtGeneratedDiagnosticMessagesJSON, "lssl"]); +task("local", ["build-fold-start", "generate-diagnostics", "lib", tscFile, servicesFile, nodeDefinitionsFile, serverFile, builtGeneratedDiagnosticMessagesJSON, "lssl", "build-fold-end"]); // Local target to build only tsc.js desc("Builds only the compiler"); @@ -998,12 +1009,22 @@ var tslintRulesOutFiles = tslintRules.map(function(p) { return path.join(builtLocalDirectory, "tslint", p + ".js"); }); desc("Compiles tslint rules to js"); -task("build-rules", tslintRulesOutFiles); +task("build-rules", ["build-rules-start"].concat(tslintRulesOutFiles).concat(["build-rules-end"])); tslintRulesFiles.forEach(function(ruleFile, i) { compileFile(tslintRulesOutFiles[i], [ruleFile], [ruleFile], [], /*useBuiltCompiler*/ false, { noOutFile: true, generateDeclarations: false, outDir: path.join(builtLocalDirectory, "tslint")}); }); +desc("Emit the start of the build-rules fold"); +task("build-rules-start", [] , function() { + if (fold.isTravis()) console.log(fold.start("build-rules")); +}); + +desc("Emit the end of the build-rules fold"); +task("build-rules-end", [] , function() { + if (fold.isTravis()) console.log(fold.end("build-rules")); +}); + function getLinterOptions() { return { configuration: require("./tslint.json"), @@ -1047,6 +1068,7 @@ var lintTargets = compilerSources desc("Runs tslint on the compiler sources. Optional arguments are: f[iles]=regex"); task("lint", ["build-rules"], function() { + if (fold.isTravis()) console.log(fold.start("lint")); var lintOptions = getLinterOptions(); var failed = 0; var fileMatcher = RegExp(process.env.f || process.env.file || process.env.files || ""); @@ -1062,6 +1084,7 @@ task("lint", ["build-rules"], function() { done[target] = true; } } + if (fold.isTravis()) console.log(fold.end("lint")); if (failed > 0) { fail('Linter errors.', failed); } diff --git a/package.json b/package.json index 9f3122c3374..25d8f0c42ea 100644 --- a/package.json +++ b/package.json @@ -30,8 +30,8 @@ }, "devDependencies": { "@types/browserify": "latest", - "@types/convert-source-map": "latest", "@types/chai": "latest", + "@types/convert-source-map": "latest", "@types/del": "latest", "@types/glob": "latest", "@types/gulp": "latest", @@ -72,6 +72,7 @@ "run-sequence": "latest", "sorcery": "latest", "through2": "latest", + "travis-fold": "latest", "ts-node": "latest", "tslint": "next", "typescript": "next" From 24d8d848f1b85b627482e8a56e50f398b1520cfd Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 11 Aug 2016 10:23:27 -0700 Subject: [PATCH 115/233] Optimize filterType to only call getUnionType if necessary --- src/compiler/checker.ts | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 43ec22df403..2ae238b9175 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8172,9 +8172,26 @@ namespace ts { } function filterType(type: Type, f: (t: Type) => boolean): Type { - return type.flags & TypeFlags.Union ? - getUnionType(filter((type).types, f)) : - f(type) ? type : neverType; + if (!(type.flags & TypeFlags.Union)) { + return f(type) ? type : neverType; + } + let types = (type).types; + let length = types.length; + let i = 0; + while (i < length && f(types[i])) i++; + if (i === length) { + return type; + } + let filtered = types.slice(0, i); + i++; + while (i < length) { + const t = types[i]; + if (f(t)) { + filtered.push(t); + } + i++; + } + return getUnionType(filtered); } function isIncomplete(flowType: FlowType) { From 9ac13abfd196f662ce606d8d99cb8791c2ad61fe Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 11 Aug 2016 11:58:23 -0700 Subject: [PATCH 116/233] Add shorthand types declaration for travis-fold (#10293) --- scripts/types/ambient.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/types/ambient.d.ts b/scripts/types/ambient.d.ts index e77e3fe8c5a..e83489801d7 100644 --- a/scripts/types/ambient.d.ts +++ b/scripts/types/ambient.d.ts @@ -22,3 +22,4 @@ declare module "into-stream" { } declare module "sorcery"; +declare module "travis-fold"; From a3845a95d56cd190fcc0a3fa359aaad37cbb4c74 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 11 Aug 2016 13:44:51 -0700 Subject: [PATCH 117/233] Optimize getTypeWithFacts --- src/compiler/checker.ts | 49 +++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2ae238b9175..e4f650691d5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7924,6 +7924,14 @@ namespace ts { return result; } + function isFunctionObjectType(type: ObjectType): boolean { + // We do a quick check for a "bind" property before performing the more expensive subtype + // check. This gives us a quicker out in the common case where an object type is not a function. + const resolved = resolveStructuredTypeMembers(type); + return !!(resolved.callSignatures.length || resolved.constructSignatures.length || + hasProperty(resolved.members, "bind") && isTypeSubtypeOf(type, globalFunctionType)); + } + function getTypeFacts(type: Type): TypeFacts { const flags = type.flags; if (flags & TypeFlags.String) { @@ -7952,8 +7960,7 @@ namespace ts { type === falseType ? TypeFacts.FalseFacts : TypeFacts.TrueFacts; } if (flags & TypeFlags.ObjectType) { - const resolved = resolveStructuredTypeMembers(type); - return resolved.callSignatures.length || resolved.constructSignatures.length || isTypeSubtypeOf(type, globalFunctionType) ? + return isFunctionObjectType(type) ? strictNullChecks ? TypeFacts.FunctionStrictFacts : TypeFacts.FunctionFacts : strictNullChecks ? TypeFacts.ObjectStrictFacts : TypeFacts.ObjectFacts; } @@ -7980,23 +7987,23 @@ namespace ts { if (!(type.flags & TypeFlags.Union)) { return getTypeFacts(type) & include ? type : neverType; } - let firstType: Type; - let types: Type[]; - for (const t of (type as UnionType).types) { - if (getTypeFacts(t) & include) { - if (!firstType) { - firstType = t; - } - else { - if (!types) { - types = [firstType]; - } - types.push(t); - } - } + const types = (type).types; + const length = types.length; + let i = 0; + while (i < length && getTypeFacts(types[i]) & include) i++; + if (i === length) { + return type; } - return types ? getUnionType(types) : - firstType ? firstType : neverType; + const filtered = types.slice(0, i); + i++; + while (i < length) { + const t = types[i]; + if (getTypeFacts(t) & include) { + filtered.push(t); + } + i++; + } + return getUnionType(filtered); } function getTypeWithDefault(type: Type, defaultExpression: Expression) { @@ -8175,14 +8182,14 @@ namespace ts { if (!(type.flags & TypeFlags.Union)) { return f(type) ? type : neverType; } - let types = (type).types; - let length = types.length; + const types = (type).types; + const length = types.length; let i = 0; while (i < length && f(types[i])) i++; if (i === length) { return type; } - let filtered = types.slice(0, i); + const filtered = types.slice(0, i); i++; while (i < length) { const t = types[i]; From c22a54fb9507d25ca0b81f1ff82de4b26154cb9a Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 11 Aug 2016 13:46:06 -0700 Subject: [PATCH 118/233] Filter out nullable and primitive types in isDiscriminantProperty --- src/compiler/checker.ts | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e4f650691d5..0ba44b34f89 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -245,7 +245,8 @@ namespace ts { NEUndefinedOrNull = 1 << 19, // x != undefined / x != null Truthy = 1 << 20, // x Falsy = 1 << 21, // !x - All = (1 << 22) - 1, + Discriminatable = 1 << 22, // May have discriminant property + All = (1 << 23) - 1, // The following members encode facts about particular kinds of types for use in the getTypeFacts function. // The presence of a particular fact means that the given test is true for some (and possibly all) values // of that kind of type. @@ -275,9 +276,9 @@ namespace ts { TrueFacts = BaseBooleanFacts | Truthy, SymbolStrictFacts = TypeofEQSymbol | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy, SymbolFacts = SymbolStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, - ObjectStrictFacts = TypeofEQObject | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy, + ObjectStrictFacts = TypeofEQObject | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy | Discriminatable, ObjectFacts = ObjectStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, - FunctionStrictFacts = TypeofEQFunction | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy, + FunctionStrictFacts = TypeofEQFunction | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy | Discriminatable, FunctionFacts = FunctionStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, UndefinedFacts = TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy, NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy, @@ -7848,18 +7849,25 @@ namespace ts { } function isDiscriminantProperty(type: Type, name: string) { - if (type) { - const nonNullType = getNonNullableType(type); - if (nonNullType.flags & TypeFlags.Union) { - const prop = getPropertyOfType(nonNullType, name); - if (prop && prop.flags & SymbolFlags.SyntheticProperty) { - if ((prop).isDiscriminantProperty === undefined) { - (prop).isDiscriminantProperty = !(prop).hasCommonType && - isUnitUnionType(getTypeOfSymbol(prop)); - } - return (prop).isDiscriminantProperty; + if (type && type.flags & TypeFlags.Union) { + let prop = getPropertyOfType(type, name); + if (!prop) { + // The type may be a union that includes nullable or primtive types. If filtering + // those out produces a different type, get the property from that type instead. + // Effectively, we're checking if this *could* be a discriminant property once nullable + // and primitive types are removed by other type guards. + const filteredType = getTypeWithFacts(type, TypeFacts.Discriminatable); + if (filteredType !== type && filteredType.flags & TypeFlags.Union) { + prop = getPropertyOfType(type, name); } } + if (prop && prop.flags & SymbolFlags.SyntheticProperty) { + if ((prop).isDiscriminantProperty === undefined) { + (prop).isDiscriminantProperty = !(prop).hasCommonType && + isUnitUnionType(getTypeOfSymbol(prop)); + } + return (prop).isDiscriminantProperty; + } } return false; } From b336c693eed9745efdabb0c7736a81080d481eb0 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 11 Aug 2016 14:38:01 -0700 Subject: [PATCH 119/233] Fix typo --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0ba44b34f89..b6d03737810 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7858,7 +7858,7 @@ namespace ts { // and primitive types are removed by other type guards. const filteredType = getTypeWithFacts(type, TypeFacts.Discriminatable); if (filteredType !== type && filteredType.flags & TypeFlags.Union) { - prop = getPropertyOfType(type, name); + prop = getPropertyOfType(filteredType, name); } } if (prop && prop.flags & SymbolFlags.SyntheticProperty) { From 29ae2b2cf153ef7d28a50053c85e637ea2d02915 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 11 Aug 2016 14:38:17 -0700 Subject: [PATCH 120/233] Add regression tests --- .../reference/discriminantsAndPrimitives.js | 84 +++++++++++ .../discriminantsAndPrimitives.symbols | 117 +++++++++++++++ .../discriminantsAndPrimitives.types | 141 ++++++++++++++++++ .../compiler/discriminantsAndPrimitives.ts | 49 ++++++ 4 files changed, 391 insertions(+) create mode 100644 tests/baselines/reference/discriminantsAndPrimitives.js create mode 100644 tests/baselines/reference/discriminantsAndPrimitives.symbols create mode 100644 tests/baselines/reference/discriminantsAndPrimitives.types create mode 100644 tests/cases/compiler/discriminantsAndPrimitives.ts diff --git a/tests/baselines/reference/discriminantsAndPrimitives.js b/tests/baselines/reference/discriminantsAndPrimitives.js new file mode 100644 index 00000000000..1d11781a707 --- /dev/null +++ b/tests/baselines/reference/discriminantsAndPrimitives.js @@ -0,0 +1,84 @@ +//// [discriminantsAndPrimitives.ts] + +// Repro from #10257 plus other tests + +interface Foo { + kind: "foo"; + name: string; +} + +interface Bar { + kind: "bar"; + length: string; +} + +function f1(x: Foo | Bar | string) { + if (typeof x !== 'string') { + switch(x.kind) { + case 'foo': + x.name; + } + } +} + +function f2(x: Foo | Bar | string | undefined) { + if (typeof x === "object") { + switch(x.kind) { + case 'foo': + x.name; + } + } +} + +function f3(x: Foo | Bar | string | null) { + if (x && typeof x !== "string") { + switch(x.kind) { + case 'foo': + x.name; + } + } +} + +function f4(x: Foo | Bar | string | number | null) { + if (x && typeof x === "object") { + switch(x.kind) { + case 'foo': + x.name; + } + } +} + +//// [discriminantsAndPrimitives.js] +// Repro from #10257 plus other tests +function f1(x) { + if (typeof x !== 'string') { + switch (x.kind) { + case 'foo': + x.name; + } + } +} +function f2(x) { + if (typeof x === "object") { + switch (x.kind) { + case 'foo': + x.name; + } + } +} +function f3(x) { + if (x && typeof x !== "string") { + switch (x.kind) { + case 'foo': + x.name; + } + } +} +function f4(x) { + if (x && typeof x === "object") { + switch (x.kind) { + case 'foo': + x.name; + } + } +} diff --git a/tests/baselines/reference/discriminantsAndPrimitives.symbols b/tests/baselines/reference/discriminantsAndPrimitives.symbols new file mode 100644 index 00000000000..c84f32cd990 --- /dev/null +++ b/tests/baselines/reference/discriminantsAndPrimitives.symbols @@ -0,0 +1,117 @@ +=== tests/cases/compiler/discriminantsAndPrimitives.ts === + +// Repro from #10257 plus other tests + +interface Foo { +>Foo : Symbol(Foo, Decl(discriminantsAndPrimitives.ts, 0, 0)) + + kind: "foo"; +>kind : Symbol(Foo.kind, Decl(discriminantsAndPrimitives.ts, 3, 15)) + + name: string; +>name : Symbol(Foo.name, Decl(discriminantsAndPrimitives.ts, 4, 16)) +} + +interface Bar { +>Bar : Symbol(Bar, Decl(discriminantsAndPrimitives.ts, 6, 1)) + + kind: "bar"; +>kind : Symbol(Bar.kind, Decl(discriminantsAndPrimitives.ts, 8, 15)) + + length: string; +>length : Symbol(Bar.length, Decl(discriminantsAndPrimitives.ts, 9, 16)) +} + +function f1(x: Foo | Bar | string) { +>f1 : Symbol(f1, Decl(discriminantsAndPrimitives.ts, 11, 1)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 13, 12)) +>Foo : Symbol(Foo, Decl(discriminantsAndPrimitives.ts, 0, 0)) +>Bar : Symbol(Bar, Decl(discriminantsAndPrimitives.ts, 6, 1)) + + if (typeof x !== 'string') { +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 13, 12)) + + switch(x.kind) { +>x.kind : Symbol(kind, Decl(discriminantsAndPrimitives.ts, 3, 15), Decl(discriminantsAndPrimitives.ts, 8, 15)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 13, 12)) +>kind : Symbol(kind, Decl(discriminantsAndPrimitives.ts, 3, 15), Decl(discriminantsAndPrimitives.ts, 8, 15)) + + case 'foo': + x.name; +>x.name : Symbol(Foo.name, Decl(discriminantsAndPrimitives.ts, 4, 16)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 13, 12)) +>name : Symbol(Foo.name, Decl(discriminantsAndPrimitives.ts, 4, 16)) + } + } +} + +function f2(x: Foo | Bar | string | undefined) { +>f2 : Symbol(f2, Decl(discriminantsAndPrimitives.ts, 20, 1)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 22, 12)) +>Foo : Symbol(Foo, Decl(discriminantsAndPrimitives.ts, 0, 0)) +>Bar : Symbol(Bar, Decl(discriminantsAndPrimitives.ts, 6, 1)) + + if (typeof x === "object") { +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 22, 12)) + + switch(x.kind) { +>x.kind : Symbol(kind, Decl(discriminantsAndPrimitives.ts, 3, 15), Decl(discriminantsAndPrimitives.ts, 8, 15)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 22, 12)) +>kind : Symbol(kind, Decl(discriminantsAndPrimitives.ts, 3, 15), Decl(discriminantsAndPrimitives.ts, 8, 15)) + + case 'foo': + x.name; +>x.name : Symbol(Foo.name, Decl(discriminantsAndPrimitives.ts, 4, 16)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 22, 12)) +>name : Symbol(Foo.name, Decl(discriminantsAndPrimitives.ts, 4, 16)) + } + } +} + +function f3(x: Foo | Bar | string | null) { +>f3 : Symbol(f3, Decl(discriminantsAndPrimitives.ts, 29, 1)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 31, 12)) +>Foo : Symbol(Foo, Decl(discriminantsAndPrimitives.ts, 0, 0)) +>Bar : Symbol(Bar, Decl(discriminantsAndPrimitives.ts, 6, 1)) + + if (x && typeof x !== "string") { +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 31, 12)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 31, 12)) + + switch(x.kind) { +>x.kind : Symbol(kind, Decl(discriminantsAndPrimitives.ts, 3, 15), Decl(discriminantsAndPrimitives.ts, 8, 15)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 31, 12)) +>kind : Symbol(kind, Decl(discriminantsAndPrimitives.ts, 3, 15), Decl(discriminantsAndPrimitives.ts, 8, 15)) + + case 'foo': + x.name; +>x.name : Symbol(Foo.name, Decl(discriminantsAndPrimitives.ts, 4, 16)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 31, 12)) +>name : Symbol(Foo.name, Decl(discriminantsAndPrimitives.ts, 4, 16)) + } + } +} + +function f4(x: Foo | Bar | string | number | null) { +>f4 : Symbol(f4, Decl(discriminantsAndPrimitives.ts, 38, 1)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 40, 12)) +>Foo : Symbol(Foo, Decl(discriminantsAndPrimitives.ts, 0, 0)) +>Bar : Symbol(Bar, Decl(discriminantsAndPrimitives.ts, 6, 1)) + + if (x && typeof x === "object") { +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 40, 12)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 40, 12)) + + switch(x.kind) { +>x.kind : Symbol(kind, Decl(discriminantsAndPrimitives.ts, 3, 15), Decl(discriminantsAndPrimitives.ts, 8, 15)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 40, 12)) +>kind : Symbol(kind, Decl(discriminantsAndPrimitives.ts, 3, 15), Decl(discriminantsAndPrimitives.ts, 8, 15)) + + case 'foo': + x.name; +>x.name : Symbol(Foo.name, Decl(discriminantsAndPrimitives.ts, 4, 16)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 40, 12)) +>name : Symbol(Foo.name, Decl(discriminantsAndPrimitives.ts, 4, 16)) + } + } +} diff --git a/tests/baselines/reference/discriminantsAndPrimitives.types b/tests/baselines/reference/discriminantsAndPrimitives.types new file mode 100644 index 00000000000..d3e1b70e599 --- /dev/null +++ b/tests/baselines/reference/discriminantsAndPrimitives.types @@ -0,0 +1,141 @@ +=== tests/cases/compiler/discriminantsAndPrimitives.ts === + +// Repro from #10257 plus other tests + +interface Foo { +>Foo : Foo + + kind: "foo"; +>kind : "foo" + + name: string; +>name : string +} + +interface Bar { +>Bar : Bar + + kind: "bar"; +>kind : "bar" + + length: string; +>length : string +} + +function f1(x: Foo | Bar | string) { +>f1 : (x: string | Foo | Bar) => void +>x : string | Foo | Bar +>Foo : Foo +>Bar : Bar + + if (typeof x !== 'string') { +>typeof x !== 'string' : boolean +>typeof x : string +>x : string | Foo | Bar +>'string' : "string" + + switch(x.kind) { +>x.kind : "foo" | "bar" +>x : Foo | Bar +>kind : "foo" | "bar" + + case 'foo': +>'foo' : "foo" + + x.name; +>x.name : string +>x : Foo +>name : string + } + } +} + +function f2(x: Foo | Bar | string | undefined) { +>f2 : (x: string | Foo | Bar | undefined) => void +>x : string | Foo | Bar | undefined +>Foo : Foo +>Bar : Bar + + if (typeof x === "object") { +>typeof x === "object" : boolean +>typeof x : string +>x : string | Foo | Bar | undefined +>"object" : "object" + + switch(x.kind) { +>x.kind : "foo" | "bar" +>x : Foo | Bar +>kind : "foo" | "bar" + + case 'foo': +>'foo' : "foo" + + x.name; +>x.name : string +>x : Foo +>name : string + } + } +} + +function f3(x: Foo | Bar | string | null) { +>f3 : (x: string | Foo | Bar | null) => void +>x : string | Foo | Bar | null +>Foo : Foo +>Bar : Bar +>null : null + + if (x && typeof x !== "string") { +>x && typeof x !== "string" : boolean | "" | null +>x : string | Foo | Bar | null +>typeof x !== "string" : boolean +>typeof x : string +>x : string | Foo | Bar +>"string" : "string" + + switch(x.kind) { +>x.kind : "foo" | "bar" +>x : Foo | Bar +>kind : "foo" | "bar" + + case 'foo': +>'foo' : "foo" + + x.name; +>x.name : string +>x : Foo +>name : string + } + } +} + +function f4(x: Foo | Bar | string | number | null) { +>f4 : (x: string | number | Foo | Bar | null) => void +>x : string | number | Foo | Bar | null +>Foo : Foo +>Bar : Bar +>null : null + + if (x && typeof x === "object") { +>x && typeof x === "object" : boolean | "" | 0 | null +>x : string | number | Foo | Bar | null +>typeof x === "object" : boolean +>typeof x : string +>x : string | number | Foo | Bar +>"object" : "object" + + switch(x.kind) { +>x.kind : "foo" | "bar" +>x : Foo | Bar +>kind : "foo" | "bar" + + case 'foo': +>'foo' : "foo" + + x.name; +>x.name : string +>x : Foo +>name : string + } + } +} diff --git a/tests/cases/compiler/discriminantsAndPrimitives.ts b/tests/cases/compiler/discriminantsAndPrimitives.ts new file mode 100644 index 00000000000..6352d741808 --- /dev/null +++ b/tests/cases/compiler/discriminantsAndPrimitives.ts @@ -0,0 +1,49 @@ +// @strictNullChecks: true + +// Repro from #10257 plus other tests + +interface Foo { + kind: "foo"; + name: string; +} + +interface Bar { + kind: "bar"; + length: string; +} + +function f1(x: Foo | Bar | string) { + if (typeof x !== 'string') { + switch(x.kind) { + case 'foo': + x.name; + } + } +} + +function f2(x: Foo | Bar | string | undefined) { + if (typeof x === "object") { + switch(x.kind) { + case 'foo': + x.name; + } + } +} + +function f3(x: Foo | Bar | string | null) { + if (x && typeof x !== "string") { + switch(x.kind) { + case 'foo': + x.name; + } + } +} + +function f4(x: Foo | Bar | string | number | null) { + if (x && typeof x === "object") { + switch(x.kind) { + case 'foo': + x.name; + } + } +} \ No newline at end of file From 644d6554fb3d4d9d2afb3ea494d695a303ae7712 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 11 Aug 2016 16:04:46 -0700 Subject: [PATCH 121/233] Optimize core filter function to only allocate when necessary --- src/compiler/core.ts | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 549410159b0..1d89d0092f3 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -147,17 +147,29 @@ namespace ts { return count; } + /** + * Filters an array by a predicate function. Returns the same array instance if the predicate is + * true for all elements, otherwise returns a new array instance containing the filtered subset. + */ export function filter(array: T[], f: (x: T) => boolean): T[] { - let result: T[]; if (array) { - result = []; - for (const item of array) { - if (f(item)) { - result.push(item); + const len = array.length; + let i = 0; + while (i < len && f(array[i])) i++; + if (i < len) { + const result = array.slice(0, i); + i++; + while (i < len) { + const item = array[i]; + if (f(item)) { + result.push(item); + } + i++; } + return result; } } - return result; + return array; } export function filterMutate(array: T[], f: (x: T) => boolean): void { From f1ea145e052cea7af3a9cbdf42f07d4f2728cb45 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 11 Aug 2016 16:06:03 -0700 Subject: [PATCH 122/233] Address CR comments + more optimizations --- src/compiler/checker.ts | 57 +++++++++-------------------------------- 1 file changed, 12 insertions(+), 45 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b6d03737810..0fb626e6281 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7852,7 +7852,7 @@ namespace ts { if (type && type.flags & TypeFlags.Union) { let prop = getPropertyOfType(type, name); if (!prop) { - // The type may be a union that includes nullable or primtive types. If filtering + // The type may be a union that includes nullable or primitive types. If filtering // those out produces a different type, get the property from that type instead. // Effectively, we're checking if this *could* be a discriminant property once nullable // and primitive types are removed by other type guards. @@ -7915,10 +7915,10 @@ namespace ts { // For example, when a variable of type number | string | boolean is assigned a value of type number | boolean, // we remove type string. function getAssignmentReducedType(declaredType: UnionType, assignedType: Type) { - if (declaredType !== assignedType && declaredType.flags & TypeFlags.Union) { - const reducedTypes = filter(declaredType.types, t => typeMaybeAssignableTo(assignedType, t)); - if (reducedTypes.length) { - return reducedTypes.length === 1 ? reducedTypes[0] : getUnionType(reducedTypes); + if (declaredType !== assignedType) { + const reducedType = filterType(declaredType, t => typeMaybeAssignableTo(assignedType, t)); + if (reducedType !== neverType) { + return reducedType; } } return declaredType; @@ -7992,26 +7992,7 @@ namespace ts { } function getTypeWithFacts(type: Type, include: TypeFacts) { - if (!(type.flags & TypeFlags.Union)) { - return getTypeFacts(type) & include ? type : neverType; - } - const types = (type).types; - const length = types.length; - let i = 0; - while (i < length && getTypeFacts(types[i]) & include) i++; - if (i === length) { - return type; - } - const filtered = types.slice(0, i); - i++; - while (i < length) { - const t = types[i]; - if (getTypeFacts(t) & include) { - filtered.push(t); - } - i++; - } - return getUnionType(filtered); + return filterType(type, t => (getTypeFacts(t) & include) !== 0); } function getTypeWithDefault(type: Type, defaultExpression: Expression) { @@ -8191,22 +8172,8 @@ namespace ts { return f(type) ? type : neverType; } const types = (type).types; - const length = types.length; - let i = 0; - while (i < length && f(types[i])) i++; - if (i === length) { - return type; - } - const filtered = types.slice(0, i); - i++; - while (i < length) { - const t = types[i]; - if (f(t)) { - filtered.push(t); - } - i++; - } - return getUnionType(filtered); + const filtered = filter(types, f); + return filtered === types ? type : getUnionType(filtered); } function isIncomplete(flowType: FlowType) { @@ -8544,7 +8511,7 @@ namespace ts { } if (assumeTrue && !(type.flags & TypeFlags.Union)) { // We narrow a non-union type to an exact primitive type if the non-union type - // is a supertype of that primtive type. For example, type 'any' can be narrowed + // is a supertype of that primitive type. For example, type 'any' can be narrowed // to one of the primitive types. const targetType = getProperty(typeofTypesByName, literal.text); if (targetType && isTypeSubtypeOf(targetType, type)) { @@ -8633,9 +8600,9 @@ namespace ts { // If the current type is a union type, remove all constituents that couldn't be instances of // the candidate type. If one or more constituents remain, return a union of those. if (type.flags & TypeFlags.Union) { - const assignableConstituents = filter((type).types, t => isTypeInstanceOf(t, candidate)); - if (assignableConstituents.length) { - return getUnionType(assignableConstituents); + const assignableType = filterType(type, t => isTypeInstanceOf(t, candidate)); + if (assignableType !== neverType) { + return assignableType; } } // If the candidate type is a subtype of the target type, narrow to the candidate type. From e64675eb85ad9bf346e0cd7495cb98e258be221d Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 12 Aug 2016 06:54:51 -0700 Subject: [PATCH 123/233] Faster path for creating union types from filterType --- src/compiler/checker.ts | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0fb626e6281..4e04c5fc11a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5352,12 +5352,12 @@ namespace ts { } } - // We deduplicate the constituent types based on object identity. If the subtypeReduction flag is - // specified we also reduce the constituent type set to only include types that aren't subtypes of - // other types. Subtype reduction is expensive for large union types and is possible only when union + // We sort and deduplicate the constituent types based on object identity. If the subtypeReduction + // flag is specified we also reduce the constituent type set to only include types that aren't subtypes + // of other types. Subtype reduction is expensive for large union types and is possible only when union // types are known not to circularly reference themselves (as is the case with union types created by // expression constructs such as array literals and the || and ?: operators). Named types can - // circularly reference themselves and therefore cannot be deduplicated during their declaration. + // circularly reference themselves and therefore cannot be subtype reduced during their declaration. // For example, "type Item = string | (() => Item" is a named type that circularly references itself. function getUnionType(types: Type[], subtypeReduction?: boolean, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type { if (types.length === 0) { @@ -5379,15 +5379,23 @@ namespace ts { typeSet.containsUndefined ? typeSet.containsNonWideningType ? undefinedType : undefinedWideningType : neverType; } - else if (typeSet.length === 1) { - return typeSet[0]; + return getUnionTypeFromSortedList(typeSet, aliasSymbol, aliasTypeArguments); + } + + // This function assumes the constituent type list is sorted and deduplicated. + function getUnionTypeFromSortedList(types: Type[], aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type { + if (types.length === 0) { + return neverType; } - const id = getTypeListId(typeSet); + if (types.length === 1) { + return types[0]; + } + const id = getTypeListId(types); let type = unionTypes[id]; if (!type) { - const propagatedFlags = getPropagatingFlagsOfTypes(typeSet, /*excludeKinds*/ TypeFlags.Nullable); + const propagatedFlags = getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable); type = unionTypes[id] = createObjectType(TypeFlags.Union | propagatedFlags); - type.types = typeSet; + type.types = types; type.aliasSymbol = aliasSymbol; type.aliasTypeArguments = aliasTypeArguments; } @@ -8168,12 +8176,12 @@ namespace ts { } function filterType(type: Type, f: (t: Type) => boolean): Type { - if (!(type.flags & TypeFlags.Union)) { - return f(type) ? type : neverType; + if (type.flags & TypeFlags.Union) { + const types = (type).types; + const filtered = filter(types, f); + return filtered === types ? type : getUnionTypeFromSortedList(filtered); } - const types = (type).types; - const filtered = filter(types, f); - return filtered === types ? type : getUnionType(filtered); + return f(type) ? type : neverType; } function isIncomplete(flowType: FlowType) { From df739fdd50b3ea3fcd8a5bd04614fe2093a4aa39 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 11 Aug 2016 12:26:22 -0700 Subject: [PATCH 124/233] Allow an @types direcotry to have a package.json which specifies `"typings": null` to disclude it from automatically included typings. --- src/compiler/program.ts | 89 ++++++++++--------- src/harness/compilerRunner.ts | 2 +- src/harness/fourslash.ts | 19 ++-- tests/baselines/reference/typingsLookup2.js | 9 ++ .../reference/typingsLookup2.symbols | 3 + .../reference/typingsLookup2.trace.json | 1 + .../baselines/reference/typingsLookup2.types | 3 + .../conformance/typings/typingsLookup2.ts | 13 +++ 8 files changed, 86 insertions(+), 53 deletions(-) create mode 100644 tests/baselines/reference/typingsLookup2.js create mode 100644 tests/baselines/reference/typingsLookup2.symbols create mode 100644 tests/baselines/reference/typingsLookup2.trace.json create mode 100644 tests/baselines/reference/typingsLookup2.types create mode 100644 tests/cases/conformance/typings/typingsLookup2.ts diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 7d40b2f3219..67a93606cc6 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -119,49 +119,31 @@ namespace ts { } function tryReadTypesSection(packageJsonPath: string, baseDirectory: string, state: ModuleResolutionState): string { - let jsonContent: { typings?: string, types?: string, main?: string }; - try { - const jsonText = state.host.readFile(packageJsonPath); - jsonContent = jsonText ? <{ typings?: string, types?: string, main?: string }>JSON.parse(jsonText) : {}; - } - catch (e) { - // gracefully handle if readFile fails or returns not JSON - jsonContent = {}; + const jsonContent = readJson(packageJsonPath, state.host); + + function tryReadFromField(fieldName: string) { + if (hasProperty(jsonContent, fieldName)) { + const typesFile = (jsonContent)[fieldName]; + if (typeof typesFile === "string") { + const typesFilePath = normalizePath(combinePaths(baseDirectory, typesFile)); + if (state.traceEnabled) { + trace(state.host, Diagnostics.package_json_has_0_field_1_that_references_2, fieldName, typesFile, typesFilePath); + } + return typesFilePath; + } + else { + if (state.traceEnabled) { + trace(state.host, Diagnostics.Expected_type_of_0_field_in_package_json_to_be_string_got_1, fieldName, typeof typesFile); + } + } + } } - let typesFile: string; - let fieldName: string; - // first try to read content of 'typings' section (backward compatibility) - if (jsonContent.typings) { - if (typeof jsonContent.typings === "string") { - fieldName = "typings"; - typesFile = jsonContent.typings; - } - else { - if (state.traceEnabled) { - trace(state.host, Diagnostics.Expected_type_of_0_field_in_package_json_to_be_string_got_1, "typings", typeof jsonContent.typings); - } - } - } - // then read 'types' - if (!typesFile && jsonContent.types) { - if (typeof jsonContent.types === "string") { - fieldName = "types"; - typesFile = jsonContent.types; - } - else { - if (state.traceEnabled) { - trace(state.host, Diagnostics.Expected_type_of_0_field_in_package_json_to_be_string_got_1, "types", typeof jsonContent.types); - } - } - } - if (typesFile) { - const typesFilePath = normalizePath(combinePaths(baseDirectory, typesFile)); - if (state.traceEnabled) { - trace(state.host, Diagnostics.package_json_has_0_field_1_that_references_2, fieldName, typesFile, typesFilePath); - } + const typesFilePath = tryReadFromField("typings") || tryReadFromField("types"); + if (typesFilePath) { return typesFilePath; } + // Use the main module for inferring types if no types package specified and the allowJs is set if (state.compilerOptions.allowJs && jsonContent.main && typeof jsonContent.main === "string") { if (state.traceEnabled) { @@ -173,6 +155,17 @@ namespace ts { return undefined; } + function readJson(path: string, host: ModuleResolutionHost): { typings?: string, types?: string, main?: string } { + try { + const jsonText = host.readFile(path); + return jsonText ? JSON.parse(jsonText) : {}; + } + catch (e) { + // gracefully handle if readFile fails or returns not JSON + return {}; + } + } + const typeReferenceExtensions = [".d.ts"]; function getEffectiveTypeRoots(options: CompilerOptions, host: ModuleResolutionHost) { @@ -717,7 +710,7 @@ namespace ts { } function loadNodeModuleFromDirectory(extensions: string[], candidate: string, failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string { - const packageJsonPath = combinePaths(candidate, "package.json"); + const packageJsonPath = pathToPackageJson(candidate); const directoryExists = !onlyRecordFailures && directoryProbablyExists(candidate, state.host); if (directoryExists && state.host.fileExists(packageJsonPath)) { if (state.traceEnabled) { @@ -747,6 +740,10 @@ namespace ts { return loadModuleFromFile(combinePaths(candidate, "index"), extensions, failedLookupLocation, !directoryExists, state); } + function pathToPackageJson(directory: string): string { + return combinePaths(directory, "package.json"); + } + function loadModuleFromNodeModulesFolder(moduleName: string, directory: string, failedLookupLocations: string[], state: ModuleResolutionState): string { const nodeModulesFolder = combinePaths(directory, "node_modules"); const nodeModulesFolderExists = directoryProbablyExists(nodeModulesFolder, state.host); @@ -1070,15 +1067,21 @@ namespace ts { } // Walk the primary type lookup locations - let result: string[] = []; + const result: string[] = []; if (host.directoryExists && host.getDirectories) { const typeRoots = getEffectiveTypeRoots(options, host); if (typeRoots) { for (const root of typeRoots) { if (host.directoryExists(root)) { for (const typeDirectivePath of host.getDirectories(root)) { - // Return just the type directive names - result = result.concat(getBaseFileName(normalizePath(typeDirectivePath))); + const normalized = normalizePath(typeDirectivePath); + const packageJsonPath = pathToPackageJson(combinePaths(root, normalized)); + // tslint:disable-next-line:no-null-keyword + const isNotNeededPackage = host.fileExists(packageJsonPath) && readJson(packageJsonPath, host).typings === null; + if (!isNotNeededPackage) { + // Return just the type directive names + result.push(getBaseFileName(normalized)); + } } } } diff --git a/src/harness/compilerRunner.ts b/src/harness/compilerRunner.ts index ecdc18394b0..35e09c9c0d0 100644 --- a/src/harness/compilerRunner.ts +++ b/src/harness/compilerRunner.ts @@ -52,7 +52,7 @@ class CompilerBaselineRunner extends RunnerBase { private makeUnitName(name: string, root: string) { const path = ts.toPath(name, root, (fileName) => Harness.Compiler.getCanonicalFileName(fileName)); const pathStart = ts.toPath(Harness.IO.getCurrentDirectory(), "", (fileName) => Harness.Compiler.getCanonicalFileName(fileName)); - return path.replace(pathStart, "/"); + return pathStart ? path.replace(pathStart, "/") : path; }; public checkTestCodeOutput(fileName: string) { diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index a42abbbc609..620eee584e7 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2395,13 +2395,14 @@ ${code} // Comment line, check for global/file @options and record them const match = optionRegex.exec(line.substr(2)); if (match) { - const fileMetadataNamesIndex = fileMetadataNames.indexOf(match[1]); + const [key, value] = match.slice(1); + const fileMetadataNamesIndex = fileMetadataNames.indexOf(key); if (fileMetadataNamesIndex === -1) { // Check if the match is already existed in the global options - if (globalOptions[match[1]] !== undefined) { - throw new Error("Global Option : '" + match[1] + "' is already existed"); + if (globalOptions[key] !== undefined) { + throw new Error(`Global option '${key}' already exists`); } - globalOptions[match[1]] = match[2]; + globalOptions[key] = value; } else { if (fileMetadataNamesIndex === fileMetadataNames.indexOf(metadataOptionNames.fileName)) { @@ -2416,12 +2417,12 @@ ${code} resetLocalData(); } - currentFileName = basePath + "/" + match[2]; - currentFileOptions[match[1]] = match[2]; + currentFileName = basePath + "/" + value; + currentFileOptions[key] = value; } else { // Add other fileMetadata flag - currentFileOptions[match[1]] = match[2]; + currentFileOptions[key] = value; } } } @@ -2509,7 +2510,7 @@ ${code} } const marker: Marker = { - fileName: fileName, + fileName, position: location.position, data: markerValue }; @@ -2526,7 +2527,7 @@ ${code} function recordMarker(fileName: string, location: LocationInformation, name: string, markerMap: MarkerMap, markers: Marker[]): Marker { const marker: Marker = { - fileName: fileName, + fileName, position: location.position }; diff --git a/tests/baselines/reference/typingsLookup2.js b/tests/baselines/reference/typingsLookup2.js new file mode 100644 index 00000000000..3e816526af2 --- /dev/null +++ b/tests/baselines/reference/typingsLookup2.js @@ -0,0 +1,9 @@ +//// [tests/cases/conformance/typings/typingsLookup2.ts] //// + +//// [package.json] +{ "typings": null } + +//// [a.ts] + + +//// [a.js] diff --git a/tests/baselines/reference/typingsLookup2.symbols b/tests/baselines/reference/typingsLookup2.symbols new file mode 100644 index 00000000000..7223c8589a6 --- /dev/null +++ b/tests/baselines/reference/typingsLookup2.symbols @@ -0,0 +1,3 @@ +=== /a.ts === + +No type information for this code. \ No newline at end of file diff --git a/tests/baselines/reference/typingsLookup2.trace.json b/tests/baselines/reference/typingsLookup2.trace.json new file mode 100644 index 00000000000..0637a088a01 --- /dev/null +++ b/tests/baselines/reference/typingsLookup2.trace.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/tests/baselines/reference/typingsLookup2.types b/tests/baselines/reference/typingsLookup2.types new file mode 100644 index 00000000000..7223c8589a6 --- /dev/null +++ b/tests/baselines/reference/typingsLookup2.types @@ -0,0 +1,3 @@ +=== /a.ts === + +No type information for this code. \ No newline at end of file diff --git a/tests/cases/conformance/typings/typingsLookup2.ts b/tests/cases/conformance/typings/typingsLookup2.ts new file mode 100644 index 00000000000..90e1e463f0a --- /dev/null +++ b/tests/cases/conformance/typings/typingsLookup2.ts @@ -0,0 +1,13 @@ +// @traceResolution: true +// @noImplicitReferences: true +// @currentDirectory: / +// This tests that an @types package with `"typings": null` is not automatically included. +// (If it were, this test would break because there are no typings to be found.) + +// @filename: /tsconfig.json +{} + +// @filename: /node_modules/@types/angular2/package.json +{ "typings": null } + +// @filename: /a.ts From b0a4e2785d3febd578b88a825a363a9401285e6c Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Fri, 12 Aug 2016 13:36:54 -0700 Subject: [PATCH 125/233] Lint --- Gulpfile.ts | 11 +++-------- src/harness/harness.ts | 2 +- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Gulpfile.ts b/Gulpfile.ts index e2d37277bcf..f0e274f3f1e 100644 --- a/Gulpfile.ts +++ b/Gulpfile.ts @@ -531,8 +531,6 @@ const localRwcBaseline = path.join(internalTests, "baselines/rwc/local"); const refRwcBaseline = path.join(internalTests, "baselines/rwc/reference"); const localTest262Baseline = path.join(internalTests, "baselines/test262/local"); -const refTest262Baseline = path.join(internalTests, "baselines/test262/reference"); - gulp.task("tests", "Builds the test infrastructure using the built compiler", [run]); gulp.task("tests-debug", "Builds the test sources and automation in debug mode", () => { @@ -811,8 +809,6 @@ gulp.task("diff-rwc", "Diffs the RWC baselines using the diff tool specified by exec(getDiffTool(), [refRwcBaseline, localRwcBaseline], done, done); }); - -const deleteEnding = '.delete'; gulp.task("baseline-accept", "Makes the most recent test results the new baseline, overwriting the old baseline", () => { return baselineAccept(""); }); @@ -827,13 +823,12 @@ function baselineCopy(subfolder = "") { } function baselineDelete(subfolder = "") { - return gulp.src(['tests/baselines/local/**/*.delete']) + return gulp.src(["tests/baselines/local/**/*.delete"]) .pipe(insert.transform((content, fileObj) => { - const target = path.join(refBaseline, fileObj.relative.substr(0, fileObj.relative.length - '.delete'.length)); - console.log(target); + const target = path.join(refBaseline, fileObj.relative.substr(0, fileObj.relative.length - ".delete".length)); del.sync(target); del.sync(fileObj.path); - return ''; + return ""; })); } diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 18a440c789b..81567265d3f 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1667,7 +1667,7 @@ namespace Harness { const encoded_actual = Utils.encodeString(actual); if (expected !== encoded_actual) { if (actual === NoContent) { - IO.writeFile(relativeFileName + '.delete', ''); + IO.writeFile(relativeFileName + ".delete", ""); } else { IO.writeFile(relativeFileName, actual); From d2060466590a9cf3064da589cd321f405525f43f Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 12 Aug 2016 14:00:50 -0700 Subject: [PATCH 126/233] Collect timing information for commands running on travis (#10308) --- Jakefile.js | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/Jakefile.js b/Jakefile.js index 0624c92f162..01aac82a47a 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -33,6 +33,28 @@ if (process.env.path !== undefined) { process.env.PATH = nodeModulesPathPrefix + process.env.PATH; } +function toNs(diff) { + return diff[0] * 1e9 + diff[1]; +} + +function mark() { + if (!fold.isTravis()) return; + var stamp = process.hrtime(); + var id = Math.floor(Math.random() * 0xFFFFFFFF).toString(16); + console.log("travis_time:start:" + id + "\r"); + return { + stamp: stamp, + id: id + }; +} + +function measure(marker) { + if (!fold.isTravis()) return; + var diff = process.hrtime(marker.stamp); + var total = [marker.stamp[0] + diff[0], marker.stamp[1] + diff[1]]; + console.log("travis_time:end:" + marker.id + ":start=" + toNs(marker.stamp) + ",finish=" + toNs(total) + ",duration=" + toNs(diff) + "\r"); +} + var compilerSources = [ "core.ts", "performance.ts", @@ -286,6 +308,7 @@ var builtLocalCompiler = path.join(builtLocalDirectory, compilerFilename); */ function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, opts, callback) { file(outFile, prereqs, function() { + var startCompileTime = mark(); opts = opts || {}; var compilerPath = useBuiltCompiler ? builtLocalCompiler : LKGCompiler; var options = "--noImplicitAny --noImplicitThis --noEmitOnError --types " @@ -362,11 +385,13 @@ function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, opts callback(); } + measure(startCompileTime); complete(); }); ex.addListener("error", function() { fs.unlinkSync(outFile); fail("Compilation of " + outFile + " unsuccessful"); + measure(startCompileTime); }); ex.run(); }, {async: true}); @@ -769,6 +794,7 @@ function runConsoleTests(defaultReporter, runInParallel) { // timeout normally isn't necessary but Travis-CI has been timing out on compiler baselines occasionally // default timeout is 2sec which really should be enough, but maybe we just need a small amount longer if(!runInParallel) { + var startTime = mark(); tests = tests ? ' -g "' + tests + '"' : ''; var cmd = "mocha" + (debug ? " --debug-brk" : "") + " -R " + reporter + tests + colors + bail + ' -t ' + testTimeout + ' ' + run; console.log(cmd); @@ -777,10 +803,12 @@ function runConsoleTests(defaultReporter, runInParallel) { process.env.NODE_ENV = "development"; exec(cmd, function () { process.env.NODE_ENV = savedNodeEnv; + measure(startTime); runLinter(); finish(); }, function(e, status) { process.env.NODE_ENV = savedNodeEnv; + measure(startTime); finish(status); }); @@ -788,9 +816,10 @@ function runConsoleTests(defaultReporter, runInParallel) { else { var savedNodeEnv = process.env.NODE_ENV; process.env.NODE_ENV = "development"; + var startTime = mark(); runTestsInParallel(taskConfigsFolder, run, { testTimeout: testTimeout, noColors: colors === " --no-colors " }, function (err) { process.env.NODE_ENV = savedNodeEnv; - + measure(startTime); // last worker clean everything and runs linter in case if there were no errors deleteTemporaryProjectOutput(); jake.rmRf(taskConfigsFolder); @@ -1069,6 +1098,7 @@ var lintTargets = compilerSources desc("Runs tslint on the compiler sources. Optional arguments are: f[iles]=regex"); task("lint", ["build-rules"], function() { if (fold.isTravis()) console.log(fold.start("lint")); + var startTime = mark(); var lintOptions = getLinterOptions(); var failed = 0; var fileMatcher = RegExp(process.env.f || process.env.file || process.env.files || ""); @@ -1084,6 +1114,7 @@ task("lint", ["build-rules"], function() { done[target] = true; } } + measure(startTime); if (fold.isTravis()) console.log(fold.end("lint")); if (failed > 0) { fail('Linter errors.', failed); From c81624435a798249530f6d2b12687db3fa391bde Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Fri, 12 Aug 2016 16:34:35 -0700 Subject: [PATCH 127/233] Simplifies performance API --- src/compiler/binder.ts | 5 +- src/compiler/checker.ts | 7 +-- src/compiler/parser.ts | 6 +- src/compiler/performance.ts | 116 ++++++++++++++++-------------------- src/compiler/program.ts | 21 ++++--- src/compiler/sourcemap.ts | 10 +++- 6 files changed, 80 insertions(+), 85 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index c355d6c9186..ff5a8b77cfd 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -89,9 +89,10 @@ namespace ts { const binder = createBinder(); export function bindSourceFile(file: SourceFile, options: CompilerOptions) { - const start = performance.mark(); + performance.mark("bindStart"); binder(file, options); - performance.measure("Bind", start); + performance.mark("bindEnd"); + performance.measure("Bind", "bindStart", "bindEnd"); } function createBinder(): (file: SourceFile, options: CompilerOptions) => void { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1b7b659e1a6..f1fff6737a3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17535,11 +17535,10 @@ namespace ts { } function checkSourceFile(node: SourceFile) { - const start = performance.mark(); - + performance.mark("checkStart"); checkSourceFileWorker(node); - - performance.measure("Check", start); + performance.mark("checkEnd"); + performance.measure("Check", "checkStart", "checkEnd"); } // Fully type check a source file and collect the relevant diagnostics. diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 6f38783d5ed..cbf767a5410 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -421,10 +421,10 @@ namespace ts { } export function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes = false, scriptKind?: ScriptKind): SourceFile { - const start = performance.mark(); + performance.mark("parseStart"); const result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, scriptKind); - - performance.measure("Parse", start); + performance.mark("parseEnd"); + performance.measure("Parse", "parseStart", "parseEnd"); return result; } diff --git a/src/compiler/performance.ts b/src/compiler/performance.ts index e496353fde8..e8f064e81cd 100644 --- a/src/compiler/performance.ts +++ b/src/compiler/performance.ts @@ -6,71 +6,57 @@ namespace ts { } /*@internal*/ +/** Performance measurements for the compiler. */ namespace ts.performance { - /** Performance measurements for the compiler. */ declare const onProfilerEvent: { (markName: string): void; profiler: boolean; }; - let profilerEvent: (markName: string) => void; - let counters: MapLike; - let measures: MapLike; + + const profilerEvent = typeof onProfilerEvent === "function" && onProfilerEvent.profiler === true + ? onProfilerEvent + : (markName: string) => { }; + + let enabled = false; + let profilerStart = 0; + let counts: Map; + let marks: Map; + let measures: Map; /** - * Emit a performance event if ts-profiler is connected. This is primarily used - * to generate heap snapshots. + * Marks a performance event. * - * @param eventName A name for the event. + * @param markName The name of the mark. */ - export function emit(eventName: string) { - if (profilerEvent) { - profilerEvent(eventName); + export function mark(markName: string) { + if (enabled) { + marks[markName] = timestamp(); + counts[markName] = (counts[markName] || 0) + 1; + profilerEvent(markName); } } - /** - * Increments a counter with the specified name. - * - * @param counterName The name of the counter. - */ - export function increment(counterName: string) { - if (counters) { - counters[counterName] = (getProperty(counters, counterName) || 0) + 1; - } - } - - /** - * Gets the value of the counter with the specified name. - * - * @param counterName The name of the counter. - */ - export function getCount(counterName: string) { - return counters && getProperty(counters, counterName) || 0; - } - - /** - * Marks the start of a performance measurement. - */ - export function mark() { - return measures ? timestamp() : 0; - } - /** * Adds a performance measurement with the specified name. * * @param measureName The name of the performance measurement. - * @param marker The timestamp of the starting mark. + * @param startMarkName The name of the starting mark. If not supplied, the point at which the + * profiler was enabled is used. + * @param endMarkName The name of the ending mark. If not supplied, the current timestamp is + * used. */ - export function measure(measureName: string, marker: number) { - if (measures) { - measures[measureName] = (getProperty(measures, measureName) || 0) + (timestamp() - marker); + export function measure(measureName: string, startMarkName?: string, endMarkName?: string) { + if (enabled) { + const end = endMarkName && marks[endMarkName] || timestamp(); + const start = startMarkName && marks[startMarkName] || profilerStart; + measures[measureName] = (measures[measureName] || 0) + (end - start); } } /** - * Iterate over each measure, performing some action - * - * @param cb The action to perform for each measure + * Gets the number of times a marker was encountered. + * + * @param markName The name of the mark. */ - export function forEachMeasure(cb: (measureName: string, duration: number) => void) { - return forEachKey(measures, key => cb(key, measures[key])); + export function getCount(markName: string) { + return counts && counts[markName] || 0; } /** @@ -79,31 +65,31 @@ namespace ts.performance { * @param measureName The name of the measure whose durations should be accumulated. */ export function getDuration(measureName: string) { - return measures && getProperty(measures, measureName) || 0; + return measures && measures[measureName] || 0; + } + + /** + * Iterate over each measure, performing some action + * + * @param cb The action to perform for each measure + */ + export function forEachMeasure(cb: (measureName: string, duration: number) => void) { + for (const key in measures) { + cb(key, measures[key]); + } } /** Enables (and resets) performance measurements for the compiler. */ export function enable() { - counters = { }; - measures = { - "I/O Read": 0, - "I/O Write": 0, - "Program": 0, - "Parse": 0, - "Bind": 0, - "Check": 0, - "Emit": 0, - }; - - profilerEvent = typeof onProfilerEvent === "function" && onProfilerEvent.profiler === true - ? onProfilerEvent - : undefined; + counts = createMap(); + marks = createMap(); + measures = createMap(); + enabled = true; + profilerStart = timestamp(); } - /** Disables (and clears) performance measurements for the compiler. */ + /** Disables performance measurements for the compiler. */ export function disable() { - counters = undefined; - measures = undefined; - profilerEvent = undefined; + enabled = false; } } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index eb50548750d..a632eed7fd2 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -857,9 +857,10 @@ namespace ts { function getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile { let text: string; try { - const start = performance.mark(); + performance.mark("ioReadStart"); text = sys.readFile(fileName, options.charset); - performance.measure("I/O Read", start); + performance.mark("ioReadEnd"); + performance.measure("I/O Read", "ioReadStart", "ioReadEnd"); } catch (e) { if (onError) { @@ -926,7 +927,7 @@ namespace ts { function writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void) { try { - const start = performance.mark(); + performance.mark("ioWriteStart"); ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName))); if (isWatchSet(options) && sys.createHash && sys.getModifiedTime) { @@ -936,7 +937,8 @@ namespace ts { sys.writeFile(fileName, data, writeByteOrderMark); } - performance.measure("I/O Write", start); + performance.mark("ioWriteEnd"); + performance.measure("I/O Write", "ioWriteStart", "ioWriteEnd"); } catch (e) { if (onError) { @@ -1118,7 +1120,7 @@ namespace ts { // Track source files that are source files found by searching under node_modules, as these shouldn't be compiled. const sourceFilesFoundSearchingNodeModules = createMap(); - const start = performance.mark(); + performance.mark("programStart"); host = host || createCompilerHost(options); @@ -1216,8 +1218,8 @@ namespace ts { }; verifyCompilerOptions(); - - performance.measure("Program", start); + performance.mark("programEnd"); + performance.measure("Program", "programStart", "programEnd"); return program; @@ -1460,14 +1462,15 @@ namespace ts { // checked is to not pass the file to getEmitResolver. const emitResolver = getDiagnosticsProducingTypeChecker().getEmitResolver((options.outFile || options.out) ? undefined : sourceFile); - const start = performance.mark(); + performance.mark("emitStart"); const emitResult = emitFiles( emitResolver, getEmitHost(writeFileCallback), sourceFile); - performance.measure("Emit", start); + performance.mark("emitEnd"); + performance.measure("Emit", "emitStart", "emitEnd"); return emitResult; } diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index 2d8c36a3d02..52e8975db76 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -46,6 +46,7 @@ namespace ts { export function createSourceMapWriter(host: EmitHost, writer: EmitTextWriter): SourceMapWriter { const compilerOptions = host.getCompilerOptions(); + const extendedDiagnostics = compilerOptions.extendedDiagnostics; let currentSourceFile: SourceFile; let sourceMapDir: string; // The directory in which sourcemap will be let stopOverridingSpan = false; @@ -240,7 +241,9 @@ namespace ts { return; } - const start = performance.mark(); + if (extendedDiagnostics) { + performance.mark("sourcemapStart"); + } const sourceLinePos = getLineAndCharacterOfPosition(currentSourceFile, pos); @@ -282,7 +285,10 @@ namespace ts { updateLastEncodedAndRecordedSpans(); - performance.measure("Source Map", start); + if (extendedDiagnostics) { + performance.mark("sourcemapEnd"); + performance.measure("Source Map", "sourcemapStart", "sourcemapEnd"); + } } function getStartPos(range: TextRange) { From 73d53e82988ff93fa2a20346127a683333e6ab94 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Sat, 13 Aug 2016 12:18:39 -0700 Subject: [PATCH 128/233] Use 'MapLike' instead of 'Map' in 'preferConstRule.ts'. --- scripts/tslint/preferConstRule.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/scripts/tslint/preferConstRule.ts b/scripts/tslint/preferConstRule.ts index aaa1b0e53d5..1d316692468 100644 --- a/scripts/tslint/preferConstRule.ts +++ b/scripts/tslint/preferConstRule.ts @@ -1,7 +1,6 @@ import * as Lint from "tslint/lib/lint"; import * as ts from "typescript"; - export class Rule extends Lint.Rules.AbstractRule { public static FAILURE_STRING_FACTORY = (identifier: string) => `Identifier '${identifier}' never appears on the LHS of an assignment - use const instead of let for its declaration.`; @@ -64,7 +63,7 @@ interface DeclarationUsages { } class PreferConstWalker extends Lint.RuleWalker { - private inScopeLetDeclarations: ts.Map[] = []; + private inScopeLetDeclarations: ts.MapLike[] = []; private errors: Lint.RuleFailure[] = []; private markAssignment(identifier: ts.Identifier) { const name = identifier.text; @@ -172,7 +171,7 @@ class PreferConstWalker extends Lint.RuleWalker { } private visitAnyForStatement(node: ts.ForOfStatement | ts.ForInStatement) { - const names: ts.Map = {}; + const names: ts.MapLike = {}; if (isLet(node.initializer)) { if (node.initializer.kind === ts.SyntaxKind.VariableDeclarationList) { this.collectLetIdentifiers(node.initializer as ts.VariableDeclarationList, names); @@ -194,7 +193,7 @@ class PreferConstWalker extends Lint.RuleWalker { } visitBlock(node: ts.Block) { - const names: ts.Map = {}; + const names: ts.MapLike = {}; for (const statement of node.statements) { if (statement.kind === ts.SyntaxKind.VariableStatement) { this.collectLetIdentifiers((statement as ts.VariableStatement).declarationList, names); @@ -205,7 +204,7 @@ class PreferConstWalker extends Lint.RuleWalker { this.popDeclarations(); } - private collectLetIdentifiers(list: ts.VariableDeclarationList, ret: ts.Map) { + private collectLetIdentifiers(list: ts.VariableDeclarationList, ret: ts.MapLike) { for (const node of list.declarations) { if (isLet(node) && !isExported(node)) { this.collectNameIdentifiers(node, node.name, ret); @@ -213,7 +212,7 @@ class PreferConstWalker extends Lint.RuleWalker { } } - private collectNameIdentifiers(declaration: ts.VariableDeclaration, node: ts.Identifier | ts.BindingPattern, table: ts.Map) { + private collectNameIdentifiers(declaration: ts.VariableDeclaration, node: ts.Identifier | ts.BindingPattern, table: ts.MapLike) { if (node.kind === ts.SyntaxKind.Identifier) { table[(node as ts.Identifier).text] = { declaration, usages: 0 }; } @@ -222,7 +221,7 @@ class PreferConstWalker extends Lint.RuleWalker { } } - private collectBindingPatternIdentifiers(value: ts.VariableDeclaration, pattern: ts.BindingPattern, table: ts.Map) { + private collectBindingPatternIdentifiers(value: ts.VariableDeclaration, pattern: ts.BindingPattern, table: ts.MapLike) { for (const element of pattern.elements) { this.collectNameIdentifiers(value, element.name, table); } From 38353027aaa9813c646cce62900eb9694012bae5 Mon Sep 17 00:00:00 2001 From: yortus Date: Sat, 13 Aug 2016 13:57:18 +0800 Subject: [PATCH 129/233] narrow from 'any' in most situations instanceof and user-defined typeguards narrow from 'any' unless the narrowed-to type is exactly 'Object' or 'Function'. This is a breaking change. --- src/compiler/checker.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1b7b659e1a6..85e33c5e4a1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8548,10 +8548,6 @@ namespace ts { } return type; } - // We never narrow type any in an instanceof guard - if (isTypeAny(type)) { - return type; - } // Check that right operand is a function type with a prototype property const rightType = checkExpression(expr.right); @@ -8569,6 +8565,11 @@ namespace ts { } } + // Don't narrow from 'any' if the target type is exactly 'Object' or 'Function' + if (isTypeAny(type) && (targetType === globalObjectType || targetType === globalFunctionType)) { + return type; + } + if (!targetType) { // Target type is type of construct signature let constructSignatures: Signature[]; @@ -8615,7 +8616,7 @@ namespace ts { } function narrowTypeByTypePredicate(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type { - if (type.flags & TypeFlags.Any || !hasMatchingArgument(callExpression, reference)) { + if (!hasMatchingArgument(callExpression, reference)) { return type; } const signature = getResolvedSignature(callExpression); @@ -8623,6 +8624,12 @@ namespace ts { if (!predicate) { return type; } + + // Don't narrow from 'any' if the predicate type is exactly 'Object' or 'Function' + if (isTypeAny(type) && (predicate.type === globalObjectType || predicate.type === globalFunctionType)) { + return type; + } + if (isIdentifierTypePredicate(predicate)) { const predicateArgument = callExpression.arguments[predicate.parameterIndex]; if (predicateArgument) { From 59c09d90e638a709ae3fbbecf5662f1e86cefdca Mon Sep 17 00:00:00 2001 From: yortus Date: Sat, 13 Aug 2016 14:49:56 +0800 Subject: [PATCH 130/233] Update instanceof conformance tests --- ...rdsWithInstanceOfByConstructorSignature.ts | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts index b81dd26652b..0817954c35c 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts @@ -13,7 +13,7 @@ if (obj1 instanceof A) { // narrowed to A. } var obj2: any; -if (obj2 instanceof A) { // can't narrow type from 'any' +if (obj2 instanceof A) { obj2.foo; obj2.bar; } @@ -35,7 +35,7 @@ if (obj3 instanceof B) { // narrowed to B. } var obj4: any; -if (obj4 instanceof B) { // can't narrow type from 'any' +if (obj4 instanceof B) { obj4.foo = "str"; obj4.foo = 1; obj4.bar = "str"; @@ -67,7 +67,7 @@ if (obj5 instanceof C) { // narrowed to C1|C2. } var obj6: any; -if (obj6 instanceof C) { // can't narrow type from 'any' +if (obj6 instanceof C) { obj6.foo; obj6.bar1; obj6.bar2; @@ -86,7 +86,7 @@ if (obj7 instanceof D) { // narrowed to D. } var obj8: any; -if (obj8 instanceof D) { // can't narrow type from 'any' +if (obj8 instanceof D) { obj8.foo; obj8.bar; } @@ -113,7 +113,7 @@ if (obj9 instanceof E) { // narrowed to E1 | E2 } var obj10: any; -if (obj10 instanceof E) { // can't narrow type from 'any' +if (obj10 instanceof E) { obj10.foo; obj10.bar1; obj10.bar2; @@ -136,7 +136,7 @@ if (obj11 instanceof F) { // can't type narrowing, construct signature returns a } var obj12: any; -if (obj12 instanceof F) { // can't narrow type from 'any' +if (obj12 instanceof F) { obj12.foo; obj12.bar; } @@ -161,7 +161,7 @@ if (obj13 instanceof G) { // narrowed to G1. G1 is return type of prototype prop } var obj14: any; -if (obj14 instanceof G) { // can't narrow type from 'any' +if (obj14 instanceof G) { obj14.foo1; obj14.foo2; } @@ -183,7 +183,19 @@ if (obj15 instanceof H) { // narrowed to H. } var obj16: any; -if (obj16 instanceof H) { // can't narrow type from 'any' +if (obj16 instanceof H) { obj16.foo1; obj16.foo2; } + +var obj17: any; +if (obj17 instanceof Object) { // can't narrow type from 'any' to 'Object' + obj17.foo1; + obj17.foo2; +} + +var obj18: any; +if (obj18 instanceof Function) { // can't narrow type from 'any' to 'Function' + obj18.foo1; + obj18.foo2; +} From cc8f3269611565ac098aa644d1cc1522a4642bf5 Mon Sep 17 00:00:00 2001 From: yortus Date: Sun, 14 Aug 2016 21:42:31 +0800 Subject: [PATCH 131/233] accept new baselines --- ...nstanceOfByConstructorSignature.errors.txt | 60 ++++++++++++++++--- ...rdsWithInstanceOfByConstructorSignature.js | 38 +++++++++--- 2 files changed, 81 insertions(+), 17 deletions(-) diff --git a/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.errors.txt b/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.errors.txt index c574118f91d..b67f7328856 100644 --- a/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.errors.txt +++ b/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.errors.txt @@ -1,16 +1,26 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(12,10): error TS2339: Property 'bar' does not exist on type 'A'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(18,10): error TS2339: Property 'bar' does not exist on type 'A'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(33,5): error TS2322: Type 'string' is not assignable to type 'number'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(34,10): error TS2339: Property 'bar' does not exist on type 'B'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(41,10): error TS2339: Property 'bar' does not exist on type 'B'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(66,10): error TS2339: Property 'bar2' does not exist on type 'C1'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(72,10): error TS2339: Property 'bar1' does not exist on type 'C1 | C2'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(73,10): error TS2339: Property 'bar2' does not exist on type 'C1 | C2'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(85,10): error TS2339: Property 'bar' does not exist on type 'D'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(91,10): error TS2339: Property 'bar' does not exist on type 'D'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(112,10): error TS2339: Property 'bar2' does not exist on type 'E1'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(118,11): error TS2339: Property 'bar1' does not exist on type 'E1 | E2'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(119,11): error TS2339: Property 'bar2' does not exist on type 'E1 | E2'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(134,11): error TS2339: Property 'foo' does not exist on type 'string | F'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(135,11): error TS2339: Property 'bar' does not exist on type 'string | F'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(160,11): error TS2339: Property 'foo2' does not exist on type 'G1'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(166,11): error TS2339: Property 'foo2' does not exist on type 'G1'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(182,11): error TS2339: Property 'bar' does not exist on type 'H'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(187,11): error TS2339: Property 'foo1' does not exist on type 'H'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(188,11): error TS2339: Property 'foo2' does not exist on type 'H'. -==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts (10 errors) ==== +==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts (20 errors) ==== interface AConstructor { new (): A; } @@ -28,9 +38,11 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru } var obj2: any; - if (obj2 instanceof A) { // can't narrow type from 'any' + if (obj2 instanceof A) { obj2.foo; obj2.bar; + ~~~ +!!! error TS2339: Property 'bar' does not exist on type 'A'. } // a construct signature with generics @@ -54,10 +66,12 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru } var obj4: any; - if (obj4 instanceof B) { // can't narrow type from 'any' + if (obj4 instanceof B) { obj4.foo = "str"; obj4.foo = 1; obj4.bar = "str"; + ~~~ +!!! error TS2339: Property 'bar' does not exist on type 'B'. } // has multiple construct signature @@ -88,10 +102,14 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru } var obj6: any; - if (obj6 instanceof C) { // can't narrow type from 'any' + if (obj6 instanceof C) { obj6.foo; obj6.bar1; + ~~~~ +!!! error TS2339: Property 'bar1' does not exist on type 'C1 | C2'. obj6.bar2; + ~~~~ +!!! error TS2339: Property 'bar2' does not exist on type 'C1 | C2'. } // with object type literal @@ -109,9 +127,11 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru } var obj8: any; - if (obj8 instanceof D) { // can't narrow type from 'any' + if (obj8 instanceof D) { obj8.foo; obj8.bar; + ~~~ +!!! error TS2339: Property 'bar' does not exist on type 'D'. } // a construct signature that returns a union type @@ -138,10 +158,14 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru } var obj10: any; - if (obj10 instanceof E) { // can't narrow type from 'any' + if (obj10 instanceof E) { obj10.foo; obj10.bar1; + ~~~~ +!!! error TS2339: Property 'bar1' does not exist on type 'E1 | E2'. obj10.bar2; + ~~~~ +!!! error TS2339: Property 'bar2' does not exist on type 'E1 | E2'. } // a construct signature that returns any @@ -165,7 +189,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru } var obj12: any; - if (obj12 instanceof F) { // can't narrow type from 'any' + if (obj12 instanceof F) { obj12.foo; obj12.bar; } @@ -192,9 +216,11 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru } var obj14: any; - if (obj14 instanceof G) { // can't narrow type from 'any' + if (obj14 instanceof G) { obj14.foo1; obj14.foo2; + ~~~~ +!!! error TS2339: Property 'foo2' does not exist on type 'G1'. } // a type with a prototype that has any type @@ -216,8 +242,24 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru } var obj16: any; - if (obj16 instanceof H) { // can't narrow type from 'any' + if (obj16 instanceof H) { obj16.foo1; + ~~~~ +!!! error TS2339: Property 'foo1' does not exist on type 'H'. obj16.foo2; + ~~~~ +!!! error TS2339: Property 'foo2' does not exist on type 'H'. + } + + var obj17: any; + if (obj17 instanceof Object) { // can't narrow type from 'any' to 'Object' + obj17.foo1; + obj17.foo2; + } + + var obj18: any; + if (obj18 instanceof Function) { // can't narrow type from 'any' to 'Function' + obj18.foo1; + obj18.foo2; } \ No newline at end of file diff --git a/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.js b/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.js index 7e6b3324470..40ef6587e75 100644 --- a/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.js +++ b/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.js @@ -14,7 +14,7 @@ if (obj1 instanceof A) { // narrowed to A. } var obj2: any; -if (obj2 instanceof A) { // can't narrow type from 'any' +if (obj2 instanceof A) { obj2.foo; obj2.bar; } @@ -36,7 +36,7 @@ if (obj3 instanceof B) { // narrowed to B. } var obj4: any; -if (obj4 instanceof B) { // can't narrow type from 'any' +if (obj4 instanceof B) { obj4.foo = "str"; obj4.foo = 1; obj4.bar = "str"; @@ -68,7 +68,7 @@ if (obj5 instanceof C) { // narrowed to C1|C2. } var obj6: any; -if (obj6 instanceof C) { // can't narrow type from 'any' +if (obj6 instanceof C) { obj6.foo; obj6.bar1; obj6.bar2; @@ -87,7 +87,7 @@ if (obj7 instanceof D) { // narrowed to D. } var obj8: any; -if (obj8 instanceof D) { // can't narrow type from 'any' +if (obj8 instanceof D) { obj8.foo; obj8.bar; } @@ -114,7 +114,7 @@ if (obj9 instanceof E) { // narrowed to E1 | E2 } var obj10: any; -if (obj10 instanceof E) { // can't narrow type from 'any' +if (obj10 instanceof E) { obj10.foo; obj10.bar1; obj10.bar2; @@ -137,7 +137,7 @@ if (obj11 instanceof F) { // can't type narrowing, construct signature returns a } var obj12: any; -if (obj12 instanceof F) { // can't narrow type from 'any' +if (obj12 instanceof F) { obj12.foo; obj12.bar; } @@ -162,7 +162,7 @@ if (obj13 instanceof G) { // narrowed to G1. G1 is return type of prototype prop } var obj14: any; -if (obj14 instanceof G) { // can't narrow type from 'any' +if (obj14 instanceof G) { obj14.foo1; obj14.foo2; } @@ -184,10 +184,22 @@ if (obj15 instanceof H) { // narrowed to H. } var obj16: any; -if (obj16 instanceof H) { // can't narrow type from 'any' +if (obj16 instanceof H) { obj16.foo1; obj16.foo2; } + +var obj17: any; +if (obj17 instanceof Object) { // can't narrow type from 'any' to 'Object' + obj17.foo1; + obj17.foo2; +} + +var obj18: any; +if (obj18 instanceof Function) { // can't narrow type from 'any' to 'Function' + obj18.foo1; + obj18.foo2; +} //// [typeGuardsWithInstanceOfByConstructorSignature.js] @@ -278,3 +290,13 @@ if (obj16 instanceof H) { obj16.foo1; obj16.foo2; } +var obj17; +if (obj17 instanceof Object) { + obj17.foo1; + obj17.foo2; +} +var obj18; +if (obj18 instanceof Function) { + obj18.foo1; + obj18.foo2; +} From 66047c8b184f231cbf77e6c3ca5d78909e038388 Mon Sep 17 00:00:00 2001 From: yortus Date: Sun, 14 Aug 2016 22:56:36 +0800 Subject: [PATCH 132/233] add tests --- .../narrowExceptionVariableInCatchClause.ts | 23 +++++++++++++ .../types/any/narrowFromAnyWithInstanceof.ts | 23 +++++++++++++ .../any/narrowFromAnyWithTypePredicate.ts | 34 +++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 tests/cases/conformance/types/any/narrowExceptionVariableInCatchClause.ts create mode 100644 tests/cases/conformance/types/any/narrowFromAnyWithInstanceof.ts create mode 100644 tests/cases/conformance/types/any/narrowFromAnyWithTypePredicate.ts diff --git a/tests/cases/conformance/types/any/narrowExceptionVariableInCatchClause.ts b/tests/cases/conformance/types/any/narrowExceptionVariableInCatchClause.ts new file mode 100644 index 00000000000..dfa60a415f9 --- /dev/null +++ b/tests/cases/conformance/types/any/narrowExceptionVariableInCatchClause.ts @@ -0,0 +1,23 @@ +declare function isFooError(x: any): x is { type: 'foo'; dontPanic(); }; + +function tryCatch() { + try { + // do stuff... + } + catch (err) { // err is implicitly 'any' and cannot be annotated + + if (isFooError(err)) { + err.dontPanic(); // OK + err.doPanic(); // ERROR: Property 'doPanic' does not exist on type '{...}' + } + + else if (err instanceof Error) { + err.message; + err.massage; // ERROR: Property 'massage' does not exist on type 'Error' + } + + else { + throw err; + } + } +} diff --git a/tests/cases/conformance/types/any/narrowFromAnyWithInstanceof.ts b/tests/cases/conformance/types/any/narrowFromAnyWithInstanceof.ts new file mode 100644 index 00000000000..4fbfc46060a --- /dev/null +++ b/tests/cases/conformance/types/any/narrowFromAnyWithInstanceof.ts @@ -0,0 +1,23 @@ +declare var x: any; + +if (x instanceof Function) { // 'any' is not narrowed when target type is 'Function' + x(); + x(1, 2, 3); + x("hello!"); + x.prop; +} + +if (x instanceof Object) { // 'any' is not narrowed when target type is 'Object' + x.method(); + x(); +} + +if (x instanceof Error) { // 'any' is narrowed to types other than 'Function'/'Object' + x.message; + x.mesage; +} + +if (x instanceof Date) { + x.getDate(); + x.getHuors(); +} diff --git a/tests/cases/conformance/types/any/narrowFromAnyWithTypePredicate.ts b/tests/cases/conformance/types/any/narrowFromAnyWithTypePredicate.ts new file mode 100644 index 00000000000..473bd349b5f --- /dev/null +++ b/tests/cases/conformance/types/any/narrowFromAnyWithTypePredicate.ts @@ -0,0 +1,34 @@ +declare var x: any; +declare function isFunction(x): x is Function; +declare function isObject(x): x is Object; +declare function isAnything(x): x is {}; +declare function isError(x): x is Error; +declare function isDate(x): x is Date; + + +if (isFunction(x)) { // 'any' is not narrowed when target type is 'Function' + x(); + x(1, 2, 3); + x("hello!"); + x.prop; +} + +if (isObject(x)) { // 'any' is not narrowed when target type is 'Object' + x.method(); + x(); +} + +if (isAnything(x)) { // 'any' is narrowed to types other than 'Function'/'Object' (including {}) + x.method(); + x(); +} + +if (isError(x)) { + x.message; + x.mesage; +} + +if (isDate(x)) { + x.getDate(); + x.getHuors(); +} From 837688f0bec2dcbe15242609fe9f75b02224c2fd Mon Sep 17 00:00:00 2001 From: yortus Date: Sun, 14 Aug 2016 22:58:21 +0800 Subject: [PATCH 133/233] accept new baselines --- ...wExceptionVariableInCatchClause.errors.txt | 33 ++++++++++ .../narrowExceptionVariableInCatchClause.js | 44 ++++++++++++++ .../narrowFromAnyWithInstanceof.errors.txt | 33 ++++++++++ .../reference/narrowFromAnyWithInstanceof.js | 45 ++++++++++++++ .../narrowFromAnyWithTypePredicate.errors.txt | 50 ++++++++++++++++ .../narrowFromAnyWithTypePredicate.js | 60 +++++++++++++++++++ 6 files changed, 265 insertions(+) create mode 100644 tests/baselines/reference/narrowExceptionVariableInCatchClause.errors.txt create mode 100644 tests/baselines/reference/narrowExceptionVariableInCatchClause.js create mode 100644 tests/baselines/reference/narrowFromAnyWithInstanceof.errors.txt create mode 100644 tests/baselines/reference/narrowFromAnyWithInstanceof.js create mode 100644 tests/baselines/reference/narrowFromAnyWithTypePredicate.errors.txt create mode 100644 tests/baselines/reference/narrowFromAnyWithTypePredicate.js diff --git a/tests/baselines/reference/narrowExceptionVariableInCatchClause.errors.txt b/tests/baselines/reference/narrowExceptionVariableInCatchClause.errors.txt new file mode 100644 index 00000000000..e435b98050d --- /dev/null +++ b/tests/baselines/reference/narrowExceptionVariableInCatchClause.errors.txt @@ -0,0 +1,33 @@ +tests/cases/conformance/types/any/narrowExceptionVariableInCatchClause.ts(11,17): error TS2339: Property 'doPanic' does not exist on type '{ type: "foo"; dontPanic(): any; }'. +tests/cases/conformance/types/any/narrowExceptionVariableInCatchClause.ts(16,17): error TS2339: Property 'massage' does not exist on type 'Error'. + + +==== tests/cases/conformance/types/any/narrowExceptionVariableInCatchClause.ts (2 errors) ==== + declare function isFooError(x: any): x is { type: 'foo'; dontPanic(); }; + + function tryCatch() { + try { + // do stuff... + } + catch (err) { // err is implicitly 'any' and cannot be annotated + + if (isFooError(err)) { + err.dontPanic(); // OK + err.doPanic(); // ERROR: Property 'doPanic' does not exist on type '{...}' + ~~~~~~~ +!!! error TS2339: Property 'doPanic' does not exist on type '{ type: "foo"; dontPanic(): any; }'. + } + + else if (err instanceof Error) { + err.message; + err.massage; // ERROR: Property 'massage' does not exist on type 'Error' + ~~~~~~~ +!!! error TS2339: Property 'massage' does not exist on type 'Error'. + } + + else { + throw err; + } + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/narrowExceptionVariableInCatchClause.js b/tests/baselines/reference/narrowExceptionVariableInCatchClause.js new file mode 100644 index 00000000000..5808ed76826 --- /dev/null +++ b/tests/baselines/reference/narrowExceptionVariableInCatchClause.js @@ -0,0 +1,44 @@ +//// [narrowExceptionVariableInCatchClause.ts] +declare function isFooError(x: any): x is { type: 'foo'; dontPanic(); }; + +function tryCatch() { + try { + // do stuff... + } + catch (err) { // err is implicitly 'any' and cannot be annotated + + if (isFooError(err)) { + err.dontPanic(); // OK + err.doPanic(); // ERROR: Property 'doPanic' does not exist on type '{...}' + } + + else if (err instanceof Error) { + err.message; + err.massage; // ERROR: Property 'massage' does not exist on type 'Error' + } + + else { + throw err; + } + } +} + + +//// [narrowExceptionVariableInCatchClause.js] +function tryCatch() { + try { + } + catch (err) { + if (isFooError(err)) { + err.dontPanic(); // OK + err.doPanic(); // ERROR: Property 'doPanic' does not exist on type '{...}' + } + else if (err instanceof Error) { + err.message; + err.massage; // ERROR: Property 'massage' does not exist on type 'Error' + } + else { + throw err; + } + } +} diff --git a/tests/baselines/reference/narrowFromAnyWithInstanceof.errors.txt b/tests/baselines/reference/narrowFromAnyWithInstanceof.errors.txt new file mode 100644 index 00000000000..3e152b0faf4 --- /dev/null +++ b/tests/baselines/reference/narrowFromAnyWithInstanceof.errors.txt @@ -0,0 +1,33 @@ +tests/cases/conformance/types/any/narrowFromAnyWithInstanceof.ts(17,7): error TS2339: Property 'mesage' does not exist on type 'Error'. +tests/cases/conformance/types/any/narrowFromAnyWithInstanceof.ts(22,7): error TS2339: Property 'getHuors' does not exist on type 'Date'. + + +==== tests/cases/conformance/types/any/narrowFromAnyWithInstanceof.ts (2 errors) ==== + declare var x: any; + + if (x instanceof Function) { // 'any' is not narrowed when target type is 'Function' + x(); + x(1, 2, 3); + x("hello!"); + x.prop; + } + + if (x instanceof Object) { // 'any' is not narrowed when target type is 'Object' + x.method(); + x(); + } + + if (x instanceof Error) { // 'any' is narrowed to types other than 'Function'/'Object' + x.message; + x.mesage; + ~~~~~~ +!!! error TS2339: Property 'mesage' does not exist on type 'Error'. + } + + if (x instanceof Date) { + x.getDate(); + x.getHuors(); + ~~~~~~~~ +!!! error TS2339: Property 'getHuors' does not exist on type 'Date'. + } + \ No newline at end of file diff --git a/tests/baselines/reference/narrowFromAnyWithInstanceof.js b/tests/baselines/reference/narrowFromAnyWithInstanceof.js new file mode 100644 index 00000000000..4cf1ca174aa --- /dev/null +++ b/tests/baselines/reference/narrowFromAnyWithInstanceof.js @@ -0,0 +1,45 @@ +//// [narrowFromAnyWithInstanceof.ts] +declare var x: any; + +if (x instanceof Function) { // 'any' is not narrowed when target type is 'Function' + x(); + x(1, 2, 3); + x("hello!"); + x.prop; +} + +if (x instanceof Object) { // 'any' is not narrowed when target type is 'Object' + x.method(); + x(); +} + +if (x instanceof Error) { // 'any' is narrowed to types other than 'Function'/'Object' + x.message; + x.mesage; +} + +if (x instanceof Date) { + x.getDate(); + x.getHuors(); +} + + +//// [narrowFromAnyWithInstanceof.js] +if (x instanceof Function) { + x(); + x(1, 2, 3); + x("hello!"); + x.prop; +} +if (x instanceof Object) { + x.method(); + x(); +} +if (x instanceof Error) { + x.message; + x.mesage; +} +if (x instanceof Date) { + x.getDate(); + x.getHuors(); +} diff --git a/tests/baselines/reference/narrowFromAnyWithTypePredicate.errors.txt b/tests/baselines/reference/narrowFromAnyWithTypePredicate.errors.txt new file mode 100644 index 00000000000..94f41becdad --- /dev/null +++ b/tests/baselines/reference/narrowFromAnyWithTypePredicate.errors.txt @@ -0,0 +1,50 @@ +tests/cases/conformance/types/any/narrowFromAnyWithTypePredicate.ts(22,7): error TS2339: Property 'method' does not exist on type '{}'. +tests/cases/conformance/types/any/narrowFromAnyWithTypePredicate.ts(23,5): error TS2349: Cannot invoke an expression whose type lacks a call signature. +tests/cases/conformance/types/any/narrowFromAnyWithTypePredicate.ts(28,7): error TS2339: Property 'mesage' does not exist on type 'Error'. +tests/cases/conformance/types/any/narrowFromAnyWithTypePredicate.ts(33,7): error TS2339: Property 'getHuors' does not exist on type 'Date'. + + +==== tests/cases/conformance/types/any/narrowFromAnyWithTypePredicate.ts (4 errors) ==== + declare var x: any; + declare function isFunction(x): x is Function; + declare function isObject(x): x is Object; + declare function isAnything(x): x is {}; + declare function isError(x): x is Error; + declare function isDate(x): x is Date; + + + if (isFunction(x)) { // 'any' is not narrowed when target type is 'Function' + x(); + x(1, 2, 3); + x("hello!"); + x.prop; + } + + if (isObject(x)) { // 'any' is not narrowed when target type is 'Object' + x.method(); + x(); + } + + if (isAnything(x)) { // 'any' is narrowed to types other than 'Function'/'Object' (including {}) + x.method(); + ~~~~~~ +!!! error TS2339: Property 'method' does not exist on type '{}'. + x(); + ~~~ +!!! error TS2349: Cannot invoke an expression whose type lacks a call signature. + } + + if (isError(x)) { + x.message; + x.mesage; + ~~~~~~ +!!! error TS2339: Property 'mesage' does not exist on type 'Error'. + } + + if (isDate(x)) { + x.getDate(); + x.getHuors(); + ~~~~~~~~ +!!! error TS2339: Property 'getHuors' does not exist on type 'Date'. + } + \ No newline at end of file diff --git a/tests/baselines/reference/narrowFromAnyWithTypePredicate.js b/tests/baselines/reference/narrowFromAnyWithTypePredicate.js new file mode 100644 index 00000000000..958a3cfd70d --- /dev/null +++ b/tests/baselines/reference/narrowFromAnyWithTypePredicate.js @@ -0,0 +1,60 @@ +//// [narrowFromAnyWithTypePredicate.ts] +declare var x: any; +declare function isFunction(x): x is Function; +declare function isObject(x): x is Object; +declare function isAnything(x): x is {}; +declare function isError(x): x is Error; +declare function isDate(x): x is Date; + + +if (isFunction(x)) { // 'any' is not narrowed when target type is 'Function' + x(); + x(1, 2, 3); + x("hello!"); + x.prop; +} + +if (isObject(x)) { // 'any' is not narrowed when target type is 'Object' + x.method(); + x(); +} + +if (isAnything(x)) { // 'any' is narrowed to types other than 'Function'/'Object' (including {}) + x.method(); + x(); +} + +if (isError(x)) { + x.message; + x.mesage; +} + +if (isDate(x)) { + x.getDate(); + x.getHuors(); +} + + +//// [narrowFromAnyWithTypePredicate.js] +if (isFunction(x)) { + x(); + x(1, 2, 3); + x("hello!"); + x.prop; +} +if (isObject(x)) { + x.method(); + x(); +} +if (isAnything(x)) { + x.method(); + x(); +} +if (isError(x)) { + x.message; + x.mesage; +} +if (isDate(x)) { + x.getDate(); + x.getHuors(); +} From 54735edc72d18966e264132a5be00465c9568e5d Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 15 Aug 2016 07:40:25 -0700 Subject: [PATCH 134/233] Use lowercase names for type reference directives --- src/compiler/program.ts | 7 ++++--- tests/baselines/reference/typingsLookup3.js | 13 +++++++++++++ tests/baselines/reference/typingsLookup3.symbols | 12 ++++++++++++ .../baselines/reference/typingsLookup3.trace.json | 12 ++++++++++++ tests/baselines/reference/typingsLookup3.types | 12 ++++++++++++ tests/cases/conformance/typings/typingsLookup3.ts | 14 ++++++++++++++ 6 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/typingsLookup3.js create mode 100644 tests/baselines/reference/typingsLookup3.symbols create mode 100644 tests/baselines/reference/typingsLookup3.trace.json create mode 100644 tests/baselines/reference/typingsLookup3.types create mode 100644 tests/cases/conformance/typings/typingsLookup3.ts diff --git a/src/compiler/program.ts b/src/compiler/program.ts index eb50548750d..5befec5ebc0 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -2012,15 +2012,16 @@ namespace ts { } function processTypeReferenceDirectives(file: SourceFile) { - const typeDirectives = map(file.typeReferenceDirectives, l => l.fileName); + const typeDirectives = map(file.typeReferenceDirectives, ref => ref.fileName.toLocaleLowerCase()); const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeDirectives, file.fileName); for (let i = 0; i < typeDirectives.length; i++) { const ref = file.typeReferenceDirectives[i]; const resolvedTypeReferenceDirective = resolutions[i]; // store resolved type directive on the file - setResolvedTypeReferenceDirective(file, ref.fileName, resolvedTypeReferenceDirective); - processTypeReferenceDirective(ref.fileName, resolvedTypeReferenceDirective, file, ref.pos, ref.end); + const fileName = ref.fileName.toLocaleLowerCase(); + setResolvedTypeReferenceDirective(file, fileName, resolvedTypeReferenceDirective); + processTypeReferenceDirective(fileName, resolvedTypeReferenceDirective, file, ref.pos, ref.end); } } diff --git a/tests/baselines/reference/typingsLookup3.js b/tests/baselines/reference/typingsLookup3.js new file mode 100644 index 00000000000..b3be036b4ae --- /dev/null +++ b/tests/baselines/reference/typingsLookup3.js @@ -0,0 +1,13 @@ +//// [tests/cases/conformance/typings/typingsLookup3.ts] //// + +//// [index.d.ts] +declare var $: { x: any }; + +//// [a.ts] +/// +$.x; + + +//// [a.js] +/// +$.x; diff --git a/tests/baselines/reference/typingsLookup3.symbols b/tests/baselines/reference/typingsLookup3.symbols new file mode 100644 index 00000000000..e641afb183b --- /dev/null +++ b/tests/baselines/reference/typingsLookup3.symbols @@ -0,0 +1,12 @@ +=== /a.ts === +/// +$.x; +>$.x : Symbol(x, Decl(index.d.ts, 0, 16)) +>$ : Symbol($, Decl(index.d.ts, 0, 11)) +>x : Symbol(x, Decl(index.d.ts, 0, 16)) + +=== /node_modules/@types/jquery/index.d.ts === +declare var $: { x: any }; +>$ : Symbol($, Decl(index.d.ts, 0, 11)) +>x : Symbol(x, Decl(index.d.ts, 0, 16)) + diff --git a/tests/baselines/reference/typingsLookup3.trace.json b/tests/baselines/reference/typingsLookup3.trace.json new file mode 100644 index 00000000000..83b0e91d6c7 --- /dev/null +++ b/tests/baselines/reference/typingsLookup3.trace.json @@ -0,0 +1,12 @@ +[ + "======== Resolving type reference directive 'jquery', containing file '/a.ts', root directory '/node_modules/@types'. ========", + "Resolving with primary search path '/node_modules/@types'", + "File '/node_modules/@types/jquery/package.json' does not exist.", + "File '/node_modules/@types/jquery/index.d.ts' exist - use it as a name resolution result.", + "======== Type reference directive 'jquery' was successfully resolved to '/node_modules/@types/jquery/index.d.ts', primary: true. ========", + "======== Resolving type reference directive 'jquery', containing file '/__inferred type names__.ts', root directory '/node_modules/@types'. ========", + "Resolving with primary search path '/node_modules/@types'", + "File '/node_modules/@types/jquery/package.json' does not exist.", + "File '/node_modules/@types/jquery/index.d.ts' exist - use it as a name resolution result.", + "======== Type reference directive 'jquery' was successfully resolved to '/node_modules/@types/jquery/index.d.ts', primary: true. ========" +] \ No newline at end of file diff --git a/tests/baselines/reference/typingsLookup3.types b/tests/baselines/reference/typingsLookup3.types new file mode 100644 index 00000000000..f57a974077e --- /dev/null +++ b/tests/baselines/reference/typingsLookup3.types @@ -0,0 +1,12 @@ +=== /a.ts === +/// +$.x; +>$.x : any +>$ : { x: any; } +>x : any + +=== /node_modules/@types/jquery/index.d.ts === +declare var $: { x: any }; +>$ : { x: any; } +>x : any + diff --git a/tests/cases/conformance/typings/typingsLookup3.ts b/tests/cases/conformance/typings/typingsLookup3.ts new file mode 100644 index 00000000000..a0bb15476b9 --- /dev/null +++ b/tests/cases/conformance/typings/typingsLookup3.ts @@ -0,0 +1,14 @@ +// @traceResolution: true +// @noImplicitReferences: true +// @currentDirectory: / +// This tests that `types` references are automatically lowercased. + +// @filename: /tsconfig.json +{ "files": "a.ts" } + +// @filename: /node_modules/@types/jquery/index.d.ts +declare var $: { x: any }; + +// @filename: /a.ts +/// +$.x; From 592805f486e9ccb755f0e286413af8343e4d0a3a Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 15 Aug 2016 07:56:45 -0700 Subject: [PATCH 135/233] Use proper response codes in web tests --- tests/webTestServer.ts | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/tests/webTestServer.ts b/tests/webTestServer.ts index ac5f046c1dd..4d2cb7d4672 100644 --- a/tests/webTestServer.ts +++ b/tests/webTestServer.ts @@ -117,19 +117,21 @@ function handleResolutionRequest(filePath: string, res: http.ServerResponse) { let resolvedPath = path.resolve(filePath, ""); resolvedPath = resolvedPath.substring(resolvedPath.indexOf("tests")); resolvedPath = switchToForwardSlashes(resolvedPath); - send("success", res, resolvedPath); - return; + send(ResponseCode.Success, res, resolvedPath); } -function send(result: "fail", res: http.ServerResponse, contents: string, contentType?: string): void; -function send(result: "success", res: http.ServerResponse, contents: string, contentType?: string): void; -function send(result: "unknown", res: http.ServerResponse, contents: string, contentType?: string): void; -function send(result: string, res: http.ServerResponse, contents: string, contentType?: string): void -function send(result: string, res: http.ServerResponse, contents: string, contentType = "binary"): void { - const responseCode = result === "success" ? 200 : result === "fail" ? 500 : result === "unknown" ? 404 : parseInt(result); +const enum ResponseCode { + Success = 200, + BadRequest = 400, + NotFound = 404, + MethodNotAllowed = 405, + PayloadTooLarge = 413, + Fail = 500 +} + +function send(responseCode: number, res: http.ServerResponse, contents: string, contentType = "binary"): void { res.writeHead(responseCode, { "Content-Type": contentType }); res.end(contents); - return; } // Reads the data from a post request and passes it to the given callback @@ -142,7 +144,7 @@ function processPost(req: http.ServerRequest, res: http.ServerResponse, callback queryData += data; if (queryData.length > 1e8) { queryData = ""; - send("413", res, undefined); + send(ResponseCode.PayloadTooLarge, res, undefined); console.log("ERROR: destroying connection"); req.connection.destroy(); } @@ -155,7 +157,7 @@ function processPost(req: http.ServerRequest, res: http.ServerResponse, callback } else { - send("405", res, undefined); + send(ResponseCode.MethodNotAllowed, res, undefined); } } @@ -201,16 +203,16 @@ function handleRequestOperation(req: http.ServerRequest, res: http.ServerRespons switch (operation) { case RequestType.GetDir: const filesInFolder = dir(reqPath, "", { recursive: true }); - send("success", res, filesInFolder.join(",")); + send(ResponseCode.Success, res, filesInFolder.join(",")); break; case RequestType.GetFile: fs.readFile(reqPath, (err, file) => { const contentType = contentTypeForExtension(path.extname(reqPath)); if (err) { - send("fail", res, err.message, contentType); + send(ResponseCode.NotFound, res, err.message, contentType); } else { - send("success", res, file, contentType); + send(ResponseCode.Success, res, file, contentType); } }); break; @@ -222,33 +224,33 @@ function handleRequestOperation(req: http.ServerRequest, res: http.ServerRespons processPost(req, res, (data) => { writeFile(reqPath, data, { recursive: true }); }); - send("success", res, undefined); + send(ResponseCode.Success, res, undefined); break; case RequestType.WriteDir: fs.mkdirSync(reqPath); - send("success", res, undefined); + send(ResponseCode.Success, res, undefined); break; case RequestType.DeleteFile: if (fs.existsSync(reqPath)) { fs.unlinkSync(reqPath); } - send("success", res, undefined); + send(ResponseCode.Success, res, undefined); break; case RequestType.DeleteDir: if (fs.existsSync(reqPath)) { fs.rmdirSync(reqPath); } - send("success", res, undefined); + send(ResponseCode.Success, res, undefined); break; case RequestType.AppendFile: processPost(req, res, (data) => { fs.appendFileSync(reqPath, data); }); - send("success", res, undefined); + send(ResponseCode.Success, res, undefined); break; case RequestType.Unknown: default: - send("unknown", res, undefined); + send(ResponseCode.BadRequest, res, undefined); break; } From ccf5bab8ad0a99c099ea21568ae602e6db165fbe Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 15 Aug 2016 08:51:15 -0700 Subject: [PATCH 136/233] Treat ambient shorthand declarations as explicit uses of the `any` type --- src/compiler/checker.ts | 5 ----- .../reference/ambientShorthand_isImplicitAny.errors.txt | 8 -------- .../baselines/reference/ambientShorthand_isImplicitAny.js | 5 ----- .../conformance/ambient/ambientShorthand_isImplicitAny.ts | 2 -- 4 files changed, 20 deletions(-) delete mode 100644 tests/baselines/reference/ambientShorthand_isImplicitAny.errors.txt delete mode 100644 tests/baselines/reference/ambientShorthand_isImplicitAny.js delete mode 100644 tests/cases/conformance/ambient/ambientShorthand_isImplicitAny.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 85e33c5e4a1..bf721a96bc3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17034,11 +17034,6 @@ namespace ts { } } - if (compilerOptions.noImplicitAny && !node.body) { - // Ambient shorthand module is an implicit any - reportImplicitAnyError(node, anyType); - } - if (node.body) { checkSourceElement(node.body); if (!isGlobalScopeAugmentation(node)) { diff --git a/tests/baselines/reference/ambientShorthand_isImplicitAny.errors.txt b/tests/baselines/reference/ambientShorthand_isImplicitAny.errors.txt deleted file mode 100644 index 875a656202d..00000000000 --- a/tests/baselines/reference/ambientShorthand_isImplicitAny.errors.txt +++ /dev/null @@ -1,8 +0,0 @@ -tests/cases/conformance/ambient/ambientShorthand_isImplicitAny.ts(1,16): error TS7005: Variable '"jquery"' implicitly has an 'any' type. - - -==== tests/cases/conformance/ambient/ambientShorthand_isImplicitAny.ts (1 errors) ==== - declare module "jquery"; - ~~~~~~~~ -!!! error TS7005: Variable '"jquery"' implicitly has an 'any' type. - \ No newline at end of file diff --git a/tests/baselines/reference/ambientShorthand_isImplicitAny.js b/tests/baselines/reference/ambientShorthand_isImplicitAny.js deleted file mode 100644 index ab05d2a9979..00000000000 --- a/tests/baselines/reference/ambientShorthand_isImplicitAny.js +++ /dev/null @@ -1,5 +0,0 @@ -//// [ambientShorthand_isImplicitAny.ts] -declare module "jquery"; - - -//// [ambientShorthand_isImplicitAny.js] diff --git a/tests/cases/conformance/ambient/ambientShorthand_isImplicitAny.ts b/tests/cases/conformance/ambient/ambientShorthand_isImplicitAny.ts deleted file mode 100644 index bf7de709ef2..00000000000 --- a/tests/cases/conformance/ambient/ambientShorthand_isImplicitAny.ts +++ /dev/null @@ -1,2 +0,0 @@ -// @noImplicitAny: true -declare module "jquery"; From 2eb159e269f79f1c7d86723b2a0d9c44739627fd Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 15 Aug 2016 10:34:07 -0700 Subject: [PATCH 137/233] Rename 'find' functions --- src/compiler/checker.ts | 2 +- src/compiler/core.ts | 9 ++++++--- src/compiler/utilities.ts | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 583ae5a1f35..37f13dbf807 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1032,7 +1032,7 @@ namespace ts { } function getDeclarationOfAliasSymbol(symbol: Symbol): Declaration { - return find(symbol.declarations, d => isAliasSymbolDeclaration(d) ? d : undefined); + return findMap(symbol.declarations, d => isAliasSymbolDeclaration(d) ? d : undefined); } function getTargetOfImportEqualsDeclaration(node: ImportEqualsDeclaration): Symbol { diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 36db85061b0..d2ea69b304d 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -110,7 +110,7 @@ namespace ts { } /** Works like Array.prototype.find, returning `undefined` if no element satisfying the predicate is found. */ - export function tryFind(array: T[], predicate: (element: T, index: number) => boolean): T | undefined { + export function find(array: T[], predicate: (element: T, index: number) => boolean): T | undefined { for (let i = 0, len = array.length; i < len; i++) { const value = array[i]; if (predicate(value, i)) { @@ -120,8 +120,11 @@ namespace ts { return undefined; } - /** Like `forEach`, but assumes existence of array and fails if no truthy value is found. */ - export function find(array: T[], callback: (element: T, index: number) => U | undefined): U { + /** + * Returns the first truthy result of `callback`, or else fails. + * This is like `forEach`, but never returns undefined. + */ + export function findMap(array: T[], callback: (element: T, index: number) => U | undefined): U { for (let i = 0, len = array.length; i < len; i++) { const result = callback(array[i], i); if (result) { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 0598c34bd7b..8b32d5351ea 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2715,7 +2715,7 @@ namespace ts { /** Return ".ts" or ".tsx" if that is the extension. */ export function tryExtractTypeScriptExtension(fileName: string): string | undefined { - return tryFind(supportedTypescriptExtensionsWithDtsFirst, extension => fileExtensionIs(fileName, extension)); + return find(supportedTypescriptExtensionsWithDtsFirst, extension => fileExtensionIs(fileName, extension)); } // Must have '.d.ts' first because if '.ts' goes first, that will be detected as the extension instead of '.d.ts'. const supportedTypescriptExtensionsWithDtsFirst = supportedTypeScriptExtensions.slice().reverse(); From a1dad91120378406332af99c81a5e9946e08e13e Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Mon, 15 Aug 2016 10:45:46 -0700 Subject: [PATCH 138/233] Parallel linting (#10313) * A perilous thing, a parallel lint * Use work queue rather than scheduling work * Dont read files for lint on main thread * Fix style --- Gulpfile.ts | 98 ++++++++++++++++----------- Jakefile.js | 142 ++++++++++++++++----------------------- scripts/parallel-lint.js | 45 +++++++++++++ 3 files changed, 162 insertions(+), 123 deletions(-) create mode 100644 scripts/parallel-lint.js diff --git a/Gulpfile.ts b/Gulpfile.ts index d677b042250..2d17325c4d8 100644 --- a/Gulpfile.ts +++ b/Gulpfile.ts @@ -34,7 +34,6 @@ import through2 = require("through2"); import merge2 = require("merge2"); import intoStream = require("into-stream"); import * as os from "os"; -import Linter = require("tslint"); import fold = require("travis-fold"); const gulp = helpMaker(originalGulp); const mochaParallel = require("./scripts/mocha-parallel.js"); @@ -929,26 +928,6 @@ gulp.task("build-rules", "Compiles tslint rules to js", () => { .pipe(gulp.dest(dest)); }); -function getLinterOptions() { - return { - configuration: require("./tslint.json"), - formatter: "prose", - formattersDirectory: undefined, - rulesDirectory: "built/local/tslint" - }; -} - -function lintFileContents(options, path, contents) { - const ll = new Linter(path, contents, options); - console.log("Linting '" + path + "'."); - return ll.lint(); -} - -function lintFile(options, path) { - const contents = fs.readFileSync(path, "utf8"); - return lintFileContents(options, path, contents); -} - const lintTargets = [ "Gulpfile.ts", "src/compiler/**/*.ts", @@ -960,29 +939,72 @@ const lintTargets = [ "tests/*.ts", "tests/webhost/*.ts" // Note: does *not* descend recursively ]; +function sendNextFile(files: {path: string}[], child: cp.ChildProcess, callback: (failures: number) => void, failures: number) { + const file = files.pop(); + if (file) { + console.log(`Linting '${file.path}'.`); + child.send({ kind: "file", name: file.path }); + } + else { + child.send({ kind: "close" }); + callback(failures); + } +} + +function spawnLintWorker(files: {path: string}[], callback: (failures: number) => void) { + const child = cp.fork("./scripts/parallel-lint"); + let failures = 0; + child.on("message", function(data) { + switch (data.kind) { + case "result": + if (data.failures > 0) { + failures += data.failures; + console.log(data.output); + } + sendNextFile(files, child, callback, failures); + break; + case "error": + console.error(data.error); + failures++; + sendNextFile(files, child, callback, failures); + break; + } + }); + sendNextFile(files, child, callback, failures); +} gulp.task("lint", "Runs tslint on the compiler sources. Optional arguments are: --f[iles]=regex", ["build-rules"], () => { const fileMatcher = RegExp(cmdLineOptions["files"]); - const lintOptions = getLinterOptions(); - let failed = 0; if (fold.isTravis()) console.log(fold.start("lint")); - return gulp.src(lintTargets) - .pipe(insert.transform((contents, file) => { - if (!fileMatcher.test(file.path)) return contents; - const result = lintFile(lintOptions, file.path); - if (result.failureCount > 0) { - console.log(result.output); - failed += result.failureCount; + + const files: {stat: fs.Stats, path: string}[] = []; + return gulp.src(lintTargets, { read: false }) + .pipe(through2.obj((chunk, enc, cb) => { + files.push(chunk); + cb(); + }, (cb) => { + files.filter(file => fileMatcher.test(file.path)).sort((filea, fileb) => filea.stat.size - fileb.stat.size); + const workerCount = (process.env.workerCount && +process.env.workerCount) || os.cpus().length; + for (let i = 0; i < workerCount; i++) { + spawnLintWorker(files, finished); } - return contents; // TODO (weswig): Automatically apply fixes? :3 - })) - .on("end", () => { - if (fold.isTravis()) console.log(fold.end("lint")); - if (failed > 0) { - console.error("Linter errors."); - process.exit(1); + + let completed = 0; + let failures = 0; + function finished(fails) { + completed++; + failures += fails; + if (completed === workerCount) { + if (fold.isTravis()) console.log(fold.end("lint")); + if (failures > 0) { + throw new Error(`Linter errors: ${failures}`); + } + else { + cb(); + } + } } - }); + })); }); diff --git a/Jakefile.js b/Jakefile.js index 01aac82a47a..e4aaf330dc7 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -4,7 +4,6 @@ var fs = require("fs"); var os = require("os"); var path = require("path"); var child_process = require("child_process"); -var Linter = require("tslint"); var fold = require("travis-fold"); var runTestsInParallel = require("./scripts/mocha-parallel").runTestsInParallel; @@ -1054,36 +1053,6 @@ task("build-rules-end", [] , function() { if (fold.isTravis()) console.log(fold.end("build-rules")); }); -function getLinterOptions() { - return { - configuration: require("./tslint.json"), - formatter: "prose", - formattersDirectory: undefined, - rulesDirectory: "built/local/tslint" - }; -} - -function lintFileContents(options, path, contents) { - var ll = new Linter(path, contents, options); - console.log("Linting '" + path + "'."); - return ll.lint(); -} - -function lintFile(options, path) { - var contents = fs.readFileSync(path, "utf8"); - return lintFileContents(options, path, contents); -} - -function lintFileAsync(options, path, cb) { - fs.readFile(path, "utf8", function(err, contents) { - if (err) { - return cb(err); - } - var result = lintFileContents(options, path, contents); - cb(undefined, result); - }); -} - var lintTargets = compilerSources .concat(harnessSources) // Other harness sources @@ -1094,75 +1063,78 @@ var lintTargets = compilerSources .concat(["Gulpfile.ts"]) .concat([nodeServerInFile, perftscPath, "tests/perfsys.ts", webhostPath]); +function sendNextFile(files, child, callback, failures) { + var file = files.pop(); + if (file) { + console.log("Linting '" + file + "'."); + child.send({kind: "file", name: file}); + } + else { + child.send({kind: "close"}); + callback(failures); + } +} + +function spawnLintWorker(files, callback) { + var child = child_process.fork("./scripts/parallel-lint"); + var failures = 0; + child.on("message", function(data) { + switch (data.kind) { + case "result": + if (data.failures > 0) { + failures += data.failures; + console.log(data.output); + } + sendNextFile(files, child, callback, failures); + break; + case "error": + console.error(data.error); + failures++; + sendNextFile(files, child, callback, failures); + break; + } + }); + sendNextFile(files, child, callback, failures); +} desc("Runs tslint on the compiler sources. Optional arguments are: f[iles]=regex"); task("lint", ["build-rules"], function() { if (fold.isTravis()) console.log(fold.start("lint")); var startTime = mark(); - var lintOptions = getLinterOptions(); var failed = 0; var fileMatcher = RegExp(process.env.f || process.env.file || process.env.files || ""); var done = {}; for (var i in lintTargets) { var target = lintTargets[i]; if (!done[target] && fileMatcher.test(target)) { - var result = lintFile(lintOptions, target); - if (result.failureCount > 0) { - console.log(result.output); - failed += result.failureCount; - } - done[target] = true; + done[target] = fs.statSync(target).size; } } - measure(startTime); - if (fold.isTravis()) console.log(fold.end("lint")); - if (failed > 0) { - fail('Linter errors.', failed); - } -}); -/** - * This is required because file watches on Windows get fires _twice_ - * when a file changes on some node/windows version configuations - * (node v4 and win 10, for example). By not running a lint for a file - * which already has a pending lint, we avoid duplicating our work. - * (And avoid printing duplicate results!) - */ -var lintSemaphores = {}; + var workerCount = (process.env.workerCount && +process.env.workerCount) || os.cpus().length; -function lintWatchFile(filename) { - fs.watch(filename, {persistent: true}, function(event) { - if (event !== "change") { - return; - } - - if (!lintSemaphores[filename]) { - lintSemaphores[filename] = true; - lintFileAsync(getLinterOptions(), filename, function(err, result) { - delete lintSemaphores[filename]; - if (err) { - console.log(err); - return; - } - if (result.failureCount > 0) { - console.log("***Lint failure***"); - for (var i = 0; i < result.failures.length; i++) { - var failure = result.failures[i]; - var start = failure.startPosition.lineAndCharacter; - var end = failure.endPosition.lineAndCharacter; - console.log("warning " + filename + " (" + (start.line + 1) + "," + (start.character + 1) + "," + (end.line + 1) + "," + (end.character + 1) + "): " + failure.failure); - } - console.log("*** Total " + result.failureCount + " failures."); - } - }); - } + var names = Object.keys(done).sort(function(namea, nameb) { + return done[namea] - done[nameb]; }); -} -desc("Watches files for changes to rerun a lint pass"); -task("lint-server", ["build-rules"], function() { - console.log("Watching ./src for changes to linted files"); - for (var i = 0; i < lintTargets.length; i++) { - lintWatchFile(lintTargets[i]); + for (var i = 0; i < workerCount; i++) { + spawnLintWorker(names, finished); } -}); + + var completed = 0; + var failures = 0; + function finished(fails) { + completed++; + failures += fails; + if (completed === workerCount) { + measure(startTime); + if (fold.isTravis()) console.log(fold.end("lint")); + if (failures > 0) { + fail('Linter errors.', failed); + } + else { + complete(); + } + } + } +}, {async: true}); diff --git a/scripts/parallel-lint.js b/scripts/parallel-lint.js new file mode 100644 index 00000000000..a9aec06c2df --- /dev/null +++ b/scripts/parallel-lint.js @@ -0,0 +1,45 @@ +var Linter = require("tslint"); +var fs = require("fs"); + +function getLinterOptions() { + return { + configuration: require("../tslint.json"), + formatter: "prose", + formattersDirectory: undefined, + rulesDirectory: "built/local/tslint" + }; +} + +function lintFileContents(options, path, contents) { + var ll = new Linter(path, contents, options); + return ll.lint(); +} + +function lintFileAsync(options, path, cb) { + fs.readFile(path, "utf8", function (err, contents) { + if (err) { + return cb(err); + } + var result = lintFileContents(options, path, contents); + cb(undefined, result); + }); +} + +process.on("message", function (data) { + switch (data.kind) { + case "file": + var target = data.name; + var lintOptions = getLinterOptions(); + lintFileAsync(lintOptions, target, function (err, result) { + if (err) { + process.send({ kind: "error", error: err.toString() }); + return; + } + process.send({ kind: "result", failures: result.failureCount, output: result.output }); + }); + break; + case "close": + process.exit(0); + break; + } +}); \ No newline at end of file From 2d1b68fdac39cf65fff40125d7d6cc10ebfb4ecb Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Mon, 15 Aug 2016 11:00:57 -0700 Subject: [PATCH 139/233] Fix the style fix (#10344) --- Gulpfile.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gulpfile.ts b/Gulpfile.ts index 2d17325c4d8..2718669b1f7 100644 --- a/Gulpfile.ts +++ b/Gulpfile.ts @@ -977,13 +977,13 @@ gulp.task("lint", "Runs tslint on the compiler sources. Optional arguments are: const fileMatcher = RegExp(cmdLineOptions["files"]); if (fold.isTravis()) console.log(fold.start("lint")); - const files: {stat: fs.Stats, path: string}[] = []; + let files: {stat: fs.Stats, path: string}[] = []; return gulp.src(lintTargets, { read: false }) .pipe(through2.obj((chunk, enc, cb) => { files.push(chunk); cb(); }, (cb) => { - files.filter(file => fileMatcher.test(file.path)).sort((filea, fileb) => filea.stat.size - fileb.stat.size); + files = files.filter(file => fileMatcher.test(file.path)).sort((filea, fileb) => filea.stat.size - fileb.stat.size); const workerCount = (process.env.workerCount && +process.env.workerCount) || os.cpus().length; for (let i = 0; i < workerCount; i++) { spawnLintWorker(files, finished); From 4e04b75d4bc4652408b99ab233de3aaa803a9467 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 15 Aug 2016 11:07:49 -0700 Subject: [PATCH 140/233] Aligned mark names with values used by ts-perf. --- src/compiler/binder.ts | 6 +++--- src/compiler/checker.ts | 6 +++--- src/compiler/parser.ts | 6 +++--- src/compiler/program.ts | 24 ++++++++++++------------ src/compiler/sourcemap.ts | 6 +++--- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index ff5a8b77cfd..b5ab2306989 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -89,10 +89,10 @@ namespace ts { const binder = createBinder(); export function bindSourceFile(file: SourceFile, options: CompilerOptions) { - performance.mark("bindStart"); + performance.mark("beforeBind"); binder(file, options); - performance.mark("bindEnd"); - performance.measure("Bind", "bindStart", "bindEnd"); + performance.mark("afterBind"); + performance.measure("Bind", "beforeBind", "afterBind"); } function createBinder(): (file: SourceFile, options: CompilerOptions) => void { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f1fff6737a3..b2b6b9442ac 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17535,10 +17535,10 @@ namespace ts { } function checkSourceFile(node: SourceFile) { - performance.mark("checkStart"); + performance.mark("beforeCheck"); checkSourceFileWorker(node); - performance.mark("checkEnd"); - performance.measure("Check", "checkStart", "checkEnd"); + performance.mark("afterCheck"); + performance.measure("Check", "beforeCheck", "afterCheck"); } // Fully type check a source file and collect the relevant diagnostics. diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index cbf767a5410..aca7d745730 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -421,10 +421,10 @@ namespace ts { } export function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes = false, scriptKind?: ScriptKind): SourceFile { - performance.mark("parseStart"); + performance.mark("beforeParse"); const result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, scriptKind); - performance.mark("parseEnd"); - performance.measure("Parse", "parseStart", "parseEnd"); + performance.mark("afterParse"); + performance.measure("Parse", "beforeParse", "afterParse"); return result; } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index a632eed7fd2..767fcb488bb 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -857,10 +857,10 @@ namespace ts { function getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile { let text: string; try { - performance.mark("ioReadStart"); + performance.mark("beforeIORead"); text = sys.readFile(fileName, options.charset); - performance.mark("ioReadEnd"); - performance.measure("I/O Read", "ioReadStart", "ioReadEnd"); + performance.mark("afterIORead"); + performance.measure("I/O Read", "beforeIORead", "afterIORead"); } catch (e) { if (onError) { @@ -927,7 +927,7 @@ namespace ts { function writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void) { try { - performance.mark("ioWriteStart"); + performance.mark("beforeIOWrite"); ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName))); if (isWatchSet(options) && sys.createHash && sys.getModifiedTime) { @@ -937,8 +937,8 @@ namespace ts { sys.writeFile(fileName, data, writeByteOrderMark); } - performance.mark("ioWriteEnd"); - performance.measure("I/O Write", "ioWriteStart", "ioWriteEnd"); + performance.mark("afterIOWrite"); + performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite"); } catch (e) { if (onError) { @@ -1120,7 +1120,7 @@ namespace ts { // Track source files that are source files found by searching under node_modules, as these shouldn't be compiled. const sourceFilesFoundSearchingNodeModules = createMap(); - performance.mark("programStart"); + performance.mark("beforeProgram"); host = host || createCompilerHost(options); @@ -1218,8 +1218,8 @@ namespace ts { }; verifyCompilerOptions(); - performance.mark("programEnd"); - performance.measure("Program", "programStart", "programEnd"); + performance.mark("afterProgram"); + performance.measure("Program", "beforeProgram", "afterProgram"); return program; @@ -1462,15 +1462,15 @@ namespace ts { // checked is to not pass the file to getEmitResolver. const emitResolver = getDiagnosticsProducingTypeChecker().getEmitResolver((options.outFile || options.out) ? undefined : sourceFile); - performance.mark("emitStart"); + performance.mark("beforeEmit"); const emitResult = emitFiles( emitResolver, getEmitHost(writeFileCallback), sourceFile); - performance.mark("emitEnd"); - performance.measure("Emit", "emitStart", "emitEnd"); + performance.mark("afterEmit"); + performance.measure("Emit", "beforeEmit", "afterEmit"); return emitResult; } diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index 52e8975db76..4046867faf1 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -242,7 +242,7 @@ namespace ts { } if (extendedDiagnostics) { - performance.mark("sourcemapStart"); + performance.mark("beforeSourcemap"); } const sourceLinePos = getLineAndCharacterOfPosition(currentSourceFile, pos); @@ -286,8 +286,8 @@ namespace ts { updateLastEncodedAndRecordedSpans(); if (extendedDiagnostics) { - performance.mark("sourcemapEnd"); - performance.measure("Source Map", "sourcemapStart", "sourcemapEnd"); + performance.mark("afterSourcemap"); + performance.measure("Source Map", "beforeSourcemap", "afterSourcemap"); } } From 2953c7f0b2f187aa68664d1e8f7fb4118f777992 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 15 Aug 2016 11:16:36 -0700 Subject: [PATCH 141/233] Use an enum in checkClassForDuplicateDeclarations to aid readability --- src/compiler/checker.ts | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ab76950ec4a..6f5d2dce121 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13794,15 +13794,19 @@ namespace ts { } function checkClassForDuplicateDeclarations(node: ClassLikeDeclaration) { - const getter = 1, setter = 2, property = getter | setter; + const enum Meaning { + Getter = 1, + Setter = 2, + Property = Getter | Setter + } - const instanceNames = createMap(); - const staticNames = createMap(); + const instanceNames = createMap(); + const staticNames = createMap(); for (const member of node.members) { if (member.kind === SyntaxKind.Constructor) { for (const param of (member as ConstructorDeclaration).parameters) { if (isParameterPropertyDeclaration(param)) { - addName(instanceNames, param.name, (param.name as Identifier).text, property); + addName(instanceNames, param.name, (param.name as Identifier).text, Meaning.Property); } } } @@ -13814,22 +13818,22 @@ namespace ts { if (memberName) { switch (member.kind) { case SyntaxKind.GetAccessor: - addName(names, member.name, memberName, getter); + addName(names, member.name, memberName, Meaning.Getter); break; case SyntaxKind.SetAccessor: - addName(names, member.name, memberName, setter); + addName(names, member.name, memberName, Meaning.Setter); break; case SyntaxKind.PropertyDeclaration: - addName(names, member.name, memberName, property); + addName(names, member.name, memberName, Meaning.Property); break; } } } } - function addName(names: Map, location: Node, name: string, meaning: number) { + function addName(names: Map, location: Node, name: string, meaning: Meaning) { const prev = names[name]; if (prev) { if (prev & meaning) { From 8f1960fd3474fdbfd70d3dd1d0d86aa6d3e17a2e Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 15 Aug 2016 11:43:40 -0700 Subject: [PATCH 142/233] Rename to Accessor --- src/compiler/checker.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6f5d2dce121..6e51e418ee4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13794,19 +13794,19 @@ namespace ts { } function checkClassForDuplicateDeclarations(node: ClassLikeDeclaration) { - const enum Meaning { + const enum Accessor { Getter = 1, Setter = 2, Property = Getter | Setter } - const instanceNames = createMap(); - const staticNames = createMap(); + const instanceNames = createMap(); + const staticNames = createMap(); for (const member of node.members) { if (member.kind === SyntaxKind.Constructor) { for (const param of (member as ConstructorDeclaration).parameters) { if (isParameterPropertyDeclaration(param)) { - addName(instanceNames, param.name, (param.name as Identifier).text, Meaning.Property); + addName(instanceNames, param.name, (param.name as Identifier).text, Accessor.Property); } } } @@ -13818,22 +13818,22 @@ namespace ts { if (memberName) { switch (member.kind) { case SyntaxKind.GetAccessor: - addName(names, member.name, memberName, Meaning.Getter); + addName(names, member.name, memberName, Accessor.Getter); break; case SyntaxKind.SetAccessor: - addName(names, member.name, memberName, Meaning.Setter); + addName(names, member.name, memberName, Accessor.Setter); break; case SyntaxKind.PropertyDeclaration: - addName(names, member.name, memberName, Meaning.Property); + addName(names, member.name, memberName, Accessor.Property); break; } } } } - function addName(names: Map, location: Node, name: string, meaning: Meaning) { + function addName(names: Map, location: Node, name: string, meaning: Accessor) { const prev = names[name]; if (prev) { if (prev & meaning) { From 7f0a02ff024a33fcf7b056b4273506e364dbde15 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 15 Aug 2016 12:03:39 -0700 Subject: [PATCH 143/233] Migrated more MapLikes to Maps --- src/compiler/checker.ts | 24 +- src/compiler/commandLineParser.ts | 33 +- src/compiler/core.ts | 287 +++++++++++++----- src/compiler/declarationEmitter.ts | 10 +- src/compiler/emitter.ts | 20 +- src/compiler/program.ts | 27 +- src/compiler/tsc.ts | 16 +- src/compiler/utilities.ts | 21 +- src/harness/fourslash.ts | 8 +- src/harness/harness.ts | 2 +- src/harness/harnessLanguageService.ts | 4 +- .../unittests/cachingInServerLSHost.ts | 2 +- .../unittests/reuseProgramStructure.ts | 16 +- .../unittests/tsserverProjectSystem.ts | 26 +- src/server/client.ts | 2 +- src/server/editorServices.ts | 20 +- src/services/jsTyping.ts | 8 +- src/services/navigateTo.ts | 2 +- src/services/navigationBar.ts | 2 +- src/services/services.ts | 28 +- src/services/shims.ts | 8 +- src/services/signatureHelp.ts | 2 +- tslint.json | 3 +- 23 files changed, 319 insertions(+), 252 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b2b6b9442ac..6a7cc108625 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -403,8 +403,8 @@ namespace ts { result.parent = symbol.parent; if (symbol.valueDeclaration) result.valueDeclaration = symbol.valueDeclaration; if (symbol.constEnumOnlyModule) result.constEnumOnlyModule = true; - if (symbol.members) result.members = cloneSymbolTable(symbol.members); - if (symbol.exports) result.exports = cloneSymbolTable(symbol.exports); + if (symbol.members) result.members = cloneMap(symbol.members); + if (symbol.exports) result.exports = cloneMap(symbol.exports); recordMergedSymbol(result, symbol); return result; } @@ -447,14 +447,6 @@ namespace ts { } } - function cloneSymbolTable(symbolTable: SymbolTable): SymbolTable { - const result = createMap(); - for (const id in symbolTable) { - result[id] = symbolTable[id]; - } - return result; - } - function mergeSymbolTable(target: SymbolTable, source: SymbolTable) { for (const id in source) { let targetSymbol = target[id]; @@ -1450,7 +1442,7 @@ namespace ts { return; } visitedSymbols.push(symbol); - const symbols = cloneSymbolTable(symbol.exports); + const symbols = cloneMap(symbol.exports); // All export * declarations are collected in an __export symbol by the binder const exportStars = symbol.exports["__export"]; if (exportStars) { @@ -1655,12 +1647,12 @@ namespace ts { } // If symbol is directly available by its name in the symbol table - if (isAccessible(lookUp(symbols, symbol.name))) { + if (isAccessible(symbols[symbol.name])) { return [symbol]; } // Check if symbol is any of the alias - return forEachValue(symbols, symbolFromSymbolTable => { + return forEachProperty(symbols, symbolFromSymbolTable => { if (symbolFromSymbolTable.flags & SymbolFlags.Alias && symbolFromSymbolTable.name !== "export=" && !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)) { @@ -6622,7 +6614,7 @@ namespace ts { const maybeCache = maybeStack[depth]; // If result is definitely true, copy assumptions to global cache, else copy to next level up const destinationCache = (result === Ternary.True || depth === 0) ? relation : maybeStack[depth - 1]; - copyMap(maybeCache, destinationCache); + copyProperties(maybeCache, destinationCache); } else { // A false result goes straight into global cache (when something is false under assumptions it @@ -7934,7 +7926,7 @@ namespace ts { // check. This gives us a quicker out in the common case where an object type is not a function. const resolved = resolveStructuredTypeMembers(type); return !!(resolved.callSignatures.length || resolved.constructSignatures.length || - hasProperty(resolved.members, "bind") && isTypeSubtypeOf(type, globalFunctionType)); + resolved.members["bind"] && isTypeSubtypeOf(type, globalFunctionType)); } function getTypeFacts(type: Type): TypeFacts { @@ -18192,7 +18184,7 @@ namespace ts { // otherwise - check if at least one export is value symbolLinks.exportsSomeValue = hasExportAssignment ? !!(moduleSymbol.flags & SymbolFlags.Value) - : forEachValue(getExportsOfModule(moduleSymbol), isValue); + : forEachProperty(getExportsOfModule(moduleSymbol), isValue); } return symbolLinks.exportsSomeValue; diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index eaf17f00dd0..44befb3943d 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -486,10 +486,11 @@ namespace ts { /* @internal */ export function createCompilerDiagnosticForInvalidCustomType(opt: CommandLineOptionOfCustomType): Diagnostic { const namesOfType: string[] = []; - forEachKey(opt.type, key => { - namesOfType.push(` '${key}'`); - }); - + for (const key in opt.type) { + if (hasProperty(opt.type, key)) { + namesOfType.push(` '${key}'`); + } + } return createCompilerDiagnostic(Diagnostics.Argument_for_0_option_must_be_Colon_1, `--${opt.name}`, namesOfType); } @@ -551,11 +552,11 @@ namespace ts { s = s.slice(s.charCodeAt(1) === CharacterCodes.minus ? 2 : 1).toLowerCase(); // Try to translate short option names to their full equivalents. - if (hasProperty(shortOptionNames, s)) { + if (s in shortOptionNames) { s = shortOptionNames[s]; } - if (hasProperty(optionNameMap, s)) { + if (s in optionNameMap) { const opt = optionNameMap[s]; if (opt.isTSConfigOnly) { @@ -811,7 +812,7 @@ namespace ts { const optionNameMap = arrayToMap(optionDeclarations, opt => opt.name); for (const id in jsonOptions) { - if (hasProperty(optionNameMap, id)) { + if (id in optionNameMap) { const opt = optionNameMap[id]; defaultOptions[opt.name] = convertJsonOption(opt, jsonOptions[id], basePath, errors); } @@ -1011,14 +1012,14 @@ namespace ts { removeWildcardFilesWithLowerPriorityExtension(file, wildcardFileMap, supportedExtensions, keyMapper); const key = keyMapper(file); - if (!hasProperty(literalFileMap, key) && !hasProperty(wildcardFileMap, key)) { + if (!(key in literalFileMap) && !(key in wildcardFileMap)) { wildcardFileMap[key] = file; } } } - const literalFiles = reduceProperties(literalFileMap, addFileToOutput, []); - const wildcardFiles = reduceProperties(wildcardFileMap, addFileToOutput, []); + const literalFiles = reduceOwnProperties(literalFileMap, addFileToOutput, []); + const wildcardFiles = reduceOwnProperties(wildcardFileMap, addFileToOutput, []); wildcardFiles.sort(host.useCaseSensitiveFileNames ? compareStrings : compareStringsCaseInsensitive); return { fileNames: literalFiles.concat(wildcardFiles), @@ -1076,7 +1077,7 @@ namespace ts { if (match) { const key = useCaseSensitiveFileNames ? match[0] : match[0].toLowerCase(); const flags = watchRecursivePattern.test(name) ? WatchDirectoryFlags.Recursive : WatchDirectoryFlags.None; - const existingFlags = getProperty(wildcardDirectories, key); + const existingFlags = wildcardDirectories[key]; if (existingFlags === undefined || existingFlags < flags) { wildcardDirectories[key] = flags; if (flags === WatchDirectoryFlags.Recursive) { @@ -1088,11 +1089,9 @@ namespace ts { // Remove any subpaths under an existing recursively watched directory. for (const key in wildcardDirectories) { - if (hasProperty(wildcardDirectories, key)) { - for (const recursiveKey of recursiveKeys) { - if (key !== recursiveKey && containsPath(recursiveKey, key, path, !useCaseSensitiveFileNames)) { - delete wildcardDirectories[key]; - } + for (const recursiveKey of recursiveKeys) { + if (key !== recursiveKey && containsPath(recursiveKey, key, path, !useCaseSensitiveFileNames)) { + delete wildcardDirectories[key]; } } } @@ -1115,7 +1114,7 @@ namespace ts { for (let i = ExtensionPriority.Highest; i < adjustedExtensionPriority; i++) { const higherPriorityExtension = extensions[i]; const higherPriorityPath = keyMapper(changeExtension(file, higherPriorityExtension)); - if (hasProperty(literalFiles, higherPriorityPath) || hasProperty(wildcardFiles, higherPriorityPath)) { + if (higherPriorityPath in literalFiles || higherPriorityPath in wildcardFiles) { return true; } } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 168a201c388..7bb5dbef2c5 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -21,10 +21,8 @@ namespace ts { const createObject = Object.create; - export function createMap(): Map { - /* tslint:disable:no-null-keyword */ - const map: Map = createObject(null); - /* tslint:enable:no-null-keyword */ + export function createMap(template?: MapLike): Map { + const map: Map = createObject(null); // tslint:disable-line:no-null-keyword // Using 'delete' on an object causes V8 to put the object in dictionary mode. // This disables creation of hidden classes, which are expensive when an object is @@ -32,6 +30,10 @@ namespace ts { map["__"] = undefined; delete map["__"]; + if (template) { + copyOwnProperties(template, map); + } + return map; } @@ -62,7 +64,7 @@ namespace ts { } function contains(path: Path) { - return hasProperty(files, toKey(path)); + return toKey(path) in files; } function remove(path: Path) { @@ -350,82 +352,192 @@ namespace ts { const hasOwnProperty = Object.prototype.hasOwnProperty; + /** + * Indicates whether a map-like contains an own property with the specified key. + * + * NOTE: This is intended for use only with MapLike objects. For Map objects, use + * the 'in' operator. + * + * @param map A map-like. + * @param key A property key. + */ export function hasProperty(map: MapLike, key: string): boolean { return hasOwnProperty.call(map, key); } - export function getKeys(map: MapLike): string[] { + /** + * Gets the value of an owned property in a map-like. + * + * NOTE: This is intended for use only with MapLike objects. For Map objects, use + * an indexer. + * + * @param map A map-like. + * @param key A property key. + */ + export function getProperty(map: MapLike, key: string): T | undefined { + return hasOwnProperty.call(map, key) ? map[key] : undefined; + } + + /** + * Gets the owned, enumerable property keys of a map-like. + * + * @param map A map-like. + */ + export function getOwnKeys(map: MapLike): string[] { const keys: string[] = []; - for (const key in map) { + for (const key in map) if (hasOwnProperty.call(map, key)) { keys.push(key); } return keys; } - export function getProperty(map: MapLike, key: string): T | undefined { - return hasProperty(map, key) ? map[key] : undefined; + /** + * Enumerates the properties of a Map, invoking a callback and returning the first truthy result. + * + * NOTE: This is intended for use with Map objects. For MapLike objects, use + * forEachOwnProperties instead as it offers better runtime safety. + * + * @param map A map for which properties should be enumerated. + * @param callback A callback to invoke for each property. + */ + export function forEachProperty(map: Map, callback: (value: T, key: string) => U): U { + let result: U; + for (const key in map) { + if (result = callback(map[key], key)) break; + } + return result; } - export function getOrUpdateProperty(map: MapLike, key: string, makeValue: () => T): T { - return hasProperty(map, key) ? map[key] : map[key] = makeValue(); + /** + * Enumerates the owned properties of a MapLike, invoking a callback and returning the first truthy result. + * + * NOTE: This is intended for use with MapLike objects. For Map objects, use + * forEachProperty instead as it offers better performance. + * + * @param map A map for which properties should be enumerated. + * @param callback A callback to invoke for each property. + */ + export function forEachOwnProperty(map: MapLike, callback: (value: T, key: string) => U): U { + let result: U; + for (const key in map) if (hasOwnProperty.call(map, key)) { + if (result = callback(map[key], key)) break; + } + return result; } - export function isEmpty(map: MapLike) { - for (const id in map) { - if (hasProperty(map, id)) { - return false; + /** + * Returns true if a Map has some matching property. + * + * @param map A map whose properties should be tested. + * @param predicate An optional callback used to test each property. + */ + export function someProperties(map: Map, predicate?: (value: T, key: string) => boolean) { + for (const key in map) { + if (!predicate || predicate(map[key], key)) return true; + } + return false; + } + + /** + * Performs a shallow copy of the properties from a source Map to a target MapLike + * + * NOTE: This is intended for use with Map objects. For MapLike objects, use + * copyOwnProperties instead as it offers better runtime safety. + * + * @param source A map from which properties should be copied. + * @param target A map to which properties should be copied. + */ + export function copyProperties(source: Map, target: MapLike): void { + for (const key in source) { + target[key] = source[key]; + } + } + + /** + * Performs a shallow copy of the owned properties from a source map to a target map-like. + * + * NOTE: This is intended for use with MapLike objects. For Map objects, use + * copyProperties instead as it offers better performance. + * + * @param source A map-like from which properties should be copied. + * @param target A map-like to which properties should be copied. + */ + export function copyOwnProperties(source: MapLike, target: MapLike): void { + for (const key in source) if (hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + + /** + * Reduce the properties of a map. + * + * NOTE: This is intended for use with Map objects. For MapLike objects, use + * reduceOwnProperties instead as it offers better runtime safety. + * + * @param map The map to reduce + * @param callback An aggregation function that is called for each entry in the map + * @param initial The initial value for the reduction. + */ + export function reduceProperties(map: Map, callback: (aggregate: U, value: T, key: string) => U, initial: U): U { + let result = initial; + for (const key in map) { + result = callback(result, map[key], String(key)); + } + return result; + } + + /** + * Reduce the properties defined on a map-like (but not from its prototype chain). + * + * NOTE: This is intended for use with MapLike objects. For Map objects, use + * reduceProperties instead as it offers better performance. + * + * @param map The map-like to reduce + * @param callback An aggregation function that is called for each entry in the map + * @param initial The initial value for the reduction. + */ + export function reduceOwnProperties(map: MapLike, callback: (aggregate: U, value: T, key: string) => U, initial: U): U { + let result = initial; + for (const key in map) if (hasOwnProperty.call(map, key)) { + result = callback(result, map[key], String(key)); + } + return result; + } + + /** + * Counts the owned properties of a map-like. + * + * @param map A map-like whose properties should be counted. + * @param predicate An optional callback used to limit which properties should be counted. + */ + export function countOwnProperties(map: MapLike, predicate?: (value: T, key: string) => boolean) { + let count = 0; + for (const key in map) if (hasOwnProperty.call(map, key)) { + if (!predicate || predicate(map[key], key)) { + count++; } } + return count; + } + + /** + * Performs a shallow equality comparison of the contents of two map-likes. + * + * @param left A map whose properties should be compared. + * @param right A map whose properties should be compared. + */ + export function equalOwnProperties(left: MapLike, right: MapLike) { + if (left === right) return true; + if (!left || !right) return false; + for (const key in left) if (hasOwnProperty.call(left, key)) { + if (!hasOwnProperty.call(right, key) === undefined || left[key] !== right[key]) return false; + } + for (const key in right) if (hasOwnProperty.call(right, key)) { + if (!hasOwnProperty.call(left, key)) return false; + } return true; } - export function clone(object: T): T { - const result: any = {}; - for (const id in object) { - result[id] = (object)[id]; - } - return result; - } - - export function extend, T2 extends MapLike<{}>>(first: T1 , second: T2): T1 & T2 { - const result: T1 & T2 = {}; - for (const id in first) { - (result as any)[id] = first[id]; - } - for (const id in second) { - if (!hasProperty(result, id)) { - (result as any)[id] = second[id]; - } - } - return result; - } - - export function forEachValue(map: MapLike, callback: (value: T) => U): U { - let result: U; - for (const id in map) { - if (result = callback(map[id])) break; - } - return result; - } - - export function forEachKey(map: MapLike, callback: (key: string) => U): U { - let result: U; - for (const id in map) { - if (result = callback(id)) break; - } - return result; - } - - export function lookUp(map: MapLike, key: string): T { - return hasProperty(map, key) ? map[key] : undefined; - } - - export function copyMap(source: MapLike, target: MapLike): void { - for (const p in source) { - target[p] = source[p]; - } - } - /** * Creates a map from the elements of an array. * @@ -436,33 +548,42 @@ namespace ts { * the same key with the given 'makeKey' function, then the element with the higher * index in the array will be the one associated with the produced key. */ - export function arrayToMap(array: T[], makeKey: (value: T) => string): Map { - const result = createMap(); - - forEach(array, value => { - result[makeKey(value)] = value; - }); - + export function arrayToMap(array: T[], makeKey: (value: T) => string): Map; + export function arrayToMap(array: T[], makeKey: (value: T) => string, makeValue: (value: T) => U): Map; + export function arrayToMap(array: T[], makeKey: (value: T) => string, makeValue?: (value: T) => U): Map { + const result = createMap(); + for (const value of array) { + result[makeKey(value)] = makeValue ? makeValue(value) : value; + } return result; } - /** - * Reduce the properties of a map. - * - * @param map The map to reduce - * @param callback An aggregation function that is called for each entry in the map - * @param initial The initial value for the reduction. - */ - export function reduceProperties(map: MapLike, callback: (aggregate: U, value: T, key: string) => U, initial: U): U { - let result = initial; - if (map) { - for (const key in map) { - if (hasProperty(map, key)) { - result = callback(result, map[key], String(key)); - } + export function cloneMap(map: Map) { + const clone = createMap(); + copyProperties(map, clone); + return clone; + } + + export function clone(object: T): T { + const result: any = {}; + for (const id in object) { + if (hasOwnProperty.call(object, id)) { + result[id] = (object)[id]; } } + return result; + } + export function extend, T2 extends MapLike<{}>>(first: T1 , second: T2): T1 & T2 { + const result: T1 & T2 = {}; + for (const id in first) { + (result as any)[id] = first[id]; + } + for (const id in second) { + if (!hasProperty(result, id)) { + (result as any)[id] = second[id]; + } + } return result; } diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index 56ff206ab01..3642ee7f10a 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -157,9 +157,7 @@ namespace ts { if (usedTypeDirectiveReferences) { for (const directive in usedTypeDirectiveReferences) { - if (hasProperty(usedTypeDirectiveReferences, directive)) { - referencesOutput += `/// ${newLine}`; - } + referencesOutput += `/// ${newLine}`; } } @@ -272,7 +270,7 @@ namespace ts { usedTypeDirectiveReferences = createMap(); } for (const directive of typeReferenceDirectives) { - if (!hasProperty(usedTypeDirectiveReferences, directive)) { + if (!(directive in usedTypeDirectiveReferences)) { usedTypeDirectiveReferences[directive] = directive; } } @@ -537,14 +535,14 @@ namespace ts { // do not need to keep track of created temp names. function getExportDefaultTempVariableName(): string { const baseName = "_default"; - if (!hasProperty(currentIdentifiers, baseName)) { + if (!(baseName in currentIdentifiers)) { return baseName; } let count = 0; while (true) { count++; const name = baseName + "_" + count; - if (!hasProperty(currentIdentifiers, name)) { + if (!(name in currentIdentifiers)) { return name; } } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 1d62a4a4f46..47495cc69bb 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -407,7 +407,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge function isUniqueLocalName(name: string, container: Node): boolean { for (let node = container; isNodeDescendentOf(node, container); node = node.nextContainer) { - if (node.locals && hasProperty(node.locals, name)) { + if (node.locals && name in node.locals) { // We conservatively include alias symbols to cover cases where they're emitted as locals if (node.locals[name].flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) { return false; @@ -669,8 +669,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge function isUniqueName(name: string): boolean { return !resolver.hasGlobalName(name) && - !hasProperty(currentFileIdentifiers, name) && - !hasProperty(generatedNameSet, name); + !(name in currentFileIdentifiers) && + !(name in generatedNameSet); } // Return the next available name in the pattern _a ... _z, _0, _1, ... @@ -2646,7 +2646,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge return false; } - return !exportEquals && exportSpecifiers && hasProperty(exportSpecifiers, (node).text); + return !exportEquals && exportSpecifiers && (node).text in exportSpecifiers; } function emitPrefixUnaryExpression(node: PrefixUnaryExpression) { @@ -3263,7 +3263,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge write(", "); } - if (!hasProperty(seen, id.text)) { + if (!(id.text in seen)) { emit(id); seen[id.text] = id.text; } @@ -3970,7 +3970,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge return; } - if (!exportEquals && exportSpecifiers && hasProperty(exportSpecifiers, name.text)) { + if (!exportEquals && exportSpecifiers && name.text in exportSpecifiers) { for (const specifier of exportSpecifiers[name.text]) { writeLine(); emitStart(specifier.name); @@ -6842,7 +6842,7 @@ const _super = (function (geti, seti) { // export { x, y } for (const specifier of (node).exportClause.elements) { const name = (specifier.propertyName || specifier.name).text; - getOrUpdateProperty(exportSpecifiers, name, () => []).push(specifier); + (exportSpecifiers[name] || (exportSpecifiers[name] = [])).push(specifier); } } break; @@ -6941,7 +6941,7 @@ const _super = (function (geti, seti) { } // local names set should only be added if we have anything exported - if (!exportedDeclarations && isEmpty(exportSpecifiers)) { + if (!exportedDeclarations && !someProperties(exportSpecifiers)) { // no exported declarations (export var ...) or export specifiers (export {x}) // check if we have any non star export declarations. let hasExportDeclarationWithExportClause = false; @@ -7091,7 +7091,7 @@ const _super = (function (geti, seti) { if (name) { // do not emit duplicate entries (in case of declaration merging) in the list of hoisted variables const text = unescapeIdentifier(name.text); - if (hasProperty(seen, text)) { + if (text in seen) { continue; } else { @@ -7460,7 +7460,7 @@ const _super = (function (geti, seti) { // for deduplication purposes in key remove leading and trailing quotes so 'a' and "a" will be considered the same const key = text.substr(1, text.length - 2); - if (hasProperty(groupIndices, key)) { + if (key in groupIndices) { // deduplicate/group entries in dependency list by the dependency name const groupIndex = groupIndices[key]; dependencyGroups[groupIndex].push(externalImports[i]); diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 767fcb488bb..0bf67b0ddb0 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -501,7 +501,7 @@ namespace ts { if (state.traceEnabled) { trace(state.host, Diagnostics.paths_option_is_specified_looking_for_a_pattern_to_match_module_name_0, moduleName); } - matchedPattern = matchPatternOrExact(getKeys(state.compilerOptions.paths), moduleName); + matchedPattern = matchPatternOrExact(getOwnKeys(state.compilerOptions.paths), moduleName); } if (matchedPattern) { @@ -875,7 +875,7 @@ namespace ts { } function directoryExists(directoryPath: string): boolean { - if (hasProperty(existingDirectories, directoryPath)) { + if (directoryPath in existingDirectories) { return true; } if (sys.directoryExists(directoryPath)) { @@ -903,7 +903,7 @@ namespace ts { const hash = sys.createHash(data); const mtimeBefore = sys.getModifiedTime(fileName); - if (mtimeBefore && hasProperty(outputFingerprints, fileName)) { + if (mtimeBefore && fileName in outputFingerprints) { const fingerprint = outputFingerprints[fileName]; // If output has not been changed, and the file has no external modification @@ -1041,14 +1041,9 @@ namespace ts { const resolutions: T[] = []; const cache = createMap(); for (const name of names) { - let result: T; - if (hasProperty(cache, name)) { - result = cache[name]; - } - else { - result = loader(name, containingFile); - cache[name] = result; - } + const result = name in cache + ? cache[name] + : cache[name] = loader(name, containingFile); resolutions.push(result); } return resolutions; @@ -1249,7 +1244,7 @@ namespace ts { classifiableNames = createMap(); for (const sourceFile of files) { - copyMap(sourceFile.classifiableNames, classifiableNames); + copyProperties(sourceFile.classifiableNames, classifiableNames); } } @@ -1277,7 +1272,7 @@ namespace ts { (oldOptions.maxNodeModuleJsDepth !== options.maxNodeModuleJsDepth) || !arrayIsEqualTo(oldOptions.typeRoots, oldOptions.typeRoots) || !arrayIsEqualTo(oldOptions.rootDirs, options.rootDirs) || - !mapIsEqualTo(oldOptions.paths, options.paths)) { + !equalOwnProperties(oldOptions.paths, options.paths)) { return false; } @@ -1399,7 +1394,7 @@ namespace ts { getSourceFile: program.getSourceFile, getSourceFileByPath: program.getSourceFileByPath, getSourceFiles: program.getSourceFiles, - isSourceFileFromExternalLibrary: (file: SourceFile) => !!lookUp(sourceFilesFoundSearchingNodeModules, file.path), + isSourceFileFromExternalLibrary: (file: SourceFile) => !!sourceFilesFoundSearchingNodeModules[file.path], writeFile: writeFileCallback || ( (fileName, data, writeByteOrderMark, onError, sourceFiles) => host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles)), isEmitBlocked, @@ -1937,7 +1932,7 @@ namespace ts { // If the file was previously found via a node_modules search, but is now being processed as a root file, // then everything it sucks in may also be marked incorrectly, and needs to be checked again. - if (file && lookUp(sourceFilesFoundSearchingNodeModules, file.path) && currentNodeModulesDepth == 0) { + if (file && sourceFilesFoundSearchingNodeModules[file.path] && currentNodeModulesDepth == 0) { sourceFilesFoundSearchingNodeModules[file.path] = false; if (!options.noResolve) { processReferencedFiles(file, getDirectoryPath(fileName), isDefaultLib); @@ -1948,7 +1943,7 @@ namespace ts { processImportedModules(file, getDirectoryPath(fileName)); } // See if we need to reprocess the imports due to prior skipped imports - else if (file && lookUp(modulesWithElidedImports, file.path)) { + else if (file && modulesWithElidedImports[file.path]) { if (currentNodeModulesDepth < maxNodeModulesJsDepth) { modulesWithElidedImports[file.path] = false; processImportedModules(file, getDirectoryPath(fileName)); diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 3b588edd101..73cf8e9c4e2 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -445,10 +445,9 @@ namespace ts { } function cachedFileExists(fileName: string): boolean { - if (hasProperty(cachedExistingFiles, fileName)) { - return cachedExistingFiles[fileName]; - } - return cachedExistingFiles[fileName] = hostFileExists(fileName); + return fileName in cachedExistingFiles + ? cachedExistingFiles[fileName] + : cachedExistingFiles[fileName] = hostFileExists(fileName); } function getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void) { @@ -704,9 +703,12 @@ namespace ts { description = getDiagnosticText(option.description); const options: string[] = []; const element = (option).element; - forEachKey(>element.type, key => { - options.push(`'${key}'`); - }); + const typeMap = >element.type; + for (const key in typeMap) { + if (hasProperty(typeMap, key)) { + options.push(`'${key}'`); + } + } optionsDescriptionMap[description] = options; } else { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index fcda8f0ce1a..779e479cc43 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -87,25 +87,6 @@ namespace ts { return node.end - node.pos; } - export function mapIsEqualTo(map1: MapLike, map2: MapLike): boolean { - if (!map1 || !map2) { - return map1 === map2; - } - return containsAll(map1, map2) && containsAll(map2, map1); - } - - function containsAll(map: MapLike, other: MapLike): boolean { - for (const key in map) { - if (!hasProperty(map, key)) { - continue; - } - if (!hasProperty(other, key) || map[key] !== other[key]) { - return false; - } - } - return true; - } - export function arrayIsEqualTo(array1: T[], array2: T[], equaler?: (a: T, b: T) => boolean): boolean { if (!array1 || !array2) { return array1 === array2; @@ -2792,7 +2773,7 @@ namespace ts { } function stringifyObject(value: any) { - return `{${reduceProperties(value, stringifyProperty, "")}}`; + return `{${reduceOwnProperties(value, stringifyProperty, "")}}`; } function stringifyProperty(memo: string, value: any, key: string) { diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index e7e076a9eae..345ce19f6ae 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -204,7 +204,7 @@ namespace FourSlash { public formatCodeOptions: ts.FormatCodeOptions; - private inputFiles: ts.MapLike = {}; // Map between inputFile's fileName and its content for easily looking up when resolving references + private inputFiles = ts.createMap(); // Map between inputFile's fileName and its content for easily looking up when resolving references // Add input file which has matched file name with the given reference-file path. // This is necessary when resolveReference flag is specified @@ -300,11 +300,11 @@ namespace FourSlash { } else { // resolveReference file-option is not specified then do not resolve any files and include all inputFiles - ts.forEachKey(this.inputFiles, fileName => { + for (const fileName in this.inputFiles) { if (!Harness.isDefaultLibraryFile(fileName)) { this.languageServiceAdapterHost.addScript(fileName, this.inputFiles[fileName], /*isRootFile*/ true); } - }); + } this.languageServiceAdapterHost.addScript(Harness.Compiler.defaultLibFileName, Harness.Compiler.getDefaultLibrarySourceFile().text, /*isRootFile*/ false); } @@ -773,7 +773,7 @@ namespace FourSlash { } public verifyRangesWithSameTextReferenceEachOther() { - ts.forEachValue(this.rangesByText(), ranges => this.verifyRangesReferenceEachOther(ranges)); + ts.forEachOwnProperty(this.rangesByText(), ranges => this.verifyRangesReferenceEachOther(ranges)); } private verifyReferencesWorker(references: ts.ReferenceEntry[], fileName: string, start: number, end: number, isWriteAccess?: boolean, isDefinition?: boolean) { diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 5ae33303d55..1f7c48d21bc 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1011,7 +1011,7 @@ namespace Harness { optionsIndex[option.name.toLowerCase()] = option; } } - return ts.lookUp(optionsIndex, name.toLowerCase()); + return ts.getProperty(optionsIndex, name.toLowerCase()); } export function setCompilerOptionsFromHarnessSetting(settings: Harness.TestCaseParser.CompilerSettings, options: ts.CompilerOptions & HarnessOptions): void { diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 4f95f19e144..b03d8788165 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -135,7 +135,7 @@ namespace Harness.LanguageService { public getFilenames(): string[] { const fileNames: string[] = []; - ts.forEachValue(this.fileNameToScript, (scriptInfo) => { + ts.forEachOwnProperty(this.fileNameToScript, (scriptInfo) => { if (scriptInfo.isRootFile) { // only include root files here // usually it means that we won't include lib.d.ts in the list of root files so it won't mess the computation of compilation root dir. @@ -146,7 +146,7 @@ namespace Harness.LanguageService { } public getScriptInfo(fileName: string): ScriptInfo { - return ts.lookUp(this.fileNameToScript, fileName); + return ts.getProperty(this.fileNameToScript, fileName); } public addScript(fileName: string, content: string, isRootFile: boolean): void { diff --git a/src/harness/unittests/cachingInServerLSHost.ts b/src/harness/unittests/cachingInServerLSHost.ts index a1e010b97df..61ab38b2d8f 100644 --- a/src/harness/unittests/cachingInServerLSHost.ts +++ b/src/harness/unittests/cachingInServerLSHost.ts @@ -8,7 +8,7 @@ namespace ts { function createDefaultServerHost(fileMap: MapLike): server.ServerHost { const existingDirectories: MapLike = {}; - forEachValue(fileMap, v => { + forEachOwnProperty(fileMap, v => { let dir = getDirectoryPath(v.name); let previous: string; do { diff --git a/src/harness/unittests/reuseProgramStructure.ts b/src/harness/unittests/reuseProgramStructure.ts index 76015667232..9d8b176a6a8 100644 --- a/src/harness/unittests/reuseProgramStructure.ts +++ b/src/harness/unittests/reuseProgramStructure.ts @@ -130,7 +130,7 @@ namespace ts { }, fileExists: fileName => hasProperty(files, fileName), readFile: fileName => { - const file = lookUp(files, fileName); + const file = getProperty(files, fileName); return file && file.text; } }; @@ -152,16 +152,6 @@ namespace ts { return program; } - function getSizeOfMap(map: MapLike): number { - let size = 0; - for (const id in map) { - if (hasProperty(map, id)) { - size++; - } - } - return size; - } - function checkResolvedModule(expected: ResolvedModule, actual: ResolvedModule): void { assert.isTrue(actual !== undefined); assert.isTrue(expected.resolvedFileName === actual.resolvedFileName, `'resolvedFileName': expected '${expected.resolvedFileName}' to be equal to '${actual.resolvedFileName}'`); @@ -183,8 +173,8 @@ namespace ts { } else { assert.isTrue(cache !== undefined, `expected ${caption} to be set`); - const actualCacheSize = getSizeOfMap(cache); - const expectedSize = getSizeOfMap(expectedContent); + const actualCacheSize = countOwnProperties(cache); + const expectedSize = countOwnProperties(expectedContent); assert.isTrue(actualCacheSize === expectedSize, `expected actual size: ${actualCacheSize} to be equal to ${expectedSize}`); for (const id in expectedContent) { diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 74b9e303cf7..1e76a636c9a 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -68,20 +68,10 @@ namespace ts { return entry; } - function sizeOfMap(map: MapLike): number { - let n = 0; - for (const name in map) { - if (hasProperty(map, name)) { - n++; - } - } - return n; - } - function checkMapKeys(caption: string, map: MapLike, expectedKeys: string[]) { - assert.equal(sizeOfMap(map), expectedKeys.length, `${caption}: incorrect size of map`); + assert.equal(countOwnProperties(map), expectedKeys.length, `${caption}: incorrect size of map`); for (const name of expectedKeys) { - assert.isTrue(hasProperty(map, name), `${caption} is expected to contain ${name}, actual keys: ${getKeys(map)}`); + assert.isTrue(hasProperty(map, name), `${caption} is expected to contain ${name}, actual keys: ${getOwnKeys(map)}`); } } @@ -208,7 +198,7 @@ namespace ts { watchDirectory(directoryName: string, callback: DirectoryWatcherCallback, recursive: boolean): DirectoryWatcher { const path = this.toPath(directoryName); - const callbacks = lookUp(this.watchedDirectories, path) || (this.watchedDirectories[path] = []); + const callbacks = getProperty(this.watchedDirectories, path) || (this.watchedDirectories[path] = []); callbacks.push({ cb: callback, recursive }); return { referenceCount: 0, @@ -229,7 +219,7 @@ namespace ts { triggerDirectoryWatcherCallback(directoryName: string, fileName: string): void { const path = this.toPath(directoryName); - const callbacks = lookUp(this.watchedDirectories, path); + const callbacks = getProperty(this.watchedDirectories, path); if (callbacks) { for (const callback of callbacks) { callback.cb(fileName); @@ -239,7 +229,7 @@ namespace ts { triggerFileWatcherCallback(fileName: string, removed?: boolean): void { const path = this.toPath(fileName); - const callbacks = lookUp(this.watchedFiles, path); + const callbacks = getProperty(this.watchedFiles, path); if (callbacks) { for (const callback of callbacks) { callback(path, removed); @@ -249,7 +239,7 @@ namespace ts { watchFile(fileName: string, callback: FileWatcherCallback) { const path = this.toPath(fileName); - const callbacks = lookUp(this.watchedFiles, path) || (this.watchedFiles[path] = []); + const callbacks = getProperty(this.watchedFiles, path) || (this.watchedFiles[path] = []); callbacks.push(callback); return { close: () => { @@ -594,7 +584,7 @@ namespace ts { content: `{ "compilerOptions": { "target": "es6" - }, + }, "files": [ "main.ts" ] }` }; @@ -621,7 +611,7 @@ namespace ts { content: `{ "compilerOptions": { "target": "es6" - }, + }, "files": [ "main.ts" ] }` }; diff --git a/src/server/client.ts b/src/server/client.ts index 3547ac52602..88177e91fad 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -37,7 +37,7 @@ namespace ts.server { } private getLineMap(fileName: string): number[] { - let lineMap = ts.lookUp(this.lineMaps, fileName); + let lineMap = this.lineMaps[fileName]; if (!lineMap) { const scriptSnapshot = this.host.getScriptSnapshot(fileName); lineMap = this.lineMaps[fileName] = ts.computeLineStarts(scriptSnapshot.getText(0, scriptSnapshot.getLength())); diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index a14c42f471e..9ba243d9704 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -136,9 +136,9 @@ namespace ts.server { for (const name of names) { // check if this is a duplicate entry in the list - let resolution = lookUp(newResolutions, name); + let resolution = newResolutions[name]; if (!resolution) { - const existingResolution = currentResolutionsInFile && ts.lookUp(currentResolutionsInFile, name); + const existingResolution = currentResolutionsInFile && currentResolutionsInFile[name]; if (moduleResolutionIsValid(existingResolution)) { // ok, it is safe to use existing name resolution results resolution = existingResolution; @@ -563,7 +563,7 @@ namespace ts.server { } let strBuilder = ""; - ts.forEachValue(this.filenameToSourceFile, + ts.forEachProperty(this.filenameToSourceFile, sourceFile => { strBuilder += sourceFile.fileName + "\n"; }); return strBuilder; } @@ -857,7 +857,7 @@ namespace ts.server { if (project.isConfiguredProject()) { project.projectFileWatcher.close(); project.directoryWatcher.close(); - forEachValue(project.directoriesWatchedForWildcards, watcher => { watcher.close(); }); + forEachProperty(project.directoriesWatchedForWildcards, watcher => { watcher.close(); }); delete project.directoriesWatchedForWildcards; this.configuredProjects = copyListRemovingItem(project, this.configuredProjects); } @@ -1124,7 +1124,7 @@ namespace ts.server { getScriptInfo(filename: string) { filename = ts.normalizePath(filename); - return ts.lookUp(this.filenameToScriptInfo, filename); + return this.filenameToScriptInfo[filename]; } /** @@ -1133,7 +1133,7 @@ namespace ts.server { */ openFile(fileName: string, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind) { fileName = ts.normalizePath(fileName); - let info = ts.lookUp(this.filenameToScriptInfo, fileName); + let info = this.filenameToScriptInfo[fileName]; if (!info) { let content: string; if (this.host.fileExists(fileName)) { @@ -1246,7 +1246,7 @@ namespace ts.server { * @param filename is absolute pathname */ closeClientFile(filename: string) { - const info = ts.lookUp(this.filenameToScriptInfo, filename); + const info = this.filenameToScriptInfo[filename]; if (info) { this.closeOpenFile(info); info.isOpen = false; @@ -1255,14 +1255,14 @@ namespace ts.server { } getProjectForFile(filename: string) { - const scriptInfo = ts.lookUp(this.filenameToScriptInfo, filename); + const scriptInfo = this.filenameToScriptInfo[filename]; if (scriptInfo) { return scriptInfo.defaultProject; } } printProjectsForFile(filename: string) { - const scriptInfo = ts.lookUp(this.filenameToScriptInfo, filename); + const scriptInfo = this.filenameToScriptInfo[filename]; if (scriptInfo) { this.psLogger.startGroup(); this.psLogger.info("Projects for " + filename); @@ -1419,7 +1419,7 @@ namespace ts.server { /*recursive*/ true ); - project.directoriesWatchedForWildcards = reduceProperties(projectOptions.wildcardDirectories, (watchers, flag, directory) => { + project.directoriesWatchedForWildcards = reduceOwnProperties(projectOptions.wildcardDirectories, (watchers, flag, directory) => { if (comparePaths(configDirectoryPath, directory, ".", !this.host.useCaseSensitiveFileNames) !== Comparison.EqualTo) { const recursive = (flag & WatchDirectoryFlags.Recursive) !== 0; this.log(`Add ${ recursive ? "recursive " : ""}watcher for: ${directory}`); diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index 05de0061d39..bac33a6bdfb 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -139,16 +139,16 @@ namespace ts.JsTyping { const jsonConfig: PackageJson = result.config; filesToWatch.push(jsonPath); if (jsonConfig.dependencies) { - mergeTypings(getKeys(jsonConfig.dependencies)); + mergeTypings(getOwnKeys(jsonConfig.dependencies)); } if (jsonConfig.devDependencies) { - mergeTypings(getKeys(jsonConfig.devDependencies)); + mergeTypings(getOwnKeys(jsonConfig.devDependencies)); } if (jsonConfig.optionalDependencies) { - mergeTypings(getKeys(jsonConfig.optionalDependencies)); + mergeTypings(getOwnKeys(jsonConfig.optionalDependencies)); } if (jsonConfig.peerDependencies) { - mergeTypings(getKeys(jsonConfig.peerDependencies)); + mergeTypings(getOwnKeys(jsonConfig.peerDependencies)); } } } diff --git a/src/services/navigateTo.ts b/src/services/navigateTo.ts index e9afb6925b5..ccded188e61 100644 --- a/src/services/navigateTo.ts +++ b/src/services/navigateTo.ts @@ -15,7 +15,7 @@ namespace ts.NavigateTo { const nameToDeclarations = sourceFile.getNamedDeclarations(); for (const name in nameToDeclarations) { - const declarations = getProperty(nameToDeclarations, name); + const declarations = nameToDeclarations[name]; if (declarations) { // First do a quick check to see if the name of the declaration matches the // last portion of the (possibly) dotted name they're searching for. diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index ebd6936ea85..dda9ef485d2 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -243,7 +243,7 @@ namespace ts.NavigationBar { return true; } - const itemsWithSameName = getProperty(nameToItems, name); + const itemsWithSameName = nameToItems[name]; if (!itemsWithSameName) { nameToItems[name] = child; return true; diff --git a/src/services/services.ts b/src/services/services.ts index 850e29030d8..0c1f5929fbf 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -990,7 +990,7 @@ namespace ts { } function getDeclarations(name: string) { - return getProperty(result, name) || (result[name] = []); + return result[name] || (result[name] = []); } function getDeclarationName(declaration: Declaration) { @@ -2042,7 +2042,7 @@ namespace ts { function fixupCompilerOptions(options: CompilerOptions, diagnostics: Diagnostic[]): CompilerOptions { // Lazily create this value to fix module loading errors. commandLineOptionsStringToEnum = commandLineOptionsStringToEnum || filter(optionDeclarations, o => - typeof o.type === "object" && !forEachValue(>o.type, v => typeof v !== "number")); + typeof o.type === "object" && !forEachOwnProperty(>o.type, v => typeof v !== "number")); options = clone(options); @@ -2058,7 +2058,7 @@ namespace ts { options[opt.name] = parseCustomTypeOption(opt, value, diagnostics); } else { - if (!forEachValue(opt.type, v => v === value)) { + if (!forEachOwnProperty(opt.type, v => v === value)) { // Supplied value isn't a valid enum value. diagnostics.push(createCompilerDiagnosticForInvalidCustomType(opt)); } @@ -2251,7 +2251,7 @@ namespace ts { } function getBucketForCompilationSettings(key: DocumentRegistryBucketKey, createIfMissing: boolean): FileMap { - let bucket = lookUp(buckets, key); + let bucket = buckets[key]; if (!bucket && createIfMissing) { buckets[key] = bucket = createFileMap(); } @@ -2260,7 +2260,7 @@ namespace ts { function reportStats() { const bucketInfoArray = Object.keys(buckets).filter(name => name && name.charAt(0) === "_").map(name => { - const entries = lookUp(buckets, name); + const entries = buckets[name]; const sourceFiles: { name: string; refCount: number; references: string[]; }[] = []; entries.forEachValue((key, entry) => { sourceFiles.push({ @@ -3099,7 +3099,7 @@ namespace ts { oldSettings.allowJs !== newSettings.allowJs || oldSettings.disableSizeLimit !== oldSettings.disableSizeLimit || oldSettings.baseUrl !== newSettings.baseUrl || - !mapIsEqualTo(oldSettings.paths, newSettings.paths)); + !equalOwnProperties(oldSettings.paths, newSettings.paths)); // Now create a new compiler const compilerHost: CompilerHost = { @@ -4114,11 +4114,11 @@ namespace ts { existingImportsOrExports[name.text] = true; } - if (isEmpty(existingImportsOrExports)) { + if (!someProperties(existingImportsOrExports)) { return filter(exportsOfModule, e => e.name !== "default"); } - return filter(exportsOfModule, e => e.name !== "default" && !lookUp(existingImportsOrExports, e.name)); + return filter(exportsOfModule, e => e.name !== "default" && !existingImportsOrExports[e.name]); } /** @@ -4165,7 +4165,7 @@ namespace ts { existingMemberNames[existingName] = true; } - return filter(contextualMemberSymbols, m => !lookUp(existingMemberNames, m.name)); + return filter(contextualMemberSymbols, m => !existingMemberNames[m.name]); } /** @@ -4187,7 +4187,7 @@ namespace ts { } } - return filter(symbols, a => !lookUp(seenNames, a.name)); + return filter(symbols, a => !seenNames[a.name]); } } @@ -4323,7 +4323,7 @@ namespace ts { const entry = createCompletionEntry(symbol, location, performCharacterChecks); if (entry) { const id = escapeIdentifier(entry.name); - if (!lookUp(uniqueNames, id)) { + if (!uniqueNames[id]) { entries.push(entry); uniqueNames[id] = id; } @@ -5151,7 +5151,7 @@ namespace ts { // Type reference directives const typeReferenceDirective = findReferenceInPosition(sourceFile.typeReferenceDirectives, position); if (typeReferenceDirective) { - const referenceFile = lookUp(program.getResolvedTypeReferenceDirectives(), typeReferenceDirective.fileName); + const referenceFile = program.getResolvedTypeReferenceDirectives()[typeReferenceDirective.fileName]; if (referenceFile && referenceFile.resolvedFileName) { return [getDefinitionInfoForFileReference(typeReferenceDirective.fileName, referenceFile.resolvedFileName)]; } @@ -5323,7 +5323,7 @@ namespace ts { for (const referencedSymbol of referencedSymbols) { for (const referenceEntry of referencedSymbol.references) { const fileName = referenceEntry.fileName; - let documentHighlights = getProperty(fileNameToDocumentHighlights, fileName); + let documentHighlights = fileNameToDocumentHighlights[fileName]; if (!documentHighlights) { documentHighlights = { fileName, highlightSpans: [] }; @@ -6068,7 +6068,7 @@ namespace ts { const nameTable = getNameTable(sourceFile); - if (lookUp(nameTable, internedName) !== undefined) { + if (nameTable[internedName] !== undefined) { result = result || []; getReferencesInNode(sourceFile, symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result, symbolToIndex); } diff --git a/src/services/shims.ts b/src/services/shims.ts index 45c4b284ae7..ff57dd9cf7a 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -311,9 +311,9 @@ namespace ts { // 'in' does not have this effect. if ("getModuleResolutionsForFile" in this.shimHost) { this.resolveModuleNames = (moduleNames: string[], containingFile: string) => { - const resolutionsInFile = >JSON.parse(this.shimHost.getModuleResolutionsForFile(containingFile)); + const resolutionsInFile = >JSON.parse(this.shimHost.getModuleResolutionsForFile(containingFile)); return map(moduleNames, name => { - const result = lookUp(resolutionsInFile, name); + const result = getProperty(resolutionsInFile, name); return result ? { resolvedFileName: result } : undefined; }); }; @@ -323,8 +323,8 @@ namespace ts { } if ("getTypeReferenceDirectiveResolutionsForFile" in this.shimHost) { this.resolveTypeReferenceDirectives = (typeDirectiveNames: string[], containingFile: string) => { - const typeDirectivesForFile = >JSON.parse(this.shimHost.getTypeReferenceDirectiveResolutionsForFile(containingFile)); - return map(typeDirectiveNames, name => lookUp(typeDirectivesForFile, name)); + const typeDirectivesForFile = >JSON.parse(this.shimHost.getTypeReferenceDirectiveResolutionsForFile(containingFile)); + return map(typeDirectiveNames, name => getProperty(typeDirectivesForFile, name)); }; } } diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index 50378aa64b1..211e55b23ba 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -237,7 +237,7 @@ namespace ts.SignatureHelp { const typeChecker = program.getTypeChecker(); for (const sourceFile of program.getSourceFiles()) { const nameToDeclarations = sourceFile.getNamedDeclarations(); - const declarations = getProperty(nameToDeclarations, name.text); + const declarations = nameToDeclarations[name.text]; if (declarations) { for (const declaration of declarations) { diff --git a/tslint.json b/tslint.json index a46e58f8c29..f058eab3dd6 100644 --- a/tslint.json +++ b/tslint.json @@ -32,7 +32,7 @@ "property-declaration": "nospace", "variable-declaration": "nospace" }], - "next-line": [true, + "next-line": [true, "check-catch", "check-else" ], @@ -44,7 +44,6 @@ "boolean-trivia": true, "type-operator-spacing": true, "prefer-const": true, - "no-in-operator": true, "no-increment-decrement": true, "object-literal-surrounding-space": true, "no-type-assertion-whitespace": true From 9c83243f33ee7084eaa07b3ee944f9f53559a930 Mon Sep 17 00:00:00 2001 From: Yui Date: Mon, 15 Aug 2016 15:16:54 -0700 Subject: [PATCH 144/233] Add ES2015 Date constructor signature that accepts another Date (#10353) --- src/lib/es2015.core.d.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/es2015.core.d.ts b/src/lib/es2015.core.d.ts index 980f610ca7d..15fbb1b77c5 100644 --- a/src/lib/es2015.core.d.ts +++ b/src/lib/es2015.core.d.ts @@ -68,6 +68,10 @@ interface ArrayConstructor { of(...items: T[]): Array; } +interface DateConstructor { + new (value: Date): Date; +} + interface Function { /** * Returns the name of the function. Function names are read-only and can not be changed. From 8f847c50340e03f39f369e6faace627bd76fd91c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 15 Aug 2016 15:20:58 -0700 Subject: [PATCH 145/233] Parameters with no assignments implicitly considered const --- src/compiler/checker.ts | 80 ++++++++++++++++++++++++++++++----------- src/compiler/types.ts | 20 ++++++----- 2 files changed, 70 insertions(+), 30 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1b7b659e1a6..8a32e23e1ff 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -531,7 +531,7 @@ namespace ts { function getNodeLinks(node: Node): NodeLinks { const nodeId = getNodeId(node); - return nodeLinks[nodeId] || (nodeLinks[nodeId] = {}); + return nodeLinks[nodeId] || (nodeLinks[nodeId] = { flags: 0 }); } function isGlobalSourceFile(node: Node) { @@ -8185,7 +8185,7 @@ namespace ts { return incomplete ? { flags: 0, type } : type; } - function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, includeOuterFunctions: boolean) { + function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, flowContainer: Node) { let key: string; if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) { return declaredType; @@ -8237,7 +8237,7 @@ namespace ts { else if (flow.flags & FlowFlags.Start) { // Check if we should continue with the control flow of the containing function. const container = (flow).container; - if (container && includeOuterFunctions) { + if (container && container !== flowContainer && reference.kind !== SyntaxKind.PropertyAccessExpression) { flow = container.flowNode; continue; } @@ -8708,21 +8708,52 @@ namespace ts { function getControlFlowContainer(node: Node): Node { while (true) { node = node.parent; - if (isFunctionLike(node) || node.kind === SyntaxKind.ModuleBlock || node.kind === SyntaxKind.SourceFile || node.kind === SyntaxKind.PropertyDeclaration) { + if (isFunctionLike(node) && !getImmediatelyInvokedFunctionExpression(node) || + node.kind === SyntaxKind.ModuleBlock || + node.kind === SyntaxKind.SourceFile || + node.kind === SyntaxKind.PropertyDeclaration) { return node; } } } - function isDeclarationIncludedInFlow(reference: Node, declaration: Declaration, includeOuterFunctions: boolean) { - const declarationContainer = getControlFlowContainer(declaration); - let container = getControlFlowContainer(reference); - while (container !== declarationContainer && - (container.kind === SyntaxKind.FunctionExpression || container.kind === SyntaxKind.ArrowFunction) && - (includeOuterFunctions || getImmediatelyInvokedFunctionExpression(container))) { - container = getControlFlowContainer(container); + // Check if a parameter is assigned anywhere within its declaring function. + function isParameterAssigned(symbol: Symbol) { + const func = getRootDeclaration(symbol.valueDeclaration).parent; + const links = getNodeLinks(func); + if (!(links.flags & NodeCheckFlags.AssignmentsMarked)) { + links.flags |= NodeCheckFlags.AssignmentsMarked; + if (!hasParentWithAssignmentsMarked(func)) { + markParameterAssignments(func); + } + } + return symbol.isAssigned || false; + } + + function hasParentWithAssignmentsMarked(node: Node) { + while (true) { + node = node.parent; + if (!node) { + return false; + } + if (isFunctionLike(node) && getNodeLinks(node).flags & NodeCheckFlags.AssignmentsMarked) { + return true; + } + } + } + + function markParameterAssignments(node: Node) { + if (node.kind === SyntaxKind.Identifier) { + if (isAssignmentTarget(node)) { + const symbol = getResolvedSymbol(node); + if (symbol.valueDeclaration && getRootDeclaration(symbol.valueDeclaration).kind === SyntaxKind.Parameter) { + symbol.isAssigned = true; + } + } + } + else { + forEachChild(node, markParameterAssignments); } - return container === declarationContainer; } function checkIdentifier(node: Identifier): Type { @@ -8777,15 +8808,22 @@ namespace ts { checkNestedBlockScopedBinding(node, symbol); const type = getTypeOfSymbol(localOrExportSymbol); - if (!(localOrExportSymbol.flags & SymbolFlags.Variable) || isAssignmentTarget(node)) { + const declaration = localOrExportSymbol.valueDeclaration; + if (!(localOrExportSymbol.flags & SymbolFlags.Variable) || isAssignmentTarget(node) || !declaration) { return type; } - const declaration = localOrExportSymbol.valueDeclaration; - const includeOuterFunctions = isReadonlySymbol(localOrExportSymbol); - const assumeInitialized = !strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || !declaration || - getRootDeclaration(declaration).kind === SyntaxKind.Parameter || isInAmbientContext(declaration) || - !isDeclarationIncludedInFlow(node, declaration, includeOuterFunctions); - const flowType = getFlowTypeOfReference(node, type, assumeInitialized, includeOuterFunctions); + + const isParameter = getRootDeclaration(declaration).kind === SyntaxKind.Parameter; + const declarationContainer = getControlFlowContainer(declaration); + let flowContainer = getControlFlowContainer(node); + while (flowContainer !== declarationContainer && + (flowContainer.kind === SyntaxKind.FunctionExpression || flowContainer.kind === SyntaxKind.ArrowFunction) && + (isReadonlySymbol(localOrExportSymbol) || isParameter && !isParameterAssigned(localOrExportSymbol))) { + flowContainer = getControlFlowContainer(flowContainer); + } + const assumeInitialized = !strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || isParameter || + flowContainer !== declarationContainer || isInAmbientContext(declaration); + const flowType = getFlowTypeOfReference(node, type, assumeInitialized, flowContainer); if (!assumeInitialized && !(getFalsyFlags(type) & TypeFlags.Undefined) && getFalsyFlags(flowType) & TypeFlags.Undefined) { error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol)); // Return the declared type to reduce follow-on errors @@ -9038,7 +9076,7 @@ namespace ts { if (isClassLike(container.parent)) { const symbol = getSymbolOfNode(container.parent); const type = container.flags & NodeFlags.Static ? getTypeOfSymbol(symbol) : (getDeclaredTypeOfSymbol(symbol)).thisType; - return getFlowTypeOfReference(node, type, /*assumeInitialized*/ true, /*includeOuterFunctions*/ true); + return getFlowTypeOfReference(node, type, /*assumeInitialized*/ true, /*flowContainer*/ undefined); } if (isInJavaScriptFile(node)) { @@ -10699,7 +10737,7 @@ namespace ts { !(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) { return propType; } - return getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true, /*includeOuterFunctions*/ false); + return getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true, /*flowContainer*/ undefined); } function isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d144c984a3f..cca94c0adc1 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2157,6 +2157,7 @@ namespace ts { /* @internal */ exportSymbol?: Symbol; // Exported symbol associated with this symbol /* @internal */ constEnumOnlyModule?: boolean; // True if module contains only const enums or other modules with only const enums /* @internal */ isReferenced?: boolean; // True if the symbol is referenced elsewhere + /* @internal */ isAssigned?: boolean; // True if the symbol has assignments } /* @internal */ @@ -2209,23 +2210,24 @@ namespace ts { AsyncMethodWithSuper = 0x00000800, // An async method that reads a value from a member of 'super'. AsyncMethodWithSuperBinding = 0x00001000, // An async method that assigns a value to a member of 'super'. CaptureArguments = 0x00002000, // Lexical 'arguments' used in body (for async functions) - EnumValuesComputed = 0x00004000, // Values for enum members have been computed, and any errors have been reported for them. - LexicalModuleMergesWithClass = 0x00008000, // Instantiated lexical module declaration is merged with a previous class declaration. - LoopWithCapturedBlockScopedBinding = 0x00010000, // Loop that contains block scoped variable captured in closure - CapturedBlockScopedBinding = 0x00020000, // Block-scoped binding that is captured in some function - BlockScopedBindingInLoop = 0x00040000, // Block-scoped binding with declaration nested inside iteration statement - ClassWithBodyScopedClassBinding = 0x00080000, // Decorated class that contains a binding to itself inside of the class body. - BodyScopedClassBinding = 0x00100000, // Binding to a decorated class inside of the class's body. - NeedsLoopOutParameter = 0x00200000, // Block scoped binding whose value should be explicitly copied outside of the converted loop + EnumValuesComputed = 0x00004000, // Values for enum members have been computed, and any errors have been reported for them. + LexicalModuleMergesWithClass = 0x00008000, // Instantiated lexical module declaration is merged with a previous class declaration. + LoopWithCapturedBlockScopedBinding = 0x00010000, // Loop that contains block scoped variable captured in closure + CapturedBlockScopedBinding = 0x00020000, // Block-scoped binding that is captured in some function + BlockScopedBindingInLoop = 0x00040000, // Block-scoped binding with declaration nested inside iteration statement + ClassWithBodyScopedClassBinding = 0x00080000, // Decorated class that contains a binding to itself inside of the class body. + BodyScopedClassBinding = 0x00100000, // Binding to a decorated class inside of the class's body. + NeedsLoopOutParameter = 0x00200000, // Block scoped binding whose value should be explicitly copied outside of the converted loop + AssignmentsMarked = 0x00400000, // Parameter assignments have been marked } /* @internal */ export interface NodeLinks { + flags?: NodeCheckFlags; // Set of flags specific to Node resolvedType?: Type; // Cached type of type node resolvedSignature?: Signature; // Cached signature of signature node or call expression resolvedSymbol?: Symbol; // Cached name resolution result resolvedIndexInfo?: IndexInfo; // Cached indexing info resolution result - flags?: NodeCheckFlags; // Set of flags specific to Node enumMemberValue?: number; // Constant value of enum member isVisible?: boolean; // Is this node visible hasReportedStatementInAmbientContext?: boolean; // Cache boolean if we report statements in ambient context From 15dae3fd8a9f76ecec3dde1b1098ca73a3263c92 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 15 Aug 2016 15:21:12 -0700 Subject: [PATCH 146/233] Add tests --- .../implicitConstParameters.errors.txt | 65 +++++++++++ .../reference/implicitConstParameters.js | 106 ++++++++++++++++++ .../cases/compiler/implicitConstParameters.ts | 57 ++++++++++ 3 files changed, 228 insertions(+) create mode 100644 tests/baselines/reference/implicitConstParameters.errors.txt create mode 100644 tests/baselines/reference/implicitConstParameters.js create mode 100644 tests/cases/compiler/implicitConstParameters.ts diff --git a/tests/baselines/reference/implicitConstParameters.errors.txt b/tests/baselines/reference/implicitConstParameters.errors.txt new file mode 100644 index 00000000000..95ff60d71f8 --- /dev/null +++ b/tests/baselines/reference/implicitConstParameters.errors.txt @@ -0,0 +1,65 @@ +tests/cases/compiler/implicitConstParameters.ts(39,27): error TS2532: Object is possibly 'undefined'. +tests/cases/compiler/implicitConstParameters.ts(45,27): error TS2532: Object is possibly 'undefined'. + + +==== tests/cases/compiler/implicitConstParameters.ts (2 errors) ==== + + function doSomething(cb: () => void) { + cb(); + } + + function fn(x: number | string) { + if (typeof x === 'number') { + doSomething(() => x.toFixed()); + } + } + + function f1(x: string | undefined) { + if (!x) { + return; + } + doSomething(() => x.length); + } + + function f2(x: string | undefined) { + if (x) { + doSomething(() => { + doSomething(() => x.length); + }); + } + } + + function f3(x: string | undefined) { + inner(); + function inner() { + if (x) { + doSomething(() => x.length); + } + } + } + + function f4(x: string | undefined) { + x = "abc"; // causes x to be considered non-const + if (x) { + doSomething(() => x.length); + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + } + + function f5(x: string | undefined) { + if (x) { + doSomething(() => x.length); + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + x = "abc"; // causes x to be considered non-const + } + + + function f6(x: string | undefined) { + const y = x || ""; + if (x) { + doSomething(() => y.length); + } + } \ No newline at end of file diff --git a/tests/baselines/reference/implicitConstParameters.js b/tests/baselines/reference/implicitConstParameters.js new file mode 100644 index 00000000000..a5faf5b1253 --- /dev/null +++ b/tests/baselines/reference/implicitConstParameters.js @@ -0,0 +1,106 @@ +//// [implicitConstParameters.ts] + +function doSomething(cb: () => void) { + cb(); +} + +function fn(x: number | string) { + if (typeof x === 'number') { + doSomething(() => x.toFixed()); + } +} + +function f1(x: string | undefined) { + if (!x) { + return; + } + doSomething(() => x.length); +} + +function f2(x: string | undefined) { + if (x) { + doSomething(() => { + doSomething(() => x.length); + }); + } +} + +function f3(x: string | undefined) { + inner(); + function inner() { + if (x) { + doSomething(() => x.length); + } + } +} + +function f4(x: string | undefined) { + x = "abc"; // causes x to be considered non-const + if (x) { + doSomething(() => x.length); + } +} + +function f5(x: string | undefined) { + if (x) { + doSomething(() => x.length); + } + x = "abc"; // causes x to be considered non-const +} + + +function f6(x: string | undefined) { + const y = x || ""; + if (x) { + doSomething(() => y.length); + } +} + +//// [implicitConstParameters.js] +function doSomething(cb) { + cb(); +} +function fn(x) { + if (typeof x === 'number') { + doSomething(function () { return x.toFixed(); }); + } +} +function f1(x) { + if (!x) { + return; + } + doSomething(function () { return x.length; }); +} +function f2(x) { + if (x) { + doSomething(function () { + doSomething(function () { return x.length; }); + }); + } +} +function f3(x) { + inner(); + function inner() { + if (x) { + doSomething(function () { return x.length; }); + } + } +} +function f4(x) { + x = "abc"; // causes x to be considered non-const + if (x) { + doSomething(function () { return x.length; }); + } +} +function f5(x) { + if (x) { + doSomething(function () { return x.length; }); + } + x = "abc"; // causes x to be considered non-const +} +function f6(x) { + var y = x || ""; + if (x) { + doSomething(function () { return y.length; }); + } +} diff --git a/tests/cases/compiler/implicitConstParameters.ts b/tests/cases/compiler/implicitConstParameters.ts new file mode 100644 index 00000000000..97996789124 --- /dev/null +++ b/tests/cases/compiler/implicitConstParameters.ts @@ -0,0 +1,57 @@ +// @strictNullChecks: true + +function doSomething(cb: () => void) { + cb(); +} + +function fn(x: number | string) { + if (typeof x === 'number') { + doSomething(() => x.toFixed()); + } +} + +function f1(x: string | undefined) { + if (!x) { + return; + } + doSomething(() => x.length); +} + +function f2(x: string | undefined) { + if (x) { + doSomething(() => { + doSomething(() => x.length); + }); + } +} + +function f3(x: string | undefined) { + inner(); + function inner() { + if (x) { + doSomething(() => x.length); + } + } +} + +function f4(x: string | undefined) { + x = "abc"; // causes x to be considered non-const + if (x) { + doSomething(() => x.length); + } +} + +function f5(x: string | undefined) { + if (x) { + doSomething(() => x.length); + } + x = "abc"; // causes x to be considered non-const +} + + +function f6(x: string | undefined) { + const y = x || ""; + if (x) { + doSomething(() => y.length); + } +} \ No newline at end of file From 1dc495adf83e143e3d5558157c939e529106beba Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 15 Aug 2016 16:41:32 -0700 Subject: [PATCH 147/233] Migrate additional MapLikes to Maps. --- src/compiler/checker.ts | 30 ++--- src/compiler/commandLineParser.ts | 32 +++--- src/compiler/core.ts | 108 +++++++++++++++++- src/compiler/emitter.ts | 16 +-- src/compiler/scanner.ts | 10 +- src/compiler/tsc.ts | 15 +-- src/compiler/types.ts | 6 +- src/compiler/utilities.ts | 4 +- src/harness/compilerRunner.ts | 6 +- src/harness/fourslash.ts | 21 ++-- src/harness/harness.ts | 10 +- src/harness/harnessLanguageService.ts | 8 +- src/harness/projectsRunner.ts | 9 +- .../unittests/cachingInServerLSHost.ts | 20 ++-- src/harness/unittests/moduleResolution.ts | 78 ++++++------- .../unittests/reuseProgramStructure.ts | 82 +++++++------ src/harness/unittests/session.ts | 8 +- .../unittests/tsserverProjectSystem.ts | 18 +-- src/server/session.ts | 7 +- src/services/jsTyping.ts | 13 +-- src/services/patternMatcher.ts | 2 +- src/services/services.ts | 8 +- 22 files changed, 295 insertions(+), 216 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6a7cc108625..c309b9e36c0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -284,7 +284,7 @@ namespace ts { NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy, } - const typeofEQFacts: MapLike = { + const typeofEQFacts = createMap({ "string": TypeFacts.TypeofEQString, "number": TypeFacts.TypeofEQNumber, "boolean": TypeFacts.TypeofEQBoolean, @@ -292,9 +292,9 @@ namespace ts { "undefined": TypeFacts.EQUndefined, "object": TypeFacts.TypeofEQObject, "function": TypeFacts.TypeofEQFunction - }; + }); - const typeofNEFacts: MapLike = { + const typeofNEFacts = createMap({ "string": TypeFacts.TypeofNEString, "number": TypeFacts.TypeofNENumber, "boolean": TypeFacts.TypeofNEBoolean, @@ -302,15 +302,15 @@ namespace ts { "undefined": TypeFacts.NEUndefined, "object": TypeFacts.TypeofNEObject, "function": TypeFacts.TypeofNEFunction - }; + }); - const typeofTypesByName: MapLike = { + const typeofTypesByName = createMap({ "string": stringType, "number": numberType, "boolean": booleanType, "symbol": esSymbolType, "undefined": undefinedType - }; + }); let jsxElementType: ObjectType; /** Things we lazy load from the JSX namespace */ @@ -8467,11 +8467,11 @@ namespace ts { return type; } const doubleEquals = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsToken; - const facts = doubleEquals ? - assumeTrue ? TypeFacts.EQUndefinedOrNull : TypeFacts.NEUndefinedOrNull : - value.kind === SyntaxKind.NullKeyword ? - assumeTrue ? TypeFacts.EQNull : TypeFacts.NENull : - assumeTrue ? TypeFacts.EQUndefined : TypeFacts.NEUndefined; + const facts = doubleEquals + ? assumeTrue ? TypeFacts.EQUndefinedOrNull : TypeFacts.NEUndefinedOrNull + : value.kind === SyntaxKind.NullKeyword + ? assumeTrue ? TypeFacts.EQNull : TypeFacts.NENull + : assumeTrue ? TypeFacts.EQUndefined : TypeFacts.NEUndefined; return getTypeWithFacts(type, facts); } if (type.flags & TypeFlags.NotUnionOrUnit) { @@ -8502,14 +8502,14 @@ namespace ts { // We narrow a non-union type to an exact primitive type if the non-union type // is a supertype of that primitive type. For example, type 'any' can be narrowed // to one of the primitive types. - const targetType = getProperty(typeofTypesByName, literal.text); + const targetType = typeofTypesByName[literal.text]; if (targetType && isTypeSubtypeOf(targetType, type)) { return targetType; } } - const facts = assumeTrue ? - getProperty(typeofEQFacts, literal.text) || TypeFacts.TypeofEQHostObject : - getProperty(typeofNEFacts, literal.text) || TypeFacts.TypeofNEHostObject; + const facts = assumeTrue + ? typeofEQFacts[literal.text] || TypeFacts.TypeofEQHostObject + : typeofNEFacts[literal.text] || TypeFacts.TypeofNEHostObject; return getTypeWithFacts(type, facts); } diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 44befb3943d..35140855d81 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -61,10 +61,10 @@ namespace ts { }, { name: "jsx", - type: { + type: createMap({ "preserve": JsxEmit.Preserve, "react": JsxEmit.React - }, + }), paramType: Diagnostics.KIND, description: Diagnostics.Specify_JSX_code_generation_Colon_preserve_or_react, }, @@ -91,7 +91,7 @@ namespace ts { { name: "module", shortName: "m", - type: { + type: createMap({ "none": ModuleKind.None, "commonjs": ModuleKind.CommonJS, "amd": ModuleKind.AMD, @@ -99,16 +99,16 @@ namespace ts { "umd": ModuleKind.UMD, "es6": ModuleKind.ES6, "es2015": ModuleKind.ES2015, - }, + }), description: Diagnostics.Specify_module_code_generation_Colon_commonjs_amd_system_umd_or_es2015, paramType: Diagnostics.KIND, }, { name: "newLine", - type: { + type: createMap({ "crlf": NewLineKind.CarriageReturnLineFeed, "lf": NewLineKind.LineFeed - }, + }), description: Diagnostics.Specify_the_end_of_line_sequence_to_be_used_when_emitting_files_Colon_CRLF_dos_or_LF_unix, paramType: Diagnostics.NEWLINE, }, @@ -250,12 +250,12 @@ namespace ts { { name: "target", shortName: "t", - type: { + type: createMap({ "es3": ScriptTarget.ES3, "es5": ScriptTarget.ES5, "es6": ScriptTarget.ES6, "es2015": ScriptTarget.ES2015, - }, + }), description: Diagnostics.Specify_ECMAScript_target_version_Colon_ES3_default_ES5_or_ES2015, paramType: Diagnostics.VERSION, }, @@ -284,10 +284,10 @@ namespace ts { }, { name: "moduleResolution", - type: { + type: createMap({ "node": ModuleResolutionKind.NodeJs, "classic": ModuleResolutionKind.Classic, - }, + }), description: Diagnostics.Specify_module_resolution_strategy_Colon_node_Node_js_or_classic_TypeScript_pre_1_6, }, { @@ -392,7 +392,7 @@ namespace ts { type: "list", element: { name: "lib", - type: { + type: createMap({ // JavaScript only "es5": "lib.es5.d.ts", "es6": "lib.es2015.d.ts", @@ -417,7 +417,7 @@ namespace ts { "es2016.array.include": "lib.es2016.array.include.d.ts", "es2017.object": "lib.es2017.object.d.ts", "es2017.sharedmemory": "lib.es2017.sharedmemory.d.ts" - }, + }), }, description: Diagnostics.Specify_library_files_to_be_included_in_the_compilation_Colon }, @@ -487,9 +487,7 @@ namespace ts { export function createCompilerDiagnosticForInvalidCustomType(opt: CommandLineOptionOfCustomType): Diagnostic { const namesOfType: string[] = []; for (const key in opt.type) { - if (hasProperty(opt.type, key)) { - namesOfType.push(` '${key}'`); - } + namesOfType.push(` '${key}'`); } return createCompilerDiagnostic(Diagnostics.Argument_for_0_option_must_be_Colon_1, `--${opt.name}`, namesOfType); } @@ -498,7 +496,7 @@ namespace ts { export function parseCustomTypeOption(opt: CommandLineOptionOfCustomType, value: string, errors: Diagnostic[]) { const key = trimString((value || "")).toLowerCase(); const map = opt.type; - if (hasProperty(map, key)) { + if (key in map) { return map[key]; } else { @@ -849,7 +847,7 @@ namespace ts { function convertJsonOptionOfCustomType(opt: CommandLineOptionOfCustomType, value: string, errors: Diagnostic[]) { const key = value.toLowerCase(); - if (hasProperty(opt.type, key)) { + if (key in opt.type) { return opt.type[key]; } else { diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 7bb5dbef2c5..d10d76b3492 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -190,6 +190,21 @@ namespace ts { return array; } + export function removeWhere(array: T[], f: (x: T) => boolean): boolean { + let outIndex = 0; + for (const item of array) { + if (!f(item)) { + array[outIndex] = item; + outIndex++; + } + } + if (outIndex !== array.length) { + array.length = outIndex; + return true; + } + return false; + } + export function filterMutate(array: T[], f: (x: T) => boolean): void { let outIndex = 0; for (const item of array) { @@ -381,6 +396,9 @@ namespace ts { /** * Gets the owned, enumerable property keys of a map-like. * + * NOTE: This is intended for use with MapLike objects. For Map objects, use + * Object.keys instead as it offers better performance. + * * @param map A map-like. */ export function getOwnKeys(map: MapLike): string[] { @@ -425,6 +443,36 @@ namespace ts { return result; } + /** + * Maps key-value pairs of a map into a new map. + * + * NOTE: The key-value pair passed to the callback is *not* safe to cache between invocations + * of the callback. + * + * @param map A map. + * @param callback A callback that maps a key-value pair into a new key-value pair. + */ + export function mapPairs(map: Map, callback: (entry: [string, T]) => [string, U]): Map { + let result: Map; + if (map) { + result = createMap(); + let inPair: [string, T]; + for (const key in map) { + if (inPair) { + inPair[0] = key; + inPair[1] = map[key]; + } + else { + inPair = [key, map[key]]; + } + + const outPair = callback(inPair); + result[outPair[0]] = outPair[1]; + } + } + return result; + } + /** * Returns true if a Map has some matching property. * @@ -504,9 +552,31 @@ namespace ts { return result; } + /** + * Counts the properties of a map. + * + * NOTE: This is intended for use with Map objects. For MapLike objects, use + * countOwnProperties instead as it offers better runtime safety. + * + * @param map A map whose properties should be counted. + * @param predicate An optional callback used to limit which properties should be counted. + */ + export function countProperties(map: Map, predicate?: (value: T, key: string) => boolean) { + let count = 0; + for (const key in map) { + if (!predicate || predicate(map[key], key)) { + count++; + } + } + return count; + } + /** * Counts the owned properties of a map-like. * + * NOTE: This is intended for use with MapLike objects. For Map objects, use + * countProperties instead as it offers better performance. + * * @param map A map-like whose properties should be counted. * @param predicate An optional callback used to limit which properties should be counted. */ @@ -521,16 +591,42 @@ namespace ts { } /** - * Performs a shallow equality comparison of the contents of two map-likes. + * Performs a shallow equality comparison of the contents of two maps. + * + * NOTE: This is intended for use with Map objects. For MapLike objects, use + * equalOwnProperties instead as it offers better runtime safety. * * @param left A map whose properties should be compared. * @param right A map whose properties should be compared. */ - export function equalOwnProperties(left: MapLike, right: MapLike) { + export function equalProperties(left: Map, right: Map, equalityComparer?: (left: T, right: T) => boolean) { + if (left === right) return true; + if (!left || !right) return false; + for (const key in left) { + if (!(key in right)) return false; + if (equalityComparer ? !equalityComparer(left[key], right[key]) : left[key] !== right[key]) return false; + } + for (const key in right) { + if (!(key in left)) return false; + } + return true; + } + + /** + * Performs a shallow equality comparison of the contents of two map-likes. + * + * NOTE: This is intended for use with MapLike objects. For Map objects, use + * equalProperties instead as it offers better performance. + * + * @param left A map-like whose properties should be compared. + * @param right A map-like whose properties should be compared. + */ + export function equalOwnProperties(left: MapLike, right: MapLike, equalityComparer?: (left: T, right: T) => boolean) { if (left === right) return true; if (!left || !right) return false; for (const key in left) if (hasOwnProperty.call(left, key)) { - if (!hasOwnProperty.call(right, key) === undefined || left[key] !== right[key]) return false; + if (!hasOwnProperty.call(right, key) === undefined) return false; + if (equalityComparer ? !equalityComparer(left[key], right[key]) : left[key] !== right[key]) return false; } for (const key in right) if (hasOwnProperty.call(right, key)) { if (!hasOwnProperty.call(left, key)) return false; @@ -577,10 +673,12 @@ namespace ts { export function extend, T2 extends MapLike<{}>>(first: T1 , second: T2): T1 & T2 { const result: T1 & T2 = {}; for (const id in first) { - (result as any)[id] = first[id]; + if (hasOwnProperty.call(first, id)) { + (result as any)[id] = first[id]; + } } for (const id in second) { - if (!hasProperty(result, id)) { + if (hasOwnProperty.call(second, id) && !hasOwnProperty.call(result, id)) { (result as any)[id] = second[id]; } } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 47495cc69bb..5c834ad0ac2 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -24,7 +24,7 @@ namespace ts { Return = 1 << 3 } - const entities: MapLike = { + const entities = createMap({ "quot": 0x0022, "amp": 0x0026, "apos": 0x0027, @@ -278,7 +278,7 @@ namespace ts { "clubs": 0x2663, "hearts": 0x2665, "diams": 0x2666 - }; + }); // Flags enum to track count of temp variables and a few dedicated names const enum TempFlags { @@ -531,7 +531,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge let currentText: string; let currentLineMap: number[]; let currentFileIdentifiers: Map; - let renamedDependencies: MapLike; + let renamedDependencies: Map; let isEs6Module: boolean; let isCurrentFileExternalModule: boolean; @@ -577,21 +577,21 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge const setSourceMapWriterEmit = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? changeSourceMapEmit : function (writer: SourceMapWriter) { }; - const moduleEmitDelegates: MapLike<(node: SourceFile, emitRelativePathAsModuleName?: boolean) => void> = { + const moduleEmitDelegates = createMap<(node: SourceFile, emitRelativePathAsModuleName?: boolean) => void>({ [ModuleKind.ES6]: emitES6Module, [ModuleKind.AMD]: emitAMDModule, [ModuleKind.System]: emitSystemModule, [ModuleKind.UMD]: emitUMDModule, [ModuleKind.CommonJS]: emitCommonJSModule, - }; + }); - const bundleEmitDelegates: MapLike<(node: SourceFile, emitRelativePathAsModuleName?: boolean) => void> = { + const bundleEmitDelegates = createMap<(node: SourceFile, emitRelativePathAsModuleName?: boolean) => void>({ [ModuleKind.ES6]() {}, [ModuleKind.AMD]: emitAMDModule, [ModuleKind.System]: emitSystemModule, [ModuleKind.UMD]() {}, [ModuleKind.CommonJS]() {}, - }; + }); return doEmit; @@ -6461,7 +6461,7 @@ const _super = (function (geti, seti) { * Here we check if alternative name was provided for a given moduleName and return it if possible. */ function tryRenameExternalModule(moduleName: LiteralExpression): string { - if (renamedDependencies && hasProperty(renamedDependencies, moduleName.text)) { + if (renamedDependencies && moduleName.text in renamedDependencies) { return `"${renamedDependencies[moduleName.text]}"`; } return undefined; diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 134dc489b2a..c1431ca23ed 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -55,7 +55,7 @@ namespace ts { tryScan(callback: () => T): T; } - const textToToken: MapLike = { + const textToToken = createMap({ "abstract": SyntaxKind.AbstractKeyword, "any": SyntaxKind.AnyKeyword, "as": SyntaxKind.AsKeyword, @@ -179,7 +179,7 @@ namespace ts { "|=": SyntaxKind.BarEqualsToken, "^=": SyntaxKind.CaretEqualsToken, "@": SyntaxKind.AtToken, - }; + }); /* As per ECMAScript Language Specification 3th Edition, Section 7.6: Identifiers @@ -271,12 +271,10 @@ namespace ts { lookupInUnicodeMap(code, unicodeES3IdentifierPart); } - function makeReverseMap(source: MapLike): string[] { + function makeReverseMap(source: Map): string[] { const result: string[] = []; for (const name in source) { - if (source.hasOwnProperty(name)) { - result[source[name]] = name; - } + result[source[name]] = name; } return result; } diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 73cf8e9c4e2..8b445bcb626 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -122,11 +122,11 @@ namespace ts { const gutterSeparator = " "; const resetEscapeSequence = "\u001b[0m"; const ellipsis = "..."; - const categoryFormatMap: MapLike = { + const categoryFormatMap = createMap({ [DiagnosticCategory.Warning]: yellowForegroundEscapeSequence, [DiagnosticCategory.Error]: redForegroundEscapeSequence, [DiagnosticCategory.Message]: blueForegroundEscapeSequence, - }; + }); function formatAndReset(text: string, formatStyle: string) { return formatStyle + text + resetEscapeSequence; @@ -703,11 +703,9 @@ namespace ts { description = getDiagnosticText(option.description); const options: string[] = []; const element = (option).element; - const typeMap = >element.type; + const typeMap = >element.type; for (const key in typeMap) { - if (hasProperty(typeMap, key)) { - options.push(`'${key}'`); - } + options.push(`'${key}'`); } optionsDescriptionMap[description] = options; } @@ -814,9 +812,8 @@ namespace ts { // Enum const typeMap = >optionDefinition.type; for (const key in typeMap) { - if (hasProperty(typeMap, key)) { - if (typeMap[key] === value) - result[name] = key; + if (typeMap[key] === value) { + result[name] = key; } } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d144c984a3f..83d299583d2 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1650,7 +1650,7 @@ namespace ts { // this map is used by transpiler to supply alternative names for dependencies (i.e. in case of bundling) /* @internal */ - renamedDependencies?: MapLike; + renamedDependencies?: Map; /** * lib.d.ts should have a reference comment like @@ -2742,7 +2742,7 @@ namespace ts { /* @internal */ export interface CommandLineOptionBase { name: string; - type: "string" | "number" | "boolean" | "object" | "list" | MapLike; // a value of a primitive type, or an object literal mapping named values to actual values + type: "string" | "number" | "boolean" | "object" | "list" | Map; // a value of a primitive type, or an object literal mapping named values to actual values isFilePath?: boolean; // True if option value is a path or fileName shortName?: string; // A short mnemonic for convenience - for instance, 'h' can be used in place of 'help' description?: DiagnosticMessage; // The message describing what the command line switch does @@ -2758,7 +2758,7 @@ namespace ts { /* @internal */ export interface CommandLineOptionOfCustomType extends CommandLineOptionBase { - type: MapLike; // an object literal mapping named values to actual values + type: Map; // an object literal mapping named values to actual values } /* @internal */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 779e479cc43..0a1f43203ce 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2040,7 +2040,7 @@ namespace ts { // the map below must be updated. Note that this regexp *does not* include the 'delete' character. // There is no reason for this other than that JSON.stringify does not handle it either. const escapedCharsRegExp = /[\\\"\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g; - const escapedCharsMap: MapLike = { + const escapedCharsMap = createMap({ "\0": "\\0", "\t": "\\t", "\v": "\\v", @@ -2053,7 +2053,7 @@ namespace ts { "\u2028": "\\u2028", // lineSeparator "\u2029": "\\u2029", // paragraphSeparator "\u0085": "\\u0085" // nextLine - }; + }); /** diff --git a/src/harness/compilerRunner.ts b/src/harness/compilerRunner.ts index fe8986984d3..66396293dc2 100644 --- a/src/harness/compilerRunner.ts +++ b/src/harness/compilerRunner.ts @@ -291,8 +291,8 @@ class CompilerBaselineRunner extends RunnerBase { const fullWalker = new TypeWriterWalker(program, /*fullTypeCheck*/ true); - const fullResults: ts.MapLike = {}; - const pullResults: ts.MapLike = {}; + const fullResults = ts.createMap(); + const pullResults = ts.createMap(); for (const sourceFile of allFiles) { fullResults[sourceFile.unitName] = fullWalker.getTypeAndSymbols(sourceFile.unitName); @@ -338,7 +338,7 @@ class CompilerBaselineRunner extends RunnerBase { } } - function generateBaseLine(typeWriterResults: ts.MapLike, isSymbolBaseline: boolean): string { + function generateBaseLine(typeWriterResults: ts.Map, isSymbolBaseline: boolean): string { const typeLines: string[] = []; const typeMap: { [fileName: string]: { [lineNum: number]: string[]; } } = {}; diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 345ce19f6ae..ba1994c8141 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -95,14 +95,14 @@ namespace FourSlash { export import IndentStyle = ts.IndentStyle; - const entityMap: ts.MapLike = { + const entityMap = ts.createMap({ "&": "&", "\"": """, "'": "'", "/": "/", "<": "<", ">": ">" - }; + }); export function escapeXmlAttributeValue(s: string) { return s.replace(/[&<>"'\/]/g, ch => entityMap[ch]); @@ -593,9 +593,9 @@ namespace FourSlash { public noItemsWithSameNameButDifferentKind(): void { const completions = this.getCompletionListAtCaret(); - const uniqueItems: ts.MapLike = {}; + const uniqueItems = ts.createMap(); for (const item of completions.entries) { - if (!ts.hasProperty(uniqueItems, item.name)) { + if (!(item.name in uniqueItems)) { uniqueItems[item.name] = item.kind; } else { @@ -1638,11 +1638,12 @@ namespace FourSlash { return this.testData.ranges; } - public rangesByText(): ts.MapLike { - const result: ts.MapLike = {}; + public rangesByText(): ts.Map { + const result = ts.createMap(); for (const range of this.getRanges()) { const text = this.rangeText(range); - (ts.getProperty(result, text) || (result[text] = [])).push(range); + const ranges = result[text] || (result[text] = []); + ranges.push(range); } return result; } @@ -1897,7 +1898,7 @@ namespace FourSlash { public verifyBraceCompletionAtPosition(negative: boolean, openingBrace: string) { - const openBraceMap: ts.MapLike = { + const openBraceMap = ts.createMap({ "(": ts.CharacterCodes.openParen, "{": ts.CharacterCodes.openBrace, "[": ts.CharacterCodes.openBracket, @@ -1905,7 +1906,7 @@ namespace FourSlash { '"': ts.CharacterCodes.doubleQuote, "`": ts.CharacterCodes.backtick, "<": ts.CharacterCodes.lessThan - }; + }); const charCode = openBraceMap[openingBrace]; @@ -2773,7 +2774,7 @@ namespace FourSlashInterface { return this.state.getRanges(); } - public rangesByText(): ts.MapLike { + public rangesByText(): ts.Map { return this.state.rangesByText(); } diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 1f7c48d21bc..e3bcb001863 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -848,9 +848,9 @@ namespace Harness { export const defaultLibFileName = "lib.d.ts"; export const es2015DefaultLibFileName = "lib.es2015.d.ts"; - const libFileNameSourceFileMap: ts.MapLike = { + const libFileNameSourceFileMap= ts.createMap({ [defaultLibFileName]: createSourceFileAndAssertInvariants(defaultLibFileName, IO.readFile(libFolder + "lib.es5.d.ts"), /*languageVersion*/ ts.ScriptTarget.Latest) - }; + }); export function getDefaultLibrarySourceFile(fileName = defaultLibFileName): ts.SourceFile { if (!isDefaultLibraryFile(fileName)) { @@ -1002,16 +1002,16 @@ namespace Harness { { name: "symlink", type: "string" } ]; - let optionsIndex: ts.MapLike; + let optionsIndex: ts.Map; function getCommandLineOption(name: string): ts.CommandLineOption { if (!optionsIndex) { - optionsIndex = {}; + optionsIndex = ts.createMap(); const optionDeclarations = harnessOptionDeclarations.concat(ts.optionDeclarations); for (const option of optionDeclarations) { optionsIndex[option.name.toLowerCase()] = option; } } - return ts.getProperty(optionsIndex, name.toLowerCase()); + return optionsIndex[name.toLowerCase()]; } export function setCompilerOptionsFromHarnessSetting(settings: Harness.TestCaseParser.CompilerSettings, options: ts.CompilerOptions & HarnessOptions): void { diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index b03d8788165..b37a6a90899 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -123,7 +123,7 @@ namespace Harness.LanguageService { } export class LanguageServiceAdapterHost { - protected fileNameToScript: ts.MapLike = {}; + protected fileNameToScript = ts.createMap(); constructor(protected cancellationToken = DefaultHostCancellationToken.Instance, protected settings = ts.getDefaultCompilerOptions()) { @@ -146,7 +146,7 @@ namespace Harness.LanguageService { } public getScriptInfo(fileName: string): ScriptInfo { - return ts.getProperty(this.fileNameToScript, fileName); + return this.fileNameToScript[fileName]; } public addScript(fileName: string, content: string, isRootFile: boolean): void { @@ -235,7 +235,7 @@ namespace Harness.LanguageService { this.getModuleResolutionsForFile = (fileName) => { const scriptInfo = this.getScriptInfo(fileName); const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ true); - const imports: ts.MapLike = {}; + const imports = ts.createMap(); for (const module of preprocessInfo.importedFiles) { const resolutionInfo = ts.resolveModuleName(module.fileName, fileName, compilerOptions, moduleResolutionHost); if (resolutionInfo.resolvedModule) { @@ -248,7 +248,7 @@ namespace Harness.LanguageService { const scriptInfo = this.getScriptInfo(fileName); if (scriptInfo) { const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ false); - const resolutions: ts.MapLike = {}; + const resolutions = ts.createMap(); const settings = this.nativeHost.getCompilationSettings(); for (const typeReferenceDirective of preprocessInfo.typeReferenceDirectives) { const resolutionInfo = ts.resolveTypeReferenceDirective(typeReferenceDirective.fileName, fileName, settings, moduleResolutionHost); diff --git a/src/harness/projectsRunner.ts b/src/harness/projectsRunner.ts index be3030a6dd6..76f042834bb 100644 --- a/src/harness/projectsRunner.ts +++ b/src/harness/projectsRunner.ts @@ -253,18 +253,15 @@ class ProjectRunner extends RunnerBase { moduleResolution: ts.ModuleResolutionKind.Classic, // currently all tests use classic module resolution kind, this will change in the future }; // Set the values specified using json - const optionNameMap: ts.MapLike = {}; - ts.forEach(ts.optionDeclarations, option => { - optionNameMap[option.name] = option; - }); + const optionNameMap = ts.arrayToMap(ts.optionDeclarations, option => option.name); for (const name in testCase) { - if (name !== "mapRoot" && name !== "sourceRoot" && ts.hasProperty(optionNameMap, name)) { + if (name !== "mapRoot" && name !== "sourceRoot" && name in optionNameMap) { const option = optionNameMap[name]; const optType = option.type; let value = testCase[name]; if (typeof optType !== "string") { const key = value.toLowerCase(); - if (ts.hasProperty(optType, key)) { + if (key in optType) { value = optType[key]; } } diff --git a/src/harness/unittests/cachingInServerLSHost.ts b/src/harness/unittests/cachingInServerLSHost.ts index 61ab38b2d8f..2885b310605 100644 --- a/src/harness/unittests/cachingInServerLSHost.ts +++ b/src/harness/unittests/cachingInServerLSHost.ts @@ -6,17 +6,17 @@ namespace ts { content: string; } - function createDefaultServerHost(fileMap: MapLike): server.ServerHost { - const existingDirectories: MapLike = {}; - forEachOwnProperty(fileMap, v => { - let dir = getDirectoryPath(v.name); + function createDefaultServerHost(fileMap: Map): server.ServerHost { + const existingDirectories = createMap(); + for (const name in fileMap) { + let dir = getDirectoryPath(name); let previous: string; do { existingDirectories[dir] = true; previous = dir; dir = getDirectoryPath(dir); } while (dir !== previous); - }); + } return { args: [], newLine: "\r\n", @@ -24,7 +24,7 @@ namespace ts { write: (s: string) => { }, readFile: (path: string, encoding?: string): string => { - return hasProperty(fileMap, path) && fileMap[path].content; + return path in fileMap ? fileMap[path].content : undefined; }, writeFile: (path: string, data: string, writeByteOrderMark?: boolean) => { throw new Error("NYI"); @@ -33,10 +33,10 @@ namespace ts { throw new Error("NYI"); }, fileExists: (path: string): boolean => { - return hasProperty(fileMap, path); + return path in fileMap; }, directoryExists: (path: string): boolean => { - return hasProperty(existingDirectories, path); + return existingDirectories[path] || false; }, createDirectory: (path: string) => { }, @@ -101,7 +101,7 @@ namespace ts { content: `foo()` }; - const serverHost = createDefaultServerHost({ [root.name]: root, [imported.name]: imported }); + const serverHost = createDefaultServerHost(createMap({ [root.name]: root, [imported.name]: imported })); const { project, rootScriptInfo } = createProject(root.name, serverHost); // ensure that imported file was found @@ -193,7 +193,7 @@ namespace ts { content: `export var y = 1` }; - const fileMap: MapLike = { [root.name]: root }; + const fileMap = createMap({ [root.name]: root }); const serverHost = createDefaultServerHost(fileMap); const originalFileExists = serverHost.fileExists; diff --git a/src/harness/unittests/moduleResolution.ts b/src/harness/unittests/moduleResolution.ts index 4b5ef9f24f1..ff0151c40a5 100644 --- a/src/harness/unittests/moduleResolution.ts +++ b/src/harness/unittests/moduleResolution.ts @@ -10,7 +10,7 @@ namespace ts { const map = arrayToMap(files, f => f.name); if (hasDirectoryExists) { - const directories: MapLike = {}; + const directories = createMap(); for (const f of files) { let name = getDirectoryPath(f.name); while (true) { @@ -25,19 +25,19 @@ namespace ts { return { readFile, directoryExists: path => { - return hasProperty(directories, path); + return path in directories; }, fileExists: path => { - assert.isTrue(hasProperty(directories, getDirectoryPath(path)), `'fileExists' '${path}' request in non-existing directory`); - return hasProperty(map, path); + assert.isTrue(getDirectoryPath(path) in directories, `'fileExists' '${path}' request in non-existing directory`); + return path in map; } }; } else { - return { readFile, fileExists: path => hasProperty(map, path), }; + return { readFile, fileExists: path => path in map, }; } function readFile(path: string): string { - return hasProperty(map, path) ? map[path].content : undefined; + return path in map ? map[path].content : undefined; } } @@ -282,12 +282,12 @@ namespace ts { }); describe("Module resolution - relative imports", () => { - function test(files: MapLike, currentDirectory: string, rootFiles: string[], expectedFilesCount: number, relativeNamesToCheck: string[]) { + function test(files: Map, currentDirectory: string, rootFiles: string[], expectedFilesCount: number, relativeNamesToCheck: string[]) { const options: CompilerOptions = { module: ModuleKind.CommonJS }; const host: CompilerHost = { getSourceFile: (fileName: string, languageVersion: ScriptTarget) => { const path = normalizePath(combinePaths(currentDirectory, fileName)); - return hasProperty(files, path) ? createSourceFile(fileName, files[path], languageVersion) : undefined; + return path in files ? createSourceFile(fileName, files[path], languageVersion) : undefined; }, getDefaultLibFileName: () => "lib.d.ts", writeFile: (fileName, content): void => { throw new Error("NotImplemented"); }, @@ -298,7 +298,7 @@ namespace ts { useCaseSensitiveFileNames: () => false, fileExists: fileName => { const path = normalizePath(combinePaths(currentDirectory, fileName)); - return hasProperty(files, path); + return path in files; }, readFile: (fileName): string => { throw new Error("NotImplemented"); } }; @@ -318,7 +318,7 @@ namespace ts { } it("should find all modules", () => { - const files: MapLike = { + const files = createMap({ "/a/b/c/first/shared.ts": ` class A {} export = A`, @@ -332,37 +332,33 @@ import Shared = require('../first/shared'); class C {} export = C; ` - }; + }); test(files, "/a/b/c/first/second", ["class_a.ts"], 3, ["../../../c/third/class_c.ts"]); }); it("should find modules in node_modules", () => { - const files: MapLike = { + const files = createMap({ "/parent/node_modules/mod/index.d.ts": "export var x", "/parent/app/myapp.ts": `import {x} from "mod"` - }; + }); test(files, "/parent/app", ["myapp.ts"], 2, []); }); it("should find file referenced via absolute and relative names", () => { - const files: MapLike = { + const files = createMap({ "/a/b/c.ts": `/// `, "/a/b/b.ts": "var x" - }; + }); test(files, "/a/b", ["c.ts", "/a/b/b.ts"], 2, []); }); }); describe("Files with different casing", () => { const library = createSourceFile("lib.d.ts", "", ScriptTarget.ES5); - function test(files: MapLike, options: CompilerOptions, currentDirectory: string, useCaseSensitiveFileNames: boolean, rootFiles: string[], diagnosticCodes: number[]): void { + function test(files: Map, options: CompilerOptions, currentDirectory: string, useCaseSensitiveFileNames: boolean, rootFiles: string[], diagnosticCodes: number[]): void { const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); if (!useCaseSensitiveFileNames) { - const f: MapLike = {}; - for (const fileName in files) { - f[getCanonicalFileName(fileName)] = files[fileName]; - } - files = f; + files = mapPairs(files, ([fileName, file]) => [getCanonicalFileName(fileName), file]); } const host: CompilerHost = { @@ -371,7 +367,7 @@ export = C; return library; } const path = getCanonicalFileName(normalizePath(combinePaths(currentDirectory, fileName))); - return hasProperty(files, path) ? createSourceFile(fileName, files[path], languageVersion) : undefined; + return path in files ? createSourceFile(fileName, files[path], languageVersion) : undefined; }, getDefaultLibFileName: () => "lib.d.ts", writeFile: (fileName, content): void => { throw new Error("NotImplemented"); }, @@ -382,7 +378,7 @@ export = C; useCaseSensitiveFileNames: () => useCaseSensitiveFileNames, fileExists: fileName => { const path = getCanonicalFileName(normalizePath(combinePaths(currentDirectory, fileName))); - return hasProperty(files, path); + return path in files; }, readFile: (fileName): string => { throw new Error("NotImplemented"); } }; @@ -395,57 +391,57 @@ export = C; } it("should succeed when the same file is referenced using absolute and relative names", () => { - const files: MapLike = { + const files = createMap({ "/a/b/c.ts": `/// `, "/a/b/d.ts": "var x" - }; + }); test(files, { module: ts.ModuleKind.AMD }, "/a/b", /*useCaseSensitiveFileNames*/ false, ["c.ts", "/a/b/d.ts"], []); }); it("should fail when two files used in program differ only in casing (tripleslash references)", () => { - const files: MapLike = { + const files = createMap({ "/a/b/c.ts": `/// `, "/a/b/d.ts": "var x" - }; + }); test(files, { module: ts.ModuleKind.AMD, forceConsistentCasingInFileNames: true }, "/a/b", /*useCaseSensitiveFileNames*/ false, ["c.ts", "d.ts"], [1149]); }); it("should fail when two files used in program differ only in casing (imports)", () => { - const files: MapLike = { + const files = createMap({ "/a/b/c.ts": `import {x} from "D"`, "/a/b/d.ts": "export var x" - }; + }); test(files, { module: ts.ModuleKind.AMD, forceConsistentCasingInFileNames: true }, "/a/b", /*useCaseSensitiveFileNames*/ false, ["c.ts", "d.ts"], [1149]); }); it("should fail when two files used in program differ only in casing (imports, relative module names)", () => { - const files: MapLike = { + const files = createMap({ "moduleA.ts": `import {x} from "./ModuleB"`, "moduleB.ts": "export var x" - }; + }); test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "", /*useCaseSensitiveFileNames*/ false, ["moduleA.ts", "moduleB.ts"], [1149]); }); it("should fail when two files exist on disk that differs only in casing", () => { - const files: MapLike = { + const files = createMap({ "/a/b/c.ts": `import {x} from "D"`, "/a/b/D.ts": "export var x", "/a/b/d.ts": "export var y" - }; + }); test(files, { module: ts.ModuleKind.AMD }, "/a/b", /*useCaseSensitiveFileNames*/ true, ["c.ts", "d.ts"], [1149]); }); it("should fail when module name in 'require' calls has inconsistent casing", () => { - const files: MapLike = { + const files = createMap({ "moduleA.ts": `import a = require("./ModuleC")`, "moduleB.ts": `import a = require("./moduleC")`, "moduleC.ts": "export var x" - }; + }); test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "", /*useCaseSensitiveFileNames*/ false, ["moduleA.ts", "moduleB.ts", "moduleC.ts"], [1149, 1149]); }); it("should fail when module names in 'require' calls has inconsistent casing and current directory has uppercase chars", () => { - const files: MapLike = { + const files = createMap({ "/a/B/c/moduleA.ts": `import a = require("./ModuleC")`, "/a/B/c/moduleB.ts": `import a = require("./moduleC")`, "/a/B/c/moduleC.ts": "export var x", @@ -453,11 +449,11 @@ export = C; import a = require("./moduleA.ts"); import b = require("./moduleB.ts"); ` - }; + }); test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "/a/B/c", /*useCaseSensitiveFileNames*/ false, ["moduleD.ts"], [1149]); }); it("should not fail when module names in 'require' calls has consistent casing and current directory has uppercase chars", () => { - const files: MapLike = { + const files = createMap({ "/a/B/c/moduleA.ts": `import a = require("./moduleC")`, "/a/B/c/moduleB.ts": `import a = require("./moduleC")`, "/a/B/c/moduleC.ts": "export var x", @@ -465,7 +461,7 @@ import b = require("./moduleB.ts"); import a = require("./moduleA.ts"); import b = require("./moduleB.ts"); ` - }; + }); test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "/a/B/c", /*useCaseSensitiveFileNames*/ false, ["moduleD.ts"], []); }); }); @@ -1023,7 +1019,7 @@ import b = require("./moduleB.ts"); const names = map(files, f => f.name); const sourceFiles = arrayToMap(map(files, f => createSourceFile(f.name, f.content, ScriptTarget.ES6)), f => f.fileName); const compilerHost: CompilerHost = { - fileExists : fileName => hasProperty(sourceFiles, fileName), + fileExists : fileName => fileName in sourceFiles, getSourceFile: fileName => sourceFiles[fileName], getDefaultLibFileName: () => "lib.d.ts", writeFile(file, text) { @@ -1034,7 +1030,7 @@ import b = require("./moduleB.ts"); getCanonicalFileName: f => f.toLowerCase(), getNewLine: () => "\r\n", useCaseSensitiveFileNames: () => false, - readFile: fileName => hasProperty(sourceFiles, fileName) ? sourceFiles[fileName].text : undefined + readFile: fileName => fileName in sourceFiles ? sourceFiles[fileName].text : undefined }; const program1 = createProgram(names, {}, compilerHost); const diagnostics1 = program1.getFileProcessingDiagnostics().getDiagnostics(); diff --git a/src/harness/unittests/reuseProgramStructure.ts b/src/harness/unittests/reuseProgramStructure.ts index 9d8b176a6a8..9f2b0c343c2 100644 --- a/src/harness/unittests/reuseProgramStructure.ts +++ b/src/harness/unittests/reuseProgramStructure.ts @@ -95,13 +95,14 @@ namespace ts { } } + function createSourceFileWithText(fileName: string, sourceText: SourceText, target: ScriptTarget) { + const file = createSourceFile(fileName, sourceText.getFullText(), target); + file.sourceText = sourceText; + return file; + } + function createTestCompilerHost(texts: NamedSourceText[], target: ScriptTarget): CompilerHost { - const files: MapLike = {}; - for (const t of texts) { - const file = createSourceFile(t.name, t.text.getFullText(), target); - file.sourceText = t.text; - files[t.name] = file; - } + const files = arrayToMap(texts, t => t.name, t => createSourceFileWithText(t.name, t.text, target)); return { getSourceFile(fileName): SourceFile { @@ -128,10 +129,9 @@ namespace ts { getNewLine(): string { return sys ? sys.newLine : newLine; }, - fileExists: fileName => hasProperty(files, fileName), + fileExists: fileName => fileName in files, readFile: fileName => { - const file = getProperty(files, fileName); - return file && file.text; + return fileName in files ? files[fileName].text : undefined; } }; } @@ -152,19 +152,29 @@ namespace ts { return program; } - function checkResolvedModule(expected: ResolvedModule, actual: ResolvedModule): void { - assert.isTrue(actual !== undefined); - assert.isTrue(expected.resolvedFileName === actual.resolvedFileName, `'resolvedFileName': expected '${expected.resolvedFileName}' to be equal to '${actual.resolvedFileName}'`); - assert.isTrue(expected.isExternalLibraryImport === actual.isExternalLibraryImport, `'isExternalLibraryImport': expected '${expected.isExternalLibraryImport}' to be equal to '${actual.isExternalLibraryImport}'`); + function checkResolvedModule(expected: ResolvedModule, actual: ResolvedModule): boolean { + if (!expected === !actual) { + if (expected) { + assert.isTrue(expected.resolvedFileName === actual.resolvedFileName, `'resolvedFileName': expected '${expected.resolvedFileName}' to be equal to '${actual.resolvedFileName}'`); + assert.isTrue(expected.isExternalLibraryImport === actual.isExternalLibraryImport, `'isExternalLibraryImport': expected '${expected.isExternalLibraryImport}' to be equal to '${actual.isExternalLibraryImport}'`); + } + return true; + } + return false; } - function checkResolvedTypeDirective(expected: ResolvedTypeReferenceDirective, actual: ResolvedTypeReferenceDirective): void { - assert.isTrue(actual !== undefined); - assert.isTrue(expected.resolvedFileName === actual.resolvedFileName, `'resolvedFileName': expected '${expected.resolvedFileName}' to be equal to '${actual.resolvedFileName}'`); - assert.isTrue(expected.primary === actual.primary, `'primary': expected '${expected.primary}' to be equal to '${actual.primary}'`); + function checkResolvedTypeDirective(expected: ResolvedTypeReferenceDirective, actual: ResolvedTypeReferenceDirective): boolean { + if (!expected === !actual) { + if (expected) { + assert.isTrue(expected.resolvedFileName === actual.resolvedFileName, `'resolvedFileName': expected '${expected.resolvedFileName}' to be equal to '${actual.resolvedFileName}'`); + assert.isTrue(expected.primary === actual.primary, `'primary': expected '${expected.primary}' to be equal to '${actual.primary}'`); + } + return true; + } + return false; } - function checkCache(caption: string, program: Program, fileName: string, expectedContent: MapLike, getCache: (f: SourceFile) => MapLike, entryChecker: (expected: T, original: T) => void): void { + function checkCache(caption: string, program: Program, fileName: string, expectedContent: Map, getCache: (f: SourceFile) => Map, entryChecker: (expected: T, original: T) => boolean): void { const file = program.getSourceFile(fileName); assert.isTrue(file !== undefined, `cannot find file ${fileName}`); const cache = getCache(file); @@ -173,31 +183,15 @@ namespace ts { } else { assert.isTrue(cache !== undefined, `expected ${caption} to be set`); - const actualCacheSize = countOwnProperties(cache); - const expectedSize = countOwnProperties(expectedContent); - assert.isTrue(actualCacheSize === expectedSize, `expected actual size: ${actualCacheSize} to be equal to ${expectedSize}`); - - for (const id in expectedContent) { - if (hasProperty(expectedContent, id)) { - - if (expectedContent[id]) { - const expected = expectedContent[id]; - const actual = cache[id]; - entryChecker(expected, actual); - } - } - else { - assert.isTrue(cache[id] === undefined); - } - } + assert.isTrue(equalProperties(expectedContent, cache, entryChecker), `contents of ${caption} did not match the expected contents.`); } } - function checkResolvedModulesCache(program: Program, fileName: string, expectedContent: MapLike): void { + function checkResolvedModulesCache(program: Program, fileName: string, expectedContent: Map): void { checkCache("resolved modules", program, fileName, expectedContent, f => f.resolvedModules, checkResolvedModule); } - function checkResolvedTypeDirectivesCache(program: Program, fileName: string, expectedContent: MapLike): void { + function checkResolvedTypeDirectivesCache(program: Program, fileName: string, expectedContent: Map): void { checkCache("resolved type directives", program, fileName, expectedContent, f => f.resolvedTypeReferenceDirectiveNames, checkResolvedTypeDirective); } @@ -303,6 +297,8 @@ namespace ts { }); it("resolution cache follows imports", () => { + (Error).stackTraceLimit = Infinity; + const files = [ { name: "a.ts", text: SourceText.New("", "import {_} from 'b'", "var x = 1") }, { name: "b.ts", text: SourceText.New("", "", "var y = 2") }, @@ -310,7 +306,7 @@ namespace ts { const options: CompilerOptions = { target }; const program_1 = newProgram(files, ["a.ts"], options); - checkResolvedModulesCache(program_1, "a.ts", { "b": { resolvedFileName: "b.ts" } }); + checkResolvedModulesCache(program_1, "a.ts", createMap({ "b": { resolvedFileName: "b.ts" } })); checkResolvedModulesCache(program_1, "b.ts", undefined); const program_2 = updateProgram(program_1, ["a.ts"], options, files => { @@ -319,7 +315,7 @@ namespace ts { assert.isTrue(program_1.structureIsReused); // content of resolution cache should not change - checkResolvedModulesCache(program_1, "a.ts", { "b": { resolvedFileName: "b.ts" } }); + checkResolvedModulesCache(program_1, "a.ts", createMap({ "b": { resolvedFileName: "b.ts" } })); checkResolvedModulesCache(program_1, "b.ts", undefined); // imports has changed - program is not reused @@ -336,7 +332,7 @@ namespace ts { files[0].text = files[0].text.updateImportsAndExports(newImports); }); assert.isTrue(!program_3.structureIsReused); - checkResolvedModulesCache(program_4, "a.ts", { "b": { resolvedFileName: "b.ts" }, "c": undefined }); + checkResolvedModulesCache(program_4, "a.ts", createMap({ "b": { resolvedFileName: "b.ts" }, "c": undefined })); }); it("resolved type directives cache follows type directives", () => { @@ -347,7 +343,7 @@ namespace ts { const options: CompilerOptions = { target, typeRoots: ["/types"] }; const program_1 = newProgram(files, ["/a.ts"], options); - checkResolvedTypeDirectivesCache(program_1, "/a.ts", { "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }); + checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMap({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } })); checkResolvedTypeDirectivesCache(program_1, "/types/typedefs/index.d.ts", undefined); const program_2 = updateProgram(program_1, ["/a.ts"], options, files => { @@ -356,7 +352,7 @@ namespace ts { assert.isTrue(program_1.structureIsReused); // content of resolution cache should not change - checkResolvedTypeDirectivesCache(program_1, "/a.ts", { "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }); + checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMap({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } })); checkResolvedTypeDirectivesCache(program_1, "/types/typedefs/index.d.ts", undefined); // type reference directives has changed - program is not reused @@ -374,7 +370,7 @@ namespace ts { files[0].text = files[0].text.updateReferences(newReferences); }); assert.isTrue(!program_3.structureIsReused); - checkResolvedTypeDirectivesCache(program_1, "/a.ts", { "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }); + checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMap({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } })); }); }); diff --git a/src/harness/unittests/session.ts b/src/harness/unittests/session.ts index e8e06da7bad..cf7486acb40 100644 --- a/src/harness/unittests/session.ts +++ b/src/harness/unittests/session.ts @@ -362,13 +362,13 @@ namespace ts.server { class InProcClient { private server: InProcSession; private seq = 0; - private callbacks: ts.MapLike<(resp: protocol.Response) => void> = {}; - private eventHandlers: ts.MapLike<(args: any) => void> = {}; + private callbacks = createMap<(resp: protocol.Response) => void>(); + private eventHandlers = createMap<(args: any) => void>(); handle(msg: protocol.Message): void { if (msg.type === "response") { const response = msg; - if (this.callbacks[response.request_seq]) { + if (response.request_seq in this.callbacks) { this.callbacks[response.request_seq](response); delete this.callbacks[response.request_seq]; } @@ -380,7 +380,7 @@ namespace ts.server { } emit(name: string, args: any): void { - if (this.eventHandlers[name]) { + if (name in this.eventHandlers) { this.eventHandlers[name](args); } } diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 1e76a636c9a..9887573436e 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -68,10 +68,10 @@ namespace ts { return entry; } - function checkMapKeys(caption: string, map: MapLike, expectedKeys: string[]) { - assert.equal(countOwnProperties(map), expectedKeys.length, `${caption}: incorrect size of map`); + function checkMapKeys(caption: string, map: Map, expectedKeys: string[]) { + assert.equal(countProperties(map), expectedKeys.length, `${caption}: incorrect size of map`); for (const name of expectedKeys) { - assert.isTrue(hasProperty(map, name), `${caption} is expected to contain ${name}, actual keys: ${getOwnKeys(map)}`); + assert.isTrue(name in map, `${caption} is expected to contain ${name}, actual keys: ${Object.keys(map)}`); } } @@ -116,8 +116,8 @@ namespace ts { private getCanonicalFileName: (s: string) => string; private toPath: (f: string) => Path; private callbackQueue: TimeOutCallback[] = []; - readonly watchedDirectories: MapLike<{ cb: DirectoryWatcherCallback, recursive: boolean }[]> = {}; - readonly watchedFiles: MapLike = {}; + readonly watchedDirectories = createMap<{ cb: DirectoryWatcherCallback, recursive: boolean }[]>(); + readonly watchedFiles = createMap(); constructor(public useCaseSensitiveFileNames: boolean, private executingFilePath: string, private currentDirectory: string, fileOrFolderList: FileOrFolder[]) { this.getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); @@ -198,7 +198,7 @@ namespace ts { watchDirectory(directoryName: string, callback: DirectoryWatcherCallback, recursive: boolean): DirectoryWatcher { const path = this.toPath(directoryName); - const callbacks = getProperty(this.watchedDirectories, path) || (this.watchedDirectories[path] = []); + const callbacks = this.watchedDirectories[path] || (this.watchedDirectories[path] = []); callbacks.push({ cb: callback, recursive }); return { referenceCount: 0, @@ -219,7 +219,7 @@ namespace ts { triggerDirectoryWatcherCallback(directoryName: string, fileName: string): void { const path = this.toPath(directoryName); - const callbacks = getProperty(this.watchedDirectories, path); + const callbacks = this.watchedDirectories[path]; if (callbacks) { for (const callback of callbacks) { callback.cb(fileName); @@ -229,7 +229,7 @@ namespace ts { triggerFileWatcherCallback(fileName: string, removed?: boolean): void { const path = this.toPath(fileName); - const callbacks = getProperty(this.watchedFiles, path); + const callbacks = this.watchedFiles[path]; if (callbacks) { for (const callback of callbacks) { callback(path, removed); @@ -239,7 +239,7 @@ namespace ts { watchFile(fileName: string, callback: FileWatcherCallback) { const path = this.toPath(fileName); - const callbacks = getProperty(this.watchedFiles, path) || (this.watchedFiles[path] = []); + const callbacks = this.watchedFiles[path] || (this.watchedFiles[path] = []); callbacks.push(callback); return { close: () => { diff --git a/src/server/session.ts b/src/server/session.ts index 13b0e6d6ce4..9383a54bd48 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1061,7 +1061,7 @@ namespace ts.server { return { response, responseRequired: true }; } - private handlers: MapLike<(request: protocol.Request) => { response?: any, responseRequired?: boolean }> = { + private handlers = createMap<(request: protocol.Request) => { response?: any, responseRequired?: boolean }>({ [CommandNames.Exit]: () => { this.exit(); return { responseRequired: false }; @@ -1198,9 +1198,10 @@ namespace ts.server { this.reloadProjects(); return { responseRequired: false }; } - }; + }); + public addProtocolHandler(command: string, handler: (request: protocol.Request) => { response?: any, responseRequired: boolean }) { - if (this.handlers[command]) { + if (command in this.handlers) { throw new Error(`Protocol handler already exists for command "${command}"`); } this.handlers[command] = handler; diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index bac33a6bdfb..4f0e06244d2 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -58,12 +58,7 @@ namespace ts.JsTyping { if (!safeList) { const result = readConfigFile(safeListPath, (path: string) => host.readFile(path)); - if (result.config) { - safeList = result.config; - } - else { - safeList = createMap(); - }; + safeList = createMap(result.config); } const filesToWatch: string[] = []; @@ -93,7 +88,7 @@ namespace ts.JsTyping { // Add the cached typing locations for inferred typings that are already installed for (const name in packageNameToTypingLocation) { - if (hasProperty(inferredTypings, name) && !inferredTypings[name]) { + if (name in inferredTypings && !inferredTypings[name]) { inferredTypings[name] = packageNameToTypingLocation[name]; } } @@ -124,7 +119,7 @@ namespace ts.JsTyping { } for (const typing of typingNames) { - if (!hasProperty(inferredTypings, typing)) { + if (!(typing in inferredTypings)) { inferredTypings[typing] = undefined; } } @@ -167,7 +162,7 @@ namespace ts.JsTyping { mergeTypings(cleanedTypingNames); } else { - mergeTypings(filter(cleanedTypingNames, f => hasProperty(safeList, f))); + mergeTypings(filter(cleanedTypingNames, f => f in safeList)); } const hasJsxFile = forEach(fileNames, f => scriptKindIs(f, /*LanguageServiceHost*/ undefined, ScriptKind.JSX)); diff --git a/src/services/patternMatcher.ts b/src/services/patternMatcher.ts index cff3f591f8a..b4f67ca056d 100644 --- a/src/services/patternMatcher.ts +++ b/src/services/patternMatcher.ts @@ -188,7 +188,7 @@ namespace ts { } function getWordSpans(word: string): TextSpan[] { - if (!hasProperty(stringToWordSpans, word)) { + if (!(word in stringToWordSpans)) { stringToWordSpans[word] = breakIntoWordSpans(word); } diff --git a/src/services/services.ts b/src/services/services.ts index 0c1f5929fbf..87093645e2a 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2042,7 +2042,7 @@ namespace ts { function fixupCompilerOptions(options: CompilerOptions, diagnostics: Diagnostic[]): CompilerOptions { // Lazily create this value to fix module loading errors. commandLineOptionsStringToEnum = commandLineOptionsStringToEnum || filter(optionDeclarations, o => - typeof o.type === "object" && !forEachOwnProperty(>o.type, v => typeof v !== "number")); + typeof o.type === "object" && !forEachProperty(o.type, v => typeof v !== "number")); options = clone(options); @@ -2117,7 +2117,9 @@ namespace ts { sourceFile.moduleName = transpileOptions.moduleName; } - sourceFile.renamedDependencies = transpileOptions.renamedDependencies; + if (transpileOptions.renamedDependencies) { + sourceFile.renamedDependencies = createMap(transpileOptions.renamedDependencies); + } const newLine = getNewLineCharacter(options); @@ -6745,7 +6747,7 @@ namespace ts { // the function will add any found symbol of the property-name, then its sub-routine will call // getPropertySymbolsFromBaseTypes again to walk up any base types to prevent revisiting already // visited symbol, interface "C", the sub-routine will pass the current symbol as previousIterationSymbol. - if (hasProperty(previousIterationSymbolsCache, symbol.name)) { + if (symbol.name in previousIterationSymbolsCache) { return; } From f7f50073d3e8166b04647617e94f438e69db5595 Mon Sep 17 00:00:00 2001 From: Yui Date: Tue, 16 Aug 2016 08:47:21 -0700 Subject: [PATCH 148/233] Fix 10625: JSX Not validating when index signature is present (#10352) * Check for type of property declaration before using index signature * Add tests and baselines * fix linting error --- src/compiler/checker.ts | 7 ++- .../tsxAttributeResolution14.errors.txt | 38 +++++++++++++++ .../reference/tsxAttributeResolution14.js | 47 +++++++++++++++++++ .../jsx/tsxAttributeResolution14.tsx | 32 +++++++++++++ 4 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 tests/baselines/reference/tsxAttributeResolution14.errors.txt create mode 100644 tests/baselines/reference/tsxAttributeResolution14.js create mode 100644 tests/cases/conformance/jsx/tsxAttributeResolution14.tsx diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 984cedbf5ad..25a35c307f4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10140,10 +10140,9 @@ namespace ts { const correspondingPropSymbol = getPropertyOfType(elementAttributesType, node.name.text); correspondingPropType = correspondingPropSymbol && getTypeOfSymbol(correspondingPropSymbol); if (isUnhyphenatedJsxName(node.name.text)) { - // Maybe there's a string indexer? - const indexerType = getIndexTypeOfType(elementAttributesType, IndexKind.String); - if (indexerType) { - correspondingPropType = indexerType; + const attributeType = getTypeOfPropertyOfType(elementAttributesType, getTextOfPropertyName(node.name)) || getIndexTypeOfType(elementAttributesType, IndexKind.String); + if (attributeType) { + correspondingPropType = attributeType; } else { // If there's no corresponding property with this name, error diff --git a/tests/baselines/reference/tsxAttributeResolution14.errors.txt b/tests/baselines/reference/tsxAttributeResolution14.errors.txt new file mode 100644 index 00000000000..81d42e49059 --- /dev/null +++ b/tests/baselines/reference/tsxAttributeResolution14.errors.txt @@ -0,0 +1,38 @@ +tests/cases/conformance/jsx/file.tsx(14,28): error TS2322: Type 'number' is not assignable to type 'string'. +tests/cases/conformance/jsx/file.tsx(16,28): error TS2322: Type 'boolean' is not assignable to type 'string | number'. + + +==== tests/cases/conformance/jsx/react.d.ts (0 errors) ==== + + declare module JSX { + interface Element { } + interface IntrinsicElements { + div: any; + } + interface ElementAttributesProperty { prop: any } + } + +==== tests/cases/conformance/jsx/file.tsx (2 errors) ==== + + interface IProps { + primaryText: string, + [propName: string]: string | number + } + + function VerticalNavMenuItem(prop: IProps) { + return
props.primaryText
+ } + + function VerticalNav() { + return ( +
+ // error + ~~~~~~~~~~~~~~~ +!!! error TS2322: Type 'number' is not assignable to type 'string'. + // ok + // error + ~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type 'boolean' is not assignable to type 'string | number'. +
+ ) + } \ No newline at end of file diff --git a/tests/baselines/reference/tsxAttributeResolution14.js b/tests/baselines/reference/tsxAttributeResolution14.js new file mode 100644 index 00000000000..d920179458c --- /dev/null +++ b/tests/baselines/reference/tsxAttributeResolution14.js @@ -0,0 +1,47 @@ +//// [tests/cases/conformance/jsx/tsxAttributeResolution14.tsx] //// + +//// [react.d.ts] + +declare module JSX { + interface Element { } + interface IntrinsicElements { + div: any; + } + interface ElementAttributesProperty { prop: any } +} + +//// [file.tsx] + +interface IProps { + primaryText: string, + [propName: string]: string | number +} + +function VerticalNavMenuItem(prop: IProps) { + return
props.primaryText
+} + +function VerticalNav() { + return ( +
+ // error + // ok + // error +
+ ) +} + +//// [file.jsx] +function VerticalNavMenuItem(prop) { + return
props.primaryText
; +} +function VerticalNav() { + return (
+ // error + // error + // ok + // ok + // error + // error +
); +} diff --git a/tests/cases/conformance/jsx/tsxAttributeResolution14.tsx b/tests/cases/conformance/jsx/tsxAttributeResolution14.tsx new file mode 100644 index 00000000000..1e4418e7fba --- /dev/null +++ b/tests/cases/conformance/jsx/tsxAttributeResolution14.tsx @@ -0,0 +1,32 @@ +//@jsx: preserve +//@module: amd + +//@filename: react.d.ts +declare module JSX { + interface Element { } + interface IntrinsicElements { + div: any; + } + interface ElementAttributesProperty { prop: any } +} + +//@filename: file.tsx + +interface IProps { + primaryText: string, + [propName: string]: string | number +} + +function VerticalNavMenuItem(prop: IProps) { + return
props.primaryText
+} + +function VerticalNav() { + return ( +
+ // error + // ok + // error +
+ ) +} \ No newline at end of file From 57701575044e8acbd55845cddb5bd7a7f08331b6 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 16 Aug 2016 09:41:33 -0700 Subject: [PATCH 149/233] Adding more comments --- src/compiler/checker.ts | 15 ++++++++++++++- src/compiler/types.ts | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8a32e23e1ff..685b32f796f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8809,21 +8809,34 @@ namespace ts { const type = getTypeOfSymbol(localOrExportSymbol); const declaration = localOrExportSymbol.valueDeclaration; + // We only narrow variables and parameters occurring in a non-assignment position. For all other + // entities we simply return the declared type. if (!(localOrExportSymbol.flags & SymbolFlags.Variable) || isAssignmentTarget(node) || !declaration) { return type; } - + // The declaration container is the innermost function that encloses the declaration of the variable + // or parameter. The flow container is the innermost function starting with which we analyze the control + // flow graph to determine the control flow based type. const isParameter = getRootDeclaration(declaration).kind === SyntaxKind.Parameter; const declarationContainer = getControlFlowContainer(declaration); let flowContainer = getControlFlowContainer(node); + // When the control flow originates in a function expression or arrow function and we are referencing + // a const variable or parameter from an outer function, we extend the origin of the control flow + // analysis to include the immediately enclosing function. while (flowContainer !== declarationContainer && (flowContainer.kind === SyntaxKind.FunctionExpression || flowContainer.kind === SyntaxKind.ArrowFunction) && (isReadonlySymbol(localOrExportSymbol) || isParameter && !isParameterAssigned(localOrExportSymbol))) { flowContainer = getControlFlowContainer(flowContainer); } + // We only look for uninitialized variables in strict null checking mode, and only when we can analyze + // the entire control flow graph from the variable's declaration (i.e. when the flow container and + // declaration container are the same). const assumeInitialized = !strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || isParameter || flowContainer !== declarationContainer || isInAmbientContext(declaration); const flowType = getFlowTypeOfReference(node, type, assumeInitialized, flowContainer); + // A variable is considered uninitialized when it is possible to analyze the entire control flow graph + // from declaration to use, and when the variable's declared type doesn't include undefined but the + // control flow based type does include undefined. if (!assumeInitialized && !(getFalsyFlags(type) & TypeFlags.Undefined) && getFalsyFlags(flowType) & TypeFlags.Undefined) { error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol)); // Return the declared type to reduce follow-on errors diff --git a/src/compiler/types.ts b/src/compiler/types.ts index cca94c0adc1..8e9953ee64d 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2157,7 +2157,7 @@ namespace ts { /* @internal */ exportSymbol?: Symbol; // Exported symbol associated with this symbol /* @internal */ constEnumOnlyModule?: boolean; // True if module contains only const enums or other modules with only const enums /* @internal */ isReferenced?: boolean; // True if the symbol is referenced elsewhere - /* @internal */ isAssigned?: boolean; // True if the symbol has assignments + /* @internal */ isAssigned?: boolean; // True if the symbol is a parameter with assignments } /* @internal */ From 889e5ac7ae00d9aefd664d11ec942c13acdd79a4 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 16 Aug 2016 11:15:15 -0700 Subject: [PATCH 150/233] Clean up/move some Map helper functions. --- src/compiler/commandLineParser.ts | 4 +- src/compiler/core.ts | 151 ++---------------- src/harness/fourslash.ts | 2 +- src/harness/harnessLanguageService.ts | 2 +- src/harness/unittests/moduleResolution.ts | 2 +- .../unittests/reuseProgramStructure.ts | 2 +- .../unittests/tsserverProjectSystem.ts | 2 +- src/server/editorServices.ts | 2 +- src/services/services.ts | 2 +- 9 files changed, 18 insertions(+), 151 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 35140855d81..616bf5e70c0 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -1016,8 +1016,8 @@ namespace ts { } } - const literalFiles = reduceOwnProperties(literalFileMap, addFileToOutput, []); - const wildcardFiles = reduceOwnProperties(wildcardFileMap, addFileToOutput, []); + const literalFiles = reduceProperties(literalFileMap, addFileToOutput, []); + const wildcardFiles = reduceProperties(wildcardFileMap, addFileToOutput, []); wildcardFiles.sort(host.useCaseSensitiveFileNames ? compareStrings : compareStringsCaseInsensitive); return { fileNames: literalFiles.concat(wildcardFiles), diff --git a/src/compiler/core.ts b/src/compiler/core.ts index d10d76b3492..40341929c02 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -30,8 +30,10 @@ namespace ts { map["__"] = undefined; delete map["__"]; - if (template) { - copyOwnProperties(template, map); + // Copies keys/values from template. Note that for..in will not throw if + // template is undefined, and instead will just exit the loop. + for (const key in template) if (hasOwnProperty.call(template, key)) { + map[key] = template[key]; } return map; @@ -412,9 +414,6 @@ namespace ts { /** * Enumerates the properties of a Map, invoking a callback and returning the first truthy result. * - * NOTE: This is intended for use with Map objects. For MapLike objects, use - * forEachOwnProperties instead as it offers better runtime safety. - * * @param map A map for which properties should be enumerated. * @param callback A callback to invoke for each property. */ @@ -426,53 +425,6 @@ namespace ts { return result; } - /** - * Enumerates the owned properties of a MapLike, invoking a callback and returning the first truthy result. - * - * NOTE: This is intended for use with MapLike objects. For Map objects, use - * forEachProperty instead as it offers better performance. - * - * @param map A map for which properties should be enumerated. - * @param callback A callback to invoke for each property. - */ - export function forEachOwnProperty(map: MapLike, callback: (value: T, key: string) => U): U { - let result: U; - for (const key in map) if (hasOwnProperty.call(map, key)) { - if (result = callback(map[key], key)) break; - } - return result; - } - - /** - * Maps key-value pairs of a map into a new map. - * - * NOTE: The key-value pair passed to the callback is *not* safe to cache between invocations - * of the callback. - * - * @param map A map. - * @param callback A callback that maps a key-value pair into a new key-value pair. - */ - export function mapPairs(map: Map, callback: (entry: [string, T]) => [string, U]): Map { - let result: Map; - if (map) { - result = createMap(); - let inPair: [string, T]; - for (const key in map) { - if (inPair) { - inPair[0] = key; - inPair[1] = map[key]; - } - else { - inPair = [key, map[key]]; - } - - const outPair = callback(inPair); - result[outPair[0]] = outPair[1]; - } - } - return result; - } - /** * Returns true if a Map has some matching property. * @@ -489,9 +441,6 @@ namespace ts { /** * Performs a shallow copy of the properties from a source Map to a target MapLike * - * NOTE: This is intended for use with Map objects. For MapLike objects, use - * copyOwnProperties instead as it offers better runtime safety. - * * @param source A map from which properties should be copied. * @param target A map to which properties should be copied. */ @@ -501,21 +450,6 @@ namespace ts { } } - /** - * Performs a shallow copy of the owned properties from a source map to a target map-like. - * - * NOTE: This is intended for use with MapLike objects. For Map objects, use - * copyProperties instead as it offers better performance. - * - * @param source A map-like from which properties should be copied. - * @param target A map-like to which properties should be copied. - */ - export function copyOwnProperties(source: MapLike, target: MapLike): void { - for (const key in source) if (hasOwnProperty.call(source, key)) { - target[key] = source[key]; - } - } - /** * Reduce the properties of a map. * @@ -552,72 +486,9 @@ namespace ts { return result; } - /** - * Counts the properties of a map. - * - * NOTE: This is intended for use with Map objects. For MapLike objects, use - * countOwnProperties instead as it offers better runtime safety. - * - * @param map A map whose properties should be counted. - * @param predicate An optional callback used to limit which properties should be counted. - */ - export function countProperties(map: Map, predicate?: (value: T, key: string) => boolean) { - let count = 0; - for (const key in map) { - if (!predicate || predicate(map[key], key)) { - count++; - } - } - return count; - } - - /** - * Counts the owned properties of a map-like. - * - * NOTE: This is intended for use with MapLike objects. For Map objects, use - * countProperties instead as it offers better performance. - * - * @param map A map-like whose properties should be counted. - * @param predicate An optional callback used to limit which properties should be counted. - */ - export function countOwnProperties(map: MapLike, predicate?: (value: T, key: string) => boolean) { - let count = 0; - for (const key in map) if (hasOwnProperty.call(map, key)) { - if (!predicate || predicate(map[key], key)) { - count++; - } - } - return count; - } - - /** - * Performs a shallow equality comparison of the contents of two maps. - * - * NOTE: This is intended for use with Map objects. For MapLike objects, use - * equalOwnProperties instead as it offers better runtime safety. - * - * @param left A map whose properties should be compared. - * @param right A map whose properties should be compared. - */ - export function equalProperties(left: Map, right: Map, equalityComparer?: (left: T, right: T) => boolean) { - if (left === right) return true; - if (!left || !right) return false; - for (const key in left) { - if (!(key in right)) return false; - if (equalityComparer ? !equalityComparer(left[key], right[key]) : left[key] !== right[key]) return false; - } - for (const key in right) { - if (!(key in left)) return false; - } - return true; - } - /** * Performs a shallow equality comparison of the contents of two map-likes. * - * NOTE: This is intended for use with MapLike objects. For Map objects, use - * equalProperties instead as it offers better performance. - * * @param left A map-like whose properties should be compared. * @param right A map-like whose properties should be compared. */ @@ -670,17 +541,13 @@ namespace ts { return result; } - export function extend, T2 extends MapLike<{}>>(first: T1 , second: T2): T1 & T2 { + export function extend(first: T1 , second: T2): T1 & T2 { const result: T1 & T2 = {}; - for (const id in first) { - if (hasOwnProperty.call(first, id)) { - (result as any)[id] = first[id]; - } + for (const id in second) if (hasOwnProperty.call(second, id)) { + (result as any)[id] = (second as any)[id]; } - for (const id in second) { - if (hasOwnProperty.call(second, id) && !hasOwnProperty.call(result, id)) { - (result as any)[id] = second[id]; - } + for (const id in first) if (hasOwnProperty.call(first, id)) { + (result as any)[id] = (first as any)[id]; } return result; } diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index ba1994c8141..f9fdd5a1159 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -773,7 +773,7 @@ namespace FourSlash { } public verifyRangesWithSameTextReferenceEachOther() { - ts.forEachOwnProperty(this.rangesByText(), ranges => this.verifyRangesReferenceEachOther(ranges)); + ts.forEachProperty(this.rangesByText(), ranges => this.verifyRangesReferenceEachOther(ranges)); } private verifyReferencesWorker(references: ts.ReferenceEntry[], fileName: string, start: number, end: number, isWriteAccess?: boolean, isDefinition?: boolean) { diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index b37a6a90899..94124432780 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -135,7 +135,7 @@ namespace Harness.LanguageService { public getFilenames(): string[] { const fileNames: string[] = []; - ts.forEachOwnProperty(this.fileNameToScript, (scriptInfo) => { + ts.forEachProperty(this.fileNameToScript, (scriptInfo) => { if (scriptInfo.isRootFile) { // only include root files here // usually it means that we won't include lib.d.ts in the list of root files so it won't mess the computation of compilation root dir. diff --git a/src/harness/unittests/moduleResolution.ts b/src/harness/unittests/moduleResolution.ts index ff0151c40a5..09febd2300a 100644 --- a/src/harness/unittests/moduleResolution.ts +++ b/src/harness/unittests/moduleResolution.ts @@ -358,7 +358,7 @@ export = C; function test(files: Map, options: CompilerOptions, currentDirectory: string, useCaseSensitiveFileNames: boolean, rootFiles: string[], diagnosticCodes: number[]): void { const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); if (!useCaseSensitiveFileNames) { - files = mapPairs(files, ([fileName, file]) => [getCanonicalFileName(fileName), file]); + files = reduceProperties(files, (files, file, fileName) => (files[getCanonicalFileName(fileName)] = file, files), createMap()); } const host: CompilerHost = { diff --git a/src/harness/unittests/reuseProgramStructure.ts b/src/harness/unittests/reuseProgramStructure.ts index 9f2b0c343c2..60687d34c55 100644 --- a/src/harness/unittests/reuseProgramStructure.ts +++ b/src/harness/unittests/reuseProgramStructure.ts @@ -183,7 +183,7 @@ namespace ts { } else { assert.isTrue(cache !== undefined, `expected ${caption} to be set`); - assert.isTrue(equalProperties(expectedContent, cache, entryChecker), `contents of ${caption} did not match the expected contents.`); + assert.isTrue(equalOwnProperties(expectedContent, cache, entryChecker), `contents of ${caption} did not match the expected contents.`); } } diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 9887573436e..49f033b9def 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -69,7 +69,7 @@ namespace ts { } function checkMapKeys(caption: string, map: Map, expectedKeys: string[]) { - assert.equal(countProperties(map), expectedKeys.length, `${caption}: incorrect size of map`); + assert.equal(reduceProperties(map, count => count + 1, 0), expectedKeys.length, `${caption}: incorrect size of map`); for (const name of expectedKeys) { assert.isTrue(name in map, `${caption} is expected to contain ${name}, actual keys: ${Object.keys(map)}`); } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 9ba243d9704..db9bdd5043b 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1419,7 +1419,7 @@ namespace ts.server { /*recursive*/ true ); - project.directoriesWatchedForWildcards = reduceOwnProperties(projectOptions.wildcardDirectories, (watchers, flag, directory) => { + project.directoriesWatchedForWildcards = reduceProperties(createMap(projectOptions.wildcardDirectories), (watchers, flag, directory) => { if (comparePaths(configDirectoryPath, directory, ".", !this.host.useCaseSensitiveFileNames) !== Comparison.EqualTo) { const recursive = (flag & WatchDirectoryFlags.Recursive) !== 0; this.log(`Add ${ recursive ? "recursive " : ""}watcher for: ${directory}`); diff --git a/src/services/services.ts b/src/services/services.ts index 87093645e2a..1153e2e9e3d 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2058,7 +2058,7 @@ namespace ts { options[opt.name] = parseCustomTypeOption(opt, value, diagnostics); } else { - if (!forEachOwnProperty(opt.type, v => v === value)) { + if (!forEachProperty(opt.type, v => v === value)) { // Supplied value isn't a valid enum value. diagnostics.push(createCompilerDiagnosticForInvalidCustomType(opt)); } From c0146556e863f39eaa1922d66b1dda6b173f7b8a Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 16 Aug 2016 11:18:24 -0700 Subject: [PATCH 151/233] Revert some formatting changes. --- src/compiler/checker.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c309b9e36c0..73ae7fc750d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8467,11 +8467,11 @@ namespace ts { return type; } const doubleEquals = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsToken; - const facts = doubleEquals - ? assumeTrue ? TypeFacts.EQUndefinedOrNull : TypeFacts.NEUndefinedOrNull - : value.kind === SyntaxKind.NullKeyword - ? assumeTrue ? TypeFacts.EQNull : TypeFacts.NENull - : assumeTrue ? TypeFacts.EQUndefined : TypeFacts.NEUndefined; + const facts = doubleEquals ? + assumeTrue ? TypeFacts.EQUndefinedOrNull : TypeFacts.NEUndefinedOrNull : + value.kind === SyntaxKind.NullKeyword ? + assumeTrue ? TypeFacts.EQNull : TypeFacts.NENull : + assumeTrue ? TypeFacts.EQUndefined : TypeFacts.NEUndefined; return getTypeWithFacts(type, facts); } if (type.flags & TypeFlags.NotUnionOrUnit) { @@ -8507,9 +8507,9 @@ namespace ts { return targetType; } } - const facts = assumeTrue - ? typeofEQFacts[literal.text] || TypeFacts.TypeofEQHostObject - : typeofNEFacts[literal.text] || TypeFacts.TypeofNEHostObject; + const facts = assumeTrue ? + typeofEQFacts[literal.text] || TypeFacts.TypeofEQHostObject : + typeofNEFacts[literal.text] || TypeFacts.TypeofNEHostObject; return getTypeWithFacts(type, facts); } From ce5e2078eed31c10c785150c22b4e31442f641e9 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 16 Aug 2016 11:29:09 -0700 Subject: [PATCH 152/233] Improve ReadonlyArray.concat to match Array The Array-based signature was incorrect and also out-of-date. --- src/lib/es5.d.ts | 7 ++- ...typeIsAssignableToReadonlyArray.errors.txt | 46 ++++++++++++++++ ...rayOfSubtypeIsAssignableToReadonlyArray.js | 54 +++++++++++++++++++ ...rayOfSubtypeIsAssignableToReadonlyArray.ts | 18 +++++++ 4 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/arrayOfSubtypeIsAssignableToReadonlyArray.errors.txt create mode 100644 tests/baselines/reference/arrayOfSubtypeIsAssignableToReadonlyArray.js create mode 100644 tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts diff --git a/src/lib/es5.d.ts b/src/lib/es5.d.ts index 496df578c19..e4cbe323c68 100644 --- a/src/lib/es5.d.ts +++ b/src/lib/es5.d.ts @@ -1006,7 +1006,12 @@ interface ReadonlyArray { * Combines two or more arrays. * @param items Additional items to add to the end of array1. */ - concat(...items: T[]): T[]; + concat(...items: T[][]): T[]; + /** + * Combines two or more arrays. + * @param items Additional items to add to the end of array1. + */ + concat(...items: (T | T[])[]): T[]; /** * Adds all the elements of an array separated by the specified separator string. * @param separator A string used to separate one element of an array from the next in the resulting String. If omitted, the array elements are separated with a comma. diff --git a/tests/baselines/reference/arrayOfSubtypeIsAssignableToReadonlyArray.errors.txt b/tests/baselines/reference/arrayOfSubtypeIsAssignableToReadonlyArray.errors.txt new file mode 100644 index 00000000000..6f4a0ef9d6d --- /dev/null +++ b/tests/baselines/reference/arrayOfSubtypeIsAssignableToReadonlyArray.errors.txt @@ -0,0 +1,46 @@ +tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(13,1): error TS2322: Type 'A[]' is not assignable to type 'ReadonlyArray'. + Types of property 'concat' are incompatible. + Type '{ (...items: A[][]): A[]; (...items: (A | A[])[]): A[]; }' is not assignable to type '{ >(...items: U[]): B[]; (...items: B[][]): B[]; (...items: (B | B[])[]): B[]; }'. + Type 'A[]' is not assignable to type 'B[]'. + Type 'A' is not assignable to type 'B'. + Property 'b' is missing in type 'A'. +tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(18,1): error TS2322: Type 'C' is not assignable to type 'ReadonlyArray'. + Types of property 'concat' are incompatible. + Type '{ (...items: A[][]): A[]; (...items: (A | A[])[]): A[]; }' is not assignable to type '{ >(...items: U[]): B[]; (...items: B[][]): B[]; (...items: (B | B[])[]): B[]; }'. + Type 'A[]' is not assignable to type 'B[]'. + Type 'A' is not assignable to type 'B'. + + +==== tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts (2 errors) ==== + class A { a } + class B extends A { b } + class C extends Array { c } + declare var ara: A[]; + declare var arb: B[]; + declare var cra: C; + declare var crb: C; + declare var rra: ReadonlyArray; + declare var rrb: ReadonlyArray; + rra = ara; + rrb = arb; // OK, Array is assignable to ReadonlyArray + rra = arb; + rrb = ara; // error: 'A' is not assignable to 'B' + ~~~ +!!! error TS2322: Type 'A[]' is not assignable to type 'ReadonlyArray'. +!!! error TS2322: Types of property 'concat' are incompatible. +!!! error TS2322: Type '{ (...items: A[][]): A[]; (...items: (A | A[])[]): A[]; }' is not assignable to type '{ >(...items: U[]): B[]; (...items: B[][]): B[]; (...items: (B | B[])[]): B[]; }'. +!!! error TS2322: Type 'A[]' is not assignable to type 'B[]'. +!!! error TS2322: Type 'A' is not assignable to type 'B'. +!!! error TS2322: Property 'b' is missing in type 'A'. + + rra = cra; + rra = crb; // OK, C is assignable to ReadonlyArray + rrb = crb; + rrb = cra; // error: 'A' is not assignable to 'B' + ~~~ +!!! error TS2322: Type 'C' is not assignable to type 'ReadonlyArray'. +!!! error TS2322: Types of property 'concat' are incompatible. +!!! error TS2322: Type '{ (...items: A[][]): A[]; (...items: (A | A[])[]): A[]; }' is not assignable to type '{ >(...items: U[]): B[]; (...items: B[][]): B[]; (...items: (B | B[])[]): B[]; }'. +!!! error TS2322: Type 'A[]' is not assignable to type 'B[]'. +!!! error TS2322: Type 'A' is not assignable to type 'B'. + \ No newline at end of file diff --git a/tests/baselines/reference/arrayOfSubtypeIsAssignableToReadonlyArray.js b/tests/baselines/reference/arrayOfSubtypeIsAssignableToReadonlyArray.js new file mode 100644 index 00000000000..255b52e16c9 --- /dev/null +++ b/tests/baselines/reference/arrayOfSubtypeIsAssignableToReadonlyArray.js @@ -0,0 +1,54 @@ +//// [arrayOfSubtypeIsAssignableToReadonlyArray.ts] +class A { a } +class B extends A { b } +class C extends Array { c } +declare var ara: A[]; +declare var arb: B[]; +declare var cra: C; +declare var crb: C; +declare var rra: ReadonlyArray; +declare var rrb: ReadonlyArray; +rra = ara; +rrb = arb; // OK, Array is assignable to ReadonlyArray +rra = arb; +rrb = ara; // error: 'A' is not assignable to 'B' + +rra = cra; +rra = crb; // OK, C is assignable to ReadonlyArray +rrb = crb; +rrb = cra; // error: 'A' is not assignable to 'B' + + +//// [arrayOfSubtypeIsAssignableToReadonlyArray.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var A = (function () { + function A() { + } + return A; +}()); +var B = (function (_super) { + __extends(B, _super); + function B() { + _super.apply(this, arguments); + } + return B; +}(A)); +var C = (function (_super) { + __extends(C, _super); + function C() { + _super.apply(this, arguments); + } + return C; +}(Array)); +rra = ara; +rrb = arb; // OK, Array is assignable to ReadonlyArray +rra = arb; +rrb = ara; // error: 'A' is not assignable to 'B' +rra = cra; +rra = crb; // OK, C is assignable to ReadonlyArray +rrb = crb; +rrb = cra; // error: 'A' is not assignable to 'B' diff --git a/tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts b/tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts new file mode 100644 index 00000000000..d621cf460bc --- /dev/null +++ b/tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts @@ -0,0 +1,18 @@ +class A { a } +class B extends A { b } +class C extends Array { c } +declare var ara: A[]; +declare var arb: B[]; +declare var cra: C; +declare var crb: C; +declare var rra: ReadonlyArray; +declare var rrb: ReadonlyArray; +rra = ara; +rrb = arb; // OK, Array is assignable to ReadonlyArray +rra = arb; +rrb = ara; // error: 'A' is not assignable to 'B' + +rra = cra; +rra = crb; // OK, C is assignable to ReadonlyArray +rrb = crb; +rrb = cra; // error: 'A' is not assignable to 'B' From dcf3946b6876d6a9408bebef297f434b05168b33 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 16 Aug 2016 11:31:32 -0700 Subject: [PATCH 153/233] Fix link to blog --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fca2890bc77..d16bc363b26 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Join the chat at https://gitter.im/Microsoft/TypeScript](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Microsoft/TypeScript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[TypeScript](http://www.typescriptlang.org/) is a language for application-scale JavaScript. TypeScript adds optional types, classes, and modules to JavaScript. TypeScript supports tools for large-scale JavaScript applications for any browser, for any host, on any OS. TypeScript compiles to readable, standards-based JavaScript. Try it out at the [playground](http://www.typescriptlang.org/Playground), and stay up to date via [our blog](http://blogs.msdn.com/typescript) and [Twitter account](https://twitter.com/typescriptlang). +[TypeScript](http://www.typescriptlang.org/) is a language for application-scale JavaScript. TypeScript adds optional types, classes, and modules to JavaScript. TypeScript supports tools for large-scale JavaScript applications for any browser, for any host, on any OS. TypeScript compiles to readable, standards-based JavaScript. Try it out at the [playground](http://www.typescriptlang.org/Playground), and stay up to date via [our blog](https://blogs.msdn.microsoft.com/typescript) and [Twitter account](https://twitter.com/typescriptlang). ## Installing From a6974474a1fb850529378570da9e914f8a44040a Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 16 Aug 2016 13:51:17 -0700 Subject: [PATCH 154/233] Remove old assertion about when we're allowed to use fileExists --- src/services/services.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/services/services.ts b/src/services/services.ts index 850e29030d8..5876da74f15 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -3114,7 +3114,6 @@ namespace ts { getCurrentDirectory: () => currentDirectory, fileExists: (fileName): boolean => { // stub missing host functionality - Debug.assert(!host.resolveModuleNames || !host.resolveTypeReferenceDirectives); return hostCache.getOrCreateEntry(fileName) !== undefined; }, readFile: (fileName): string => { From a66b38af56ab1b18e106829c23cbaf43ca4d38bb Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Tue, 16 Aug 2016 13:59:13 -0700 Subject: [PATCH 155/233] Set isNewIdentifierLocation to true for JavaScript files --- src/services/services.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/services.ts b/src/services/services.ts index aea4d5f872e..80eb0b3f46d 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4247,7 +4247,7 @@ namespace ts { addRange(entries, keywordCompletions); } - return { isMemberCompletion, isNewIdentifierLocation, entries }; + return { isMemberCompletion, isNewIdentifierLocation: isSourceFileJavaScript(sourceFile) ? true : isNewIdentifierLocation, entries }; function getJavaScriptCompletionEntries(sourceFile: SourceFile, position: number, uniqueNames: Map): CompletionEntry[] { const entries: CompletionEntry[] = []; From a36e15558e3e0608fc03986b422877a9a59d1b86 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Tue, 16 Aug 2016 14:04:02 -0700 Subject: [PATCH 156/233] Update error message for conflicting type definitions Fixes #10370 --- src/compiler/diagnosticMessages.json | 2 +- src/compiler/program.ts | 2 +- tests/baselines/reference/library-reference-5.errors.txt | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index c1eead94f14..c6a3698ea16 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2231,7 +2231,7 @@ "category": "Error", "code": 4082 }, - "Conflicting library definitions for '{0}' found at '{1}' and '{2}'. Copy the correct file to the 'typings' folder to resolve this conflict.": { + "Conflicting definitions for '{0}' found at '{1}' and '{2}'. Consider installing a specific version of this library to resolve the conflict.": { "category": "Message", "code": 4090 }, diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 767fcb488bb..3f698e82758 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -2048,7 +2048,7 @@ namespace ts { const otherFileText = host.readFile(resolvedTypeReferenceDirective.resolvedFileName); if (otherFileText !== getSourceFile(previousResolution.resolvedFileName).text) { fileProcessingDiagnostics.add(createDiagnostic(refFile, refPos, refEnd, - Diagnostics.Conflicting_library_definitions_for_0_found_at_1_and_2_Copy_the_correct_file_to_the_typings_folder_to_resolve_this_conflict, + Diagnostics.Conflicting_definitions_for_0_found_at_1_and_2_Consider_installing_a_specific_version_of_this_library_to_resolve_the_conflict, typeReferenceDirective, resolvedTypeReferenceDirective.resolvedFileName, previousResolution.resolvedFileName diff --git a/tests/baselines/reference/library-reference-5.errors.txt b/tests/baselines/reference/library-reference-5.errors.txt index a3729bc3a99..c23b49c61de 100644 --- a/tests/baselines/reference/library-reference-5.errors.txt +++ b/tests/baselines/reference/library-reference-5.errors.txt @@ -1,4 +1,4 @@ -/node_modules/bar/index.d.ts(1,1): message TS4090: Conflicting library definitions for 'alpha' found at '/node_modules/bar/node_modules/alpha/index.d.ts' and '/node_modules/foo/node_modules/alpha/index.d.ts'. Copy the correct file to the 'typings' folder to resolve this conflict. +/node_modules/bar/index.d.ts(1,1): message TS4090: Conflicting definitions for 'alpha' found at '/node_modules/bar/node_modules/alpha/index.d.ts' and '/node_modules/foo/node_modules/alpha/index.d.ts'. Consider installing a specific version of this library to resolve the conflict. ==== /src/root.ts (0 errors) ==== @@ -18,7 +18,7 @@ ==== /node_modules/bar/index.d.ts (1 errors) ==== /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -!!! message TS4090: Conflicting library definitions for 'alpha' found at '/node_modules/bar/node_modules/alpha/index.d.ts' and '/node_modules/foo/node_modules/alpha/index.d.ts'. Copy the correct file to the 'typings' folder to resolve this conflict. +!!! message TS4090: Conflicting definitions for 'alpha' found at '/node_modules/bar/node_modules/alpha/index.d.ts' and '/node_modules/foo/node_modules/alpha/index.d.ts'. Consider installing a specific version of this library to resolve the conflict. declare var bar: any; ==== /node_modules/bar/node_modules/alpha/index.d.ts (0 errors) ==== From c42f1cb0d0fd298039bf6fe374e1d2a6acfe6214 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 17 Aug 2016 06:21:49 -0700 Subject: [PATCH 157/233] Explain why we lower-case type reference directives --- src/compiler/program.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 004f4102667..1c491aeeaac 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -2010,6 +2010,7 @@ namespace ts { } function processTypeReferenceDirectives(file: SourceFile) { + // We lower-case all type references because npm automatically lowercases all packages. See GH#9824. const typeDirectives = map(file.typeReferenceDirectives, ref => ref.fileName.toLocaleLowerCase()); const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeDirectives, file.fileName); From 07a8f31a2d294fb7d9aca3fe937cab0bb5345e21 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 17 Aug 2016 10:43:02 -0700 Subject: [PATCH 158/233] Correctly merge bindThisPropertyAssignment Also simply it considerably after noticing that it's *only* called for Javascript files, so there was a lot of dead code for TS cases that never happened. --- src/compiler/binder.ts | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index ee82a88d7a5..5aaf6f50183 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1977,33 +1977,25 @@ namespace ts { } function bindThisPropertyAssignment(node: BinaryExpression) { + Debug.assert(isInJavaScriptFile(node)); // Declare a 'member' if the container is an ES5 class or ES6 constructor - let assignee: Node; if (container.kind === SyntaxKind.FunctionDeclaration || container.kind === SyntaxKind.FunctionExpression) { - assignee = container; + container.symbol.members = container.symbol.members || createMap(); + // It's acceptable for multiple 'this' assignments of the same identifier to occur + declareSymbol(container.symbol.members, container.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property); } else if (container.kind === SyntaxKind.Constructor) { - if (isInJavaScriptFile(node)) { - // this.foo assignment in a JavaScript class - // Bind this property to the containing class - const saveContainer = container; - container = container.parent; - bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property, SymbolFlags.None); - container = saveContainer; - return; - } - else { - assignee = container.parent; + // this.foo assignment in a JavaScript class + // Bind this property to the containing class + const saveContainer = container; + container = container.parent; + // AND it can be overwritten by subsequent method declarations + const symbol = bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property, SymbolFlags.None); + if (symbol) { + (symbol as Symbol).isReplaceableByMethod = true; } + container = saveContainer; } - else { - return; - } - assignee.symbol.members = assignee.symbol.members || createMap(); - // It's acceptable for multiple 'this' assignments of the same identifier to occur - // AND it can be overwritten by subsequent method declarations - const symbol = declareSymbol(assignee.symbol.members, assignee.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property); - symbol.isReplaceableByMethod = true; } function bindPrototypePropertyAssignment(node: BinaryExpression) { From c73efe2fb629bcb3a283eaf7979d7070705546cc Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 17 Aug 2016 10:45:35 -0700 Subject: [PATCH 159/233] Fix comment --- src/compiler/binder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 5aaf6f50183..d8017d601ad 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1989,9 +1989,9 @@ namespace ts { // Bind this property to the containing class const saveContainer = container; container = container.parent; - // AND it can be overwritten by subsequent method declarations const symbol = bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property, SymbolFlags.None); if (symbol) { + // constructor-declared symbols can be overwritten by subsequent method declarations (symbol as Symbol).isReplaceableByMethod = true; } container = saveContainer; From 2d1639fac81af24c2d015a5eca107a3c45ee11d4 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 17 Aug 2016 13:30:03 -0700 Subject: [PATCH 160/233] Property handle imcomplete control flow types in nested loops --- src/compiler/checker.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3d7fb7eb7e1..24fe429c6e3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8364,13 +8364,18 @@ namespace ts { // each antecedent code path. const antecedentTypes: Type[] = []; let subtypeReduction = false; + let firstAntecedentType: FlowType; flowLoopNodes[flowLoopCount] = flow; flowLoopKeys[flowLoopCount] = key; flowLoopTypes[flowLoopCount] = antecedentTypes; for (const antecedent of flow.antecedents) { flowLoopCount++; - const type = getTypeFromFlowType(getTypeAtFlowNode(antecedent)); + const flowType = getTypeAtFlowNode(antecedent); flowLoopCount--; + if (!firstAntecedentType) { + firstAntecedentType = flowType; + } + const type = getTypeFromFlowType(flowType); // If we see a value appear in the cache it is a sign that control flow analysis // was restarted and completed by checkExpressionCached. We can simply pick up // the resulting type and bail out. @@ -8393,7 +8398,13 @@ namespace ts { break; } } - return cache[key] = getUnionType(antecedentTypes, subtypeReduction); + // The result is incomplete if the first antecedent (the non-looping control flow path) + // is incomplete. + const result = getUnionType(antecedentTypes, subtypeReduction); + if (isIncomplete(firstAntecedentType)) { + return createFlowType(result, /*incomplete*/ true); + } + return cache[key] = result; } function isMatchingReferenceDiscriminant(expr: Expression) { From 44476f1984959180370e10397b835619b448b11b Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Wed, 17 Aug 2016 13:30:03 -0700 Subject: [PATCH 161/233] Update due to CR suggestion --- src/services/services.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/services.ts b/src/services/services.ts index 80eb0b3f46d..1dd44577d7f 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4247,7 +4247,7 @@ namespace ts { addRange(entries, keywordCompletions); } - return { isMemberCompletion, isNewIdentifierLocation: isSourceFileJavaScript(sourceFile) ? true : isNewIdentifierLocation, entries }; + return { isMemberCompletion, isNewIdentifierLocation: isNewIdentifierLocation || isSourceFileJavaScript(sourceFile), entries }; function getJavaScriptCompletionEntries(sourceFile: SourceFile, position: number, uniqueNames: Map): CompletionEntry[] { const entries: CompletionEntry[] = []; From b93cdecdf5165ad0b569ca9a6b9d54626b4855b6 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 17 Aug 2016 13:30:13 -0700 Subject: [PATCH 162/233] Add regression test --- .../reference/nestedLoopTypeGuards.js | 63 ++++++++++++ .../reference/nestedLoopTypeGuards.symbols | 66 +++++++++++++ .../reference/nestedLoopTypeGuards.types | 96 +++++++++++++++++++ tests/cases/compiler/nestedLoopTypeGuards.ts | 31 ++++++ 4 files changed, 256 insertions(+) create mode 100644 tests/baselines/reference/nestedLoopTypeGuards.js create mode 100644 tests/baselines/reference/nestedLoopTypeGuards.symbols create mode 100644 tests/baselines/reference/nestedLoopTypeGuards.types create mode 100644 tests/cases/compiler/nestedLoopTypeGuards.ts diff --git a/tests/baselines/reference/nestedLoopTypeGuards.js b/tests/baselines/reference/nestedLoopTypeGuards.js new file mode 100644 index 00000000000..ab1148413ef --- /dev/null +++ b/tests/baselines/reference/nestedLoopTypeGuards.js @@ -0,0 +1,63 @@ +//// [nestedLoopTypeGuards.ts] +// Repros from #10378 + +function f1() { + var a: boolean | number | string; + if (typeof a !== 'boolean') { + // a is narrowed to "number | string" + for (var i = 0; i < 1; i++) { + for (var j = 0; j < 1; j++) {} + if (typeof a === 'string') { + // a is narrowed to "string' + for (var j = 0; j < 1; j++) { + a.length; // Should not error here + } + } + } + } +} + +function f2() { + var a: string | number; + if (typeof a === 'string') { + while (1) { + while (1) {} + if (typeof a === 'string') { + while (1) { + a.length; // Should not error here + } + } + } + } +} + +//// [nestedLoopTypeGuards.js] +// Repros from #10378 +function f1() { + var a; + if (typeof a !== 'boolean') { + // a is narrowed to "number | string" + for (var i = 0; i < 1; i++) { + for (var j = 0; j < 1; j++) { } + if (typeof a === 'string') { + // a is narrowed to "string' + for (var j = 0; j < 1; j++) { + a.length; // Should not error here + } + } + } + } +} +function f2() { + var a; + if (typeof a === 'string') { + while (1) { + while (1) { } + if (typeof a === 'string') { + while (1) { + a.length; // Should not error here + } + } + } + } +} diff --git a/tests/baselines/reference/nestedLoopTypeGuards.symbols b/tests/baselines/reference/nestedLoopTypeGuards.symbols new file mode 100644 index 00000000000..74426576d5a --- /dev/null +++ b/tests/baselines/reference/nestedLoopTypeGuards.symbols @@ -0,0 +1,66 @@ +=== tests/cases/compiler/nestedLoopTypeGuards.ts === +// Repros from #10378 + +function f1() { +>f1 : Symbol(f1, Decl(nestedLoopTypeGuards.ts, 0, 0)) + + var a: boolean | number | string; +>a : Symbol(a, Decl(nestedLoopTypeGuards.ts, 3, 7)) + + if (typeof a !== 'boolean') { +>a : Symbol(a, Decl(nestedLoopTypeGuards.ts, 3, 7)) + + // a is narrowed to "number | string" + for (var i = 0; i < 1; i++) { +>i : Symbol(i, Decl(nestedLoopTypeGuards.ts, 6, 16)) +>i : Symbol(i, Decl(nestedLoopTypeGuards.ts, 6, 16)) +>i : Symbol(i, Decl(nestedLoopTypeGuards.ts, 6, 16)) + + for (var j = 0; j < 1; j++) {} +>j : Symbol(j, Decl(nestedLoopTypeGuards.ts, 7, 20), Decl(nestedLoopTypeGuards.ts, 10, 24)) +>j : Symbol(j, Decl(nestedLoopTypeGuards.ts, 7, 20), Decl(nestedLoopTypeGuards.ts, 10, 24)) +>j : Symbol(j, Decl(nestedLoopTypeGuards.ts, 7, 20), Decl(nestedLoopTypeGuards.ts, 10, 24)) + + if (typeof a === 'string') { +>a : Symbol(a, Decl(nestedLoopTypeGuards.ts, 3, 7)) + + // a is narrowed to "string' + for (var j = 0; j < 1; j++) { +>j : Symbol(j, Decl(nestedLoopTypeGuards.ts, 7, 20), Decl(nestedLoopTypeGuards.ts, 10, 24)) +>j : Symbol(j, Decl(nestedLoopTypeGuards.ts, 7, 20), Decl(nestedLoopTypeGuards.ts, 10, 24)) +>j : Symbol(j, Decl(nestedLoopTypeGuards.ts, 7, 20), Decl(nestedLoopTypeGuards.ts, 10, 24)) + + a.length; // Should not error here +>a.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>a : Symbol(a, Decl(nestedLoopTypeGuards.ts, 3, 7)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) + } + } + } + } +} + +function f2() { +>f2 : Symbol(f2, Decl(nestedLoopTypeGuards.ts, 16, 1)) + + var a: string | number; +>a : Symbol(a, Decl(nestedLoopTypeGuards.ts, 19, 7)) + + if (typeof a === 'string') { +>a : Symbol(a, Decl(nestedLoopTypeGuards.ts, 19, 7)) + + while (1) { + while (1) {} + if (typeof a === 'string') { +>a : Symbol(a, Decl(nestedLoopTypeGuards.ts, 19, 7)) + + while (1) { + a.length; // Should not error here +>a.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>a : Symbol(a, Decl(nestedLoopTypeGuards.ts, 19, 7)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) + } + } + } + } +} diff --git a/tests/baselines/reference/nestedLoopTypeGuards.types b/tests/baselines/reference/nestedLoopTypeGuards.types new file mode 100644 index 00000000000..9611fba2fbf --- /dev/null +++ b/tests/baselines/reference/nestedLoopTypeGuards.types @@ -0,0 +1,96 @@ +=== tests/cases/compiler/nestedLoopTypeGuards.ts === +// Repros from #10378 + +function f1() { +>f1 : () => void + + var a: boolean | number | string; +>a : string | number | boolean + + if (typeof a !== 'boolean') { +>typeof a !== 'boolean' : boolean +>typeof a : string +>a : string | number | boolean +>'boolean' : "boolean" + + // a is narrowed to "number | string" + for (var i = 0; i < 1; i++) { +>i : number +>0 : number +>i < 1 : boolean +>i : number +>1 : number +>i++ : number +>i : number + + for (var j = 0; j < 1; j++) {} +>j : number +>0 : number +>j < 1 : boolean +>j : number +>1 : number +>j++ : number +>j : number + + if (typeof a === 'string') { +>typeof a === 'string' : boolean +>typeof a : string +>a : string | number +>'string' : "string" + + // a is narrowed to "string' + for (var j = 0; j < 1; j++) { +>j : number +>0 : number +>j < 1 : boolean +>j : number +>1 : number +>j++ : number +>j : number + + a.length; // Should not error here +>a.length : number +>a : string +>length : number + } + } + } + } +} + +function f2() { +>f2 : () => void + + var a: string | number; +>a : string | number + + if (typeof a === 'string') { +>typeof a === 'string' : boolean +>typeof a : string +>a : string | number +>'string' : "string" + + while (1) { +>1 : number + + while (1) {} +>1 : number + + if (typeof a === 'string') { +>typeof a === 'string' : boolean +>typeof a : string +>a : string +>'string' : "string" + + while (1) { +>1 : number + + a.length; // Should not error here +>a.length : number +>a : string +>length : number + } + } + } + } +} diff --git a/tests/cases/compiler/nestedLoopTypeGuards.ts b/tests/cases/compiler/nestedLoopTypeGuards.ts new file mode 100644 index 00000000000..90d7912dec5 --- /dev/null +++ b/tests/cases/compiler/nestedLoopTypeGuards.ts @@ -0,0 +1,31 @@ +// Repros from #10378 + +function f1() { + var a: boolean | number | string; + if (typeof a !== 'boolean') { + // a is narrowed to "number | string" + for (var i = 0; i < 1; i++) { + for (var j = 0; j < 1; j++) {} + if (typeof a === 'string') { + // a is narrowed to "string' + for (var j = 0; j < 1; j++) { + a.length; // Should not error here + } + } + } + } +} + +function f2() { + var a: string | number; + if (typeof a === 'string') { + while (1) { + while (1) {} + if (typeof a === 'string') { + while (1) { + a.length; // Should not error here + } + } + } + } +} \ No newline at end of file From 0f483d6546296da0dd4805eb7e7cdbdb1f436fd9 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 17 Aug 2016 14:21:49 -0700 Subject: [PATCH 163/233] Assign and instantiate contextual this type if not present --- src/compiler/checker.ts | 17 +++-- ...instantiateContextuallyTypedGenericThis.js | 2 +- ...ntiateContextuallyTypedGenericThis.symbols | 4 +- ...tantiateContextuallyTypedGenericThis.types | 13 ++-- .../reference/thisTypeInFunctions.symbols | 18 ++--- .../reference/thisTypeInFunctions.types | 70 +++++++++---------- .../reference/thisTypeInFunctions2.symbols | 7 +- .../reference/thisTypeInFunctions2.types | 6 +- ...instantiateContextuallyTypedGenericThis.ts | 2 +- 9 files changed, 72 insertions(+), 67 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1aeeb1f2c17..17cfdf8976f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4785,9 +4785,6 @@ namespace ts { function getThisTypeOfSignature(signature: Signature): Type | undefined { if (signature.thisParameter) { - if (signature.mapper) { - signature = instantiateSignature(signature, signature.mapper); - } return getTypeOfSymbol(signature.thisParameter); } } @@ -9085,15 +9082,15 @@ namespace ts { return getInferredClassType(classSymbol); } } - const type = getContextuallyTypedThisType(container); - if (type) { - return type; - } const thisType = getThisTypeOfDeclaration(container); if (thisType) { return thisType; } + const type = getContextuallyTypedThisType(container); + if (type) { + return type; + } } if (isClassLike(container.parent)) { const symbol = getSymbolOfNode(container.parent); @@ -12277,8 +12274,10 @@ namespace ts { function assignContextualParameterTypes(signature: Signature, context: Signature, mapper: TypeMapper) { const len = signature.parameters.length - (signature.hasRestParameter ? 1 : 0); if (context.thisParameter) { - // save the mapper in case we need to type `this` later - context.mapper = mapper; + if (!signature.thisParameter) { + signature.thisParameter = createTransientSymbol(context.thisParameter, undefined); + } + assignTypeToParameterAndFixTypeParameters(signature.thisParameter, getTypeOfSymbol(context.thisParameter), mapper); } for (let i = 0; i < len; i++) { const parameter = signature.parameters[i]; diff --git a/tests/baselines/reference/instantiateContextuallyTypedGenericThis.js b/tests/baselines/reference/instantiateContextuallyTypedGenericThis.js index 0233180873d..176df15985f 100644 --- a/tests/baselines/reference/instantiateContextuallyTypedGenericThis.js +++ b/tests/baselines/reference/instantiateContextuallyTypedGenericThis.js @@ -2,7 +2,7 @@ interface JQuery { each( collection: T[], callback: (this: T, dit: T) => T - ): any; + ): T[]; } let $: JQuery; diff --git a/tests/baselines/reference/instantiateContextuallyTypedGenericThis.symbols b/tests/baselines/reference/instantiateContextuallyTypedGenericThis.symbols index 34a477b706a..1db5b30dd0b 100644 --- a/tests/baselines/reference/instantiateContextuallyTypedGenericThis.symbols +++ b/tests/baselines/reference/instantiateContextuallyTypedGenericThis.symbols @@ -16,7 +16,8 @@ interface JQuery { >T : Symbol(T, Decl(instantiateContextuallyTypedGenericThis.ts, 1, 9)) >T : Symbol(T, Decl(instantiateContextuallyTypedGenericThis.ts, 1, 9)) - ): any; + ): T[]; +>T : Symbol(T, Decl(instantiateContextuallyTypedGenericThis.ts, 1, 9)) } let $: JQuery; @@ -38,6 +39,7 @@ $.each(lines, function(dit) { >dit : Symbol(dit, Decl(instantiateContextuallyTypedGenericThis.ts, 8, 23)) >charAt : Symbol(String.charAt, Decl(lib.d.ts, --, --)) >this.charAt : Symbol(String.charAt, Decl(lib.d.ts, --, --)) +>this : Symbol(this, Decl(instantiateContextuallyTypedGenericThis.ts, 2, 36)) >charAt : Symbol(String.charAt, Decl(lib.d.ts, --, --)) }); diff --git a/tests/baselines/reference/instantiateContextuallyTypedGenericThis.types b/tests/baselines/reference/instantiateContextuallyTypedGenericThis.types index 060d35be21a..5cdce6b99fe 100644 --- a/tests/baselines/reference/instantiateContextuallyTypedGenericThis.types +++ b/tests/baselines/reference/instantiateContextuallyTypedGenericThis.types @@ -3,7 +3,7 @@ interface JQuery { >JQuery : JQuery each( ->each : (collection: T[], callback: (this: T, dit: T) => T) => any +>each : (collection: T[], callback: (this: T, dit: T) => T) => T[] >T : T collection: T[], callback: (this: T, dit: T) => T @@ -16,7 +16,8 @@ interface JQuery { >T : T >T : T - ): any; + ): T[]; +>T : T } let $: JQuery; @@ -27,12 +28,12 @@ let lines: string[]; >lines : string[] $.each(lines, function(dit) { ->$.each(lines, function(dit) { return dit.charAt(0) + this.charAt(1);}) : any ->$.each : (collection: T[], callback: (this: T, dit: T) => T) => any +>$.each(lines, function(dit) { return dit.charAt(0) + this.charAt(1);}) : string[] +>$.each : (collection: T[], callback: (this: T, dit: T) => T) => T[] >$ : JQuery ->each : (collection: T[], callback: (this: T, dit: T) => T) => any +>each : (collection: T[], callback: (this: T, dit: T) => T) => T[] >lines : string[] ->function(dit) { return dit.charAt(0) + this.charAt(1);} : (dit: string) => string +>function(dit) { return dit.charAt(0) + this.charAt(1);} : (this: string, dit: string) => string >dit : string return dit.charAt(0) + this.charAt(1); diff --git a/tests/baselines/reference/thisTypeInFunctions.symbols b/tests/baselines/reference/thisTypeInFunctions.symbols index 8a0be7427ed..ad16785f288 100644 --- a/tests/baselines/reference/thisTypeInFunctions.symbols +++ b/tests/baselines/reference/thisTypeInFunctions.symbols @@ -135,7 +135,7 @@ let impl: I = { return this.a; >this.a : Symbol(a, Decl(thisTypeInFunctions.ts, 24, 30)) ->this : Symbol(, Decl(thisTypeInFunctions.ts, 24, 28)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 24, 23)) >a : Symbol(a, Decl(thisTypeInFunctions.ts, 24, 30)) }, @@ -144,7 +144,7 @@ let impl: I = { return this.a; >this.a : Symbol(I.a, Decl(thisTypeInFunctions.ts, 20, 13)) ->this : Symbol(I, Decl(thisTypeInFunctions.ts, 19, 21)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 25, 22)) >a : Symbol(I.a, Decl(thisTypeInFunctions.ts, 20, 13)) }, @@ -153,7 +153,7 @@ let impl: I = { return this.a; >this.a : Symbol(I.a, Decl(thisTypeInFunctions.ts, 20, 13)) ->this : Symbol(I, Decl(thisTypeInFunctions.ts, 19, 21)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 26, 17)) >a : Symbol(I.a, Decl(thisTypeInFunctions.ts, 20, 13)) }, @@ -173,7 +173,7 @@ impl.explicitStructural = function() { return this.a; }; >impl : Symbol(impl, Decl(thisTypeInFunctions.ts, 37, 3)) >explicitStructural : Symbol(I.explicitStructural, Decl(thisTypeInFunctions.ts, 23, 38)) >this.a : Symbol(a, Decl(thisTypeInFunctions.ts, 24, 30)) ->this : Symbol(, Decl(thisTypeInFunctions.ts, 24, 28)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 24, 23)) >a : Symbol(a, Decl(thisTypeInFunctions.ts, 24, 30)) impl.explicitInterface = function() { return this.a; }; @@ -181,7 +181,7 @@ impl.explicitInterface = function() { return this.a; }; >impl : Symbol(impl, Decl(thisTypeInFunctions.ts, 37, 3)) >explicitInterface : Symbol(I.explicitInterface, Decl(thisTypeInFunctions.ts, 24, 50)) >this.a : Symbol(I.a, Decl(thisTypeInFunctions.ts, 20, 13)) ->this : Symbol(I, Decl(thisTypeInFunctions.ts, 19, 21)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 25, 22)) >a : Symbol(I.a, Decl(thisTypeInFunctions.ts, 20, 13)) impl.explicitStructural = () => 12; @@ -199,7 +199,7 @@ impl.explicitThis = function () { return this.a; }; >impl : Symbol(impl, Decl(thisTypeInFunctions.ts, 37, 3)) >explicitThis : Symbol(I.explicitThis, Decl(thisTypeInFunctions.ts, 25, 39)) >this.a : Symbol(I.a, Decl(thisTypeInFunctions.ts, 20, 13)) ->this : Symbol(I, Decl(thisTypeInFunctions.ts, 19, 21)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 26, 17)) >a : Symbol(I.a, Decl(thisTypeInFunctions.ts, 20, 13)) // parameter checking @@ -536,7 +536,7 @@ c.explicitC = function(m) { return this.n + m }; >explicitC : Symbol(C.explicitC, Decl(thisTypeInFunctions.ts, 8, 5)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 126, 23)) >this.n : Symbol(C.n, Decl(thisTypeInFunctions.ts, 4, 9)) ->this : Symbol(C, Decl(thisTypeInFunctions.ts, 3, 1)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 9, 14)) >n : Symbol(C.n, Decl(thisTypeInFunctions.ts, 4, 9)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 126, 23)) @@ -546,7 +546,7 @@ c.explicitProperty = function(m) { return this.n + m }; >explicitProperty : Symbol(C.explicitProperty, Decl(thisTypeInFunctions.ts, 11, 5)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 127, 30)) >this.n : Symbol(n, Decl(thisTypeInFunctions.ts, 12, 28)) ->this : Symbol(, Decl(thisTypeInFunctions.ts, 12, 26)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 12, 21)) >n : Symbol(n, Decl(thisTypeInFunctions.ts, 12, 28)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 127, 30)) @@ -556,7 +556,7 @@ c.explicitThis = function(m) { return this.n + m }; >explicitThis : Symbol(C.explicitThis, Decl(thisTypeInFunctions.ts, 5, 14)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 128, 26)) >this.n : Symbol(C.n, Decl(thisTypeInFunctions.ts, 4, 9)) ->this : Symbol(C, Decl(thisTypeInFunctions.ts, 3, 1)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 6, 17)) >n : Symbol(C.n, Decl(thisTypeInFunctions.ts, 4, 9)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 128, 26)) diff --git a/tests/baselines/reference/thisTypeInFunctions.types b/tests/baselines/reference/thisTypeInFunctions.types index 24c4fb87baf..863ecbce628 100644 --- a/tests/baselines/reference/thisTypeInFunctions.types +++ b/tests/baselines/reference/thisTypeInFunctions.types @@ -132,7 +132,7 @@ function implicitThis(n: number): number { let impl: I = { >impl : I >I : I ->{ a: 12, explicitVoid2: () => this.a, // ok, this: any because it refers to some outer object (window?) explicitVoid1() { return 12; }, explicitStructural() { return this.a; }, explicitInterface() { return this.a; }, explicitThis() { return this.a; },} : { a: number; explicitVoid2: () => any; explicitVoid1(): number; explicitStructural(): number; explicitInterface(): number; explicitThis(): number; } +>{ a: 12, explicitVoid2: () => this.a, // ok, this: any because it refers to some outer object (window?) explicitVoid1() { return 12; }, explicitStructural() { return this.a; }, explicitInterface() { return this.a; }, explicitThis() { return this.a; },} : { a: number; explicitVoid2: () => any; explicitVoid1(this: void): number; explicitStructural(this: { a: number; }): number; explicitInterface(this: I): number; explicitThis(this: I): number; } a: 12, >a : number @@ -146,11 +146,11 @@ let impl: I = { >a : any explicitVoid1() { return 12; }, ->explicitVoid1 : () => number +>explicitVoid1 : (this: void) => number >12 : number explicitStructural() { ->explicitStructural : () => number +>explicitStructural : (this: { a: number; }) => number return this.a; >this.a : number @@ -159,7 +159,7 @@ let impl: I = { }, explicitInterface() { ->explicitInterface : () => number +>explicitInterface : (this: I) => number return this.a; >this.a : number @@ -168,7 +168,7 @@ let impl: I = { }, explicitThis() { ->explicitThis : () => number +>explicitThis : (this: I) => number return this.a; >this.a : number @@ -178,11 +178,11 @@ let impl: I = { }, } impl.explicitVoid1 = function () { return 12; }; ->impl.explicitVoid1 = function () { return 12; } : () => number +>impl.explicitVoid1 = function () { return 12; } : (this: void) => number >impl.explicitVoid1 : (this: void) => number >impl : I >explicitVoid1 : (this: void) => number ->function () { return 12; } : () => number +>function () { return 12; } : (this: void) => number >12 : number impl.explicitVoid2 = () => 12; @@ -194,21 +194,21 @@ impl.explicitVoid2 = () => 12; >12 : number impl.explicitStructural = function() { return this.a; }; ->impl.explicitStructural = function() { return this.a; } : () => number +>impl.explicitStructural = function() { return this.a; } : (this: { a: number; }) => number >impl.explicitStructural : (this: { a: number; }) => number >impl : I >explicitStructural : (this: { a: number; }) => number ->function() { return this.a; } : () => number +>function() { return this.a; } : (this: { a: number; }) => number >this.a : number >this : { a: number; } >a : number impl.explicitInterface = function() { return this.a; }; ->impl.explicitInterface = function() { return this.a; } : () => number +>impl.explicitInterface = function() { return this.a; } : (this: I) => number >impl.explicitInterface : (this: I) => number >impl : I >explicitInterface : (this: I) => number ->function() { return this.a; } : () => number +>function() { return this.a; } : (this: I) => number >this.a : number >this : I >a : number @@ -230,11 +230,11 @@ impl.explicitInterface = () => 12; >12 : number impl.explicitThis = function () { return this.a; }; ->impl.explicitThis = function () { return this.a; } : () => number +>impl.explicitThis = function () { return this.a; } : (this: I) => number >impl.explicitThis : (this: I) => number >impl : I >explicitThis : (this: I) => number ->function () { return this.a; } : () => number +>function () { return this.a; } : (this: I) => number >this.a : number >this : I >a : number @@ -433,7 +433,7 @@ let unboundToSpecified: (this: { y: number }, x: number) => number = x => x + th >this : { y: number; } >y : number >x : number ->x => x + this.y : (x: number) => any +>x => x + this.y : (this: { y: number; }, x: number) => any >x : number >x + this.y : any >x : number @@ -472,7 +472,7 @@ let specifiedLambda: (this: void, x: number) => number = x => x + 12; >specifiedLambda : (this: void, x: number) => number >this : void >x : number ->x => x + 12 : (x: number) => number +>x => x + 12 : (this: void, x: number) => number >x : number >x + 12 : number >x : number @@ -560,40 +560,40 @@ c.explicitProperty = reconstructed.explicitProperty; // lambdas are assignable to anything c.explicitC = m => m; ->c.explicitC = m => m : (m: number) => number +>c.explicitC = m => m : (this: C, m: number) => number >c.explicitC : (this: C, m: number) => number >c : C >explicitC : (this: C, m: number) => number ->m => m : (m: number) => number +>m => m : (this: C, m: number) => number >m : number >m : number c.explicitThis = m => m; ->c.explicitThis = m => m : (m: number) => number +>c.explicitThis = m => m : (this: C, m: number) => number >c.explicitThis : (this: C, m: number) => number >c : C >explicitThis : (this: C, m: number) => number ->m => m : (m: number) => number +>m => m : (this: C, m: number) => number >m : number >m : number c.explicitProperty = m => m; ->c.explicitProperty = m => m : (m: number) => number +>c.explicitProperty = m => m : (this: { n: number; }, m: number) => number >c.explicitProperty : (this: { n: number; }, m: number) => number >c : C >explicitProperty : (this: { n: number; }, m: number) => number ->m => m : (m: number) => number +>m => m : (this: { n: number; }, m: number) => number >m : number >m : number // this inside lambdas refer to outer scope // the outer-scoped lambda at top-level is still just `any` c.explicitC = m => m + this.n; ->c.explicitC = m => m + this.n : (m: number) => any +>c.explicitC = m => m + this.n : (this: C, m: number) => any >c.explicitC : (this: C, m: number) => number >c : C >explicitC : (this: C, m: number) => number ->m => m + this.n : (m: number) => any +>m => m + this.n : (this: C, m: number) => any >m : number >m + this.n : any >m : number @@ -602,11 +602,11 @@ c.explicitC = m => m + this.n; >n : any c.explicitThis = m => m + this.n; ->c.explicitThis = m => m + this.n : (m: number) => any +>c.explicitThis = m => m + this.n : (this: C, m: number) => any >c.explicitThis : (this: C, m: number) => number >c : C >explicitThis : (this: C, m: number) => number ->m => m + this.n : (m: number) => any +>m => m + this.n : (this: C, m: number) => any >m : number >m + this.n : any >m : number @@ -615,11 +615,11 @@ c.explicitThis = m => m + this.n; >n : any c.explicitProperty = m => m + this.n; ->c.explicitProperty = m => m + this.n : (m: number) => any +>c.explicitProperty = m => m + this.n : (this: { n: number; }, m: number) => any >c.explicitProperty : (this: { n: number; }, m: number) => number >c : C >explicitProperty : (this: { n: number; }, m: number) => number ->m => m + this.n : (m: number) => any +>m => m + this.n : (this: { n: number; }, m: number) => any >m : number >m + this.n : any >m : number @@ -652,11 +652,11 @@ c.explicitThis = function(this: C, m: number) { return this.n + m }; // this:any compatibility c.explicitC = function(m) { return this.n + m }; ->c.explicitC = function(m) { return this.n + m } : (m: number) => number +>c.explicitC = function(m) { return this.n + m } : (this: C, m: number) => number >c.explicitC : (this: C, m: number) => number >c : C >explicitC : (this: C, m: number) => number ->function(m) { return this.n + m } : (m: number) => number +>function(m) { return this.n + m } : (this: C, m: number) => number >m : number >this.n + m : number >this.n : number @@ -665,11 +665,11 @@ c.explicitC = function(m) { return this.n + m }; >m : number c.explicitProperty = function(m) { return this.n + m }; ->c.explicitProperty = function(m) { return this.n + m } : (m: number) => number +>c.explicitProperty = function(m) { return this.n + m } : (this: { n: number; }, m: number) => number >c.explicitProperty : (this: { n: number; }, m: number) => number >c : C >explicitProperty : (this: { n: number; }, m: number) => number ->function(m) { return this.n + m } : (m: number) => number +>function(m) { return this.n + m } : (this: { n: number; }, m: number) => number >m : number >this.n + m : number >this.n : number @@ -678,11 +678,11 @@ c.explicitProperty = function(m) { return this.n + m }; >m : number c.explicitThis = function(m) { return this.n + m }; ->c.explicitThis = function(m) { return this.n + m } : (m: number) => number +>c.explicitThis = function(m) { return this.n + m } : (this: C, m: number) => number >c.explicitThis : (this: C, m: number) => number >c : C >explicitThis : (this: C, m: number) => number ->function(m) { return this.n + m } : (m: number) => number +>function(m) { return this.n + m } : (this: C, m: number) => number >m : number >this.n + m : number >this.n : number @@ -723,11 +723,11 @@ c.explicitC = function(this: B, m: number) { return this.n + m }; // this:void compatibility c.explicitVoid = n => n; ->c.explicitVoid = n => n : (n: number) => number +>c.explicitVoid = n => n : (this: void, n: number) => number >c.explicitVoid : (this: void, m: number) => number >c : C >explicitVoid : (this: void, m: number) => number ->n => n : (n: number) => number +>n => n : (this: void, n: number) => number >n : number >n : number diff --git a/tests/baselines/reference/thisTypeInFunctions2.symbols b/tests/baselines/reference/thisTypeInFunctions2.symbols index cdc7fb321d4..5cc26e3ad43 100644 --- a/tests/baselines/reference/thisTypeInFunctions2.symbols +++ b/tests/baselines/reference/thisTypeInFunctions2.symbols @@ -61,12 +61,12 @@ extend1({ >init : Symbol(init, Decl(thisTypeInFunctions2.ts, 20, 9)) this // this: IndexedWithThis because of contextual typing. ->this : Symbol(IndexedWithThis, Decl(thisTypeInFunctions2.ts, 0, 0)) +>this : Symbol(this, Decl(thisTypeInFunctions2.ts, 2, 12)) // this.mine this.willDestroy >this.willDestroy : Symbol(IndexedWithThis.willDestroy, Decl(thisTypeInFunctions2.ts, 2, 32)) ->this : Symbol(IndexedWithThis, Decl(thisTypeInFunctions2.ts, 0, 0)) +>this : Symbol(this, Decl(thisTypeInFunctions2.ts, 2, 12)) >willDestroy : Symbol(IndexedWithThis.willDestroy, Decl(thisTypeInFunctions2.ts, 2, 32)) }, @@ -77,7 +77,10 @@ extend1({ >foo : Symbol(foo, Decl(thisTypeInFunctions2.ts, 26, 13)) this.url; // this: any because 'foo' matches the string indexer +>this : Symbol(this, Decl(thisTypeInFunctions2.ts, 4, 87)) + this.willDestroy; +>this : Symbol(this, Decl(thisTypeInFunctions2.ts, 4, 87)) } }); extend2({ diff --git a/tests/baselines/reference/thisTypeInFunctions2.types b/tests/baselines/reference/thisTypeInFunctions2.types index 371b5f5cafc..25fb2f28a60 100644 --- a/tests/baselines/reference/thisTypeInFunctions2.types +++ b/tests/baselines/reference/thisTypeInFunctions2.types @@ -58,10 +58,10 @@ declare function simple(arg: SimpleInterface): void; extend1({ >extend1({ init() { this // this: IndexedWithThis because of contextual typing. // this.mine this.willDestroy }, mine: 12, foo() { this.url; // this: any because 'foo' matches the string indexer this.willDestroy; }}) : void >extend1 : (args: IndexedWithThis) => void ->{ init() { this // this: IndexedWithThis because of contextual typing. // this.mine this.willDestroy }, mine: 12, foo() { this.url; // this: any because 'foo' matches the string indexer this.willDestroy; }} : { init(): void; mine: number; foo(): void; } +>{ init() { this // this: IndexedWithThis because of contextual typing. // this.mine this.willDestroy }, mine: 12, foo() { this.url; // this: any because 'foo' matches the string indexer this.willDestroy; }} : { init(this: IndexedWithThis): void; mine: number; foo(this: any): void; } init() { ->init : () => void +>init : (this: IndexedWithThis) => void this // this: IndexedWithThis because of contextual typing. >this : IndexedWithThis @@ -78,7 +78,7 @@ extend1({ >12 : number foo() { ->foo : () => void +>foo : (this: any) => void this.url; // this: any because 'foo' matches the string indexer >this.url : any diff --git a/tests/cases/compiler/instantiateContextuallyTypedGenericThis.ts b/tests/cases/compiler/instantiateContextuallyTypedGenericThis.ts index f25e66ecfb8..f06f06ee7a1 100644 --- a/tests/cases/compiler/instantiateContextuallyTypedGenericThis.ts +++ b/tests/cases/compiler/instantiateContextuallyTypedGenericThis.ts @@ -1,7 +1,7 @@ interface JQuery { each( collection: T[], callback: (this: T, dit: T) => T - ): any; + ): T[]; } let $: JQuery; From da8fc5d5a90dc18e83a137ba7e04d38854fc1c7d Mon Sep 17 00:00:00 2001 From: Yui Date: Wed, 17 Aug 2016 15:23:28 -0700 Subject: [PATCH 164/233] Fix 10289: correctly generate tsconfig.json with --lib (#10355) * Separate generate tsconfig into its own function and implement init with --lib # Conflicts: # src/compiler/tsc.ts * Add tests and baselines; Update function name Add unittests and baselines Add unittests and baselines for generating tsconfig Move unittest into harness folder Update harness tsconfig.json USe correct function name * Use new MapLike interstead. Update unittest # Conflicts: # src/compiler/commandLineParser.ts * Update JakeFile * Add tests for incorrect cases * Address PR : remove explicity write node_modules --- Jakefile.js | 3 +- src/compiler/commandLineParser.ts | 96 +++++++++++++++++++ src/compiler/program.ts | 8 -- src/compiler/tsc.ts | 58 +---------- src/compiler/types.ts | 2 +- src/harness/tsconfig.json | 3 +- src/harness/unittests/initializeTSConfig.ts | 44 +++++++++ .../tsconfig.json | 8 ++ .../tsconfig.json | 9 ++ .../tsconfig.json | 9 ++ .../tsconfig.json | 13 +++ .../tsconfig.json | 12 +++ .../tsconfig.json | 8 ++ .../tsconfig.json | 12 +++ .../tsconfig.json | 12 +++ 15 files changed, 229 insertions(+), 68 deletions(-) create mode 100644 src/harness/unittests/initializeTSConfig.ts create mode 100644 tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json create mode 100644 tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json create mode 100644 tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json create mode 100644 tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json create mode 100644 tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json create mode 100644 tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json create mode 100644 tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json create mode 100644 tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json diff --git a/Jakefile.js b/Jakefile.js index 992839a8661..441f6aef4f9 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -180,7 +180,8 @@ var harnessSources = harnessCoreSources.concat([ "convertCompilerOptionsFromJson.ts", "convertTypingOptionsFromJson.ts", "tsserverProjectSystem.ts", - "matchFiles.ts" + "matchFiles.ts", + "initializeTSConfig.ts", ].map(function (f) { return path.join(unittestsDirectory, f); })).concat([ diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 51dc2b498f1..6406455d713 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -466,6 +466,14 @@ namespace ts { shortOptionNames: Map; } + /* @internal */ + export const defaultInitCompilerOptions: CompilerOptions = { + module: ModuleKind.CommonJS, + target: ScriptTarget.ES5, + noImplicitAny: false, + sourceMap: false, + }; + let optionNameMapCache: OptionNameMap; /* @internal */ @@ -671,6 +679,94 @@ namespace ts { } } + /** + * Generate tsconfig configuration when running command line "--init" + * @param options commandlineOptions to be generated into tsconfig.json + * @param fileNames array of filenames to be generated into tsconfig.json + */ + /* @internal */ + export function generateTSConfig(options: CompilerOptions, fileNames: string[]): { compilerOptions: Map } { + const compilerOptions = extend(options, defaultInitCompilerOptions); + const configurations: any = { + compilerOptions: serializeCompilerOptions(compilerOptions) + }; + if (fileNames && fileNames.length) { + // only set the files property if we have at least one file + configurations.files = fileNames; + } + + return configurations; + + function getCustomTypeMapOfCommandLineOption(optionDefinition: CommandLineOption): Map | undefined { + if (optionDefinition.type === "string" || optionDefinition.type === "number" || optionDefinition.type === "boolean") { + // this is of a type CommandLineOptionOfPrimitiveType + return undefined; + } + else if (optionDefinition.type === "list") { + return getCustomTypeMapOfCommandLineOption((optionDefinition).element); + } + else { + return (optionDefinition).type; + } + } + + function getNameOfCompilerOptionValue(value: CompilerOptionsValue, customTypeMap: MapLike): string | undefined { + // There is a typeMap associated with this command-line option so use it to map value back to its name + for (const key in customTypeMap) { + if (customTypeMap[key] === value) { + return key; + } + } + return undefined; + } + + function serializeCompilerOptions(options: CompilerOptions): Map { + const result = createMap(); + const optionsNameMap = getOptionNameMap().optionNameMap; + + for (const name in options) { + if (hasProperty(options, name)) { + // tsconfig only options cannot be specified via command line, + // so we can assume that only types that can appear here string | number | boolean + switch (name) { + case "init": + case "watch": + case "version": + case "help": + case "project": + break; + default: + const value = options[name]; + let optionDefinition = optionsNameMap[name.toLowerCase()]; + if (optionDefinition) { + const customTypeMap = getCustomTypeMapOfCommandLineOption(optionDefinition); + if (!customTypeMap) { + // There is no map associated with this compiler option then use the value as-is + // This is the case if the value is expect to be string, number, boolean or list of string + result[name] = value; + } + else { + if (optionDefinition.type === "list") { + const convertedValue: string[] = []; + for (const element of value as (string | number)[]) { + convertedValue.push(getNameOfCompilerOptionValue(element, customTypeMap)); + } + result[name] = convertedValue; + } + else { + // There is a typeMap associated with this command-line option so use it to map value back to its name + result[name] = getNameOfCompilerOptionValue(value, customTypeMap); + } + } + } + break; + } + } + } + return result; + } + } + /** * Remove the comments from a json like text. * Comments can be single line comments (starting with # or //) or multiline comments using / * * / diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 1c491aeeaac..e732fe21872 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -828,14 +828,6 @@ namespace ts { : { resolvedModule: undefined, failedLookupLocations }; } - /* @internal */ - export const defaultInitCompilerOptions: CompilerOptions = { - module: ModuleKind.CommonJS, - target: ScriptTarget.ES5, - noImplicitAny: false, - sourceMap: false, - }; - interface OutputFingerprint { hash: string; byteOrderMark: boolean; diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 8b445bcb626..62b58609086 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -763,67 +763,11 @@ namespace ts { reportDiagnostic(createCompilerDiagnostic(Diagnostics.A_tsconfig_json_file_is_already_defined_at_Colon_0, file), /* host */ undefined); } else { - const compilerOptions = extend(options, defaultInitCompilerOptions); - const configurations: any = { - compilerOptions: serializeCompilerOptions(compilerOptions) - }; - - if (fileNames && fileNames.length) { - // only set the files property if we have at least one file - configurations.files = fileNames; - } - else { - configurations.exclude = ["node_modules"]; - if (compilerOptions.outDir) { - configurations.exclude.push(compilerOptions.outDir); - } - } - - sys.writeFile(file, JSON.stringify(configurations, undefined, 4)); + sys.writeFile(file, JSON.stringify(generateTSConfig(options, fileNames), undefined, 4)); reportDiagnostic(createCompilerDiagnostic(Diagnostics.Successfully_created_a_tsconfig_json_file), /* host */ undefined); } return; - - function serializeCompilerOptions(options: CompilerOptions): Map { - const result = createMap(); - const optionsNameMap = getOptionNameMap().optionNameMap; - - for (const name in options) { - if (hasProperty(options, name)) { - // tsconfig only options cannot be specified via command line, - // so we can assume that only types that can appear here string | number | boolean - const value = options[name]; - switch (name) { - case "init": - case "watch": - case "version": - case "help": - case "project": - break; - default: - let optionDefinition = optionsNameMap[name.toLowerCase()]; - if (optionDefinition) { - if (typeof optionDefinition.type === "string") { - // string, number or boolean - result[name] = value; - } - else { - // Enum - const typeMap = >optionDefinition.type; - for (const key in typeMap) { - if (typeMap[key] === value) { - result[name] = key; - } - } - } - } - break; - } - } - } - return result; - } } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 0bbbd99bdd5..7594d2c3fb8 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2766,7 +2766,7 @@ namespace ts { /* @internal */ export interface CommandLineOptionOfCustomType extends CommandLineOptionBase { - type: Map; // an object literal mapping named values to actual values + type: Map; // an object literal mapping named values to actual values } /* @internal */ diff --git a/src/harness/tsconfig.json b/src/harness/tsconfig.json index 3b9025c27c3..a21faf9dd07 100644 --- a/src/harness/tsconfig.json +++ b/src/harness/tsconfig.json @@ -89,6 +89,7 @@ "./unittests/convertCompilerOptionsFromJson.ts", "./unittests/convertTypingOptionsFromJson.ts", "./unittests/tsserverProjectSystem.ts", - "./unittests/matchFiles.ts" + "./unittests/matchFiles.ts", + "./unittests/initializeTSConfig.ts" ] } diff --git a/src/harness/unittests/initializeTSConfig.ts b/src/harness/unittests/initializeTSConfig.ts new file mode 100644 index 00000000000..059f07e0115 --- /dev/null +++ b/src/harness/unittests/initializeTSConfig.ts @@ -0,0 +1,44 @@ +/// +/// + +namespace ts { + describe("initTSConfig", () => { + function initTSConfigCorrectly(name: string, commandLinesArgs: string[]) { + describe(name, () => { + const commandLine = parseCommandLine(commandLinesArgs); + const initResult = generateTSConfig(commandLine.options, commandLine.fileNames); + const outputFileName = `tsConfig/${name.replace(/[^a-z0-9\-. ]/ig, "")}/tsconfig.json`; + + it(`Correct output for ${outputFileName}`, () => { + Harness.Baseline.runBaseline("Correct output", outputFileName, () => { + if (initResult) { + return JSON.stringify(initResult, undefined, 4); + } + else { + // This can happen if compiler recieve invalid compiler-options + /* tslint:disable:no-null-keyword */ + return null; + /* tslint:enable:no-null-keyword */ + } + }); + }); + }); + } + + initTSConfigCorrectly("Default initialized TSConfig", ["--init"]); + + initTSConfigCorrectly("Initialized TSConfig with files options", ["--init", "file0.st", "file1.ts", "file2.ts"]); + + initTSConfigCorrectly("Initialized TSConfig with boolean value compiler options", ["--init", "--noUnusedLocals"]); + + initTSConfigCorrectly("Initialized TSConfig with enum value compiler options", ["--init", "--target", "es5", "--jsx", "react"]); + + initTSConfigCorrectly("Initialized TSConfig with list compiler options", ["--init", "--types", "jquery,mocha"]); + + initTSConfigCorrectly("Initialized TSConfig with list compiler options with enum value", ["--init", "--lib", "es5,es2015.core"]); + + initTSConfigCorrectly("Initialized TSConfig with incorrect compiler option", ["--init", "--someNonExistOption"]); + + initTSConfigCorrectly("Initialized TSConfig with incorrect compiler option value", ["--init", "--lib", "nonExistLib,es5,es2015.promise"]); + }); +} \ No newline at end of file diff --git a/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json b/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json new file mode 100644 index 00000000000..ea891967cb5 --- /dev/null +++ b/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "noImplicitAny": false, + "sourceMap": false + } +} \ No newline at end of file diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json new file mode 100644 index 00000000000..abe135b4f16 --- /dev/null +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "noImplicitAny": false, + "sourceMap": false, + "noUnusedLocals": true + } +} \ No newline at end of file diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json new file mode 100644 index 00000000000..e28b66c8c2b --- /dev/null +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "noImplicitAny": false, + "sourceMap": false, + "jsx": "react" + } +} \ No newline at end of file diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json new file mode 100644 index 00000000000..5273b3cb7c8 --- /dev/null +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "noImplicitAny": false, + "sourceMap": false + }, + "files": [ + "file0.st", + "file1.ts", + "file2.ts" + ] +} \ No newline at end of file diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json new file mode 100644 index 00000000000..fa9cb6cad84 --- /dev/null +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "noImplicitAny": false, + "sourceMap": false, + "lib": [ + "es5", + "es2015.promise" + ] + } +} \ No newline at end of file diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json new file mode 100644 index 00000000000..ea891967cb5 --- /dev/null +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "noImplicitAny": false, + "sourceMap": false + } +} \ No newline at end of file diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json new file mode 100644 index 00000000000..3ff8208d9d6 --- /dev/null +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "noImplicitAny": false, + "sourceMap": false, + "lib": [ + "es5", + "es2015.core" + ] + } +} \ No newline at end of file diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json new file mode 100644 index 00000000000..b1740ac4c12 --- /dev/null +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "noImplicitAny": false, + "sourceMap": false, + "types": [ + "jquery", + "mocha" + ] + } +} \ No newline at end of file From aa834d7f17933c913015a772b7e0bab1af18dde8 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 17 Aug 2016 15:49:57 -0700 Subject: [PATCH 165/233] JSDoc supports null, undefined and never types --- src/compiler/checker.ts | 6 ++++++ src/compiler/parser.ts | 5 ++++- src/compiler/types.ts | 5 ++++- .../reference/jsdocNeverUndefinedNull.js | 20 +++++++++++++++++++ .../reference/jsdocNeverUndefinedNull.symbols | 14 +++++++++++++ .../reference/jsdocNeverUndefinedNull.types | 14 +++++++++++++ .../jsdoc/jsdocNeverUndefinedNull.ts | 11 ++++++++++ 7 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/jsdocNeverUndefinedNull.js create mode 100644 tests/baselines/reference/jsdocNeverUndefinedNull.symbols create mode 100644 tests/baselines/reference/jsdocNeverUndefinedNull.types create mode 100644 tests/cases/conformance/jsdoc/jsdocNeverUndefinedNull.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 01f75dc12da..62a252b6d8d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5547,6 +5547,12 @@ namespace ts { return nullType; case SyntaxKind.NeverKeyword: return neverType; + case SyntaxKind.JSDocNullKeyword: + return nullType; + case SyntaxKind.JSDocUndefinedKeyword: + return undefinedType; + case SyntaxKind.JSDocNeverKeyword: + return neverType; case SyntaxKind.ThisType: case SyntaxKind.ThisKeyword: return getTypeFromThisTypeNode(node); diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index b05451340d1..f78346fd5cd 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1241,7 +1241,7 @@ namespace ts { // not in error recovery. If we're in error recovery, we don't want an errant // semicolon to be treated as a class member (since they're almost always used // for statements. - return lookAhead(isClassMemberStart) || (token() === SyntaxKind.SemicolonToken && !inErrorRecovery); + return lookAhead(isClassMemberStart) || (token() === SyntaxKind.SemicolonToken && !inErrorRecovery); case ParsingContext.EnumMembers: // Include open bracket computed properties. This technically also lets in indexers, // which would be a candidate for improved error reporting. @@ -5890,6 +5890,9 @@ namespace ts { case SyntaxKind.BooleanKeyword: case SyntaxKind.SymbolKeyword: case SyntaxKind.VoidKeyword: + case SyntaxKind.NullKeyword: + case SyntaxKind.UndefinedKeyword: + case SyntaxKind.NeverKeyword: return parseTokenNode(); case SyntaxKind.StringLiteral: case SyntaxKind.NumericLiteral: diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 0bbbd99bdd5..1ada1e547f2 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -351,6 +351,9 @@ namespace ts { JSDocPropertyTag, JSDocTypeLiteral, JSDocLiteralType, + JSDocNullKeyword, + JSDocUndefinedKeyword, + JSDocNeverKeyword, // Synthesized list SyntaxList, @@ -383,7 +386,7 @@ namespace ts { FirstJSDocNode = JSDocTypeExpression, LastJSDocNode = JSDocLiteralType, FirstJSDocTagNode = JSDocComment, - LastJSDocTagNode = JSDocLiteralType + LastJSDocTagNode = JSDocNeverKeyword } export const enum NodeFlags { diff --git a/tests/baselines/reference/jsdocNeverUndefinedNull.js b/tests/baselines/reference/jsdocNeverUndefinedNull.js new file mode 100644 index 00000000000..e57b7091a3b --- /dev/null +++ b/tests/baselines/reference/jsdocNeverUndefinedNull.js @@ -0,0 +1,20 @@ +//// [in.js] +/** + * @param {never} p1 + * @param {undefined} p2 + * @param {null} p3 + * @returns {void} nothing + */ +function f(p1, p2, p3) { +} + + +//// [out.js] +/** + * @param {never} p1 + * @param {undefined} p2 + * @param {null} p3 + * @returns {void} nothing + */ +function f(p1, p2, p3) { +} diff --git a/tests/baselines/reference/jsdocNeverUndefinedNull.symbols b/tests/baselines/reference/jsdocNeverUndefinedNull.symbols new file mode 100644 index 00000000000..a57636ed344 --- /dev/null +++ b/tests/baselines/reference/jsdocNeverUndefinedNull.symbols @@ -0,0 +1,14 @@ +=== tests/cases/conformance/jsdoc/in.js === +/** + * @param {never} p1 + * @param {undefined} p2 + * @param {null} p3 + * @returns {void} nothing + */ +function f(p1, p2, p3) { +>f : Symbol(f, Decl(in.js, 0, 0)) +>p1 : Symbol(p1, Decl(in.js, 6, 11)) +>p2 : Symbol(p2, Decl(in.js, 6, 14)) +>p3 : Symbol(p3, Decl(in.js, 6, 18)) +} + diff --git a/tests/baselines/reference/jsdocNeverUndefinedNull.types b/tests/baselines/reference/jsdocNeverUndefinedNull.types new file mode 100644 index 00000000000..fe57cc298e2 --- /dev/null +++ b/tests/baselines/reference/jsdocNeverUndefinedNull.types @@ -0,0 +1,14 @@ +=== tests/cases/conformance/jsdoc/in.js === +/** + * @param {never} p1 + * @param {undefined} p2 + * @param {null} p3 + * @returns {void} nothing + */ +function f(p1, p2, p3) { +>f : (p1: never, p2: undefined, p3: null) => void +>p1 : never +>p2 : undefined +>p3 : null +} + diff --git a/tests/cases/conformance/jsdoc/jsdocNeverUndefinedNull.ts b/tests/cases/conformance/jsdoc/jsdocNeverUndefinedNull.ts new file mode 100644 index 00000000000..c095bc1c920 --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocNeverUndefinedNull.ts @@ -0,0 +1,11 @@ +// @allowJs: true +// @filename: in.js +// @out: out.js +/** + * @param {never} p1 + * @param {undefined} p2 + * @param {null} p3 + * @returns {void} nothing + */ +function f(p1, p2, p3) { +} From 164beb38d8606e6bf062ca2a0606dffaf8a14f3e Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 17 Aug 2016 16:11:45 -0700 Subject: [PATCH 166/233] Update baselines in jsDocParsing unit tests --- src/compiler/parser.ts | 2 +- src/harness/unittests/jsDocParsing.ts | 24 +++++------------------- 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index f78346fd5cd..ea69a0270c1 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1241,7 +1241,7 @@ namespace ts { // not in error recovery. If we're in error recovery, we don't want an errant // semicolon to be treated as a class member (since they're almost always used // for statements. - return lookAhead(isClassMemberStart) || (token() === SyntaxKind.SemicolonToken && !inErrorRecovery); + return lookAhead(isClassMemberStart) || (token() === SyntaxKind.SemicolonToken && !inErrorRecovery); case ParsingContext.EnumMembers: // Include open bracket computed properties. This technically also lets in indexers, // which would be a candidate for improved error reporting. diff --git a/src/harness/unittests/jsDocParsing.ts b/src/harness/unittests/jsDocParsing.ts index d1ca42f3861..a9987ef3176 100644 --- a/src/harness/unittests/jsDocParsing.ts +++ b/src/harness/unittests/jsDocParsing.ts @@ -767,16 +767,9 @@ namespace ts { parsesCorrectly( "{null}", `{ - "kind": "JSDocTypeReference", + "kind": "NullKeyword", "pos": 1, - "end": 5, - "name": { - "kind": "Identifier", - "pos": 1, - "end": 5, - "originalKeywordKind": "NullKeyword", - "text": "null" - } + "end": 5 }`); }); @@ -784,16 +777,9 @@ namespace ts { parsesCorrectly( "{undefined}", `{ - "kind": "JSDocTypeReference", + "kind": "UndefinedKeyword", "pos": 1, - "end": 10, - "name": { - "kind": "Identifier", - "pos": 1, - "end": 10, - "originalKeywordKind": "UndefinedKeyword", - "text": "undefined" - } + "end": 10 }`); }); @@ -2379,4 +2365,4 @@ namespace ts { }); }); }); -} \ No newline at end of file +} From 73a857b306d9d86d95ef346abb932c7964e70554 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Wed, 17 Aug 2016 18:10:06 -0700 Subject: [PATCH 167/233] Restored comments to explain spreading 'arguments' into calls to 'super'. --- src/compiler/emitter.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 15220438d23..292a77a309f 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -5311,6 +5311,21 @@ const _super = (function (geti, seti) { emitSignatureParameters(ctor); } else { + // The ES2015 spec specifies in 14.5.14. Runtime Semantics: ClassDefinitionEvaluation: + // If constructor is empty, then + // If ClassHeritag_eopt is present and protoParent is not null, then + // Let constructor be the result of parsing the source text + // constructor(...args) { super (...args);} + // using the syntactic grammar with the goal symbol MethodDefinition[~Yield]. + // Else, + // Let constructor be the result of parsing the source text + // constructor( ){ } + // using the syntactic grammar with the goal symbol MethodDefinition[~Yield]. + // + // While we could emit the '...args' rest parameter, certain later tools in the pipeline might + // downlevel the '...args' portion less efficiently by naively copying the contents of 'arguments' to an array. + // Instead, we'll avoid using a rest parameter and spread into the super call as + // 'super(...arguments)' instead of 'super(...args)', as you can see below. write("()"); } } @@ -5349,6 +5364,7 @@ const _super = (function (geti, seti) { write("_super.apply(this, arguments);"); } else { + // See comment above on using '...arguments' instead of '...args'. write("super(...arguments);"); } emitEnd(baseTypeElement); From 111b7c55f892817c728720f398502428d370154e Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Wed, 17 Aug 2016 20:09:09 -0700 Subject: [PATCH 168/233] Added test. --- .../completionListInObjectLiteral4.ts | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tests/cases/fourslash/completionListInObjectLiteral4.ts diff --git a/tests/cases/fourslash/completionListInObjectLiteral4.ts b/tests/cases/fourslash/completionListInObjectLiteral4.ts new file mode 100644 index 00000000000..c00462c8255 --- /dev/null +++ b/tests/cases/fourslash/completionListInObjectLiteral4.ts @@ -0,0 +1,28 @@ +/// + +// @strictNullChecks: true +////interface Thing { +//// hello: number; +//// world: string; +////} +//// +////declare function funcA(x : Thing): void; +////declare function funcB(x?: Thing): void; +////declare function funcC(x : Thing | null): void; +////declare function funcD(x : Thing | undefined): void; +////declare function funcE(x : Thing | null | undefined): void; +////declare function funcF(x?: Thing | null | undefined): void; +//// +////funcA({ /*A*/ }); +////funcB({ /*B*/ }); +////funcC({ /*C*/ }); +////funcD({ /*D*/ }); +////funcE({ /*E*/ }); +////funcF({ /*F*/ }); + + +for (const marker of test.markers()) { + goTo.position(marker.position); + verify.completionListContains("hello"); + verify.completionListContains("world"); +} From c1e70c97c066065fceade318af77e8b49f867dc7 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Wed, 17 Aug 2016 20:32:52 -0700 Subject: [PATCH 169/233] Use the non-nullable type of the contextual type for object completions. --- src/services/services.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/services/services.ts b/src/services/services.ts index ea85caf1332..9ef14315cb6 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -3789,7 +3789,11 @@ namespace ts { // other than those within the declared type. isNewIdentifierLocation = true; + // If the object literal is being assigned to something of type 'null | { hello: string }', + // it clearly isn't trying to satisfy the 'null' type. So we grab the non-nullable type if possible. typeForObject = typeChecker.getContextualType(objectLikeContainer); + typeForObject = typeForObject && typeForObject.getNonNullableType(); + existingMembers = (objectLikeContainer).properties; } else if (objectLikeContainer.kind === SyntaxKind.ObjectBindingPattern) { @@ -3801,7 +3805,7 @@ namespace ts { // We don't want to complete using the type acquired by the shape // of the binding pattern; we are only interested in types acquired // through type declaration or inference. - // Also proceed if rootDeclaration is parameter and if its containing function expression\arrow function is contextually typed - + // Also proceed if rootDeclaration is a parameter and if its containing function expression/arrow function is contextually typed - // type of parameter will flow in from the contextual type of the function let canGetType = !!(rootDeclaration.initializer || rootDeclaration.type); if (!canGetType && rootDeclaration.kind === SyntaxKind.Parameter) { From d24afc2ad0af8834b635afd8305b44b1f5ad6f77 Mon Sep 17 00:00:00 2001 From: Zhengbo Li Date: Thu, 18 Aug 2016 00:06:48 -0700 Subject: [PATCH 170/233] Return non-JsDocComment children ... to make syntactic classification work --- src/services/services.ts | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 148d3af427c..c4b42f94226 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -21,6 +21,7 @@ namespace ts { getChildCount(sourceFile?: SourceFile): number; getChildAt(index: number, sourceFile?: SourceFile): Node; getChildren(sourceFile?: SourceFile): Node[]; + getNonJsDocCommentChildren?(sourceFile?: SourceFile): Node[]; getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number; getFullStart(): number; getEnd(): number; @@ -196,6 +197,7 @@ namespace ts { public parent: Node; public jsDocComments: JSDocComment[]; private _children: Node[]; + private _nonJsDocCommentChildren: Node[]; constructor(kind: SyntaxKind, pos: number, end: number) { this.pos = pos; @@ -273,20 +275,22 @@ namespace ts { } private createChildren(sourceFile?: SourceFile) { - let children: Node[]; + let jsDocCommentChildren: Node[]; + let nonJsDocCommentChildren: Node[]; if (this.kind >= SyntaxKind.FirstNode) { scanner.setText((sourceFile || this.getSourceFile()).text); - children = []; + jsDocCommentChildren = []; + nonJsDocCommentChildren = []; let pos = this.pos; const useJSDocScanner = this.kind >= SyntaxKind.FirstJSDocTagNode && this.kind <= SyntaxKind.LastJSDocTagNode; - const processNode = (node: Node) => { + const processNode = (node: Node, children = nonJsDocCommentChildren) => { if (pos < node.pos) { pos = this.addSyntheticNodes(children, pos, node.pos, useJSDocScanner); } children.push(node); pos = node.end; }; - const processNodes = (nodes: NodeArray) => { + const processNodes = (nodes: NodeArray, children = nonJsDocCommentChildren) => { if (pos < nodes.pos) { pos = this.addSyntheticNodes(children, pos, nodes.pos, useJSDocScanner); } @@ -296,16 +300,21 @@ namespace ts { // jsDocComments need to be the first children if (this.jsDocComments) { for (const jsDocComment of this.jsDocComments) { - processNode(jsDocComment); + processNode(jsDocComment, jsDocCommentChildren); } } + // For syntactic classifications, all trivia are classcified together, including jsdoc comments. + // For that to work, the jsdoc comments should still be the leading trivia of the first child. + // Restoring the scanner position ensures that. + pos = this.pos; forEachChild(this, processNode, processNodes); if (pos < this.end) { - this.addSyntheticNodes(children, pos, this.end); + this.addSyntheticNodes(nonJsDocCommentChildren, pos, this.end); } scanner.setText(undefined); } - this._children = children || emptyArray; + this._nonJsDocCommentChildren = nonJsDocCommentChildren || emptyArray; + this._children = concatenate(jsDocCommentChildren, this._nonJsDocCommentChildren); } public getChildCount(sourceFile?: SourceFile): number { @@ -323,6 +332,18 @@ namespace ts { return this._children; } + public getNonJsDocCommentChildren(sourceFile?: SourceFile): Node[] { + // If the cached children were cleared, that means some node before the current node has changed. + // so even if we have a cached nonJsDocCommentChildren, it would be outdated as well. + if (!this._children) { + this.createChildren(sourceFile); + } + // If the node has cached children but not nonJsDocCommentChildren, it means the children is not created + // via calling the "createChildren" method, so it can only be a SyntaxList. As SyntaxList cannot have jsDocCommentChildren + // anyways, we can just return its children. + return this._nonJsDocCommentChildren ? this._nonJsDocCommentChildren : this._children; + } + public getFirstToken(sourceFile?: SourceFile): Node { const children = this.getChildren(sourceFile); if (!children.length) { @@ -7725,7 +7746,7 @@ namespace ts { if (decodedTextSpanIntersectsWith(spanStart, spanLength, element.pos, element.getFullWidth())) { checkForClassificationCancellation(element.kind); - const children = element.getChildren(sourceFile); + const children = element.getNonJsDocCommentChildren ? element.getNonJsDocCommentChildren(sourceFile) : element.getChildren(sourceFile); for (let i = 0, n = children.length; i < n; i++) { const child = children[i]; if (!tryClassifyNode(child)) { From 2e572201fdac48ca459380080e1a388fef8bc862 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 18 Aug 2016 07:38:20 -0700 Subject: [PATCH 171/233] Add more tests for `export = foo.bar`. --- .../reference/exportDefaultProperty2.js | 33 +++++++++++++++++++ .../reference/exportDefaultProperty2.symbols | 32 ++++++++++++++++++ .../reference/exportDefaultProperty2.types | 33 +++++++++++++++++++ .../reference/exportEqualsProperty2.js | 32 ++++++++++++++++++ .../reference/exportEqualsProperty2.symbols | 32 ++++++++++++++++++ .../reference/exportEqualsProperty2.types | 33 +++++++++++++++++++ .../cases/compiler/exportDefaultProperty2.ts | 15 +++++++++ tests/cases/compiler/exportEqualsProperty2.ts | 15 +++++++++ 8 files changed, 225 insertions(+) create mode 100644 tests/baselines/reference/exportDefaultProperty2.js create mode 100644 tests/baselines/reference/exportDefaultProperty2.symbols create mode 100644 tests/baselines/reference/exportDefaultProperty2.types create mode 100644 tests/baselines/reference/exportEqualsProperty2.js create mode 100644 tests/baselines/reference/exportEqualsProperty2.symbols create mode 100644 tests/baselines/reference/exportEqualsProperty2.types create mode 100644 tests/cases/compiler/exportDefaultProperty2.ts create mode 100644 tests/cases/compiler/exportEqualsProperty2.ts diff --git a/tests/baselines/reference/exportDefaultProperty2.js b/tests/baselines/reference/exportDefaultProperty2.js new file mode 100644 index 00000000000..88f0fbb4e20 --- /dev/null +++ b/tests/baselines/reference/exportDefaultProperty2.js @@ -0,0 +1,33 @@ +//// [tests/cases/compiler/exportDefaultProperty2.ts] //// + +//// [a.ts] +// This test is just like exportEqualsProperty2, but with `export default`. + +class C { + static B: number; +} +namespace C { + export interface B { c: number } +} + +export default C.B; + +//// [b.ts] +import B from "./a.ts"; +const x: B = { c: B }; + + +//// [a.js] +// This test is just like exportEqualsProperty2, but with `export default`. +"use strict"; +var C = (function () { + function C() { + } + return C; +}()); +exports.__esModule = true; +exports["default"] = C.B; +//// [b.js] +"use strict"; +var a_ts_1 = require("./a.ts"); +var x = { c: a_ts_1["default"] }; diff --git a/tests/baselines/reference/exportDefaultProperty2.symbols b/tests/baselines/reference/exportDefaultProperty2.symbols new file mode 100644 index 00000000000..7ac99d53a08 --- /dev/null +++ b/tests/baselines/reference/exportDefaultProperty2.symbols @@ -0,0 +1,32 @@ +=== tests/cases/compiler/a.ts === +// This test is just like exportEqualsProperty2, but with `export default`. + +class C { +>C : Symbol(C, Decl(a.ts, 0, 0), Decl(a.ts, 4, 1)) + + static B: number; +>B : Symbol(default, Decl(a.ts, 2, 9), Decl(a.ts, 5, 13)) +} +namespace C { +>C : Symbol(C, Decl(a.ts, 0, 0), Decl(a.ts, 4, 1)) + + export interface B { c: number } +>B : Symbol(B, Decl(a.ts, 2, 9), Decl(a.ts, 5, 13)) +>c : Symbol(B.c, Decl(a.ts, 6, 24)) +} + +export default C.B; +>C.B : Symbol(default, Decl(a.ts, 2, 9), Decl(a.ts, 5, 13)) +>C : Symbol(C, Decl(a.ts, 0, 0), Decl(a.ts, 4, 1)) +>B : Symbol(default, Decl(a.ts, 2, 9), Decl(a.ts, 5, 13)) + +=== tests/cases/compiler/b.ts === +import B from "./a.ts"; +>B : Symbol(B, Decl(b.ts, 0, 6)) + +const x: B = { c: B }; +>x : Symbol(x, Decl(b.ts, 1, 5)) +>B : Symbol(B, Decl(b.ts, 0, 6)) +>c : Symbol(c, Decl(b.ts, 1, 14)) +>B : Symbol(B, Decl(b.ts, 0, 6)) + diff --git a/tests/baselines/reference/exportDefaultProperty2.types b/tests/baselines/reference/exportDefaultProperty2.types new file mode 100644 index 00000000000..8dff23f1c89 --- /dev/null +++ b/tests/baselines/reference/exportDefaultProperty2.types @@ -0,0 +1,33 @@ +=== tests/cases/compiler/a.ts === +// This test is just like exportEqualsProperty2, but with `export default`. + +class C { +>C : C + + static B: number; +>B : number +} +namespace C { +>C : typeof C + + export interface B { c: number } +>B : B +>c : number +} + +export default C.B; +>C.B : number +>C : typeof C +>B : number + +=== tests/cases/compiler/b.ts === +import B from "./a.ts"; +>B : number + +const x: B = { c: B }; +>x : B +>B : B +>{ c: B } : { c: number; } +>c : number +>B : number + diff --git a/tests/baselines/reference/exportEqualsProperty2.js b/tests/baselines/reference/exportEqualsProperty2.js new file mode 100644 index 00000000000..283fa3996a7 --- /dev/null +++ b/tests/baselines/reference/exportEqualsProperty2.js @@ -0,0 +1,32 @@ +//// [tests/cases/compiler/exportEqualsProperty2.ts] //// + +//// [a.ts] +// This test is just like exportDefaultProperty2, but with `export =`. + +class C { + static B: number; +} +namespace C { + export interface B { c: number } +} + +export = C.B; + +//// [b.ts] +import B = require("./a.ts"); +const x: B = { c: B }; + + +//// [a.js] +// This test is just like exportDefaultProperty2, but with `export =`. +"use strict"; +var C = (function () { + function C() { + } + return C; +}()); +module.exports = C.B; +//// [b.js] +"use strict"; +var B = require("./a.ts"); +var x = { c: B }; diff --git a/tests/baselines/reference/exportEqualsProperty2.symbols b/tests/baselines/reference/exportEqualsProperty2.symbols new file mode 100644 index 00000000000..a765e95bdf3 --- /dev/null +++ b/tests/baselines/reference/exportEqualsProperty2.symbols @@ -0,0 +1,32 @@ +=== tests/cases/compiler/b.ts === +import B = require("./a.ts"); +>B : Symbol(B, Decl(b.ts, 0, 0)) + +const x: B = { c: B }; +>x : Symbol(x, Decl(b.ts, 1, 5)) +>B : Symbol(B, Decl(b.ts, 0, 0)) +>c : Symbol(c, Decl(b.ts, 1, 14)) +>B : Symbol(B, Decl(b.ts, 0, 0)) + +=== tests/cases/compiler/a.ts === +// This test is just like exportDefaultProperty2, but with `export =`. + +class C { +>C : Symbol(C, Decl(a.ts, 0, 0), Decl(a.ts, 4, 1)) + + static B: number; +>B : Symbol(C.B, Decl(a.ts, 2, 9), Decl(a.ts, 5, 13)) +} +namespace C { +>C : Symbol(C, Decl(a.ts, 0, 0), Decl(a.ts, 4, 1)) + + export interface B { c: number } +>B : Symbol(B, Decl(a.ts, 2, 9), Decl(a.ts, 5, 13)) +>c : Symbol(B.c, Decl(a.ts, 6, 24)) +} + +export = C.B; +>C.B : Symbol(C.B, Decl(a.ts, 2, 9), Decl(a.ts, 5, 13)) +>C : Symbol(C, Decl(a.ts, 0, 0), Decl(a.ts, 4, 1)) +>B : Symbol(C.B, Decl(a.ts, 2, 9), Decl(a.ts, 5, 13)) + diff --git a/tests/baselines/reference/exportEqualsProperty2.types b/tests/baselines/reference/exportEqualsProperty2.types new file mode 100644 index 00000000000..90b239e7bb6 --- /dev/null +++ b/tests/baselines/reference/exportEqualsProperty2.types @@ -0,0 +1,33 @@ +=== tests/cases/compiler/b.ts === +import B = require("./a.ts"); +>B : number + +const x: B = { c: B }; +>x : B +>B : B +>{ c: B } : { c: number; } +>c : number +>B : number + +=== tests/cases/compiler/a.ts === +// This test is just like exportDefaultProperty2, but with `export =`. + +class C { +>C : C + + static B: number; +>B : number +} +namespace C { +>C : typeof C + + export interface B { c: number } +>B : B +>c : number +} + +export = C.B; +>C.B : number +>C : typeof C +>B : number + diff --git a/tests/cases/compiler/exportDefaultProperty2.ts b/tests/cases/compiler/exportDefaultProperty2.ts new file mode 100644 index 00000000000..180a58a22d4 --- /dev/null +++ b/tests/cases/compiler/exportDefaultProperty2.ts @@ -0,0 +1,15 @@ +// This test is just like exportEqualsProperty2, but with `export default`. + +// @Filename: a.ts +class C { + static B: number; +} +namespace C { + export interface B { c: number } +} + +export default C.B; + +// @Filename: b.ts +import B from "./a.ts"; +const x: B = { c: B }; diff --git a/tests/cases/compiler/exportEqualsProperty2.ts b/tests/cases/compiler/exportEqualsProperty2.ts new file mode 100644 index 00000000000..6a07837334a --- /dev/null +++ b/tests/cases/compiler/exportEqualsProperty2.ts @@ -0,0 +1,15 @@ +// This test is just like exportDefaultProperty2, but with `export =`. + +// @Filename: a.ts +class C { + static B: number; +} +namespace C { + export interface B { c: number } +} + +export = C.B; + +// @Filename: b.ts +import B = require("./a.ts"); +const x: B = { c: B }; From 67b6c56ee8eacb554f5780e08ce887481f7934c4 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 18 Aug 2016 07:44:20 -0700 Subject: [PATCH 172/233] Output test baselines to tests/baselines/local instead of root --- src/harness/harness.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 9ed817eae49..db8c30ddcc9 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1667,10 +1667,10 @@ namespace Harness { const encoded_actual = Utils.encodeString(actual); if (expected !== encoded_actual) { if (actual === NoContent) { - IO.writeFile(relativeFileName + ".delete", ""); + IO.writeFile(localPath(relativeFileName + ".delete"), ""); } else { - IO.writeFile(relativeFileName, actual); + IO.writeFile(localPath(relativeFileName), actual); } // Overwrite & issue error const errMsg = "The baseline file " + relativeFileName + " has changed."; @@ -1678,6 +1678,7 @@ namespace Harness { } } + export function runBaseline( descriptionForDescribe: string, relativeFileName: string, From 8fc17afa003d53aa7baf914883f2b20606e8a879 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 18 Aug 2016 14:11:30 -0700 Subject: [PATCH 173/233] Move supportedTypescriptExtensionsWithDtsFirst next to supportedTypeScriptExtensions and rename --- src/compiler/core.ts | 2 ++ src/compiler/utilities.ts | 6 ++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index d2ea69b304d..808f8a538df 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1243,6 +1243,8 @@ namespace ts { * List of supported extensions in order of file resolution precedence. */ export const supportedTypeScriptExtensions = [".ts", ".tsx", ".d.ts"]; + /** Must have ".d.ts" first because if ".ts" goes first, that will be detected as the extension instead of ".d.ts". */ + export const supportedTypescriptExtensionsForExtractExtension = [".d.ts", ".ts", ".tsx"]; export const supportedJavascriptExtensions = [".js", ".jsx"]; const allSupportedExtensions = supportedTypeScriptExtensions.concat(supportedJavascriptExtensions); diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 8b32d5351ea..faf210e461f 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2713,12 +2713,10 @@ namespace ts { return forEach(supportedTypeScriptExtensions, extension => fileExtensionIs(fileName, extension)); } - /** Return ".ts" or ".tsx" if that is the extension. */ + /** Return ".ts", ".d.ts", or ".tsx", if that is the extension. */ export function tryExtractTypeScriptExtension(fileName: string): string | undefined { - return find(supportedTypescriptExtensionsWithDtsFirst, extension => fileExtensionIs(fileName, extension)); + return find(supportedTypescriptExtensionsForExtractExtension, extension => fileExtensionIs(fileName, extension)); } - // Must have '.d.ts' first because if '.ts' goes first, that will be detected as the extension instead of '.d.ts'. - const supportedTypescriptExtensionsWithDtsFirst = supportedTypeScriptExtensions.slice().reverse(); /** * Replace each instance of non-ascii characters by one, two, three, or four escape sequences From 952d2fecc1817eb86d241a9aa6f93ac560be1d27 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 18 Aug 2016 14:19:17 -0700 Subject: [PATCH 174/233] Fix comment --- src/compiler/parser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 68753fdb638..361e293011b 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3386,7 +3386,7 @@ namespace ts { * 6) - UnaryExpression[?yield] * 7) ~ UnaryExpression[?yield] * 8) ! UnaryExpression[?yield] - * 9) [+Await] await AwaitExpression[?yield] + * 9) [+Await] await UnaryExpression[?yield] */ function parseSimpleUnaryExpression(): UnaryExpression { switch (token) { From b8963ba8f1bc17da896c4ade47e7a1f199ffda57 Mon Sep 17 00:00:00 2001 From: Yui Date: Thu, 18 Aug 2016 14:39:15 -0700 Subject: [PATCH 175/233] Fix RWC Runner (#10420) * Use /// +/// import fs = require('fs'); import path = require('path'); diff --git a/src/harness/rwcRunner.ts b/src/harness/rwcRunner.ts index 1266ffa5d37..7ea191e3c83 100644 --- a/src/harness/rwcRunner.ts +++ b/src/harness/rwcRunner.ts @@ -198,7 +198,8 @@ namespace RWC { } // Do not include the library in the baselines to avoid noise const baselineFiles = inputFiles.concat(otherFiles).filter(f => !Harness.isDefaultLibraryFile(f.unitName)); - return Harness.Compiler.getErrorBaseline(baselineFiles, compilerResult.errors); + const errors = compilerResult.errors.filter(e => !Harness.isDefaultLibraryFile(e.file.fileName)); + return Harness.Compiler.getErrorBaseline(baselineFiles, errors); }, false, baselineOpts); }); From 03dcdda44342a07ea10701ae1725f174941d2d97 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Thu, 18 Aug 2016 17:12:40 -0700 Subject: [PATCH 176/233] Treat special property access symbol differently ... when retriving documentation --- src/services/services.ts | 18 +++++++++++++++++ .../completionEntryDetailAcrossFiles01.ts | 20 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 tests/cases/fourslash/server/completionEntryDetailAcrossFiles01.ts diff --git a/src/services/services.ts b/src/services/services.ts index ea85caf1332..64b872d14a7 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4913,6 +4913,24 @@ namespace ts { if (!documentation) { documentation = symbol.getDocumentationComment(); + if ((!documentation || documentation.length === 0) && symbol.flags & SymbolFlags.Property) { + // For some special property access expressions like `experts.foo = foo` or `module.exports.foo = foo` + // there documentation comments might be attached to the right hand side symbol of their declarations. + // The pattern of such special property access is that the parent symbol is the symbol of the file. + if (symbol.parent && forEach(symbol.parent.declarations, declaration => declaration.kind === SyntaxKind.SourceFile)) { + forEach(symbol.declarations, declaration => { + if (declaration.parent && declaration.parent.kind === SyntaxKind.BinaryExpression) { + const rhsSymbol = program.getTypeChecker().getSymbolAtLocation((declaration.parent).right); + if (rhsSymbol) { + documentation = rhsSymbol.getDocumentationComment(); + if (documentation && documentation.length > 0) { + return true; + } + } + } + }); + } + } } return { displayParts, documentation, symbolKind }; diff --git a/tests/cases/fourslash/server/completionEntryDetailAcrossFiles01.ts b/tests/cases/fourslash/server/completionEntryDetailAcrossFiles01.ts new file mode 100644 index 00000000000..243975fde2d --- /dev/null +++ b/tests/cases/fourslash/server/completionEntryDetailAcrossFiles01.ts @@ -0,0 +1,20 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: a.js +//// /** +//// * Modify the parameter +//// * @param {string} p1 +//// */ +//// var foo = function (p1) { } +//// exports.foo = foo; +//// fo/*1*/ + +// @Filename: b.ts +//// import a = require("./a"); +//// a.fo/*2*/ + +goTo.marker('1'); +verify.completionEntryDetailIs("foo", "var foo: (p1: string) => void", "Modify the parameter"); +goTo.marker('2'); +verify.completionEntryDetailIs("foo", "(property) a.foo: (p1: string) => void", "Modify the parameter"); From b452469419cfed00dc5b04f4d286c2c700ef2619 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Fri, 19 Aug 2016 06:14:28 -0700 Subject: [PATCH 177/233] Fix tests --- tests/baselines/reference/exportDefaultProperty2.js | 6 +++--- tests/baselines/reference/exportDefaultProperty2.symbols | 2 +- tests/baselines/reference/exportDefaultProperty2.types | 2 +- tests/baselines/reference/exportEqualsProperty2.js | 4 ++-- tests/baselines/reference/exportEqualsProperty2.symbols | 2 +- tests/baselines/reference/exportEqualsProperty2.types | 2 +- tests/cases/compiler/exportDefaultProperty2.ts | 2 +- tests/cases/compiler/exportEqualsProperty2.ts | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/baselines/reference/exportDefaultProperty2.js b/tests/baselines/reference/exportDefaultProperty2.js index 88f0fbb4e20..2b0a5dce526 100644 --- a/tests/baselines/reference/exportDefaultProperty2.js +++ b/tests/baselines/reference/exportDefaultProperty2.js @@ -13,7 +13,7 @@ namespace C { export default C.B; //// [b.ts] -import B from "./a.ts"; +import B from "./a"; const x: B = { c: B }; @@ -29,5 +29,5 @@ exports.__esModule = true; exports["default"] = C.B; //// [b.js] "use strict"; -var a_ts_1 = require("./a.ts"); -var x = { c: a_ts_1["default"] }; +var a_1 = require("./a"); +var x = { c: a_1["default"] }; diff --git a/tests/baselines/reference/exportDefaultProperty2.symbols b/tests/baselines/reference/exportDefaultProperty2.symbols index 7ac99d53a08..d3d7519aa94 100644 --- a/tests/baselines/reference/exportDefaultProperty2.symbols +++ b/tests/baselines/reference/exportDefaultProperty2.symbols @@ -21,7 +21,7 @@ export default C.B; >B : Symbol(default, Decl(a.ts, 2, 9), Decl(a.ts, 5, 13)) === tests/cases/compiler/b.ts === -import B from "./a.ts"; +import B from "./a"; >B : Symbol(B, Decl(b.ts, 0, 6)) const x: B = { c: B }; diff --git a/tests/baselines/reference/exportDefaultProperty2.types b/tests/baselines/reference/exportDefaultProperty2.types index 8dff23f1c89..2fc9d3a4750 100644 --- a/tests/baselines/reference/exportDefaultProperty2.types +++ b/tests/baselines/reference/exportDefaultProperty2.types @@ -21,7 +21,7 @@ export default C.B; >B : number === tests/cases/compiler/b.ts === -import B from "./a.ts"; +import B from "./a"; >B : number const x: B = { c: B }; diff --git a/tests/baselines/reference/exportEqualsProperty2.js b/tests/baselines/reference/exportEqualsProperty2.js index 283fa3996a7..b5d91431722 100644 --- a/tests/baselines/reference/exportEqualsProperty2.js +++ b/tests/baselines/reference/exportEqualsProperty2.js @@ -13,7 +13,7 @@ namespace C { export = C.B; //// [b.ts] -import B = require("./a.ts"); +import B = require("./a"); const x: B = { c: B }; @@ -28,5 +28,5 @@ var C = (function () { module.exports = C.B; //// [b.js] "use strict"; -var B = require("./a.ts"); +var B = require("./a"); var x = { c: B }; diff --git a/tests/baselines/reference/exportEqualsProperty2.symbols b/tests/baselines/reference/exportEqualsProperty2.symbols index a765e95bdf3..df7eda76661 100644 --- a/tests/baselines/reference/exportEqualsProperty2.symbols +++ b/tests/baselines/reference/exportEqualsProperty2.symbols @@ -1,5 +1,5 @@ === tests/cases/compiler/b.ts === -import B = require("./a.ts"); +import B = require("./a"); >B : Symbol(B, Decl(b.ts, 0, 0)) const x: B = { c: B }; diff --git a/tests/baselines/reference/exportEqualsProperty2.types b/tests/baselines/reference/exportEqualsProperty2.types index 90b239e7bb6..4594cbfd015 100644 --- a/tests/baselines/reference/exportEqualsProperty2.types +++ b/tests/baselines/reference/exportEqualsProperty2.types @@ -1,5 +1,5 @@ === tests/cases/compiler/b.ts === -import B = require("./a.ts"); +import B = require("./a"); >B : number const x: B = { c: B }; diff --git a/tests/cases/compiler/exportDefaultProperty2.ts b/tests/cases/compiler/exportDefaultProperty2.ts index 180a58a22d4..0db617c93e1 100644 --- a/tests/cases/compiler/exportDefaultProperty2.ts +++ b/tests/cases/compiler/exportDefaultProperty2.ts @@ -11,5 +11,5 @@ namespace C { export default C.B; // @Filename: b.ts -import B from "./a.ts"; +import B from "./a"; const x: B = { c: B }; diff --git a/tests/cases/compiler/exportEqualsProperty2.ts b/tests/cases/compiler/exportEqualsProperty2.ts index 6a07837334a..61f117f1629 100644 --- a/tests/cases/compiler/exportEqualsProperty2.ts +++ b/tests/cases/compiler/exportEqualsProperty2.ts @@ -11,5 +11,5 @@ namespace C { export = C.B; // @Filename: b.ts -import B = require("./a.ts"); +import B = require("./a"); const x: B = { c: B }; From 7f6e36c7e1308bc4c87dacb9eb458059d4377b77 Mon Sep 17 00:00:00 2001 From: Yui Date: Fri, 19 Aug 2016 10:03:23 -0700 Subject: [PATCH 178/233] Update shim version to be 2.1 (#10424) --- src/services/shims.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/shims.ts b/src/services/shims.ts index ff57dd9cf7a..9a581ed6be1 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -1203,6 +1203,6 @@ namespace TypeScript.Services { // TODO: it should be moved into a namespace though. /* @internal */ -const toolsVersion = "1.9"; +const toolsVersion = "2.1"; /* tslint:enable:no-unused-variable */ From 0168ab2051bc9a364f8f148296cf71848fc4bba9 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 19 Aug 2016 13:34:06 -0700 Subject: [PATCH 179/233] Check return code paths on getters (#10102) * Check return paths on getters * Remove TODO comment --- src/compiler/checker.ts | 12 ++--- .../getterControlFlowStrictNull.errors.txt | 27 +++++++++++ .../reference/getterControlFlowStrictNull.js | 47 +++++++++++++++++++ .../compiler/getterControlFlowStrictNull.ts | 20 ++++++++ 4 files changed, 99 insertions(+), 7 deletions(-) create mode 100644 tests/baselines/reference/getterControlFlowStrictNull.errors.txt create mode 100644 tests/baselines/reference/getterControlFlowStrictNull.js create mode 100644 tests/cases/compiler/getterControlFlowStrictNull.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d954ea51170..8568cd5feed 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14126,12 +14126,7 @@ namespace ts { checkSignatureDeclaration(node); if (node.kind === SyntaxKind.GetAccessor) { if (!isInAmbientContext(node) && nodeIsPresent(node.body) && (node.flags & NodeFlags.HasImplicitReturn)) { - if (node.flags & NodeFlags.HasExplicitReturn) { - if (compilerOptions.noImplicitReturns) { - error(node.name, Diagnostics.Not_all_code_paths_return_a_value); - } - } - else { + if (!(node.flags & NodeFlags.HasExplicitReturn)) { error(node.name, Diagnostics.A_get_accessor_must_return_a_value); } } @@ -14161,7 +14156,10 @@ namespace ts { checkAccessorDeclarationTypesIdentical(node, otherAccessor, getThisTypeOfDeclaration, Diagnostics.get_and_set_accessor_must_have_the_same_this_type); } } - getTypeOfAccessors(getSymbolOfNode(node)); + const returnType = getTypeOfAccessors(getSymbolOfNode(node)); + if (node.kind === SyntaxKind.GetAccessor) { + checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnType); + } } if (node.parent.kind !== SyntaxKind.ObjectLiteralExpression) { checkSourceElement(node.body); diff --git a/tests/baselines/reference/getterControlFlowStrictNull.errors.txt b/tests/baselines/reference/getterControlFlowStrictNull.errors.txt new file mode 100644 index 00000000000..1c4f02d6a75 --- /dev/null +++ b/tests/baselines/reference/getterControlFlowStrictNull.errors.txt @@ -0,0 +1,27 @@ +tests/cases/compiler/getterControlFlowStrictNull.ts(2,9): error TS2366: Function lacks ending return statement and return type does not include 'undefined'. +tests/cases/compiler/getterControlFlowStrictNull.ts(11,14): error TS2366: Function lacks ending return statement and return type does not include 'undefined'. + + +==== tests/cases/compiler/getterControlFlowStrictNull.ts (2 errors) ==== + class A { + a(): string | null { + ~~~~~~~~~~~~~ +!!! error TS2366: Function lacks ending return statement and return type does not include 'undefined'. + if (Math.random() > 0.5) { + return ''; + } + + // it does error here as expected + } + } + class B { + get a(): string | null { + ~~~~~~~~~~~~~ +!!! error TS2366: Function lacks ending return statement and return type does not include 'undefined'. + if (Math.random() > 0.5) { + return ''; + } + + // it should error here because it returns undefined + } + } \ No newline at end of file diff --git a/tests/baselines/reference/getterControlFlowStrictNull.js b/tests/baselines/reference/getterControlFlowStrictNull.js new file mode 100644 index 00000000000..c3f7e410d0a --- /dev/null +++ b/tests/baselines/reference/getterControlFlowStrictNull.js @@ -0,0 +1,47 @@ +//// [getterControlFlowStrictNull.ts] +class A { + a(): string | null { + if (Math.random() > 0.5) { + return ''; + } + + // it does error here as expected + } +} +class B { + get a(): string | null { + if (Math.random() > 0.5) { + return ''; + } + + // it should error here because it returns undefined + } +} + +//// [getterControlFlowStrictNull.js] +var A = (function () { + function A() { + } + A.prototype.a = function () { + if (Math.random() > 0.5) { + return ''; + } + // it does error here as expected + }; + return A; +}()); +var B = (function () { + function B() { + } + Object.defineProperty(B.prototype, "a", { + get: function () { + if (Math.random() > 0.5) { + return ''; + } + // it should error here because it returns undefined + }, + enumerable: true, + configurable: true + }); + return B; +}()); diff --git a/tests/cases/compiler/getterControlFlowStrictNull.ts b/tests/cases/compiler/getterControlFlowStrictNull.ts new file mode 100644 index 00000000000..44fbe359789 --- /dev/null +++ b/tests/cases/compiler/getterControlFlowStrictNull.ts @@ -0,0 +1,20 @@ +//@strictNullChecks: true +//@target: ES5 +class A { + a(): string | null { + if (Math.random() > 0.5) { + return ''; + } + + // it does error here as expected + } +} +class B { + get a(): string | null { + if (Math.random() > 0.5) { + return ''; + } + + // it should error here because it returns undefined + } +} \ No newline at end of file From da6d95101fd8adfacbdb5209782dc560cf09c62f Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 19 Aug 2016 13:56:27 -0700 Subject: [PATCH 180/233] Remove extraneous arguments from harness's runBaseline (#10419) * Remove extraneous arguments from runBaseline * Address comments from @yuit --- src/harness/compilerRunner.ts | 16 ++-- src/harness/fourslash.ts | 12 +-- src/harness/harness.ts | 84 +++++++++------------ src/harness/projectsRunner.ts | 10 +-- src/harness/rwcRunner.ts | 24 +++--- src/harness/test262Runner.ts | 12 +-- src/harness/unittests/initializeTSConfig.ts | 2 +- src/harness/unittests/transpile.ts | 8 +- 8 files changed, 73 insertions(+), 95 deletions(-) diff --git a/src/harness/compilerRunner.ts b/src/harness/compilerRunner.ts index 66396293dc2..88e81ffcf34 100644 --- a/src/harness/compilerRunner.ts +++ b/src/harness/compilerRunner.ts @@ -147,7 +147,7 @@ class CompilerBaselineRunner extends RunnerBase { // check errors it("Correct errors for " + fileName, () => { if (this.errors) { - Harness.Baseline.runBaseline("Correct errors for " + fileName, justName.replace(/\.tsx?$/, ".errors.txt"), (): string => { + Harness.Baseline.runBaseline(justName.replace(/\.tsx?$/, ".errors.txt"), (): string => { if (result.errors.length === 0) return null; return getErrorBaseline(toBeCompiled, otherFiles, result); }); @@ -156,7 +156,7 @@ class CompilerBaselineRunner extends RunnerBase { it (`Correct module resolution tracing for ${fileName}`, () => { if (options.traceResolution) { - Harness.Baseline.runBaseline("Correct module resolution tracing for " + fileName, justName.replace(/\.tsx?$/, ".trace.json"), () => { + Harness.Baseline.runBaseline(justName.replace(/\.tsx?$/, ".trace.json"), () => { return JSON.stringify(result.traceResults || [], undefined, 4); }); } @@ -165,7 +165,7 @@ class CompilerBaselineRunner extends RunnerBase { // Source maps? it("Correct sourcemap content for " + fileName, () => { if (options.sourceMap || options.inlineSourceMap) { - Harness.Baseline.runBaseline("Correct sourcemap content for " + fileName, justName.replace(/\.tsx?$/, ".sourcemap.txt"), () => { + Harness.Baseline.runBaseline(justName.replace(/\.tsx?$/, ".sourcemap.txt"), () => { const record = result.getSourceMapRecord(); if (options.noEmitOnError && result.errors.length !== 0 && record === undefined) { // Because of the noEmitOnError option no files are created. We need to return null because baselining isn"t required. @@ -183,7 +183,7 @@ class CompilerBaselineRunner extends RunnerBase { } // check js output - Harness.Baseline.runBaseline("Correct JS output for " + fileName, justName.replace(/\.tsx?/, ".js"), () => { + Harness.Baseline.runBaseline(justName.replace(/\.tsx?/, ".js"), () => { let tsCode = ""; const tsSources = otherFiles.concat(toBeCompiled); if (tsSources.length > 1) { @@ -242,7 +242,7 @@ class CompilerBaselineRunner extends RunnerBase { throw new Error("Number of sourcemap files should be same as js files."); } - Harness.Baseline.runBaseline("Correct Sourcemap output for " + fileName, justName.replace(/\.tsx?/, ".js.map"), () => { + Harness.Baseline.runBaseline(justName.replace(/\.tsx?/, ".js.map"), () => { if (options.noEmitOnError && result.errors.length !== 0 && result.sourceMaps.length === 0) { // We need to return null here or the runBaseLine will actually create a empty file. // Baselining isn't required here because there is no output. @@ -330,11 +330,11 @@ class CompilerBaselineRunner extends RunnerBase { const pullExtension = isSymbolBaseLine ? ".symbols.pull" : ".types.pull"; if (fullBaseLine !== pullBaseLine) { - Harness.Baseline.runBaseline("Correct full information for " + fileName, justName.replace(/\.tsx?/, fullExtension), () => fullBaseLine); - Harness.Baseline.runBaseline("Correct pull information for " + fileName, justName.replace(/\.tsx?/, pullExtension), () => pullBaseLine); + Harness.Baseline.runBaseline(justName.replace(/\.tsx?/, fullExtension), () => fullBaseLine); + Harness.Baseline.runBaseline(justName.replace(/\.tsx?/, pullExtension), () => pullBaseLine); } else { - Harness.Baseline.runBaseline("Correct information for " + fileName, justName.replace(/\.tsx?/, fullExtension), () => fullBaseLine); + Harness.Baseline.runBaseline(justName.replace(/\.tsx?/, fullExtension), () => fullBaseLine); } } diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index b5fa53763cb..63731417f48 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1132,12 +1132,10 @@ namespace FourSlash { } Harness.Baseline.runBaseline( - "Breakpoint Locations for " + this.activeFile.fileName, baselineFile, () => { return this.baselineCurrentFileLocations(pos => this.getBreakpointStatementLocation(pos)); - }, - true /* run immediately */); + }); } public baselineGetEmitOutput() { @@ -1159,7 +1157,6 @@ namespace FourSlash { } Harness.Baseline.runBaseline( - "Generate getEmitOutput baseline : " + emitFiles.join(" "), this.testData.globalOptions[metadataOptionNames.baselineFile], () => { let resultString = ""; @@ -1185,8 +1182,7 @@ namespace FourSlash { }); return resultString; - }, - true /* run immediately */); + }); } public printBreakpointLocation(pos: number) { @@ -1730,13 +1726,11 @@ namespace FourSlash { public baselineCurrentFileNameOrDottedNameSpans() { Harness.Baseline.runBaseline( - "Name OrDottedNameSpans for " + this.activeFile.fileName, this.testData.globalOptions[metadataOptionNames.baselineFile], () => { return this.baselineCurrentFileLocations(pos => this.getNameOrDottedNameSpan(pos)); - }, - true /* run immediately */); + }); } public printNameOrDottedNameSpans(pos: number) { diff --git a/src/harness/harness.ts b/src/harness/harness.ts index db8c30ddcc9..3375b8e47e7 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1604,31 +1604,7 @@ namespace Harness { } const fileCache: { [idx: string]: boolean } = {}; - function generateActual(actualFileName: string, generateContent: () => string): string { - // For now this is written using TypeScript, because sys is not available when running old test cases. - // But we need to move to sys once we have - // Creates the directory including its parent if not already present - function createDirectoryStructure(dirName: string) { - if (fileCache[dirName] || IO.directoryExists(dirName)) { - fileCache[dirName] = true; - return; - } - - const parentDirectory = IO.directoryName(dirName); - if (parentDirectory != "") { - createDirectoryStructure(parentDirectory); - } - IO.createDirectory(dirName); - fileCache[dirName] = true; - } - - // Create folders if needed - createDirectoryStructure(Harness.IO.directoryName(actualFileName)); - - // Delete the actual file in case it fails - if (IO.fileExists(actualFileName)) { - IO.deleteFile(actualFileName); - } + function generateActual(generateContent: () => string): string { const actual = generateContent(); @@ -1663,43 +1639,51 @@ namespace Harness { return { expected, actual }; } - function writeComparison(expected: string, actual: string, relativeFileName: string, actualFileName: string, descriptionForDescribe: string) { + function writeComparison(expected: string, actual: string, relativeFileName: string, actualFileName: string) { + // For now this is written using TypeScript, because sys is not available when running old test cases. + // But we need to move to sys once we have + // Creates the directory including its parent if not already present + function createDirectoryStructure(dirName: string) { + if (fileCache[dirName] || IO.directoryExists(dirName)) { + fileCache[dirName] = true; + return; + } + + const parentDirectory = IO.directoryName(dirName); + if (parentDirectory != "") { + createDirectoryStructure(parentDirectory); + } + IO.createDirectory(dirName); + fileCache[dirName] = true; + } + + // Create folders if needed + createDirectoryStructure(Harness.IO.directoryName(actualFileName)); + + // Delete the actual file in case it fails + if (IO.fileExists(actualFileName)) { + IO.deleteFile(actualFileName); + } + const encoded_actual = Utils.encodeString(actual); if (expected !== encoded_actual) { if (actual === NoContent) { - IO.writeFile(localPath(relativeFileName + ".delete"), ""); + IO.writeFile(actualFileName + ".delete", ""); } else { - IO.writeFile(localPath(relativeFileName), actual); + IO.writeFile(actualFileName, actual); } - // Overwrite & issue error - const errMsg = "The baseline file " + relativeFileName + " has changed."; - throw new Error(errMsg); + throw new Error(`The baseline file ${relativeFileName} has changed.`); } } + export function runBaseline(relativeFileName: string, generateContent: () => string, opts?: BaselineOptions): void { - export function runBaseline( - descriptionForDescribe: string, - relativeFileName: string, - generateContent: () => string, - runImmediately = false, - opts?: BaselineOptions): void { - - let actual = undefined; const actualFileName = localPath(relativeFileName, opts && opts.Baselinefolder, opts && opts.Subfolder); - if (runImmediately) { - actual = generateActual(actualFileName, generateContent); - const comparison = compareToBaseline(actual, relativeFileName, opts); - writeComparison(comparison.expected, comparison.actual, relativeFileName, actualFileName, descriptionForDescribe); - } - else { - actual = generateActual(actualFileName, generateContent); - - const comparison = compareToBaseline(actual, relativeFileName, opts); - writeComparison(comparison.expected, comparison.actual, relativeFileName, actualFileName, descriptionForDescribe); - } + const actual = generateActual(generateContent); + const comparison = compareToBaseline(actual, relativeFileName, opts); + writeComparison(comparison.expected, comparison.actual, relativeFileName, actualFileName); } } diff --git a/src/harness/projectsRunner.ts b/src/harness/projectsRunner.ts index 76f042834bb..080c6edfa87 100644 --- a/src/harness/projectsRunner.ts +++ b/src/harness/projectsRunner.ts @@ -459,7 +459,7 @@ class ProjectRunner extends RunnerBase { }); it("Resolution information of (" + moduleNameToString(moduleKind) + "): " + testCaseFileName, () => { - Harness.Baseline.runBaseline("Resolution information of (" + moduleNameToString(compilerResult.moduleKind) + "): " + testCaseFileName, getBaselineFolder(compilerResult.moduleKind) + testCaseJustName + ".json", () => { + Harness.Baseline.runBaseline(getBaselineFolder(compilerResult.moduleKind) + testCaseJustName + ".json", () => { return JSON.stringify(getCompilerResolutionInfo(), undefined, " "); }); }); @@ -467,7 +467,7 @@ class ProjectRunner extends RunnerBase { it("Errors for (" + moduleNameToString(moduleKind) + "): " + testCaseFileName, () => { if (compilerResult.errors.length) { - Harness.Baseline.runBaseline("Errors for (" + moduleNameToString(compilerResult.moduleKind) + "): " + testCaseFileName, getBaselineFolder(compilerResult.moduleKind) + testCaseJustName + ".errors.txt", () => { + Harness.Baseline.runBaseline(getBaselineFolder(compilerResult.moduleKind) + testCaseJustName + ".errors.txt", () => { return getErrorsBaseline(compilerResult); }); } @@ -481,7 +481,7 @@ class ProjectRunner extends RunnerBase { // There may be multiple files with different baselines. Run all and report at the end, else // it stops copying the remaining emitted files from 'local/projectOutput' to 'local/project'. try { - Harness.Baseline.runBaseline("Baseline of emitted result (" + moduleNameToString(compilerResult.moduleKind) + "): " + testCaseFileName, getBaselineFolder(compilerResult.moduleKind) + outputFile.fileName, () => { + Harness.Baseline.runBaseline(getBaselineFolder(compilerResult.moduleKind) + outputFile.fileName, () => { try { return Harness.IO.readFile(getProjectOutputFolder(outputFile.fileName, compilerResult.moduleKind)); } @@ -503,7 +503,7 @@ class ProjectRunner extends RunnerBase { it("SourceMapRecord for (" + moduleNameToString(moduleKind) + "): " + testCaseFileName, () => { if (compilerResult.sourceMapData) { - Harness.Baseline.runBaseline("SourceMapRecord for (" + moduleNameToString(compilerResult.moduleKind) + "): " + testCaseFileName, getBaselineFolder(compilerResult.moduleKind) + testCaseJustName + ".sourcemap.txt", () => { + Harness.Baseline.runBaseline(getBaselineFolder(compilerResult.moduleKind) + testCaseJustName + ".sourcemap.txt", () => { return Harness.SourceMapRecorder.getSourceMapRecord(compilerResult.sourceMapData, compilerResult.program, ts.filter(compilerResult.outputFiles, outputFile => Harness.Compiler.isJS(outputFile.emittedFileName))); }); @@ -516,7 +516,7 @@ class ProjectRunner extends RunnerBase { if (!compilerResult.errors.length && testCase.declaration) { const dTsCompileResult = compileCompileDTsFiles(compilerResult); if (dTsCompileResult && dTsCompileResult.errors.length) { - Harness.Baseline.runBaseline("Errors in generated Dts files for (" + moduleNameToString(compilerResult.moduleKind) + "): " + testCaseFileName, getBaselineFolder(compilerResult.moduleKind) + testCaseJustName + ".dts.errors.txt", () => { + Harness.Baseline.runBaseline(getBaselineFolder(compilerResult.moduleKind) + testCaseJustName + ".dts.errors.txt", () => { return getErrorsBaseline(dTsCompileResult); }); } diff --git a/src/harness/rwcRunner.ts b/src/harness/rwcRunner.ts index 7ea191e3c83..17346016fbb 100644 --- a/src/harness/rwcRunner.ts +++ b/src/harness/rwcRunner.ts @@ -158,41 +158,41 @@ namespace RWC { it("has the expected emitted code", () => { - Harness.Baseline.runBaseline("has the expected emitted code", baseName + ".output.js", () => { + Harness.Baseline.runBaseline(baseName + ".output.js", () => { return Harness.Compiler.collateOutputs(compilerResult.files); - }, false, baselineOpts); + }, baselineOpts); }); it("has the expected declaration file content", () => { - Harness.Baseline.runBaseline("has the expected declaration file content", baseName + ".d.ts", () => { + Harness.Baseline.runBaseline(baseName + ".d.ts", () => { if (!compilerResult.declFilesCode.length) { return null; } return Harness.Compiler.collateOutputs(compilerResult.declFilesCode); - }, false, baselineOpts); + }, baselineOpts); }); it("has the expected source maps", () => { - Harness.Baseline.runBaseline("has the expected source maps", baseName + ".map", () => { + Harness.Baseline.runBaseline(baseName + ".map", () => { if (!compilerResult.sourceMaps.length) { return null; } return Harness.Compiler.collateOutputs(compilerResult.sourceMaps); - }, false, baselineOpts); + }, baselineOpts); }); /*it("has correct source map record", () => { if (compilerOptions.sourceMap) { - Harness.Baseline.runBaseline("has correct source map record", baseName + ".sourcemap.txt", () => { + Harness.Baseline.runBaseline(baseName + ".sourcemap.txt", () => { return compilerResult.getSourceMapRecord(); - }, false, baselineOpts); + }, baselineOpts); } });*/ it("has the expected errors", () => { - Harness.Baseline.runBaseline("has the expected errors", baseName + ".errors.txt", () => { + Harness.Baseline.runBaseline(baseName + ".errors.txt", () => { if (compilerResult.errors.length === 0) { return null; } @@ -200,14 +200,14 @@ namespace RWC { const baselineFiles = inputFiles.concat(otherFiles).filter(f => !Harness.isDefaultLibraryFile(f.unitName)); const errors = compilerResult.errors.filter(e => !Harness.isDefaultLibraryFile(e.file.fileName)); return Harness.Compiler.getErrorBaseline(baselineFiles, errors); - }, false, baselineOpts); + }, baselineOpts); }); // Ideally, a generated declaration file will have no errors. But we allow generated // declaration file errors as part of the baseline. it("has the expected errors in generated declaration files", () => { if (compilerOptions.declaration && !compilerResult.errors.length) { - Harness.Baseline.runBaseline("has the expected errors in generated declaration files", baseName + ".dts.errors.txt", () => { + Harness.Baseline.runBaseline(baseName + ".dts.errors.txt", () => { const declFileCompilationResult = Harness.Compiler.compileDeclarationFiles( inputFiles, otherFiles, compilerResult, /*harnessSettings*/ undefined, compilerOptions, currentDirectory); @@ -218,7 +218,7 @@ namespace RWC { return Harness.Compiler.minimalDiagnosticsToString(declFileCompilationResult.declResult.errors) + Harness.IO.newLine() + Harness.IO.newLine() + Harness.Compiler.getErrorBaseline(declFileCompilationResult.declInputFiles.concat(declFileCompilationResult.declOtherFiles), declFileCompilationResult.declResult.errors); - }, false, baselineOpts); + }, baselineOpts); } }); diff --git a/src/harness/test262Runner.ts b/src/harness/test262Runner.ts index c44b2286b83..66cf474824b 100644 --- a/src/harness/test262Runner.ts +++ b/src/harness/test262Runner.ts @@ -67,21 +67,21 @@ class Test262BaselineRunner extends RunnerBase { }); it("has the expected emitted code", () => { - Harness.Baseline.runBaseline("has the expected emitted code", testState.filename + ".output.js", () => { + Harness.Baseline.runBaseline(testState.filename + ".output.js", () => { const files = testState.compilerResult.files.filter(f => f.fileName !== Test262BaselineRunner.helpersFilePath); return Harness.Compiler.collateOutputs(files); - }, false, Test262BaselineRunner.baselineOptions); + }, Test262BaselineRunner.baselineOptions); }); it("has the expected errors", () => { - Harness.Baseline.runBaseline("has the expected errors", testState.filename + ".errors.txt", () => { + Harness.Baseline.runBaseline(testState.filename + ".errors.txt", () => { const errors = testState.compilerResult.errors; if (errors.length === 0) { return null; } return Harness.Compiler.getErrorBaseline(testState.inputFiles, errors); - }, false, Test262BaselineRunner.baselineOptions); + }, Test262BaselineRunner.baselineOptions); }); it("satisfies invariants", () => { @@ -90,10 +90,10 @@ class Test262BaselineRunner extends RunnerBase { }); it("has the expected AST", () => { - Harness.Baseline.runBaseline("has the expected AST", testState.filename + ".AST.txt", () => { + Harness.Baseline.runBaseline(testState.filename + ".AST.txt", () => { const sourceFile = testState.compilerResult.program.getSourceFile(Test262BaselineRunner.getTestFilePath(testState.filename)); return Utils.sourceFileToJSON(sourceFile); - }, false, Test262BaselineRunner.baselineOptions); + }, Test262BaselineRunner.baselineOptions); }); }); } diff --git a/src/harness/unittests/initializeTSConfig.ts b/src/harness/unittests/initializeTSConfig.ts index 059f07e0115..cb995212a94 100644 --- a/src/harness/unittests/initializeTSConfig.ts +++ b/src/harness/unittests/initializeTSConfig.ts @@ -10,7 +10,7 @@ namespace ts { const outputFileName = `tsConfig/${name.replace(/[^a-z0-9\-. ]/ig, "")}/tsconfig.json`; it(`Correct output for ${outputFileName}`, () => { - Harness.Baseline.runBaseline("Correct output", outputFileName, () => { + Harness.Baseline.runBaseline(outputFileName, () => { if (initResult) { return JSON.stringify(initResult, undefined, 4); } diff --git a/src/harness/unittests/transpile.ts b/src/harness/unittests/transpile.ts index 547d10b9fbc..2dd9c89a1cb 100644 --- a/src/harness/unittests/transpile.ts +++ b/src/harness/unittests/transpile.ts @@ -62,7 +62,7 @@ namespace ts { }); it("Correct errors for " + justName, () => { - Harness.Baseline.runBaseline("Correct errors", justName.replace(/\.tsx?$/, ".errors.txt"), () => { + Harness.Baseline.runBaseline(justName.replace(/\.tsx?$/, ".errors.txt"), () => { if (transpileResult.diagnostics.length === 0) { /* tslint:disable:no-null-keyword */ return null; @@ -75,7 +75,7 @@ namespace ts { if (canUseOldTranspile) { it("Correct errors (old transpile) for " + justName, () => { - Harness.Baseline.runBaseline("Correct errors", justName.replace(/\.tsx?$/, ".oldTranspile.errors.txt"), () => { + Harness.Baseline.runBaseline(justName.replace(/\.tsx?$/, ".oldTranspile.errors.txt"), () => { if (oldTranspileDiagnostics.length === 0) { /* tslint:disable:no-null-keyword */ return null; @@ -88,7 +88,7 @@ namespace ts { } it("Correct output for " + justName, () => { - Harness.Baseline.runBaseline("Correct output", justName.replace(/\.tsx?$/, ".js"), () => { + Harness.Baseline.runBaseline(justName.replace(/\.tsx?$/, ".js"), () => { if (transpileResult.outputText) { return transpileResult.outputText; } @@ -104,7 +104,7 @@ namespace ts { if (canUseOldTranspile) { it("Correct output (old transpile) for " + justName, () => { - Harness.Baseline.runBaseline("Correct output", justName.replace(/\.tsx?$/, ".oldTranspile.js"), () => { + Harness.Baseline.runBaseline(justName.replace(/\.tsx?$/, ".oldTranspile.js"), () => { return oldTranspileResult; }); }); From 6c60e5b1050c7c1fcd76c016aaac804368dd6d23 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Fri, 19 Aug 2016 14:34:14 -0700 Subject: [PATCH 181/233] Remove needless call to basename --- Jakefile.js | 323 +++++++++++++++++++++++++++------------------------- 1 file changed, 167 insertions(+), 156 deletions(-) diff --git a/Jakefile.js b/Jakefile.js index 441f6aef4f9..4c751945c87 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -27,9 +27,9 @@ var thirdParty = "ThirdPartyNoticeText.txt"; // add node_modules to path so we don't need global modules, prefer the modules by adding them first var nodeModulesPathPrefix = path.resolve("./node_modules/.bin/") + path.delimiter; if (process.env.path !== undefined) { - process.env.path = nodeModulesPathPrefix + process.env.path; + process.env.path = nodeModulesPathPrefix + process.env.path; } else if (process.env.PATH !== undefined) { - process.env.PATH = nodeModulesPathPrefix + process.env.PATH; + process.env.PATH = nodeModulesPathPrefix + process.env.PATH; } function toNs(diff) { @@ -205,11 +205,11 @@ var es2015LibrarySources = [ "es2015.symbol.wellknown.d.ts" ]; -var es2015LibrarySourceMap = es2015LibrarySources.map(function(source) { - return { target: "lib." + source, sources: ["header.d.ts", source] }; +var es2015LibrarySourceMap = es2015LibrarySources.map(function (source) { + return { target: "lib." + source, sources: ["header.d.ts", source] }; }); -var es2016LibrarySource = [ "es2016.array.include.d.ts" ]; +var es2016LibrarySource = ["es2016.array.include.d.ts"]; var es2016LibrarySourceMap = es2016LibrarySource.map(function (source) { return { target: "lib." + source, sources: ["header.d.ts", source] }; @@ -227,21 +227,21 @@ var es2017LibrarySourceMap = es2017LibrarySource.map(function (source) { var hostsLibrarySources = ["dom.generated.d.ts", "webworker.importscripts.d.ts", "scripthost.d.ts"]; var librarySourceMap = [ - // Host library - { target: "lib.dom.d.ts", sources: ["header.d.ts", "dom.generated.d.ts"] }, - { target: "lib.dom.iterable.d.ts", sources: ["header.d.ts", "dom.iterable.d.ts"] }, - { target: "lib.webworker.d.ts", sources: ["header.d.ts", "webworker.generated.d.ts"] }, - { target: "lib.scripthost.d.ts", sources: ["header.d.ts", "scripthost.d.ts"] }, + // Host library + { target: "lib.dom.d.ts", sources: ["header.d.ts", "dom.generated.d.ts"] }, + { target: "lib.dom.iterable.d.ts", sources: ["header.d.ts", "dom.iterable.d.ts"] }, + { target: "lib.webworker.d.ts", sources: ["header.d.ts", "webworker.generated.d.ts"] }, + { target: "lib.scripthost.d.ts", sources: ["header.d.ts", "scripthost.d.ts"] }, - // JavaScript library - { target: "lib.es5.d.ts", sources: ["header.d.ts", "es5.d.ts"] }, - { target: "lib.es2015.d.ts", sources: ["header.d.ts", "es2015.d.ts"] }, - { target: "lib.es2016.d.ts", sources: ["header.d.ts", "es2016.d.ts"] }, - { target: "lib.es2017.d.ts", sources: ["header.d.ts", "es2017.d.ts"] }, + // JavaScript library + { target: "lib.es5.d.ts", sources: ["header.d.ts", "es5.d.ts"] }, + { target: "lib.es2015.d.ts", sources: ["header.d.ts", "es2015.d.ts"] }, + { target: "lib.es2016.d.ts", sources: ["header.d.ts", "es2016.d.ts"] }, + { target: "lib.es2017.d.ts", sources: ["header.d.ts", "es2017.d.ts"] }, - // JavaScript + all host library - { target: "lib.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(hostsLibrarySources) }, - { target: "lib.es6.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(es2015LibrarySources, hostsLibrarySources, "dom.iterable.d.ts") } + // JavaScript + all host library + { target: "lib.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(hostsLibrarySources) }, + { target: "lib.es6.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(es2015LibrarySources, hostsLibrarySources, "dom.iterable.d.ts") } ].concat(es2015LibrarySourceMap, es2016LibrarySourceMap, es2017LibrarySourceMap); var libraryTargets = librarySourceMap.map(function (f) { @@ -257,7 +257,7 @@ function prependFile(prefixFile, destinationFile) { fail(destinationFile + " failed to be created!"); } var temp = "temptemp"; - jake.cpR(prefixFile, temp, {silent: true}); + jake.cpR(prefixFile, temp, { silent: true }); fs.appendFileSync(temp, fs.readFileSync(destinationFile)); fs.renameSync(temp, destinationFile); } @@ -269,11 +269,11 @@ function concatenateFiles(destinationFile, sourceFiles) { if (!fs.existsSync(sourceFiles[0])) { fail(sourceFiles[0] + " does not exist!"); } - jake.cpR(sourceFiles[0], temp, {silent: true}); + jake.cpR(sourceFiles[0], temp, { silent: true }); // append all files in sequence for (var i = 1; i < sourceFiles.length; i++) { if (!fs.existsSync(sourceFiles[i])) { - fail(sourceFiles[i] + " does not exist!"); + fail(sourceFiles[i] + " does not exist!"); } fs.appendFileSync(temp, fs.readFileSync(sourceFiles[i])); } @@ -307,11 +307,11 @@ var builtLocalCompiler = path.join(builtLocalDirectory, compilerFilename); * @param callback: a function to execute after the compilation process ends */ function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, opts, callback) { - file(outFile, prereqs, function() { + file(outFile, prereqs, function () { var startCompileTime = mark(); opts = opts || {}; var compilerPath = useBuiltCompiler ? builtLocalCompiler : LKGCompiler; - var options = "--noImplicitAny --noImplicitThis --noEmitOnError --types " + var options = "--noImplicitAny --noImplicitThis --noEmitOnError --types " if (opts.types) { options += opts.types.join(","); } @@ -341,7 +341,7 @@ function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, opts options += " --module commonjs"; } - if(opts.noResolve) { + if (opts.noResolve) { options += " --noResolve"; } @@ -368,13 +368,13 @@ function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, opts var ex = jake.createExec([cmd]); // Add listeners for output and error - ex.addListener("stdout", function(output) { + ex.addListener("stdout", function (output) { process.stdout.write(output); }); - ex.addListener("stderr", function(error) { + ex.addListener("stderr", function (error) { process.stderr.write(error); }); - ex.addListener("cmdEnd", function() { + ex.addListener("cmdEnd", function () { if (!useDebugMode && prefixes && fs.existsSync(outFile)) { for (var i in prefixes) { prependFile(prefixes[i], outFile); @@ -388,13 +388,13 @@ function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, opts measure(startCompileTime); complete(); }); - ex.addListener("error", function() { + ex.addListener("error", function () { fs.unlinkSync(outFile); fail("Compilation of " + outFile + " unsuccessful"); measure(startCompileTime); }); ex.run(); - }, {async: true}); + }, { async: true }); } // Prerequisite task for built directory and library typings @@ -407,7 +407,7 @@ for (var i in libraryTargets) { var sources = [copyright].concat(entry.sources.map(function (s) { return path.join(libraryDirectory, s); })); - file(target, [builtLocalDirectory].concat(sources), function() { + file(target, [builtLocalDirectory].concat(sources), function () { concatenateFiles(target, sources); }); })(i); @@ -430,30 +430,30 @@ file(processDiagnosticMessagesTs); // processDiagnosticMessages script compileFile(processDiagnosticMessagesJs, - [processDiagnosticMessagesTs], - [processDiagnosticMessagesTs], - [], + [processDiagnosticMessagesTs], + [processDiagnosticMessagesTs], + [], /*useBuiltCompiler*/ false); // The generated diagnostics map; built for the compiler and for the 'generate-diagnostics' task file(diagnosticInfoMapTs, [processDiagnosticMessagesJs, diagnosticMessagesJson], function () { - var cmd = host + " " + processDiagnosticMessagesJs + " " + diagnosticMessagesJson; + var cmd = host + " " + processDiagnosticMessagesJs + " " + diagnosticMessagesJson; console.log(cmd); var ex = jake.createExec([cmd]); // Add listeners for output and error - ex.addListener("stdout", function(output) { + ex.addListener("stdout", function (output) { process.stdout.write(output); }); - ex.addListener("stderr", function(error) { + ex.addListener("stderr", function (error) { process.stderr.write(error); }); - ex.addListener("cmdEnd", function() { + ex.addListener("cmdEnd", function () { complete(); }); ex.run(); -}, {async: true}); +}, { async: true }); -file(builtGeneratedDiagnosticMessagesJSON,[generatedDiagnosticMessagesJSON], function() { +file(builtGeneratedDiagnosticMessagesJSON, [generatedDiagnosticMessagesJSON], function () { if (fs.existsSync(builtLocalDirectory)) { jake.cpR(generatedDiagnosticMessagesJSON, builtGeneratedDiagnosticMessagesJSON); } @@ -471,17 +471,17 @@ var programTs = path.join(compilerDirectory, "program.ts"); file(configureNightlyTs); compileFile(/*outfile*/configureNightlyJs, - /*sources*/ [configureNightlyTs], - /*prereqs*/ [configureNightlyTs], - /*prefixes*/ [], + /*sources*/[configureNightlyTs], + /*prereqs*/[configureNightlyTs], + /*prefixes*/[], /*useBuiltCompiler*/ false, - { noOutFile: false, generateDeclarations: false, keepComments: false, noResolve: false, stripInternal: false }); + { noOutFile: false, generateDeclarations: false, keepComments: false, noResolve: false, stripInternal: false }); -task("setDebugMode", function() { +task("setDebugMode", function () { useDebugMode = true; }); -task("configure-nightly", [configureNightlyJs], function() { +task("configure-nightly", [configureNightlyJs], function () { var cmd = host + " " + configureNightlyJs + " " + packageJson + " " + programTs; console.log(cmd); exec(cmd); @@ -522,63 +522,65 @@ var nodePackageFile = path.join(builtLocalDirectory, "typescript.js"); var nodeDefinitionsFile = path.join(builtLocalDirectory, "typescript.d.ts"); var nodeStandaloneDefinitionsFile = path.join(builtLocalDirectory, "typescript_standalone.d.ts"); -compileFile(servicesFile, servicesSources,[builtLocalDirectory, copyright].concat(servicesSources), - /*prefixes*/ [copyright], +compileFile(servicesFile, servicesSources, [builtLocalDirectory, copyright].concat(servicesSources), + /*prefixes*/[copyright], /*useBuiltCompiler*/ true, - /*opts*/ { noOutFile: false, - generateDeclarations: true, - preserveConstEnums: true, - keepComments: true, - noResolve: false, - stripInternal: true - }, + /*opts*/ { + noOutFile: false, + generateDeclarations: true, + preserveConstEnums: true, + keepComments: true, + noResolve: false, + stripInternal: true + }, /*callback*/ function () { - jake.cpR(servicesFile, nodePackageFile, {silent: true}); + jake.cpR(servicesFile, nodePackageFile, { silent: true }); - prependFile(copyright, standaloneDefinitionsFile); + prependFile(copyright, standaloneDefinitionsFile); - // Stanalone/web definition file using global 'ts' namespace - jake.cpR(standaloneDefinitionsFile, nodeDefinitionsFile, {silent: true}); - var definitionFileContents = fs.readFileSync(nodeDefinitionsFile).toString(); - definitionFileContents = definitionFileContents.replace(/^(\s*)(export )?const enum (\S+) {(\s*)$/gm, '$1$2enum $3 {$4'); - fs.writeFileSync(standaloneDefinitionsFile, definitionFileContents); + // Stanalone/web definition file using global 'ts' namespace + jake.cpR(standaloneDefinitionsFile, nodeDefinitionsFile, { silent: true }); + var definitionFileContents = fs.readFileSync(nodeDefinitionsFile).toString(); + definitionFileContents = definitionFileContents.replace(/^(\s*)(export )?const enum (\S+) {(\s*)$/gm, '$1$2enum $3 {$4'); + fs.writeFileSync(standaloneDefinitionsFile, definitionFileContents); - // Official node package definition file, pointed to by 'typings' in package.json - // Created by appending 'export = ts;' at the end of the standalone file to turn it into an external module - var nodeDefinitionsFileContents = definitionFileContents + "\r\nexport = ts;"; - fs.writeFileSync(nodeDefinitionsFile, nodeDefinitionsFileContents); + // Official node package definition file, pointed to by 'typings' in package.json + // Created by appending 'export = ts;' at the end of the standalone file to turn it into an external module + var nodeDefinitionsFileContents = definitionFileContents + "\r\nexport = ts;"; + fs.writeFileSync(nodeDefinitionsFile, nodeDefinitionsFileContents); - // Node package definition file to be distributed without the package. Created by replacing - // 'ts' namespace with '"typescript"' as a module. - var nodeStandaloneDefinitionsFileContents = definitionFileContents.replace(/declare (namespace|module) ts/g, 'declare module "typescript"'); - fs.writeFileSync(nodeStandaloneDefinitionsFile, nodeStandaloneDefinitionsFileContents); - }); + // Node package definition file to be distributed without the package. Created by replacing + // 'ts' namespace with '"typescript"' as a module. + var nodeStandaloneDefinitionsFileContents = definitionFileContents.replace(/declare (namespace|module) ts/g, 'declare module "typescript"'); + fs.writeFileSync(nodeStandaloneDefinitionsFile, nodeStandaloneDefinitionsFileContents); + }); compileFile( servicesFileInBrowserTest, servicesSources, [builtLocalDirectory, copyright].concat(servicesSources), - /*prefixes*/ [copyright], + /*prefixes*/[copyright], /*useBuiltCompiler*/ true, - { noOutFile: false, - generateDeclarations: true, - preserveConstEnums: true, - keepComments: true, - noResolve: false, - stripInternal: true, - noMapRoot: true, - inlineSourceMap: true - }); + { + noOutFile: false, + generateDeclarations: true, + preserveConstEnums: true, + keepComments: true, + noResolve: false, + stripInternal: true, + noMapRoot: true, + inlineSourceMap: true + }); var serverFile = path.join(builtLocalDirectory, "tsserver.js"); -compileFile(serverFile, serverSources,[builtLocalDirectory, copyright].concat(serverSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true, { types: ["node"] }); +compileFile(serverFile, serverSources, [builtLocalDirectory, copyright].concat(serverSources), /*prefixes*/[copyright], /*useBuiltCompiler*/ true, { types: ["node"] }); var tsserverLibraryFile = path.join(builtLocalDirectory, "tsserverlibrary.js"); var tsserverLibraryDefinitionFile = path.join(builtLocalDirectory, "tsserverlibrary.d.ts"); compileFile( tsserverLibraryFile, languageServiceLibrarySources, [builtLocalDirectory, copyright, builtLocalCompiler].concat(languageServiceLibrarySources).concat(libraryTargets), - /*prefixes*/ [copyright], + /*prefixes*/[copyright], /*useBuiltCompiler*/ true, { noOutFile: false, generateDeclarations: true }); @@ -587,12 +589,12 @@ desc("Builds language service server library"); task("lssl", [tsserverLibraryFile, tsserverLibraryDefinitionFile]); desc("Emit the start of the build fold"); -task("build-fold-start", [] , function() { +task("build-fold-start", [], function () { if (fold.isTravis()) console.log(fold.start("build")); }); desc("Emit the end of the build fold"); -task("build-fold-end", [] , function() { +task("build-fold-end", [], function () { if (fold.isTravis()) console.log(fold.end("build")); }); @@ -606,7 +608,7 @@ task("tsc", ["generate-diagnostics", "lib", tscFile]); // Local target to build the compiler and services desc("Sets release mode flag"); -task("release", function() { +task("release", function () { useDebugMode = false; }); @@ -616,7 +618,7 @@ task("default", ["local"]); // Cleans the built directory desc("Cleans the compiler output, declare files, and tests"); -task("clean", function() { +task("clean", function () { jake.rmRf(builtDirectory); }); @@ -630,9 +632,9 @@ file(word2mdTs); // word2md script compileFile(word2mdJs, - [word2mdTs], - [word2mdTs], - [], + [word2mdTs], + [word2mdTs], + [], /*useBuiltCompiler*/ false); // The generated spec.md; built for the 'generate-spec' task @@ -644,7 +646,7 @@ file(specMd, [word2mdJs, specWord], function () { child_process.exec(cmd, function () { complete(); }); -}, {async: true}); +}, { async: true }); desc("Generates a Markdown version of the Language Specification"); @@ -653,14 +655,14 @@ task("generate-spec", [specMd]); // Makes a new LKG. This target does not build anything, but errors if not all the outputs are present in the built/local directory desc("Makes a new LKG out of the built js files"); -task("LKG", ["clean", "release", "local"].concat(libraryTargets), function() { +task("LKG", ["clean", "release", "local"].concat(libraryTargets), function () { var expectedFiles = [tscFile, servicesFile, serverFile, nodePackageFile, nodeDefinitionsFile, standaloneDefinitionsFile, tsserverLibraryFile, tsserverLibraryDefinitionFile].concat(libraryTargets); var missingFiles = expectedFiles.filter(function (f) { return !fs.existsSync(f); }); if (missingFiles.length > 0) { fail("Cannot replace the LKG unless all built targets are present in directory " + builtLocalDirectory + - ". The following files are missing:\n" + missingFiles.join("\n")); + ". The following files are missing:\n" + missingFiles.join("\n")); } // Copy all the targets into the LKG directory jake.mkdirP(LKGDirectory); @@ -681,8 +683,8 @@ var run = path.join(builtLocalDirectory, "run.js"); compileFile( /*outFile*/ run, /*source*/ harnessSources, - /*prereqs*/ [builtLocalDirectory, tscFile].concat(libraryTargets).concat(servicesSources).concat(harnessSources), - /*prefixes*/ [], + /*prereqs*/[builtLocalDirectory, tscFile].concat(libraryTargets).concat(servicesSources).concat(harnessSources), + /*prefixes*/[], /*useBuiltCompiler:*/ true, /*opts*/ { inlineSourceMap: true, types: ["node", "mocha", "chai"] }); @@ -701,22 +703,22 @@ desc("Builds the test infrastructure using the built compiler"); task("tests", ["local", run].concat(libraryTargets)); function exec(cmd, completeHandler, errorHandler) { - var ex = jake.createExec([cmd], {windowsVerbatimArguments: true}); + var ex = jake.createExec([cmd], { windowsVerbatimArguments: true }); // Add listeners for output and error - ex.addListener("stdout", function(output) { + ex.addListener("stdout", function (output) { process.stdout.write(output); }); - ex.addListener("stderr", function(error) { + ex.addListener("stderr", function (error) { process.stderr.write(error); }); - ex.addListener("cmdEnd", function() { + ex.addListener("cmdEnd", function () { if (completeHandler) { completeHandler(); } complete(); }); - ex.addListener("error", function(e, status) { - if(errorHandler) { + ex.addListener("error", function (e, status) { + if (errorHandler) { errorHandler(e, status); } else { fail("Process exited with code " + status); @@ -760,7 +762,7 @@ function runConsoleTests(defaultReporter, runInParallel) { tests = process.env.test || process.env.tests || process.env.t; var light = process.env.light || false; var testConfigFile = 'test.config'; - if(fs.existsSync(testConfigFile)) { + if (fs.existsSync(testConfigFile)) { fs.unlinkSync(testConfigFile); } var workerCount, taskConfigsFolder; @@ -793,7 +795,7 @@ function runConsoleTests(defaultReporter, runInParallel) { // timeout normally isn't necessary but Travis-CI has been timing out on compiler baselines occasionally // default timeout is 2sec which really should be enough, but maybe we just need a small amount longer - if(!runInParallel) { + if (!runInParallel) { var startTime = mark(); tests = tests ? ' -g "' + tests + '"' : ''; var cmd = "mocha" + (debug ? " --debug-brk" : "") + " -R " + reporter + tests + colors + bail + ' -t ' + testTimeout + ' ' + run; @@ -806,7 +808,7 @@ function runConsoleTests(defaultReporter, runInParallel) { measure(startTime); runLinter(); finish(); - }, function(e, status) { + }, function (e, status) { process.env.NODE_ENV = savedNodeEnv; measure(startTime); finish(status); @@ -860,14 +862,14 @@ function runConsoleTests(defaultReporter, runInParallel) { var testTimeout = 20000; desc("Runs all the tests in parallel using the built run.js file. Optional arguments are: t[ests]=category1|category2|... d[ebug]=true."); -task("runtests-parallel", ["build-rules", "tests", builtLocalDirectory], function() { +task("runtests-parallel", ["build-rules", "tests", builtLocalDirectory], function () { runConsoleTests('min', /*runInParallel*/ true); -}, {async: true}); +}, { async: true }); desc("Runs the tests using the built run.js file. Optional arguments are: t[ests]=regex r[eporter]=[list|spec|json|] d[ebug]=true color[s]=false lint=true bail=false."); -task("runtests", ["build-rules", "tests", builtLocalDirectory], function() { +task("runtests", ["build-rules", "tests", builtLocalDirectory], function () { runConsoleTests('mocha-fivemat-progress-reporter', /*runInParallel*/ false); -}, {async: true}); +}, { async: true }); desc("Generates code coverage data via instanbul"); task("generate-code-coverage", ["tests", builtLocalDirectory], function () { @@ -882,23 +884,23 @@ var nodeServerInFile = "tests/webTestServer.ts"; compileFile(nodeServerOutFile, [nodeServerInFile], [builtLocalDirectory, tscFile], [], /*useBuiltCompiler:*/ true, { noOutFile: true }); desc("Runs browserify on run.js to produce a file suitable for running tests in the browser"); -task("browserify", ["tests", builtLocalDirectory, nodeServerOutFile], function() { +task("browserify", ["tests", builtLocalDirectory, nodeServerOutFile], function () { var cmd = 'browserify built/local/run.js -d -o built/local/bundle.js'; exec(cmd); -}, {async: true}); +}, { async: true }); desc("Runs the tests using the built run.js file like 'jake runtests'. Syntax is jake runtests-browser. Additional optional parameters tests=[regex], browser=[chrome|IE]"); -task("runtests-browser", ["tests", "browserify", builtLocalDirectory, servicesFileInBrowserTest], function() { +task("runtests-browser", ["tests", "browserify", builtLocalDirectory, servicesFileInBrowserTest], function () { cleanTestDirs(); host = "node"; browser = process.env.browser || process.env.b || "IE"; tests = process.env.test || process.env.tests || process.env.t; var light = process.env.light || false; var testConfigFile = 'test.config'; - if(fs.existsSync(testConfigFile)) { + if (fs.existsSync(testConfigFile)) { fs.unlinkSync(testConfigFile); } - if(tests || light) { + if (tests || light) { writeTestConfigFile(tests, light); } @@ -906,7 +908,7 @@ task("runtests-browser", ["tests", "browserify", builtLocalDirectory, servicesFi var cmd = host + " tests/webTestServer.js " + browser + " " + JSON.stringify(tests); console.log(cmd); exec(cmd); -}, {async: true}); +}, { async: true }); function getDiffTool() { var program = process.env['DIFF']; @@ -919,17 +921,17 @@ function getDiffTool() { // Baseline Diff desc("Diffs the compiler baselines using the diff tool specified by the 'DIFF' environment variable"); task('diff', function () { - var cmd = '"' + getDiffTool() + '" ' + refBaseline + ' ' + localBaseline; + var cmd = '"' + getDiffTool() + '" ' + refBaseline + ' ' + localBaseline; console.log(cmd); exec(cmd); -}, {async: true}); +}, { async: true }); desc("Diffs the RWC baselines using the diff tool specified by the 'DIFF' environment variable"); task('diff-rwc', function () { - var cmd = '"' + getDiffTool() + '" ' + refRwcBaseline + ' ' + localRwcBaseline; + var cmd = '"' + getDiffTool() + '" ' + refRwcBaseline + ' ' + localRwcBaseline; console.log(cmd); exec(cmd); -}, {async: true}); +}, { async: true }); desc("Builds the test sources and automation in debug mode"); task("tests-debug", ["setDebugMode", "tests"]); @@ -937,30 +939,39 @@ task("tests-debug", ["setDebugMode", "tests"]); // Makes the test results the new baseline desc("Makes the most recent test results the new baseline, overwriting the old baseline"); -task("baseline-accept", function(hardOrSoft) { - var files = jake.readdirR(localBaseline); - var deleteEnding = '.delete'; - for (var i in files) { - if (files[i].substr(files[i].length - deleteEnding.length) === deleteEnding) { - var filename = path.basename(files[i]); - filename = filename.substr(0, filename.length - deleteEnding.length); - fs.unlink(path.join(refBaseline, filename)); - } else { - jake.cpR(files[i], refBaseline); - } - } +task("baseline-accept", function () { + acceptBaseline(""); }); +function acceptBaseline(containerFolder) { + var sourceFolder = path.join(localBaseline, containerFolder); + var targetFolder = path.join(refBaseline, containerFolder); + console.log('Accept baselines from ' + sourceFolder + ' to ' + targetFolder); + var files = fs.readdirSync(sourceFolder); + var deleteEnding = '.delete'; + for (var i in files) { + var filename = files[i]; + if (filename.substr(filename.length - deleteEnding.length) === deleteEnding) { + filename = filename.substr(0, filename.length - deleteEnding.length); + fs.unlinkSync(path.join(targetFolder, filename)); + } else { + var target = path.join(targetFolder, filename); + if (fs.existsSync(target)) { + fs.unlinkSync(target); + } + fs.renameSync(path.join(sourceFolder, filename), target); + } + } +} + desc("Makes the most recent rwc test results the new baseline, overwriting the old baseline"); -task("baseline-accept-rwc", function() { - jake.rmRf(refRwcBaseline); - fs.renameSync(localRwcBaseline, refRwcBaseline); +task("baseline-accept-rwc", function () { + acceptBaseline("rwc"); }); desc("Makes the most recent test262 test results the new baseline, overwriting the old baseline"); -task("baseline-accept-test262", function() { - jake.rmRf(refTest262Baseline); - fs.renameSync(localTest262Baseline, refTest262Baseline); +task("baseline-accept-test262", function () { + acceptBaseline("test262"); }); @@ -970,8 +981,8 @@ var webhostJsPath = "tests/webhost/webtsc.js"; compileFile(webhostJsPath, [webhostPath], [tscFile, webhostPath].concat(libraryTargets), [], /*useBuiltCompiler*/true); desc("Builds the tsc web host"); -task("webhost", [webhostJsPath], function() { - jake.cpR(path.join(builtLocalDirectory, "lib.d.ts"), "tests/webhost/", {silent: true}); +task("webhost", [webhostJsPath], function () { + jake.cpR(path.join(builtLocalDirectory, "lib.d.ts"), "tests/webhost/", { silent: true }); }); // Perf compiler @@ -984,38 +995,38 @@ task("perftsc", [perftscJsPath]); // Instrumented compiler var loggedIOpath = harnessDirectory + 'loggedIO.ts'; var loggedIOJsPath = builtLocalDirectory + 'loggedIO.js'; -file(loggedIOJsPath, [builtLocalDirectory, loggedIOpath], function() { +file(loggedIOJsPath, [builtLocalDirectory, loggedIOpath], function () { var temp = builtLocalDirectory + 'temp'; jake.mkdirP(temp); var options = "--outdir " + temp + ' ' + loggedIOpath; var cmd = host + " " + LKGDirectory + compilerFilename + " " + options + " "; console.log(cmd + "\n"); var ex = jake.createExec([cmd]); - ex.addListener("cmdEnd", function() { + ex.addListener("cmdEnd", function () { fs.renameSync(temp + '/harness/loggedIO.js', loggedIOJsPath); jake.rmRf(temp); complete(); }); ex.run(); -}, {async: true}); +}, { async: true }); var instrumenterPath = harnessDirectory + 'instrumenter.ts'; var instrumenterJsPath = builtLocalDirectory + 'instrumenter.js'; compileFile(instrumenterJsPath, [instrumenterPath], [tscFile, instrumenterPath].concat(libraryTargets), [], /*useBuiltCompiler*/ true); desc("Builds an instrumented tsc.js"); -task('tsc-instrumented', [loggedIOJsPath, instrumenterJsPath, tscFile], function() { +task('tsc-instrumented', [loggedIOJsPath, instrumenterJsPath, tscFile], function () { var cmd = host + ' ' + instrumenterJsPath + ' record iocapture ' + builtLocalDirectory + compilerFilename; console.log(cmd); var ex = jake.createExec([cmd]); - ex.addListener("cmdEnd", function() { + ex.addListener("cmdEnd", function () { complete(); }); ex.run(); }, { async: true }); desc("Updates the sublime plugin's tsserver"); -task("update-sublime", ["local", serverFile], function() { +task("update-sublime", ["local", serverFile], function () { jake.cpR(serverFile, "../TypeScript-Sublime-Plugin/tsserver/"); jake.cpR(serverFile + ".map", "../TypeScript-Sublime-Plugin/tsserver/"); }); @@ -1031,33 +1042,33 @@ var tslintRules = [ "objectLiteralSurroundingSpaceRule", "noTypeAssertionWhitespaceRule" ]; -var tslintRulesFiles = tslintRules.map(function(p) { +var tslintRulesFiles = tslintRules.map(function (p) { return path.join(tslintRuleDir, p + ".ts"); }); -var tslintRulesOutFiles = tslintRules.map(function(p) { +var tslintRulesOutFiles = tslintRules.map(function (p) { return path.join(builtLocalDirectory, "tslint", p + ".js"); }); desc("Compiles tslint rules to js"); task("build-rules", ["build-rules-start"].concat(tslintRulesOutFiles).concat(["build-rules-end"])); -tslintRulesFiles.forEach(function(ruleFile, i) { +tslintRulesFiles.forEach(function (ruleFile, i) { compileFile(tslintRulesOutFiles[i], [ruleFile], [ruleFile], [], /*useBuiltCompiler*/ false, - { noOutFile: true, generateDeclarations: false, outDir: path.join(builtLocalDirectory, "tslint")}); + { noOutFile: true, generateDeclarations: false, outDir: path.join(builtLocalDirectory, "tslint") }); }); desc("Emit the start of the build-rules fold"); -task("build-rules-start", [] , function() { +task("build-rules-start", [], function () { if (fold.isTravis()) console.log(fold.start("build-rules")); }); desc("Emit the end of the build-rules fold"); -task("build-rules-end", [] , function() { +task("build-rules-end", [], function () { if (fold.isTravis()) console.log(fold.end("build-rules")); }); var lintTargets = compilerSources .concat(harnessSources) // Other harness sources - .concat(["instrumenter.ts"].map(function(f) { return path.join(harnessDirectory, f) })) + .concat(["instrumenter.ts"].map(function (f) { return path.join(harnessDirectory, f) })) .concat(serverCoreSources) .concat(tslintRulesFiles) .concat(servicesSources) @@ -1068,10 +1079,10 @@ function sendNextFile(files, child, callback, failures) { var file = files.pop(); if (file) { console.log("Linting '" + file + "'."); - child.send({kind: "file", name: file}); + child.send({ kind: "file", name: file }); } else { - child.send({kind: "close"}); + child.send({ kind: "close" }); callback(failures); } } @@ -1079,7 +1090,7 @@ function sendNextFile(files, child, callback, failures) { function spawnLintWorker(files, callback) { var child = child_process.fork("./scripts/parallel-lint"); var failures = 0; - child.on("message", function(data) { + child.on("message", function (data) { switch (data.kind) { case "result": if (data.failures > 0) { @@ -1099,7 +1110,7 @@ function spawnLintWorker(files, callback) { } desc("Runs tslint on the compiler sources. Optional arguments are: f[iles]=regex"); -task("lint", ["build-rules"], function() { +task("lint", ["build-rules"], function () { if (fold.isTravis()) console.log(fold.start("lint")); var startTime = mark(); var failed = 0; @@ -1114,7 +1125,7 @@ task("lint", ["build-rules"], function() { var workerCount = (process.env.workerCount && +process.env.workerCount) || os.cpus().length; - var names = Object.keys(done).sort(function(namea, nameb) { + var names = Object.keys(done).sort(function (namea, nameb) { return done[namea] - done[nameb]; }); @@ -1138,4 +1149,4 @@ task("lint", ["build-rules"], function() { } } } -}, {async: true}); +}, { async: true }); From 8ad2744e9a7d0d7eb30286f0b6718b7b5f6cc4f9 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 19 Aug 2016 15:44:14 -0700 Subject: [PATCH 182/233] Refactor baseliners out of compiler runner (#10440) --- src/harness/compilerRunner.ts | 222 ++-------------------------------- src/harness/harness.ts | 214 ++++++++++++++++++++++++++++++++ src/harness/rwcRunner.ts | 7 +- 3 files changed, 227 insertions(+), 216 deletions(-) diff --git a/src/harness/compilerRunner.ts b/src/harness/compilerRunner.ts index 88e81ffcf34..a64435b7ddc 100644 --- a/src/harness/compilerRunner.ts +++ b/src/harness/compilerRunner.ts @@ -1,8 +1,6 @@ /// /// /// -// In harness baselines, null is different than undefined. See `generateActual` in `harness.ts`. -/* tslint:disable:no-null-keyword */ const enum CompilerTestType { Conformance, @@ -136,21 +134,10 @@ class CompilerBaselineRunner extends RunnerBase { otherFiles = undefined; }); - function getByteOrderMarkText(file: Harness.Compiler.GeneratedFile): string { - return file.writeByteOrderMark ? "\u00EF\u00BB\u00BF" : ""; - } - - function getErrorBaseline(toBeCompiled: Harness.Compiler.TestFile[], otherFiles: Harness.Compiler.TestFile[], result: Harness.Compiler.CompilerResult) { - return Harness.Compiler.getErrorBaseline(toBeCompiled.concat(otherFiles), result.errors); - } - // check errors it("Correct errors for " + fileName, () => { if (this.errors) { - Harness.Baseline.runBaseline(justName.replace(/\.tsx?$/, ".errors.txt"), (): string => { - if (result.errors.length === 0) return null; - return getErrorBaseline(toBeCompiled, otherFiles, result); - }); + Harness.Compiler.doErrorBaseline(justName, toBeCompiled.concat(otherFiles), result.errors); } }); @@ -168,8 +155,10 @@ class CompilerBaselineRunner extends RunnerBase { Harness.Baseline.runBaseline(justName.replace(/\.tsx?$/, ".sourcemap.txt"), () => { const record = result.getSourceMapRecord(); if (options.noEmitOnError && result.errors.length !== 0 && record === undefined) { - // Because of the noEmitOnError option no files are created. We need to return null because baselining isn"t required. + // Because of the noEmitOnError option no files are created. We need to return null because baselining isn't required. + /* tslint:disable:no-null-keyword */ return null; + /* tslint:enable:no-null-keyword */ } return record; }); @@ -178,87 +167,12 @@ class CompilerBaselineRunner extends RunnerBase { it("Correct JS output for " + fileName, () => { if (hasNonDtsFiles && this.emit) { - if (!options.noEmit && result.files.length === 0 && result.errors.length === 0) { - throw new Error("Expected at least one js file to be emitted or at least one error to be created."); - } - - // check js output - Harness.Baseline.runBaseline(justName.replace(/\.tsx?/, ".js"), () => { - let tsCode = ""; - const tsSources = otherFiles.concat(toBeCompiled); - if (tsSources.length > 1) { - tsCode += "//// [" + fileName + "] ////\r\n\r\n"; - } - for (let i = 0; i < tsSources.length; i++) { - tsCode += "//// [" + Harness.Path.getFileName(tsSources[i].unitName) + "]\r\n"; - tsCode += tsSources[i].content + (i < (tsSources.length - 1) ? "\r\n" : ""); - } - - let jsCode = ""; - for (let i = 0; i < result.files.length; i++) { - jsCode += "//// [" + Harness.Path.getFileName(result.files[i].fileName) + "]\r\n"; - jsCode += getByteOrderMarkText(result.files[i]); - jsCode += result.files[i].code; - } - - if (result.declFilesCode.length > 0) { - jsCode += "\r\n\r\n"; - for (let i = 0; i < result.declFilesCode.length; i++) { - jsCode += "//// [" + Harness.Path.getFileName(result.declFilesCode[i].fileName) + "]\r\n"; - jsCode += getByteOrderMarkText(result.declFilesCode[i]); - jsCode += result.declFilesCode[i].code; - } - } - - const declFileCompilationResult = - Harness.Compiler.compileDeclarationFiles( - toBeCompiled, otherFiles, result, harnessSettings, options, /*currentDirectory*/ undefined); - - if (declFileCompilationResult && declFileCompilationResult.declResult.errors.length) { - jsCode += "\r\n\r\n//// [DtsFileErrors]\r\n"; - jsCode += "\r\n\r\n"; - jsCode += getErrorBaseline(declFileCompilationResult.declInputFiles, declFileCompilationResult.declOtherFiles, declFileCompilationResult.declResult); - } - - if (jsCode.length > 0) { - return tsCode + "\r\n\r\n" + jsCode; - } - else { - return null; - } - }); + Harness.Compiler.doJsEmitBaseline(justName, fileName, options, result, toBeCompiled, otherFiles, harnessSettings); } }); it("Correct Sourcemap output for " + fileName, () => { - if (options.inlineSourceMap) { - if (result.sourceMaps.length > 0) { - throw new Error("No sourcemap files should be generated if inlineSourceMaps was set."); - } - return null; - } - else if (options.sourceMap) { - if (result.sourceMaps.length !== result.files.length) { - throw new Error("Number of sourcemap files should be same as js files."); - } - - Harness.Baseline.runBaseline(justName.replace(/\.tsx?/, ".js.map"), () => { - if (options.noEmitOnError && result.errors.length !== 0 && result.sourceMaps.length === 0) { - // We need to return null here or the runBaseLine will actually create a empty file. - // Baselining isn't required here because there is no output. - return null; - } - - let sourceMapCode = ""; - for (let i = 0; i < result.sourceMaps.length; i++) { - sourceMapCode += "//// [" + Harness.Path.getFileName(result.sourceMaps[i].fileName) + "]\r\n"; - sourceMapCode += getByteOrderMarkText(result.sourceMaps[i]); - sourceMapCode += result.sourceMaps[i].code; - } - - return sourceMapCode; - }); - } + Harness.Compiler.doSourcemapBaseline(justName, options, result); }); it("Correct type/symbol baselines for " + fileName, () => { @@ -266,129 +180,7 @@ class CompilerBaselineRunner extends RunnerBase { return; } - // NEWTODO: Type baselines - if (result.errors.length !== 0) { - return; - } - - // The full walker simulates the types that you would get from doing a full - // compile. The pull walker simulates the types you get when you just do - // a type query for a random node (like how the LS would do it). Most of the - // time, these will be the same. However, occasionally, they can be different. - // Specifically, when the compiler internally depends on symbol IDs to order - // things, then we may see different results because symbols can be created in a - // different order with 'pull' operations, and thus can produce slightly differing - // output. - // - // For example, with a full type check, we may see a type displayed as: number | string - // But with a pull type check, we may see it as: string | number - // - // These types are equivalent, but depend on what order the compiler observed - // certain parts of the program. - - const program = result.program; - const allFiles = toBeCompiled.concat(otherFiles).filter(file => !!program.getSourceFile(file.unitName)); - - const fullWalker = new TypeWriterWalker(program, /*fullTypeCheck*/ true); - - const fullResults = ts.createMap(); - const pullResults = ts.createMap(); - - for (const sourceFile of allFiles) { - fullResults[sourceFile.unitName] = fullWalker.getTypeAndSymbols(sourceFile.unitName); - pullResults[sourceFile.unitName] = fullWalker.getTypeAndSymbols(sourceFile.unitName); - } - - // Produce baselines. The first gives the types for all expressions. - // The second gives symbols for all identifiers. - let e1: Error, e2: Error; - try { - checkBaseLines(/*isSymbolBaseLine*/ false); - } - catch (e) { - e1 = e; - } - - try { - checkBaseLines(/*isSymbolBaseLine*/ true); - } - catch (e) { - e2 = e; - } - - if (e1 || e2) { - throw e1 || e2; - } - - return; - - function checkBaseLines(isSymbolBaseLine: boolean) { - const fullBaseLine = generateBaseLine(fullResults, isSymbolBaseLine); - const pullBaseLine = generateBaseLine(pullResults, isSymbolBaseLine); - - const fullExtension = isSymbolBaseLine ? ".symbols" : ".types"; - const pullExtension = isSymbolBaseLine ? ".symbols.pull" : ".types.pull"; - - if (fullBaseLine !== pullBaseLine) { - Harness.Baseline.runBaseline(justName.replace(/\.tsx?/, fullExtension), () => fullBaseLine); - Harness.Baseline.runBaseline(justName.replace(/\.tsx?/, pullExtension), () => pullBaseLine); - } - else { - Harness.Baseline.runBaseline(justName.replace(/\.tsx?/, fullExtension), () => fullBaseLine); - } - } - - function generateBaseLine(typeWriterResults: ts.Map, isSymbolBaseline: boolean): string { - const typeLines: string[] = []; - const typeMap: { [fileName: string]: { [lineNum: number]: string[]; } } = {}; - - allFiles.forEach(file => { - const codeLines = file.content.split("\n"); - typeWriterResults[file.unitName].forEach(result => { - if (isSymbolBaseline && !result.symbol) { - return; - } - - const typeOrSymbolString = isSymbolBaseline ? result.symbol : result.type; - const formattedLine = result.sourceText.replace(/\r?\n/g, "") + " : " + typeOrSymbolString; - if (!typeMap[file.unitName]) { - typeMap[file.unitName] = {}; - } - - let typeInfo = [formattedLine]; - const existingTypeInfo = typeMap[file.unitName][result.line]; - if (existingTypeInfo) { - typeInfo = existingTypeInfo.concat(typeInfo); - } - typeMap[file.unitName][result.line] = typeInfo; - }); - - typeLines.push("=== " + file.unitName + " ===\r\n"); - for (let i = 0; i < codeLines.length; i++) { - const currentCodeLine = codeLines[i]; - typeLines.push(currentCodeLine + "\r\n"); - if (typeMap[file.unitName]) { - const typeInfo = typeMap[file.unitName][i]; - if (typeInfo) { - typeInfo.forEach(ty => { - typeLines.push(">" + ty + "\r\n"); - }); - if (i + 1 < codeLines.length && (codeLines[i + 1].match(/^\s*[{|}]\s*$/) || codeLines[i + 1].trim() === "")) { - } - else { - typeLines.push("\r\n"); - } - } - } - else { - typeLines.push("No type information for this code."); - } - } - }); - - return typeLines.join(""); - } - + Harness.Compiler.doTypeAndSymbolBaseline(justName, result, toBeCompiled.concat(otherFiles).filter(file => !!result.program.getSourceFile(file.unitName))); }); }); } diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 3375b8e47e7..14cb3147c32 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1328,6 +1328,220 @@ namespace Harness { Harness.IO.newLine() + Harness.IO.newLine() + outputLines.join("\r\n"); } + export function doErrorBaseline(baselinePath: string, inputFiles: TestFile[], errors: ts.Diagnostic[]) { + Harness.Baseline.runBaseline(baselinePath.replace(/\.tsx?$/, ".errors.txt"), (): string => { + if (errors.length === 0) { + /* tslint:disable:no-null-keyword */ + return null; + /* tslint:enable:no-null-keyword */ + } + return getErrorBaseline(inputFiles, errors); + }); + } + + export function doTypeAndSymbolBaseline(baselinePath: string, result: CompilerResult, allFiles: {unitName: string, content: string}[]) { + if (result.errors.length !== 0) { + return; + } + // The full walker simulates the types that you would get from doing a full + // compile. The pull walker simulates the types you get when you just do + // a type query for a random node (like how the LS would do it). Most of the + // time, these will be the same. However, occasionally, they can be different. + // Specifically, when the compiler internally depends on symbol IDs to order + // things, then we may see different results because symbols can be created in a + // different order with 'pull' operations, and thus can produce slightly differing + // output. + // + // For example, with a full type check, we may see a type displayed as: number | string + // But with a pull type check, we may see it as: string | number + // + // These types are equivalent, but depend on what order the compiler observed + // certain parts of the program. + + const program = result.program; + + const fullWalker = new TypeWriterWalker(program, /*fullTypeCheck*/ true); + + const fullResults = ts.createMap(); + + for (const sourceFile of allFiles) { + fullResults[sourceFile.unitName] = fullWalker.getTypeAndSymbols(sourceFile.unitName); + } + + // Produce baselines. The first gives the types for all expressions. + // The second gives symbols for all identifiers. + let e1: Error, e2: Error; + try { + checkBaseLines(/*isSymbolBaseLine*/ false); + } + catch (e) { + e1 = e; + } + + try { + checkBaseLines(/*isSymbolBaseLine*/ true); + } + catch (e) { + e2 = e; + } + + if (e1 || e2) { + throw e1 || e2; + } + + return; + + function checkBaseLines(isSymbolBaseLine: boolean) { + const fullBaseLine = generateBaseLine(fullResults, isSymbolBaseLine); + + const fullExtension = isSymbolBaseLine ? ".symbols" : ".types"; + + Harness.Baseline.runBaseline(baselinePath.replace(/\.tsx?/, fullExtension), () => fullBaseLine); + } + + function generateBaseLine(typeWriterResults: ts.Map, isSymbolBaseline: boolean): string { + const typeLines: string[] = []; + const typeMap: { [fileName: string]: { [lineNum: number]: string[]; } } = {}; + + allFiles.forEach(file => { + const codeLines = file.content.split("\n"); + typeWriterResults[file.unitName].forEach(result => { + if (isSymbolBaseline && !result.symbol) { + return; + } + + const typeOrSymbolString = isSymbolBaseline ? result.symbol : result.type; + const formattedLine = result.sourceText.replace(/\r?\n/g, "") + " : " + typeOrSymbolString; + if (!typeMap[file.unitName]) { + typeMap[file.unitName] = {}; + } + + let typeInfo = [formattedLine]; + const existingTypeInfo = typeMap[file.unitName][result.line]; + if (existingTypeInfo) { + typeInfo = existingTypeInfo.concat(typeInfo); + } + typeMap[file.unitName][result.line] = typeInfo; + }); + + typeLines.push("=== " + file.unitName + " ===\r\n"); + for (let i = 0; i < codeLines.length; i++) { + const currentCodeLine = codeLines[i]; + typeLines.push(currentCodeLine + "\r\n"); + if (typeMap[file.unitName]) { + const typeInfo = typeMap[file.unitName][i]; + if (typeInfo) { + typeInfo.forEach(ty => { + typeLines.push(">" + ty + "\r\n"); + }); + if (i + 1 < codeLines.length && (codeLines[i + 1].match(/^\s*[{|}]\s*$/) || codeLines[i + 1].trim() === "")) { + } + else { + typeLines.push("\r\n"); + } + } + } + else { + typeLines.push("No type information for this code."); + } + } + }); + + return typeLines.join(""); + } + } + + function getByteOrderMarkText(file: Harness.Compiler.GeneratedFile): string { + return file.writeByteOrderMark ? "\u00EF\u00BB\u00BF" : ""; + } + + export function doSourcemapBaseline(baselinePath: string, options: ts.CompilerOptions, result: CompilerResult) { + if (options.inlineSourceMap) { + if (result.sourceMaps.length > 0) { + throw new Error("No sourcemap files should be generated if inlineSourceMaps was set."); + } + return; + } + else if (options.sourceMap) { + if (result.sourceMaps.length !== result.files.length) { + throw new Error("Number of sourcemap files should be same as js files."); + } + + Harness.Baseline.runBaseline(baselinePath.replace(/\.tsx?/, ".js.map"), () => { + if (options.noEmitOnError && result.errors.length !== 0 && result.sourceMaps.length === 0) { + // We need to return null here or the runBaseLine will actually create a empty file. + // Baselining isn't required here because there is no output. + /* tslint:disable:no-null-keyword */ + return null; + /* tslint:enable:no-null-keyword */ + } + + let sourceMapCode = ""; + for (let i = 0; i < result.sourceMaps.length; i++) { + sourceMapCode += "//// [" + Harness.Path.getFileName(result.sourceMaps[i].fileName) + "]\r\n"; + sourceMapCode += getByteOrderMarkText(result.sourceMaps[i]); + sourceMapCode += result.sourceMaps[i].code; + } + + return sourceMapCode; + }); + } + } + + export function doJsEmitBaseline(baselinePath: string, header: string, options: ts.CompilerOptions, result: CompilerResult, toBeCompiled: Harness.Compiler.TestFile[], otherFiles: Harness.Compiler.TestFile[], harnessSettings: Harness.TestCaseParser.CompilerSettings) { + if (!options.noEmit && result.files.length === 0 && result.errors.length === 0) { + throw new Error("Expected at least one js file to be emitted or at least one error to be created."); + } + + // check js output + Harness.Baseline.runBaseline(baselinePath.replace(/\.tsx?/, ".js"), () => { + let tsCode = ""; + const tsSources = otherFiles.concat(toBeCompiled); + if (tsSources.length > 1) { + tsCode += "//// [" + header + "] ////\r\n\r\n"; + } + for (let i = 0; i < tsSources.length; i++) { + tsCode += "//// [" + Harness.Path.getFileName(tsSources[i].unitName) + "]\r\n"; + tsCode += tsSources[i].content + (i < (tsSources.length - 1) ? "\r\n" : ""); + } + + let jsCode = ""; + for (let i = 0; i < result.files.length; i++) { + jsCode += "//// [" + Harness.Path.getFileName(result.files[i].fileName) + "]\r\n"; + jsCode += getByteOrderMarkText(result.files[i]); + jsCode += result.files[i].code; + } + + if (result.declFilesCode.length > 0) { + jsCode += "\r\n\r\n"; + for (let i = 0; i < result.declFilesCode.length; i++) { + jsCode += "//// [" + Harness.Path.getFileName(result.declFilesCode[i].fileName) + "]\r\n"; + jsCode += getByteOrderMarkText(result.declFilesCode[i]); + jsCode += result.declFilesCode[i].code; + } + } + + const declFileCompilationResult = + Harness.Compiler.compileDeclarationFiles( + toBeCompiled, otherFiles, result, harnessSettings, options, /*currentDirectory*/ undefined); + + if (declFileCompilationResult && declFileCompilationResult.declResult.errors.length) { + jsCode += "\r\n\r\n//// [DtsFileErrors]\r\n"; + jsCode += "\r\n\r\n"; + jsCode += Harness.Compiler.getErrorBaseline(declFileCompilationResult.declInputFiles.concat(declFileCompilationResult.declOtherFiles), declFileCompilationResult.declResult.errors); + } + + if (jsCode.length > 0) { + return tsCode + "\r\n\r\n" + jsCode; + } + else { + /* tslint:disable:no-null-keyword */ + return null; + /* tslint:enable:no-null-keyword */ + } + }); + } + export function collateOutputs(outputFiles: Harness.Compiler.GeneratedFile[]): string { // Collect, test, and sort the fileNames outputFiles.sort((a, b) => cleanName(a.fileName).localeCompare(cleanName(b.fileName))); diff --git a/src/harness/rwcRunner.ts b/src/harness/rwcRunner.ts index 17346016fbb..5720f9b541c 100644 --- a/src/harness/rwcRunner.ts +++ b/src/harness/rwcRunner.ts @@ -222,7 +222,12 @@ namespace RWC { } }); - // TODO: Type baselines (need to refactor out from compilerRunner) + it("has the expected types", () => { + Harness.Compiler.doTypeAndSymbolBaseline(baseName, compilerResult, inputFiles + .concat(otherFiles) + .filter(file => !!compilerResult.program.getSourceFile(file.unitName)) + .filter(e => !Harness.isDefaultLibraryFile(e.unitName))); + }); }); } } From 057357be88319368da2e1380da93915075ed05fc Mon Sep 17 00:00:00 2001 From: zhengbli Date: Fri, 19 Aug 2016 15:48:46 -0700 Subject: [PATCH 183/233] CR feedback --- src/services/services.ts | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 64b872d14a7..94b69113da7 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4913,22 +4913,24 @@ namespace ts { if (!documentation) { documentation = symbol.getDocumentationComment(); - if ((!documentation || documentation.length === 0) && symbol.flags & SymbolFlags.Property) { + if (documentation.length === 0 && symbol.flags & SymbolFlags.Property) { // For some special property access expressions like `experts.foo = foo` or `module.exports.foo = foo` // there documentation comments might be attached to the right hand side symbol of their declarations. // The pattern of such special property access is that the parent symbol is the symbol of the file. if (symbol.parent && forEach(symbol.parent.declarations, declaration => declaration.kind === SyntaxKind.SourceFile)) { - forEach(symbol.declarations, declaration => { - if (declaration.parent && declaration.parent.kind === SyntaxKind.BinaryExpression) { - const rhsSymbol = program.getTypeChecker().getSymbolAtLocation((declaration.parent).right); - if (rhsSymbol) { - documentation = rhsSymbol.getDocumentationComment(); - if (documentation && documentation.length > 0) { - return true; - } + for(const declaration of symbol.declarations) { + if (!declaration.parent || declaration.parent.kind !== SyntaxKind.BinaryExpression) { + continue; + } + + const rhsSymbol = program.getTypeChecker().getSymbolAtLocation((declaration.parent).right); + if (rhsSymbol) { + documentation = rhsSymbol.getDocumentationComment(); + if (documentation.length > 0) { + return; } } - }); + } } } } From a5bb13f6c1b5020c61d681432f33d5f3f939c941 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Fri, 19 Aug 2016 16:53:36 -0700 Subject: [PATCH 184/233] fix broken tests --- src/services/services.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 94b69113da7..c59551457ba 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4918,17 +4918,19 @@ namespace ts { // there documentation comments might be attached to the right hand side symbol of their declarations. // The pattern of such special property access is that the parent symbol is the symbol of the file. if (symbol.parent && forEach(symbol.parent.declarations, declaration => declaration.kind === SyntaxKind.SourceFile)) { - for(const declaration of symbol.declarations) { + for (const declaration of symbol.declarations) { if (!declaration.parent || declaration.parent.kind !== SyntaxKind.BinaryExpression) { continue; } const rhsSymbol = program.getTypeChecker().getSymbolAtLocation((declaration.parent).right); - if (rhsSymbol) { - documentation = rhsSymbol.getDocumentationComment(); - if (documentation.length > 0) { - return; - } + if (!rhsSymbol) { + continue; + } + + documentation = rhsSymbol.getDocumentationComment(); + if (documentation.length > 0) { + break; } } } From a531b87b3c668f56a4140699f89b7fcc4160dfbc Mon Sep 17 00:00:00 2001 From: Yui Date: Fri, 19 Aug 2016 17:06:05 -0700 Subject: [PATCH 185/233] Pass in baselineOpts into types baselines so that RWC baselines can be written to internal folder (#10443) --- src/harness/harness.ts | 4 ++-- src/harness/rwcRunner.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 14cb3147c32..9e27f500b80 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1339,7 +1339,7 @@ namespace Harness { }); } - export function doTypeAndSymbolBaseline(baselinePath: string, result: CompilerResult, allFiles: {unitName: string, content: string}[]) { + export function doTypeAndSymbolBaseline(baselinePath: string, result: CompilerResult, allFiles: {unitName: string, content: string}[], opts?: Harness.Baseline.BaselineOptions) { if (result.errors.length !== 0) { return; } @@ -1396,7 +1396,7 @@ namespace Harness { const fullExtension = isSymbolBaseLine ? ".symbols" : ".types"; - Harness.Baseline.runBaseline(baselinePath.replace(/\.tsx?/, fullExtension), () => fullBaseLine); + Harness.Baseline.runBaseline(baselinePath.replace(/\.tsx?/, fullExtension), () => fullBaseLine, opts); } function generateBaseLine(typeWriterResults: ts.Map, isSymbolBaseline: boolean): string { diff --git a/src/harness/rwcRunner.ts b/src/harness/rwcRunner.ts index 5720f9b541c..d56e8d6d35f 100644 --- a/src/harness/rwcRunner.ts +++ b/src/harness/rwcRunner.ts @@ -226,7 +226,7 @@ namespace RWC { Harness.Compiler.doTypeAndSymbolBaseline(baseName, compilerResult, inputFiles .concat(otherFiles) .filter(file => !!compilerResult.program.getSourceFile(file.unitName)) - .filter(e => !Harness.isDefaultLibraryFile(e.unitName))); + .filter(e => !Harness.isDefaultLibraryFile(e.unitName)), baselineOpts); }); }); } From 806e142cbf244f6b694b79d80e9f3dc5ed804e9e Mon Sep 17 00:00:00 2001 From: Yuichi Nukiyama Date: Sat, 20 Aug 2016 12:36:57 +0900 Subject: [PATCH 186/233] Add error message Add error message when trying to relate primitives to the boxed/apparent backing types. --- src/compiler/checker.ts | 9 +++++- src/compiler/diagnosticMessages.json | 4 +++ .../apparentTypeSubtyping.errors.txt | 2 ++ .../apparentTypeSupertype.errors.txt | 2 ++ .../reference/arrayLiterals3.errors.txt | 2 ++ .../assignFromBooleanInterface.errors.txt | 2 ++ .../assignFromBooleanInterface2.errors.txt | 2 ++ .../assignFromNumberInterface.errors.txt | 2 ++ .../assignFromNumberInterface2.errors.txt | 2 ++ .../assignFromStringInterface.errors.txt | 2 ++ .../assignFromStringInterface2.errors.txt | 2 ++ .../reference/nativeToBoxedTypes.errors.txt | 29 +++++++++++++++++++ .../baselines/reference/nativeToBoxedTypes.js | 23 +++++++++++++++ .../reference/primitiveMembers.errors.txt | 2 ++ tests/cases/compiler/nativeToBoxedTypes.ts | 11 +++++++ 15 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/nativeToBoxedTypes.errors.txt create mode 100644 tests/baselines/reference/nativeToBoxedTypes.js create mode 100644 tests/cases/compiler/nativeToBoxedTypes.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8568cd5feed..bc8af9e6322 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6258,13 +6258,20 @@ namespace ts { targetType = typeToString(target, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType); } + // check if trying to relate primitives to the boxed/apparent backing types. + if ((sourceType === "Number" && targetType === "number") || + (sourceType === "String" && targetType === "string") || + (sourceType === "Boolean" && targetType === "boolean")) { + reportError(Diagnostics._0_is_a_primitive_type_while_1_is_a_boxed_object_Prefer_using_0_when_possible, targetType, sourceType); + } + if (!message) { message = relation === comparableRelation ? Diagnostics.Type_0_is_not_comparable_to_type_1 : Diagnostics.Type_0_is_not_assignable_to_type_1; } - reportError(message, sourceType, targetType); + reportError(message, sourceType, targetType); } // Compare two types and return diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 6210a20afa6..fb8bc21ec95 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1955,6 +1955,10 @@ "category": "Error", "code": 2691 }, + "'{0}' is a primitive type while '{1}' is a boxed object. Prefer using '{0}' when possible.": { + "category": "Error", + "code": 2692 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", "code": 4000 diff --git a/tests/baselines/reference/apparentTypeSubtyping.errors.txt b/tests/baselines/reference/apparentTypeSubtyping.errors.txt index e1a53ef6a73..c19cfa44360 100644 --- a/tests/baselines/reference/apparentTypeSubtyping.errors.txt +++ b/tests/baselines/reference/apparentTypeSubtyping.errors.txt @@ -1,6 +1,7 @@ tests/cases/conformance/types/typeRelationships/apparentType/apparentTypeSubtyping.ts(9,7): error TS2415: Class 'Derived' incorrectly extends base class 'Base'. Types of property 'x' are incompatible. Type 'String' is not assignable to type 'string'. + 'string' is a primitive type while 'String' is a boxed object. Prefer using 'string' when possible. ==== tests/cases/conformance/types/typeRelationships/apparentType/apparentTypeSubtyping.ts (1 errors) ==== @@ -17,6 +18,7 @@ tests/cases/conformance/types/typeRelationships/apparentType/apparentTypeSubtypi !!! error TS2415: Class 'Derived' incorrectly extends base class 'Base'. !!! error TS2415: Types of property 'x' are incompatible. !!! error TS2415: Type 'String' is not assignable to type 'string'. +!!! error TS2415: 'string' is a primitive type while 'String' is a boxed object. Prefer using 'string' when possible. x: String; } diff --git a/tests/baselines/reference/apparentTypeSupertype.errors.txt b/tests/baselines/reference/apparentTypeSupertype.errors.txt index 5fe5c92a5b3..b7195557e8d 100644 --- a/tests/baselines/reference/apparentTypeSupertype.errors.txt +++ b/tests/baselines/reference/apparentTypeSupertype.errors.txt @@ -2,6 +2,7 @@ tests/cases/conformance/types/typeRelationships/apparentType/apparentTypeSuperty Types of property 'x' are incompatible. Type 'U' is not assignable to type 'string'. Type 'String' is not assignable to type 'string'. + 'string' is a primitive type while 'String' is a boxed object. Prefer using 'string' when possible. ==== tests/cases/conformance/types/typeRelationships/apparentType/apparentTypeSupertype.ts (1 errors) ==== @@ -19,5 +20,6 @@ tests/cases/conformance/types/typeRelationships/apparentType/apparentTypeSuperty !!! error TS2415: Types of property 'x' are incompatible. !!! error TS2415: Type 'U' is not assignable to type 'string'. !!! error TS2415: Type 'String' is not assignable to type 'string'. +!!! error TS2415: 'string' is a primitive type while 'String' is a boxed object. Prefer using 'string' when possible. x: U; } \ No newline at end of file diff --git a/tests/baselines/reference/arrayLiterals3.errors.txt b/tests/baselines/reference/arrayLiterals3.errors.txt index 8e6c7eccf81..eb0c6dc51b7 100644 --- a/tests/baselines/reference/arrayLiterals3.errors.txt +++ b/tests/baselines/reference/arrayLiterals3.errors.txt @@ -18,6 +18,7 @@ tests/cases/conformance/expressions/arrayLiterals/arrayLiterals3.ts(34,5): error Types of parameters 'items' and 'items' are incompatible. Type 'Number' is not assignable to type 'string | number'. Type 'Number' is not assignable to type 'number'. + 'number' is a primitive type while 'Number' is a boxed object. Prefer using 'number' when possible. ==== tests/cases/conformance/expressions/arrayLiterals/arrayLiterals3.ts (6 errors) ==== @@ -81,4 +82,5 @@ tests/cases/conformance/expressions/arrayLiterals/arrayLiterals3.ts(34,5): error !!! error TS2322: Types of parameters 'items' and 'items' are incompatible. !!! error TS2322: Type 'Number' is not assignable to type 'string | number'. !!! error TS2322: Type 'Number' is not assignable to type 'number'. +!!! error TS2322: 'number' is a primitive type while 'Number' is a boxed object. Prefer using 'number' when possible. \ No newline at end of file diff --git a/tests/baselines/reference/assignFromBooleanInterface.errors.txt b/tests/baselines/reference/assignFromBooleanInterface.errors.txt index 4b06603a9bf..f5c92a47569 100644 --- a/tests/baselines/reference/assignFromBooleanInterface.errors.txt +++ b/tests/baselines/reference/assignFromBooleanInterface.errors.txt @@ -1,4 +1,5 @@ tests/cases/conformance/types/primitives/boolean/assignFromBooleanInterface.ts(3,1): error TS2322: Type 'Boolean' is not assignable to type 'boolean'. + 'boolean' is a primitive type while 'Boolean' is a boxed object. Prefer using 'boolean' when possible. ==== tests/cases/conformance/types/primitives/boolean/assignFromBooleanInterface.ts (1 errors) ==== @@ -7,4 +8,5 @@ tests/cases/conformance/types/primitives/boolean/assignFromBooleanInterface.ts(3 x = a; ~ !!! error TS2322: Type 'Boolean' is not assignable to type 'boolean'. +!!! error TS2322: 'boolean' is a primitive type while 'Boolean' is a boxed object. Prefer using 'boolean' when possible. a = x; \ No newline at end of file diff --git a/tests/baselines/reference/assignFromBooleanInterface2.errors.txt b/tests/baselines/reference/assignFromBooleanInterface2.errors.txt index c03eab60c74..bfbf56eec5a 100644 --- a/tests/baselines/reference/assignFromBooleanInterface2.errors.txt +++ b/tests/baselines/reference/assignFromBooleanInterface2.errors.txt @@ -3,6 +3,7 @@ tests/cases/conformance/types/primitives/boolean/assignFromBooleanInterface2.ts( Type '() => Object' is not assignable to type '() => boolean'. Type 'Object' is not assignable to type 'boolean'. tests/cases/conformance/types/primitives/boolean/assignFromBooleanInterface2.ts(19,1): error TS2322: Type 'Boolean' is not assignable to type 'boolean'. + 'boolean' is a primitive type while 'Boolean' is a boxed object. Prefer using 'boolean' when possible. tests/cases/conformance/types/primitives/boolean/assignFromBooleanInterface2.ts(20,1): error TS2322: Type 'NotBoolean' is not assignable to type 'boolean'. @@ -33,6 +34,7 @@ tests/cases/conformance/types/primitives/boolean/assignFromBooleanInterface2.ts( x = a; // expected error ~ !!! error TS2322: Type 'Boolean' is not assignable to type 'boolean'. +!!! error TS2322: 'boolean' is a primitive type while 'Boolean' is a boxed object. Prefer using 'boolean' when possible. x = b; // expected error ~ !!! error TS2322: Type 'NotBoolean' is not assignable to type 'boolean'. diff --git a/tests/baselines/reference/assignFromNumberInterface.errors.txt b/tests/baselines/reference/assignFromNumberInterface.errors.txt index 0c8c4a5f5ac..e68ec76f68f 100644 --- a/tests/baselines/reference/assignFromNumberInterface.errors.txt +++ b/tests/baselines/reference/assignFromNumberInterface.errors.txt @@ -1,4 +1,5 @@ tests/cases/conformance/types/primitives/number/assignFromNumberInterface.ts(3,1): error TS2322: Type 'Number' is not assignable to type 'number'. + 'number' is a primitive type while 'Number' is a boxed object. Prefer using 'number' when possible. ==== tests/cases/conformance/types/primitives/number/assignFromNumberInterface.ts (1 errors) ==== @@ -7,4 +8,5 @@ tests/cases/conformance/types/primitives/number/assignFromNumberInterface.ts(3,1 x = a; ~ !!! error TS2322: Type 'Number' is not assignable to type 'number'. +!!! error TS2322: 'number' is a primitive type while 'Number' is a boxed object. Prefer using 'number' when possible. a = x; \ No newline at end of file diff --git a/tests/baselines/reference/assignFromNumberInterface2.errors.txt b/tests/baselines/reference/assignFromNumberInterface2.errors.txt index 85639cdeeaf..5cae4654760 100644 --- a/tests/baselines/reference/assignFromNumberInterface2.errors.txt +++ b/tests/baselines/reference/assignFromNumberInterface2.errors.txt @@ -1,4 +1,5 @@ tests/cases/conformance/types/primitives/number/assignFromNumberInterface2.ts(24,1): error TS2322: Type 'Number' is not assignable to type 'number'. + 'number' is a primitive type while 'Number' is a boxed object. Prefer using 'number' when possible. tests/cases/conformance/types/primitives/number/assignFromNumberInterface2.ts(25,1): error TS2322: Type 'NotNumber' is not assignable to type 'number'. @@ -29,6 +30,7 @@ tests/cases/conformance/types/primitives/number/assignFromNumberInterface2.ts(25 x = a; // expected error ~ !!! error TS2322: Type 'Number' is not assignable to type 'number'. +!!! error TS2322: 'number' is a primitive type while 'Number' is a boxed object. Prefer using 'number' when possible. x = b; // expected error ~ !!! error TS2322: Type 'NotNumber' is not assignable to type 'number'. diff --git a/tests/baselines/reference/assignFromStringInterface.errors.txt b/tests/baselines/reference/assignFromStringInterface.errors.txt index 6ec8afad120..80268f4b06a 100644 --- a/tests/baselines/reference/assignFromStringInterface.errors.txt +++ b/tests/baselines/reference/assignFromStringInterface.errors.txt @@ -1,4 +1,5 @@ tests/cases/conformance/types/primitives/string/assignFromStringInterface.ts(3,1): error TS2322: Type 'String' is not assignable to type 'string'. + 'string' is a primitive type while 'String' is a boxed object. Prefer using 'string' when possible. ==== tests/cases/conformance/types/primitives/string/assignFromStringInterface.ts (1 errors) ==== @@ -7,4 +8,5 @@ tests/cases/conformance/types/primitives/string/assignFromStringInterface.ts(3,1 x = a; ~ !!! error TS2322: Type 'String' is not assignable to type 'string'. +!!! error TS2322: 'string' is a primitive type while 'String' is a boxed object. Prefer using 'string' when possible. a = x; \ No newline at end of file diff --git a/tests/baselines/reference/assignFromStringInterface2.errors.txt b/tests/baselines/reference/assignFromStringInterface2.errors.txt index 4e09eb6eacb..d20bdaaba32 100644 --- a/tests/baselines/reference/assignFromStringInterface2.errors.txt +++ b/tests/baselines/reference/assignFromStringInterface2.errors.txt @@ -1,4 +1,5 @@ tests/cases/conformance/types/primitives/string/assignFromStringInterface2.ts(47,1): error TS2322: Type 'String' is not assignable to type 'string'. + 'string' is a primitive type while 'String' is a boxed object. Prefer using 'string' when possible. tests/cases/conformance/types/primitives/string/assignFromStringInterface2.ts(48,1): error TS2322: Type 'NotString' is not assignable to type 'string'. @@ -52,6 +53,7 @@ tests/cases/conformance/types/primitives/string/assignFromStringInterface2.ts(48 x = a; // expected error ~ !!! error TS2322: Type 'String' is not assignable to type 'string'. +!!! error TS2322: 'string' is a primitive type while 'String' is a boxed object. Prefer using 'string' when possible. x = b; // expected error ~ !!! error TS2322: Type 'NotString' is not assignable to type 'string'. diff --git a/tests/baselines/reference/nativeToBoxedTypes.errors.txt b/tests/baselines/reference/nativeToBoxedTypes.errors.txt new file mode 100644 index 00000000000..c471e6b0e7b --- /dev/null +++ b/tests/baselines/reference/nativeToBoxedTypes.errors.txt @@ -0,0 +1,29 @@ +tests/cases/compiler/nativeToBoxedTypes.ts(3,1): error TS2322: Type 'Number' is not assignable to type 'number'. + 'number' is a primitive type while 'Number' is a boxed object. Prefer using 'number' when possible. +tests/cases/compiler/nativeToBoxedTypes.ts(7,1): error TS2322: Type 'String' is not assignable to type 'string'. + 'string' is a primitive type while 'String' is a boxed object. Prefer using 'string' when possible. +tests/cases/compiler/nativeToBoxedTypes.ts(11,1): error TS2322: Type 'Boolean' is not assignable to type 'boolean'. + 'boolean' is a primitive type while 'Boolean' is a boxed object. Prefer using 'boolean' when possible. + + +==== tests/cases/compiler/nativeToBoxedTypes.ts (3 errors) ==== + var N = new Number(); + var n = 100; + n = N; + ~ +!!! error TS2322: Type 'Number' is not assignable to type 'number'. +!!! error TS2322: 'number' is a primitive type while 'Number' is a boxed object. Prefer using 'number' when possible. + + var S = new String(); + var s = "foge"; + s = S; + ~ +!!! error TS2322: Type 'String' is not assignable to type 'string'. +!!! error TS2322: 'string' is a primitive type while 'String' is a boxed object. Prefer using 'string' when possible. + + var B = new Boolean(); + var b = true; + b = B; + ~ +!!! error TS2322: Type 'Boolean' is not assignable to type 'boolean'. +!!! error TS2322: 'boolean' is a primitive type while 'Boolean' is a boxed object. Prefer using 'boolean' when possible. \ No newline at end of file diff --git a/tests/baselines/reference/nativeToBoxedTypes.js b/tests/baselines/reference/nativeToBoxedTypes.js new file mode 100644 index 00000000000..074b39fa08d --- /dev/null +++ b/tests/baselines/reference/nativeToBoxedTypes.js @@ -0,0 +1,23 @@ +//// [nativeToBoxedTypes.ts] +var N = new Number(); +var n = 100; +n = N; + +var S = new String(); +var s = "foge"; +s = S; + +var B = new Boolean(); +var b = true; +b = B; + +//// [nativeToBoxedTypes.js] +var N = new Number(); +var n = 100; +n = N; +var S = new String(); +var s = "foge"; +s = S; +var B = new Boolean(); +var b = true; +b = B; diff --git a/tests/baselines/reference/primitiveMembers.errors.txt b/tests/baselines/reference/primitiveMembers.errors.txt index da8b75f9bc5..24f02486d00 100644 --- a/tests/baselines/reference/primitiveMembers.errors.txt +++ b/tests/baselines/reference/primitiveMembers.errors.txt @@ -1,5 +1,6 @@ tests/cases/compiler/primitiveMembers.ts(5,3): error TS2339: Property 'toBAZ' does not exist on type 'number'. tests/cases/compiler/primitiveMembers.ts(11,1): error TS2322: Type 'Number' is not assignable to type 'number'. + 'number' is a primitive type while 'Number' is a boxed object. Prefer using 'number' when possible. ==== tests/cases/compiler/primitiveMembers.ts (2 errors) ==== @@ -18,6 +19,7 @@ tests/cases/compiler/primitiveMembers.ts(11,1): error TS2322: Type 'Number' is n n = N; // should not work, as 'number' has a different brand ~ !!! error TS2322: Type 'Number' is not assignable to type 'number'. +!!! error TS2322: 'number' is a primitive type while 'Number' is a boxed object. Prefer using 'number' when possible. N = n; // should work var o: Object = {} diff --git a/tests/cases/compiler/nativeToBoxedTypes.ts b/tests/cases/compiler/nativeToBoxedTypes.ts new file mode 100644 index 00000000000..6cd51251b06 --- /dev/null +++ b/tests/cases/compiler/nativeToBoxedTypes.ts @@ -0,0 +1,11 @@ +var N = new Number(); +var n = 100; +n = N; + +var S = new String(); +var s = "foge"; +s = S; + +var B = new Boolean(); +var b = true; +b = B; \ No newline at end of file From 37a9e6a9ccee232831238fe03446610b2645828d Mon Sep 17 00:00:00 2001 From: Yuichi Nukiyama Date: Sat, 20 Aug 2016 13:44:09 +0900 Subject: [PATCH 187/233] fix linting error --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bc8af9e6322..e279b07bb1f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6271,7 +6271,7 @@ namespace ts { Diagnostics.Type_0_is_not_assignable_to_type_1; } - reportError(message, sourceType, targetType); + reportError(message, sourceType, targetType); } // Compare two types and return From 0c01874b310e5fe677aa39b92fc50e81823080a5 Mon Sep 17 00:00:00 2001 From: Yuichi Nukiyama Date: Sat, 20 Aug 2016 21:24:46 +0900 Subject: [PATCH 188/233] follow advise --- src/compiler/checker.ts | 24 +++++++++++++------ .../reference/symbolType15.errors.txt | 4 +++- tests/cases/compiler/nativeToBoxedTypes.ts | 6 ++++- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e279b07bb1f..279517bc1ec 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6258,13 +6258,6 @@ namespace ts { targetType = typeToString(target, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType); } - // check if trying to relate primitives to the boxed/apparent backing types. - if ((sourceType === "Number" && targetType === "number") || - (sourceType === "String" && targetType === "string") || - (sourceType === "Boolean" && targetType === "boolean")) { - reportError(Diagnostics._0_is_a_primitive_type_while_1_is_a_boxed_object_Prefer_using_0_when_possible, targetType, sourceType); - } - if (!message) { message = relation === comparableRelation ? Diagnostics.Type_0_is_not_comparable_to_type_1 : @@ -6274,6 +6267,19 @@ namespace ts { reportError(message, sourceType, targetType); } + function tryElaborateErrorsForPrimitivesAndObjects(source: Type, target: Type) { + const sourceType = typeToString(source); + const targetType = typeToString(target); + + if ((globalStringType === source && stringType === target) || + (globalNumberType === source && numberType === target) || + (globalBooleanType === source && booleanType === target) || + (getGlobalESSymbolType() === source && esSymbolType === target)) { + console.log(source);console.log(target); + reportError(Diagnostics._0_is_a_primitive_type_while_1_is_a_boxed_object_Prefer_using_0_when_possible, targetType, sourceType); + } + } + // Compare two types and return // Ternary.True if they are related with no assumptions, // Ternary.Maybe if they are related with assumptions of other relationships, or @@ -6396,6 +6402,10 @@ namespace ts { } } + if (source.flags & TypeFlags.ObjectType && target.flags & TypeFlags.Primitive) { + tryElaborateErrorsForPrimitivesAndObjects(source, target); + } + if (reportErrors) { reportRelationError(headMessage, source, target); } diff --git a/tests/baselines/reference/symbolType15.errors.txt b/tests/baselines/reference/symbolType15.errors.txt index eb63e5798d5..4a27ea24c59 100644 --- a/tests/baselines/reference/symbolType15.errors.txt +++ b/tests/baselines/reference/symbolType15.errors.txt @@ -1,4 +1,5 @@ tests/cases/conformance/es6/Symbols/symbolType15.ts(5,1): error TS2322: Type 'Symbol' is not assignable to type 'symbol'. + 'symbol' is a primitive type while 'Symbol' is a boxed object. Prefer using 'symbol' when possible. ==== tests/cases/conformance/es6/Symbols/symbolType15.ts (1 errors) ==== @@ -8,4 +9,5 @@ tests/cases/conformance/es6/Symbols/symbolType15.ts(5,1): error TS2322: Type 'Sy symObj = sym; sym = symObj; ~~~ -!!! error TS2322: Type 'Symbol' is not assignable to type 'symbol'. \ No newline at end of file +!!! error TS2322: Type 'Symbol' is not assignable to type 'symbol'. +!!! error TS2322: 'symbol' is a primitive type while 'Symbol' is a boxed object. Prefer using 'symbol' when possible. \ No newline at end of file diff --git a/tests/cases/compiler/nativeToBoxedTypes.ts b/tests/cases/compiler/nativeToBoxedTypes.ts index 6cd51251b06..a34dd5cdd3b 100644 --- a/tests/cases/compiler/nativeToBoxedTypes.ts +++ b/tests/cases/compiler/nativeToBoxedTypes.ts @@ -8,4 +8,8 @@ s = S; var B = new Boolean(); var b = true; -b = B; \ No newline at end of file +b = B; + +var sym: symbol; +var Sym: Symbol; +sym = Sym; \ No newline at end of file From bc0c137a89ed8313655d7f77848a13554fa555a9 Mon Sep 17 00:00:00 2001 From: Yuichi Nukiyama Date: Sat, 20 Aug 2016 21:36:17 +0900 Subject: [PATCH 189/233] remove extra code --- src/compiler/checker.ts | 1 - .../baselines/reference/nativeToBoxedTypes.errors.txt | 11 +++++++++-- tests/baselines/reference/nativeToBoxedTypes.js | 9 ++++++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 279517bc1ec..7ff5b5b19ed 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6275,7 +6275,6 @@ namespace ts { (globalNumberType === source && numberType === target) || (globalBooleanType === source && booleanType === target) || (getGlobalESSymbolType() === source && esSymbolType === target)) { - console.log(source);console.log(target); reportError(Diagnostics._0_is_a_primitive_type_while_1_is_a_boxed_object_Prefer_using_0_when_possible, targetType, sourceType); } } diff --git a/tests/baselines/reference/nativeToBoxedTypes.errors.txt b/tests/baselines/reference/nativeToBoxedTypes.errors.txt index c471e6b0e7b..255e0414fd9 100644 --- a/tests/baselines/reference/nativeToBoxedTypes.errors.txt +++ b/tests/baselines/reference/nativeToBoxedTypes.errors.txt @@ -4,9 +4,10 @@ tests/cases/compiler/nativeToBoxedTypes.ts(7,1): error TS2322: Type 'String' is 'string' is a primitive type while 'String' is a boxed object. Prefer using 'string' when possible. tests/cases/compiler/nativeToBoxedTypes.ts(11,1): error TS2322: Type 'Boolean' is not assignable to type 'boolean'. 'boolean' is a primitive type while 'Boolean' is a boxed object. Prefer using 'boolean' when possible. +tests/cases/compiler/nativeToBoxedTypes.ts(14,10): error TS2304: Cannot find name 'Symbol'. -==== tests/cases/compiler/nativeToBoxedTypes.ts (3 errors) ==== +==== tests/cases/compiler/nativeToBoxedTypes.ts (4 errors) ==== var N = new Number(); var n = 100; n = N; @@ -26,4 +27,10 @@ tests/cases/compiler/nativeToBoxedTypes.ts(11,1): error TS2322: Type 'Boolean' i b = B; ~ !!! error TS2322: Type 'Boolean' is not assignable to type 'boolean'. -!!! error TS2322: 'boolean' is a primitive type while 'Boolean' is a boxed object. Prefer using 'boolean' when possible. \ No newline at end of file +!!! error TS2322: 'boolean' is a primitive type while 'Boolean' is a boxed object. Prefer using 'boolean' when possible. + + var sym: symbol; + var Sym: Symbol; + ~~~~~~ +!!! error TS2304: Cannot find name 'Symbol'. + sym = Sym; \ No newline at end of file diff --git a/tests/baselines/reference/nativeToBoxedTypes.js b/tests/baselines/reference/nativeToBoxedTypes.js index 074b39fa08d..db47c72fa10 100644 --- a/tests/baselines/reference/nativeToBoxedTypes.js +++ b/tests/baselines/reference/nativeToBoxedTypes.js @@ -9,7 +9,11 @@ s = S; var B = new Boolean(); var b = true; -b = B; +b = B; + +var sym: symbol; +var Sym: Symbol; +sym = Sym; //// [nativeToBoxedTypes.js] var N = new Number(); @@ -21,3 +25,6 @@ s = S; var B = new Boolean(); var b = true; b = B; +var sym; +var Sym; +sym = Sym; From eeec775da0f3dabe9c73254c02b5254bab84c882 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Sat, 20 Aug 2016 19:12:11 -0700 Subject: [PATCH 190/233] Add more test for 10426 --- .../completionEntryDetailAcrossFiles02.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/cases/fourslash/server/completionEntryDetailAcrossFiles02.ts diff --git a/tests/cases/fourslash/server/completionEntryDetailAcrossFiles02.ts b/tests/cases/fourslash/server/completionEntryDetailAcrossFiles02.ts new file mode 100644 index 00000000000..2c5e53ea224 --- /dev/null +++ b/tests/cases/fourslash/server/completionEntryDetailAcrossFiles02.ts @@ -0,0 +1,20 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: a.js +//// /** +//// * Modify the parameter +//// * @param {string} p1 +//// */ +//// var foo = function (p1) { } +//// module.exports.foo = foo; +//// fo/*1*/ + +// @Filename: b.ts +//// import a = require("./a"); +//// a.fo/*2*/ + +goTo.marker('1'); +verify.completionEntryDetailIs("foo", "var foo: (p1: string) => void", "Modify the parameter"); +goTo.marker('2'); +verify.completionEntryDetailIs("foo", "(property) a.foo: (p1: string) => void", "Modify the parameter"); From dc7b18e4e24796a40fc24801b3c5425e71200f1d Mon Sep 17 00:00:00 2001 From: Yuichi Nukiyama Date: Sun, 21 Aug 2016 11:42:41 +0900 Subject: [PATCH 191/233] fix some errors --- src/compiler/checker.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7ff5b5b19ed..3a6608c71d1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6401,11 +6401,10 @@ namespace ts { } } - if (source.flags & TypeFlags.ObjectType && target.flags & TypeFlags.Primitive) { - tryElaborateErrorsForPrimitivesAndObjects(source, target); - } - if (reportErrors) { + if (source.flags & TypeFlags.ObjectType && target.flags & TypeFlags.Primitive) { + tryElaborateErrorsForPrimitivesAndObjects(source, target); + } reportRelationError(headMessage, source, target); } return Ternary.False; From 794d3e91d07021409ef4a985dd90fc431f4f04c5 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Sat, 20 Aug 2016 20:35:58 -0700 Subject: [PATCH 192/233] routine update of dom libs --- src/lib/dom.generated.d.ts | 70 +++++++++++++++++++++++++------- src/lib/webworker.generated.d.ts | 2 +- 2 files changed, 56 insertions(+), 16 deletions(-) diff --git a/src/lib/dom.generated.d.ts b/src/lib/dom.generated.d.ts index d2938f6e983..ee657f64a65 100644 --- a/src/lib/dom.generated.d.ts +++ b/src/lib/dom.generated.d.ts @@ -126,6 +126,7 @@ interface KeyAlgorithm { } interface KeyboardEventInit extends EventModifierInit { + code?: string; key?: string; location?: number; repeat?: boolean; @@ -2288,7 +2289,7 @@ declare var DeviceRotationRate: { new(): DeviceRotationRate; } -interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEvent { +interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEvent, ParentNode { /** * Sets or gets the URL for the current document. */ @@ -3351,7 +3352,7 @@ declare var Document: { new(): Document; } -interface DocumentFragment extends Node, NodeSelector { +interface DocumentFragment extends Node, NodeSelector, ParentNode { addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void; } @@ -3420,7 +3421,7 @@ declare var EXT_texture_filter_anisotropic: { readonly TEXTURE_MAX_ANISOTROPY_EXT: number; } -interface Element extends Node, GlobalEventHandlers, ElementTraversal, NodeSelector, ChildNode { +interface Element extends Node, GlobalEventHandlers, ElementTraversal, NodeSelector, ChildNode, ParentNode { readonly classList: DOMTokenList; className: string; readonly clientHeight: number; @@ -3675,6 +3676,16 @@ interface Element extends Node, GlobalEventHandlers, ElementTraversal, NodeSelec getElementsByClassName(classNames: string): NodeListOf; matches(selector: string): boolean; closest(selector: string): Element | null; + scrollIntoView(arg?: boolean | ScrollIntoViewOptions): void; + scroll(options?: ScrollToOptions): void; + scroll(x: number, y: number): void; + scrollTo(options?: ScrollToOptions): void; + scrollTo(x: number, y: number): void; + scrollBy(options?: ScrollToOptions): void; + scrollBy(x: number, y: number): void; + insertAdjacentElement(position: string, insertedElement: Element): Element | null; + insertAdjacentHTML(where: string, html: string): void; + insertAdjacentText(where: string, text: string): void; addEventListener(type: "MSGestureChange", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void; addEventListener(type: "MSGestureDoubleTap", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void; addEventListener(type: "MSGestureEnd", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void; @@ -4446,7 +4457,7 @@ interface HTMLCanvasElement extends HTMLElement { * @param type The standard MIME type for the image format to return. If you do not specify this parameter, the default value is a PNG format image. */ toDataURL(type?: string, ...args: any[]): string; - toBlob(callback: (result: Blob | null) => void, ... arguments: any[]): void; + toBlob(callback: (result: Blob | null) => void, type?: string, ...arguments: any[]): void; } declare var HTMLCanvasElement: { @@ -4621,11 +4632,7 @@ interface HTMLElement extends Element { click(): void; dragDrop(): boolean; focus(): void; - insertAdjacentElement(position: string, insertedElement: Element): Element; - insertAdjacentHTML(where: string, html: string): void; - insertAdjacentText(where: string, text: string): void; msGetInputContext(): MSInputMethodContext; - scrollIntoView(top?: boolean): void; setActive(): void; addEventListener(type: "MSContentZoom", listener: (this: this, ev: UIEvent) => any, useCapture?: boolean): void; addEventListener(type: "MSGestureChange", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void; @@ -5890,6 +5897,7 @@ interface HTMLLinkElement extends HTMLElement, LinkStyle { */ type: string; import?: Document; + integrity: string; addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void; } @@ -6178,7 +6186,7 @@ interface HTMLMediaElement extends HTMLElement { */ canPlayType(type: string): string; /** - * Fires immediately after the client loads the object. + * Resets the audio or video object and loads a new media resource. */ load(): void; /** @@ -6751,6 +6759,7 @@ interface HTMLScriptElement extends HTMLElement { * Sets or retrieves the MIME type for the associated scripting engine. */ type: string; + integrity: string; } declare var HTMLScriptElement: { @@ -7756,6 +7765,7 @@ interface KeyboardEvent extends UIEvent { readonly repeat: boolean; readonly shiftKey: boolean; readonly which: number; + readonly code: string; getModifierState(keyArg: string): boolean; initKeyboardEvent(typeArg: string, canBubbleArg: boolean, cancelableArg: boolean, viewArg: Window, keyArg: string, locationArg: number, modifiersListArg: string, repeat: boolean, locale: string): void; readonly DOM_KEY_LOCATION_JOYSTICK: number; @@ -9128,6 +9138,7 @@ interface PerformanceTiming { readonly responseStart: number; readonly unloadEventEnd: number; readonly unloadEventStart: number; + readonly secureConnectionStart: number; toJSON(): any; } @@ -11405,8 +11416,8 @@ declare var StereoPannerNode: { interface Storage { readonly length: number; clear(): void; - getItem(key: string): string; - key(index: number): string; + getItem(key: string): string | null; + key(index: number): string | null; removeItem(key: string): void; setItem(key: string, data: string): void; [key: string]: any; @@ -12947,7 +12958,7 @@ interface Window extends EventTarget, WindowTimers, WindowSessionStorage, Window onunload: (this: this, ev: Event) => any; onvolumechange: (this: this, ev: Event) => any; onwaiting: (this: this, ev: Event) => any; - readonly opener: Window; + opener: any; orientation: string | number; readonly outerHeight: number; readonly outerWidth: number; @@ -13002,6 +13013,9 @@ interface Window extends EventTarget, WindowTimers, WindowSessionStorage, Window webkitConvertPointFromNodeToPage(node: Node, pt: WebKitPoint): WebKitPoint; webkitConvertPointFromPageToNode(node: Node, pt: WebKitPoint): WebKitPoint; webkitRequestAnimationFrame(callback: FrameRequestCallback): number; + scroll(options?: ScrollToOptions): void; + scrollTo(options?: ScrollToOptions): void; + scrollBy(options?: ScrollToOptions): void; addEventListener(type: "MSGestureChange", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void; addEventListener(type: "MSGestureDoubleTap", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void; addEventListener(type: "MSGestureEnd", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void; @@ -14029,6 +14043,20 @@ interface ProgressEventInit extends EventInit { total?: number; } +interface ScrollOptions { + behavior?: ScrollBehavior; +} + +interface ScrollToOptions extends ScrollOptions { + left?: number; + top?: number; +} + +interface ScrollIntoViewOptions extends ScrollOptions { + block?: ScrollLogicalPosition; + inline?: ScrollLogicalPosition; +} + interface ClipboardEventInit extends EventInit { data?: string; dataType?: string; @@ -14072,7 +14100,7 @@ interface EcdsaParams extends Algorithm { } interface EcKeyGenParams extends Algorithm { - typedCurve: string; + namedCurve: string; } interface EcKeyAlgorithm extends KeyAlgorithm { @@ -14208,6 +14236,13 @@ interface JsonWebKey { k?: string; } +interface ParentNode { + readonly children: HTMLCollection; + readonly firstElementChild: Element; + readonly lastElementChild: Element; + readonly childElementCount: number; +} + declare type EventListenerOrEventListenerObject = EventListener | EventListenerObject; interface ErrorEventHandler { @@ -14278,7 +14313,7 @@ declare var location: Location; declare var locationbar: BarProp; declare var menubar: BarProp; declare var msCredentials: MSCredentials; -declare var name: string; +declare const name: never; declare var navigator: Navigator; declare var offscreenBuffering: string | boolean; declare var onabort: (this: Window, ev: UIEvent) => any; @@ -14372,7 +14407,7 @@ declare var ontouchstart: (ev: TouchEvent) => any; declare var onunload: (this: Window, ev: Event) => any; declare var onvolumechange: (this: Window, ev: Event) => any; declare var onwaiting: (this: Window, ev: Event) => any; -declare var opener: Window; +declare var opener: any; declare var orientation: string | number; declare var outerHeight: number; declare var outerWidth: number; @@ -14425,6 +14460,9 @@ declare function webkitCancelAnimationFrame(handle: number): void; declare function webkitConvertPointFromNodeToPage(node: Node, pt: WebKitPoint): WebKitPoint; declare function webkitConvertPointFromPageToNode(node: Node, pt: WebKitPoint): WebKitPoint; declare function webkitRequestAnimationFrame(callback: FrameRequestCallback): number; +declare function scroll(options?: ScrollToOptions): void; +declare function scrollTo(options?: ScrollToOptions): void; +declare function scrollBy(options?: ScrollToOptions): void; declare function toString(): string; declare function addEventListener(type: string, listener?: EventListenerOrEventListenerObject, useCapture?: boolean): void; declare function dispatchEvent(evt: Event): boolean; @@ -14580,6 +14618,8 @@ type MSOutboundPayload = MSVideoSendPayload | MSAudioSendPayload; type RTCIceGatherCandidate = RTCIceCandidate | RTCIceCandidateComplete; type RTCTransport = RTCDtlsTransport | RTCSrtpSdesTransport; type payloadtype = number; +type ScrollBehavior = "auto" | "instant" | "smooth"; +type ScrollLogicalPosition = "start" | "center" | "end" | "nearest"; type IDBValidKey = number | string | Date | IDBArrayKey; type BufferSource = ArrayBuffer | ArrayBufferView; type MouseWheelEvent = WheelEvent; \ No newline at end of file diff --git a/src/lib/webworker.generated.d.ts b/src/lib/webworker.generated.d.ts index c61a1a8b8a6..483348f3f75 100644 --- a/src/lib/webworker.generated.d.ts +++ b/src/lib/webworker.generated.d.ts @@ -1000,7 +1000,7 @@ interface EcdsaParams extends Algorithm { } interface EcKeyGenParams extends Algorithm { - typedCurve: string; + namedCurve: string; } interface EcKeyAlgorithm extends KeyAlgorithm { From e445e012c4fe9a3673204988491266a41d56fd7a Mon Sep 17 00:00:00 2001 From: zhengbli Date: Sat, 20 Aug 2016 20:41:33 -0700 Subject: [PATCH 193/233] Add test for jsdoc syntactic classification for function declaration --- .../syntacticClassificationsDocComment4.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 tests/cases/fourslash/syntacticClassificationsDocComment4.ts diff --git a/tests/cases/fourslash/syntacticClassificationsDocComment4.ts b/tests/cases/fourslash/syntacticClassificationsDocComment4.ts new file mode 100644 index 00000000000..d8ac2f12d8b --- /dev/null +++ b/tests/cases/fourslash/syntacticClassificationsDocComment4.ts @@ -0,0 +1,24 @@ +/// + +//// /** @param {number} p1 */ +//// function foo(p1) {} + +var c = classification; +verify.syntacticClassificationsAre( + c.comment("/** "), + c.punctuation("@"), + c.docCommentTagName("param"), + c.comment(" "), + c.punctuation("{"), + c.keyword("number"), + c.punctuation("}"), + c.comment(" "), + c.parameterName("p1"), + c.comment(" */"), + c.keyword("function"), + c.identifier("foo"), + c.punctuation("("), + c.parameterName("p1"), + c.punctuation(")"), + c.punctuation("{"), + c.punctuation("}")); From 6d2323b0d81bc85e33bb0bce8b292938d8a0de27 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Sat, 20 Aug 2016 20:41:47 -0700 Subject: [PATCH 194/233] Simplify implementation --- src/compiler/utilities.ts | 4 ++++ src/services/services.ts | 37 ++++++++++++------------------------- 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 0a1f43203ce..f1573f9b58a 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -301,6 +301,10 @@ namespace ts { return node.kind >= SyntaxKind.FirstJSDocNode && node.kind <= SyntaxKind.LastJSDocNode; } + export function isJSDocTag(node: Node) { + return node.kind >= SyntaxKind.FirstJSDocTagNode && node.kind <= SyntaxKind.LastJSDocTagNode; + } + export function getNonDecoratorTokenPosOfNode(node: Node, sourceFile?: SourceFile): number { if (nodeIsMissing(node) || !node.decorators) { return getTokenPosOfNode(node, sourceFile); diff --git a/src/services/services.ts b/src/services/services.ts index c4b42f94226..22f5a74eb50 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -21,7 +21,6 @@ namespace ts { getChildCount(sourceFile?: SourceFile): number; getChildAt(index: number, sourceFile?: SourceFile): Node; getChildren(sourceFile?: SourceFile): Node[]; - getNonJsDocCommentChildren?(sourceFile?: SourceFile): Node[]; getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number; getFullStart(): number; getEnd(): number; @@ -197,7 +196,6 @@ namespace ts { public parent: Node; public jsDocComments: JSDocComment[]; private _children: Node[]; - private _nonJsDocCommentChildren: Node[]; constructor(kind: SyntaxKind, pos: number, end: number) { this.pos = pos; @@ -275,22 +273,20 @@ namespace ts { } private createChildren(sourceFile?: SourceFile) { - let jsDocCommentChildren: Node[]; - let nonJsDocCommentChildren: Node[]; + let children: Node[]; if (this.kind >= SyntaxKind.FirstNode) { scanner.setText((sourceFile || this.getSourceFile()).text); - jsDocCommentChildren = []; - nonJsDocCommentChildren = []; + children = []; let pos = this.pos; const useJSDocScanner = this.kind >= SyntaxKind.FirstJSDocTagNode && this.kind <= SyntaxKind.LastJSDocTagNode; - const processNode = (node: Node, children = nonJsDocCommentChildren) => { + const processNode = (node: Node) => { if (pos < node.pos) { pos = this.addSyntheticNodes(children, pos, node.pos, useJSDocScanner); } children.push(node); pos = node.end; }; - const processNodes = (nodes: NodeArray, children = nonJsDocCommentChildren) => { + const processNodes = (nodes: NodeArray) => { if (pos < nodes.pos) { pos = this.addSyntheticNodes(children, pos, nodes.pos, useJSDocScanner); } @@ -300,7 +296,7 @@ namespace ts { // jsDocComments need to be the first children if (this.jsDocComments) { for (const jsDocComment of this.jsDocComments) { - processNode(jsDocComment, jsDocCommentChildren); + processNode(jsDocComment); } } // For syntactic classifications, all trivia are classcified together, including jsdoc comments. @@ -309,12 +305,11 @@ namespace ts { pos = this.pos; forEachChild(this, processNode, processNodes); if (pos < this.end) { - this.addSyntheticNodes(nonJsDocCommentChildren, pos, this.end); + this.addSyntheticNodes(children, pos, this.end); } scanner.setText(undefined); } - this._nonJsDocCommentChildren = nonJsDocCommentChildren || emptyArray; - this._children = concatenate(jsDocCommentChildren, this._nonJsDocCommentChildren); + this._children = children || emptyArray; } public getChildCount(sourceFile?: SourceFile): number { @@ -332,18 +327,6 @@ namespace ts { return this._children; } - public getNonJsDocCommentChildren(sourceFile?: SourceFile): Node[] { - // If the cached children were cleared, that means some node before the current node has changed. - // so even if we have a cached nonJsDocCommentChildren, it would be outdated as well. - if (!this._children) { - this.createChildren(sourceFile); - } - // If the node has cached children but not nonJsDocCommentChildren, it means the children is not created - // via calling the "createChildren" method, so it can only be a SyntaxList. As SyntaxList cannot have jsDocCommentChildren - // anyways, we can just return its children. - return this._nonJsDocCommentChildren ? this._nonJsDocCommentChildren : this._children; - } - public getFirstToken(sourceFile?: SourceFile): Node { const children = this.getChildren(sourceFile); if (!children.length) { @@ -7591,6 +7574,10 @@ namespace ts { * False will mean that node is not classified and traverse routine should recurse into node contents. */ function tryClassifyNode(node: Node): boolean { + if (isJSDocTag(node)) { + return true; + } + if (nodeIsMissing(node)) { return true; } @@ -7746,7 +7733,7 @@ namespace ts { if (decodedTextSpanIntersectsWith(spanStart, spanLength, element.pos, element.getFullWidth())) { checkForClassificationCancellation(element.kind); - const children = element.getNonJsDocCommentChildren ? element.getNonJsDocCommentChildren(sourceFile) : element.getChildren(sourceFile); + const children = element.getChildren(sourceFile); for (let i = 0, n = children.length; i < n; i++) { const child = children[i]; if (!tryClassifyNode(child)) { From 84386f9d3b4ac8309ab5f3ce936613f14cad9106 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Sat, 20 Aug 2016 20:46:56 -0700 Subject: [PATCH 195/233] Tolerate certain errors in tsconfig.json --- src/server/editorServices.ts | 50 +++++++++++++++++++++--------------- src/server/session.ts | 20 ++++++++++----- 2 files changed, 42 insertions(+), 28 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index db9bdd5043b..0d84fa54a4a 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -603,8 +603,11 @@ namespace ts.server { return projects.length > 1 ? deduplicate(result, areEqual) : result; } + export type ProjectServiceEvent = + { eventName: "context", data: { project: Project, fileName: string } } | { eventName: "configFileDiag", data: { triggerFile?: string, configFileName: string, diagnostics: Diagnostic[] } } + export interface ProjectServiceEventHandler { - (eventName: string, project: Project, fileName: string): void; + (event: ProjectServiceEvent): void; } export interface HostConfiguration { @@ -748,10 +751,13 @@ namespace ts.server { return ts.normalizePath(name); } - watchedProjectConfigFileChanged(project: Project) { + watchedProjectConfigFileChanged(project: Project): void { this.log("Config file changed: " + project.projectFilename); - this.updateConfiguredProject(project); + const configFileErrors = this.updateConfiguredProject(project); this.updateProjectStructure(); + if (configFileErrors && configFileErrors.length > 0) { + this.eventHandler({ eventName: "configFileDiag", data: { triggerFile: project.projectFilename, configFileName: project.projectFilename, diagnostics: configFileErrors } }); + } } log(msg: string, type = "Err") { @@ -828,13 +834,13 @@ namespace ts.server { for (let j = 0, flen = this.openFileRoots.length; j < flen; j++) { const openFile = this.openFileRoots[j]; if (this.eventHandler) { - this.eventHandler("context", openFile.defaultProject, openFile.fileName); + this.eventHandler({ eventName: "context", data: { project: openFile.defaultProject, fileName: openFile.fileName } }); } } for (let j = 0, flen = this.openFilesReferenced.length; j < flen; j++) { const openFile = this.openFilesReferenced[j]; if (this.eventHandler) { - this.eventHandler("context", openFile.defaultProject, openFile.fileName); + this.eventHandler({ eventName: "context", data: { project: openFile.defaultProject, fileName: openFile.fileName } }); } } } @@ -1234,11 +1240,12 @@ namespace ts.server { else { this.updateConfiguredProject(project); } + return { configFileName }; } else { this.log("No config files found."); } - return configFileName ? { configFileName } : {}; + return {}; } /** @@ -1328,7 +1335,7 @@ namespace ts.server { return undefined; } - configFileToProjectOptions(configFilename: string): { succeeded: boolean, projectOptions?: ProjectOptions, errors?: Diagnostic[] } { + configFileToProjectOptions(configFilename: string): { succeeded: boolean, projectOptions?: ProjectOptions, errors: Diagnostic[] } { configFilename = ts.normalizePath(configFilename); // file references will be relative to dirPath (or absolute) const dirPath = ts.getDirectoryPath(configFilename); @@ -1341,20 +1348,19 @@ namespace ts.server { const parsedCommandLine = ts.parseJsonConfigFileContent(rawConfig.config, this.host, dirPath, /*existingOptions*/ {}, configFilename); Debug.assert(!!parsedCommandLine.fileNames); - if (parsedCommandLine.errors && (parsedCommandLine.errors.length > 0)) { - return { succeeded: false, errors: parsedCommandLine.errors }; - } - else if (parsedCommandLine.fileNames.length === 0) { + if (parsedCommandLine.fileNames.length === 0) { const error = createCompilerDiagnostic(Diagnostics.The_config_file_0_found_doesn_t_contain_any_source_files, configFilename); - return { succeeded: false, errors: [error] }; + return { succeeded: false, errors: concatenate(parsedCommandLine.errors, [error]) }; } else { + // if the project has some files, we can continue with the parsed options and tolerate + // errors in the parsedCommandLine const projectOptions: ProjectOptions = { files: parsedCommandLine.fileNames, wildcardDirectories: parsedCommandLine.wildcardDirectories, compilerOptions: parsedCommandLine.options, }; - return { succeeded: true, projectOptions }; + return { succeeded: true, projectOptions, errors: parsedCommandLine.errors }; } } } @@ -1377,10 +1383,11 @@ namespace ts.server { return false; } - openConfigFile(configFilename: string, clientFileName?: string): { success: boolean, project?: Project, errors?: Diagnostic[] } { - const { succeeded, projectOptions, errors } = this.configFileToProjectOptions(configFilename); + openConfigFile(configFilename: string, clientFileName?: string): { success: boolean, project?: Project, errors: Diagnostic[] } { + const { succeeded, projectOptions, errors: errorsFromConfigFile } = this.configFileToProjectOptions(configFilename); + // Note: even if "succeeded"" is true, "errors" may still exist, as they are just tolerated if (!succeeded) { - return { success: false, errors }; + return { success: false, errors: errorsFromConfigFile }; } else { if (!projectOptions.compilerOptions.disableSizeLimit && projectOptions.compilerOptions.allowJs) { @@ -1392,7 +1399,7 @@ namespace ts.server { project.projectFileWatcher = this.host.watchFile( toPath(configFilename, configFilename, createGetCanonicalFileName(sys.useCaseSensitiveFileNames)), _ => this.watchedProjectConfigFileChanged(project)); - return { success: true, project }; + return { success: true, project, errors: errorsFromConfigFile }; } } @@ -1433,7 +1440,7 @@ namespace ts.server { return watchers; }, >{}); - return { success: true, project: project, errors }; + return { success: true, project: project, errors: concatenate(errors, errorsFromConfigFile) }; } } @@ -1451,7 +1458,7 @@ namespace ts.server { if (projectOptions.compilerOptions && !projectOptions.compilerOptions.disableSizeLimit && this.exceedTotalNonTsFileSizeLimit(projectOptions.files)) { project.setProjectOptions(projectOptions); if (project.languageServiceDiabled) { - return; + return errors; } project.disableLanguageService(); @@ -1459,7 +1466,7 @@ namespace ts.server { project.directoryWatcher.close(); project.directoryWatcher = undefined; } - return; + return errors; } if (project.languageServiceDiabled) { @@ -1478,7 +1485,7 @@ namespace ts.server { } } project.finishGraph(); - return; + return errors; } // if the project is too large, the root files might not have been all loaded if the total @@ -1524,6 +1531,7 @@ namespace ts.server { project.setProjectOptions(projectOptions); project.finishGraph(); } + return errors; } } diff --git a/src/server/session.ts b/src/server/session.ts index 9383a54bd48..00468330ffb 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -153,16 +153,22 @@ namespace ts.server { private logger: Logger ) { this.projectService = - new ProjectService(host, logger, (eventName, project, fileName) => { - this.handleEvent(eventName, project, fileName); + new ProjectService(host, logger, event => { + this.handleEvent(event); }); } - private handleEvent(eventName: string, project: Project, fileName: string) { - if (eventName == "context") { - this.projectService.log("got context event, updating diagnostics for" + fileName, "Info"); - this.updateErrorCheck([{ fileName, project }], this.changeSeq, - (n) => n === this.changeSeq, 100); + private handleEvent(event: ProjectServiceEvent) { + switch (event.eventName) { + case "context": + const { project, fileName } = event.data; + this.projectService.log("got context event, updating diagnostics for" + fileName, "Info"); + this.updateErrorCheck([{ fileName, project }], this.changeSeq, + (n) => n === this.changeSeq, 100); + break; + case "configFileDiag": + const { triggerFile, configFileName, diagnostics } = event.data; + this.configFileDiagnosticEvent(triggerFile, configFileName, diagnostics); } } From b2074172e1ef7b6f389d398e737268c16dc2cd93 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Sat, 20 Aug 2016 20:47:30 -0700 Subject: [PATCH 196/233] Add test for configFile error tolerance --- src/harness/unittests/tsserverProjectSystem.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 49f033b9def..d4ed84191b4 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -623,5 +623,23 @@ namespace ts { checkNumberOfConfiguredProjects(projectService, 1); checkNumberOfInferredProjects(projectService, 0); }); + + it("should tolerate config file errors and still try to build a project", () => { + const configFile: FileOrFolder = { + path: "/a/b/tsconfig.json", + content: `{ + "compilerOptions": { + "target": "es6", + "allowAnything": true + }, + "someOtherProperty": {} + }` + }; + const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", [commonFile1, commonFile2, libFile, configFile]); + const projectService = new server.ProjectService(host, nullLogger); + projectService.openClientFile(commonFile1.path); + checkNumberOfConfiguredProjects(projectService, 1); + checkConfiguredProjectRootFiles(projectService.configuredProjects[0], [commonFile1.path, commonFile2.path]); + }); }); } \ No newline at end of file From a8ab52fd573dd28e1ee8a19f6271e6e98b35dd64 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Sat, 20 Aug 2016 20:47:35 -0700 Subject: [PATCH 197/233] Use TS parser to tolerate more errors in tsconfig.json --- src/harness/unittests/tsconfigParsing.ts | 20 +++ src/server/editorServices.ts | 173 ++++++++++++----------- src/services/utilities.ts | 20 +++ 3 files changed, 132 insertions(+), 81 deletions(-) diff --git a/src/harness/unittests/tsconfigParsing.ts b/src/harness/unittests/tsconfigParsing.ts index 557379dff3b..2c9bcdb8423 100644 --- a/src/harness/unittests/tsconfigParsing.ts +++ b/src/harness/unittests/tsconfigParsing.ts @@ -181,5 +181,25 @@ namespace ts { ["/d.ts", "/folder/e.ts"] ); }); + + it("parse and re-emit tsconfig.json file with diagnostics", () => { + const content = `{ + "compilerOptions": { + "allowJs": true + "outDir": "bin" + } + "files": ["file1.ts"] + }`; + const { configJsonObject, diagnostics } = parseAndReEmitConfigJSONFile(content); + const expectedResult = { + compilerOptions: { + allowJs: true, + outDir: "bin" + }, + files: ["file1.ts"] + }; + assert.isTrue(diagnostics.length === 2); + assert.equal(JSON.stringify(configJsonObject), JSON.stringify(expectedResult)); + }); }); } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 0d84fa54a4a..255ad8547ea 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -702,7 +702,8 @@ namespace ts.server { } handleProjectFileListChanges(project: Project) { - const { projectOptions } = this.configFileToProjectOptions(project.projectFilename); + const { projectOptions, errors } = this.configFileToProjectOptions(project.projectFilename); + this.reportConfigFileDiagnostics(project.projectFilename, errors); const newRootFiles = projectOptions.files.map((f => this.getCanonicalFileName(f))); const currentRootFiles = project.getRootFiles().map((f => this.getCanonicalFileName(f))); @@ -720,18 +721,32 @@ namespace ts.server { } } + reportConfigFileDiagnostics(configFileName: string, diagnostics: Diagnostic[], triggerFile?: string) { + if (diagnostics && diagnostics.length > 0) { + this.eventHandler({ + eventName: "configFileDiag", + data: { configFileName, diagnostics, triggerFile } + }); + } + } + /** * This is the callback function when a watched directory has an added tsconfig file. */ directoryWatchedForTsconfigChanged(fileName: string) { - if (ts.getBaseFileName(fileName) != "tsconfig.json") { + if (ts.getBaseFileName(fileName) !== "tsconfig.json") { this.log(fileName + " is not tsconfig.json"); return; } this.log("Detected newly added tsconfig file: " + fileName); - const { projectOptions } = this.configFileToProjectOptions(fileName); + const { projectOptions, errors } = this.configFileToProjectOptions(fileName); + this.reportConfigFileDiagnostics(fileName, errors); + + if (!projectOptions) { + return; + } const rootFilesInTsconfig = projectOptions.files.map(f => this.getCanonicalFileName(f)); const openFileRoots = this.openFileRoots.map(s => this.getCanonicalFileName(s.fileName)); @@ -1224,7 +1239,7 @@ namespace ts.server { const project = this.findConfiguredProjectByConfigFile(configFileName); if (!project) { const configResult = this.openConfigFile(configFileName, fileName); - if (!configResult.success) { + if (!configResult.project) { return { configFileName, configFileErrors: configResult.errors }; } else { @@ -1335,33 +1350,31 @@ namespace ts.server { return undefined; } - configFileToProjectOptions(configFilename: string): { succeeded: boolean, projectOptions?: ProjectOptions, errors: Diagnostic[] } { + configFileToProjectOptions(configFilename: string): { projectOptions?: ProjectOptions, errors: Diagnostic[] } { configFilename = ts.normalizePath(configFilename); + let errors: Diagnostic[] = []; // file references will be relative to dirPath (or absolute) const dirPath = ts.getDirectoryPath(configFilename); const contents = this.host.readFile(configFilename); - const rawConfig: { config?: ProjectOptions; error?: Diagnostic; } = ts.parseConfigFileTextToJson(configFilename, contents); - if (rawConfig.error) { - return { succeeded: false, errors: [rawConfig.error] }; + const { configJsonObject, diagnostics } = ts.parseAndReEmitConfigJSONFile(contents); + errors = concatenate(errors, diagnostics); + const parsedCommandLine = ts.parseJsonConfigFileContent(configJsonObject, this.host, dirPath, /*existingOptions*/ {}, configFilename); + errors = concatenate(errors, parsedCommandLine.errors); + Debug.assert(!!parsedCommandLine.fileNames); + + if (parsedCommandLine.fileNames.length === 0) { + errors.push(createCompilerDiagnostic(Diagnostics.The_config_file_0_found_doesn_t_contain_any_source_files, configFilename)); + return { errors }; } else { - const parsedCommandLine = ts.parseJsonConfigFileContent(rawConfig.config, this.host, dirPath, /*existingOptions*/ {}, configFilename); - Debug.assert(!!parsedCommandLine.fileNames); - - if (parsedCommandLine.fileNames.length === 0) { - const error = createCompilerDiagnostic(Diagnostics.The_config_file_0_found_doesn_t_contain_any_source_files, configFilename); - return { succeeded: false, errors: concatenate(parsedCommandLine.errors, [error]) }; - } - else { - // if the project has some files, we can continue with the parsed options and tolerate - // errors in the parsedCommandLine - const projectOptions: ProjectOptions = { - files: parsedCommandLine.fileNames, - wildcardDirectories: parsedCommandLine.wildcardDirectories, - compilerOptions: parsedCommandLine.options, - }; - return { succeeded: true, projectOptions, errors: parsedCommandLine.errors }; - } + // if the project has some files, we can continue with the parsed options and tolerate + // errors in the parsedCommandLine + const projectOptions: ProjectOptions = { + files: parsedCommandLine.fileNames, + wildcardDirectories: parsedCommandLine.wildcardDirectories, + compilerOptions: parsedCommandLine.options, + }; + return { projectOptions, errors }; } } @@ -1383,65 +1396,63 @@ namespace ts.server { return false; } - openConfigFile(configFilename: string, clientFileName?: string): { success: boolean, project?: Project, errors: Diagnostic[] } { - const { succeeded, projectOptions, errors: errorsFromConfigFile } = this.configFileToProjectOptions(configFilename); - // Note: even if "succeeded"" is true, "errors" may still exist, as they are just tolerated - if (!succeeded) { - return { success: false, errors: errorsFromConfigFile }; + openConfigFile(configFilename: string, clientFileName?: string): { project?: Project, errors: Diagnostic[] } { + const parseConfigFileResult = this.configFileToProjectOptions(configFilename); + let errors = parseConfigFileResult.errors; + if (!parseConfigFileResult.projectOptions) { + return { errors }; } - else { - if (!projectOptions.compilerOptions.disableSizeLimit && projectOptions.compilerOptions.allowJs) { - if (this.exceedTotalNonTsFileSizeLimit(projectOptions.files)) { - const project = this.createProject(configFilename, projectOptions, /*languageServiceDisabled*/ true); + const projectOptions = parseConfigFileResult.projectOptions; + if (!projectOptions.compilerOptions.disableSizeLimit && projectOptions.compilerOptions.allowJs) { + if (this.exceedTotalNonTsFileSizeLimit(projectOptions.files)) { + const project = this.createProject(configFilename, projectOptions, /*languageServiceDisabled*/ true); - // for configured projects with languageService disabled, we only watch its config file, - // do not care about the directory changes in the folder. - project.projectFileWatcher = this.host.watchFile( - toPath(configFilename, configFilename, createGetCanonicalFileName(sys.useCaseSensitiveFileNames)), - _ => this.watchedProjectConfigFileChanged(project)); - return { success: true, project, errors: errorsFromConfigFile }; - } + // for configured projects with languageService disabled, we only watch its config file, + // do not care about the directory changes in the folder. + project.projectFileWatcher = this.host.watchFile( + toPath(configFilename, configFilename, createGetCanonicalFileName(sys.useCaseSensitiveFileNames)), + _ => this.watchedProjectConfigFileChanged(project)); + return { project, errors }; + } + } + + const project = this.createProject(configFilename, projectOptions); + for (const rootFilename of projectOptions.files) { + if (this.host.fileExists(rootFilename)) { + const info = this.openFile(rootFilename, /*openedByClient*/ clientFileName == rootFilename); + project.addRoot(info); + } + else { + (errors || (errors = [])).push(createCompilerDiagnostic(Diagnostics.File_0_not_found, rootFilename)); + } + } + project.finishGraph(); + project.projectFileWatcher = this.host.watchFile(configFilename, _ => this.watchedProjectConfigFileChanged(project)); + + const configDirectoryPath = ts.getDirectoryPath(configFilename); + + this.log("Add recursive watcher for: " + configDirectoryPath); + project.directoryWatcher = this.host.watchDirectory( + configDirectoryPath, + path => this.directoryWatchedForSourceFilesChanged(project, path), + /*recursive*/ true + ); + + project.directoriesWatchedForWildcards = reduceProperties(createMap(projectOptions.wildcardDirectories), (watchers, flag, directory) => { + if (comparePaths(configDirectoryPath, directory, ".", !this.host.useCaseSensitiveFileNames) !== Comparison.EqualTo) { + const recursive = (flag & WatchDirectoryFlags.Recursive) !== 0; + this.log(`Add ${ recursive ? "recursive " : ""}watcher for: ${directory}`); + watchers[directory] = this.host.watchDirectory( + directory, + path => this.directoryWatchedForSourceFilesChanged(project, path), + recursive + ); } - const project = this.createProject(configFilename, projectOptions); - let errors: Diagnostic[]; - for (const rootFilename of projectOptions.files) { - if (this.host.fileExists(rootFilename)) { - const info = this.openFile(rootFilename, /*openedByClient*/ clientFileName == rootFilename); - project.addRoot(info); - } - else { - (errors || (errors = [])).push(createCompilerDiagnostic(Diagnostics.File_0_not_found, rootFilename)); - } - } - project.finishGraph(); - project.projectFileWatcher = this.host.watchFile(configFilename, _ => this.watchedProjectConfigFileChanged(project)); + return watchers; + }, >{}); - const configDirectoryPath = ts.getDirectoryPath(configFilename); - - this.log("Add recursive watcher for: " + configDirectoryPath); - project.directoryWatcher = this.host.watchDirectory( - configDirectoryPath, - path => this.directoryWatchedForSourceFilesChanged(project, path), - /*recursive*/ true - ); - - project.directoriesWatchedForWildcards = reduceProperties(createMap(projectOptions.wildcardDirectories), (watchers, flag, directory) => { - if (comparePaths(configDirectoryPath, directory, ".", !this.host.useCaseSensitiveFileNames) !== Comparison.EqualTo) { - const recursive = (flag & WatchDirectoryFlags.Recursive) !== 0; - this.log(`Add ${ recursive ? "recursive " : ""}watcher for: ${directory}`); - watchers[directory] = this.host.watchDirectory( - directory, - path => this.directoryWatchedForSourceFilesChanged(project, path), - recursive - ); - } - - return watchers; - }, >{}); - - return { success: true, project: project, errors: concatenate(errors, errorsFromConfigFile) }; - } + return { project: project, errors }; } updateConfiguredProject(project: Project): Diagnostic[] { @@ -1450,8 +1461,8 @@ namespace ts.server { this.removeProject(project); } else { - const { succeeded, projectOptions, errors } = this.configFileToProjectOptions(project.projectFilename); - if (!succeeded) { + const { projectOptions, errors } = this.configFileToProjectOptions(project.projectFilename); + if (!projectOptions) { return errors; } else { diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 7f41cdbb3a4..858f889bc6c 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -925,4 +925,24 @@ namespace ts { } return ensureScriptKind(fileName, scriptKind); } + + export function parseAndReEmitConfigJSONFile(content: string) { + const options: TranspileOptions = { + fileName: "config.js", + compilerOptions: { + target: ScriptTarget.ES6 + }, + reportDiagnostics: true + }; + const { outputText, diagnostics } = ts.transpileModule("(" + content + ")", options); + // Becasue the content was wrapped in "()", the start position of diagnostics needs to be subtract by 1 + // also, the emitted result will have "(" in the beginning and ");" in the end. We need to strip these + // as well + const trimmedOutput = outputText.trim(); + const configJsonObject = JSON.parse(trimmedOutput.substring(1, trimmedOutput.length - 2)); + for (const diagnostic of diagnostics) { + diagnostic.start = diagnostic.start - 1; + } + return { configJsonObject, diagnostics }; + } } \ No newline at end of file From 582d8b8fc8047fcda47024a37bf5258d2050fe3b Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 21 Aug 2016 16:18:45 -0700 Subject: [PATCH 198/233] Implement tuple types as type references to synthesized generic types --- src/compiler/checker.ts | 177 +++++++++++++++++++--------------------- src/compiler/types.ts | 5 +- 2 files changed, 87 insertions(+), 95 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8568cd5feed..d87dbd1696c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -106,7 +106,7 @@ namespace ts { isOptionalParameter }; - const tupleTypes = createMap(); + const tupleTypes: TupleType[] = []; const unionTypes = createMap(); const intersectionTypes = createMap(); const stringLiteralTypes = createMap(); @@ -2145,9 +2145,6 @@ namespace ts { // The specified symbol flags need to be reinterpreted as type flags buildSymbolDisplay(type.symbol, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, nextFlags); } - else if (type.flags & TypeFlags.Tuple) { - writeTupleType(type); - } else if (!(flags & TypeFormatFlags.InTypeAlias) && type.flags & (TypeFlags.Anonymous | TypeFlags.UnionOrIntersection) && type.aliasSymbol) { const typeArguments = type.aliasTypeArguments; writeSymbolTypeReference(type.aliasSymbol, typeArguments, 0, typeArguments ? typeArguments.length : 0, nextFlags); @@ -2214,6 +2211,11 @@ namespace ts { writePunctuation(writer, SyntaxKind.OpenBracketToken); writePunctuation(writer, SyntaxKind.CloseBracketToken); } + else if (type.target.flags & TypeFlags.Tuple) { + writePunctuation(writer, SyntaxKind.OpenBracketToken); + writeTypeList(type.typeArguments.slice(0, type.target.typeParameters.length), SyntaxKind.CommaToken); + writePunctuation(writer, SyntaxKind.CloseBracketToken); + } else { // Write the type reference in the format f.g.C where A and B are type arguments // for outer type parameters, and f and g are the respective declaring containers of those @@ -2242,12 +2244,6 @@ namespace ts { } } - function writeTupleType(type: TupleType) { - writePunctuation(writer, SyntaxKind.OpenBracketToken); - writeTypeList(type.elementTypes, SyntaxKind.CommaToken); - writePunctuation(writer, SyntaxKind.CloseBracketToken); - } - function writeUnionOrIntersectionType(type: UnionOrIntersectionType, flags: TypeFormatFlags) { if (flags & TypeFormatFlags.InElementType) { writePunctuation(writer, SyntaxKind.OpenParenToken); @@ -2958,7 +2954,7 @@ namespace ts { : elementType; if (!type) { if (isTupleType(parentType)) { - error(declaration, Diagnostics.Tuple_type_0_with_length_1_cannot_be_assigned_to_tuple_with_length_2, typeToString(parentType), (parentType).elementTypes.length, pattern.elements.length); + error(declaration, Diagnostics.Tuple_type_0_with_length_1_cannot_be_assigned_to_tuple_with_length_2, typeToString(parentType), getTypeReferenceArity(parentType), pattern.elements.length); } else { error(declaration, Diagnostics.Type_0_has_no_property_1, typeToString(parentType), propName); @@ -3150,12 +3146,12 @@ namespace ts { } // If the pattern has at least one element, and no rest element, then it should imply a tuple type. const elementTypes = map(elements, e => e.kind === SyntaxKind.OmittedExpression ? anyType : getTypeFromBindingElement(e, includePatternInType, reportErrors)); + let result = createTupleType(elementTypes); if (includePatternInType) { - const result = createNewTupleType(elementTypes); + result = cloneTypeReference(result); result.pattern = pattern; - return result; } - return createTupleType(elementTypes); + return result; } // Return the type implied by a binding pattern. This is the type implied purely by the binding pattern itself @@ -3564,18 +3560,19 @@ namespace ts { } function getBaseTypes(type: InterfaceType): ObjectType[] { - const isClass = type.symbol.flags & SymbolFlags.Class; - const isInterface = type.symbol.flags & SymbolFlags.Interface; if (!type.resolvedBaseTypes) { - if (!isClass && !isInterface) { - Debug.fail("type must be class or interface"); + if (type.flags & TypeFlags.Tuple) { + type.resolvedBaseTypes = [createArrayType(getUnionType(type.typeParameters))]; } - if (isClass) { + else if (type.symbol.flags & SymbolFlags.Class) { resolveBaseTypesOfClass(type); } - if (isInterface) { + else if (type.symbol.flags & SymbolFlags.Interface) { resolveBaseTypesOfInterface(type); } + else { + Debug.fail("type must be class or interface"); + } } return type.resolvedBaseTypes; } @@ -4001,20 +3998,25 @@ namespace ts { return createTypeReference((type).target, concatenate((type).typeArguments, [thisArgument || (type).target.thisType])); } - if (type.flags & TypeFlags.Tuple) { - return createTupleType((type as TupleType).elementTypes, thisArgument); - } return type; } function resolveObjectTypeMembers(type: ObjectType, source: InterfaceTypeWithDeclaredMembers, typeParameters: TypeParameter[], typeArguments: Type[]) { - let mapper = identityMapper; - let members = source.symbol.members; - let callSignatures = source.declaredCallSignatures; - let constructSignatures = source.declaredConstructSignatures; - let stringIndexInfo = source.declaredStringIndexInfo; - let numberIndexInfo = source.declaredNumberIndexInfo; - if (!rangeEquals(typeParameters, typeArguments, 0, typeParameters.length)) { + let mapper: TypeMapper; + let members: SymbolTable; + let callSignatures: Signature[]; + let constructSignatures: Signature[]; + let stringIndexInfo: IndexInfo; + let numberIndexInfo: IndexInfo; + if (rangeEquals(typeParameters, typeArguments, 0, typeParameters.length)) { + mapper = identityMapper; + members = source.symbol ? source.symbol.members : createSymbolTable(source.declaredProperties); + callSignatures = source.declaredCallSignatures; + constructSignatures = source.declaredConstructSignatures; + stringIndexInfo = source.declaredStringIndexInfo; + numberIndexInfo = source.declaredNumberIndexInfo; + } + else { mapper = createTypeMapper(typeParameters, typeArguments); members = createInstantiatedSymbolTable(source.declaredProperties, mapper, /*mappingThisOnly*/ typeParameters.length === 1); callSignatures = instantiateList(source.declaredCallSignatures, mapper, instantiateSignature); @@ -4024,7 +4026,7 @@ namespace ts { } const baseTypes = getBaseTypes(source); if (baseTypes.length) { - if (members === source.symbol.members) { + if (source.symbol && members === source.symbol.members) { members = createSymbolTable(source.declaredProperties); } const thisArgument = lastOrUndefined(typeArguments); @@ -4094,26 +4096,6 @@ namespace ts { return result; } - function createTupleTypeMemberSymbols(memberTypes: Type[]): SymbolTable { - const members = createMap(); - for (let i = 0; i < memberTypes.length; i++) { - const symbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "" + i); - symbol.type = memberTypes[i]; - members[i] = symbol; - } - return members; - } - - function resolveTupleTypeMembers(type: TupleType) { - const arrayElementType = getUnionType(type.elementTypes); - // Make the tuple type itself the 'this' type by including an extra type argument - // (Unless it's provided in the case that the tuple is a type parameter constraint) - const arrayType = resolveStructuredTypeMembers(createTypeFromGenericGlobalType(globalArrayType, [arrayElementType, type.thisType || type])); - const members = createTupleTypeMemberSymbols(type.elementTypes); - addInheritedMembers(members, arrayType.properties); - setObjectTypeMembers(type, members, arrayType.callSignatures, arrayType.constructSignatures, arrayType.stringIndexInfo, arrayType.numberIndexInfo); - } - function findMatchingSignature(signatureList: Signature[], signature: Signature, partialMatch: boolean, ignoreThisTypes: boolean, ignoreReturnTypes: boolean): Signature { for (const s of signatureList) { if (compareSignaturesIdentical(s, signature, partialMatch, ignoreThisTypes, ignoreReturnTypes, compareTypesIdentical)) { @@ -4292,9 +4274,6 @@ namespace ts { else if (type.flags & TypeFlags.Anonymous) { resolveAnonymousTypeMembers(type); } - else if (type.flags & TypeFlags.Tuple) { - resolveTupleTypeMembers(type); - } else if (type.flags & TypeFlags.Union) { resolveUnionTypeMembers(type); } @@ -4992,6 +4971,13 @@ namespace ts { return type; } + function cloneTypeReference(source: TypeReference): TypeReference { + let type = createObjectType(source.flags, source.symbol); + type.target = source.target; + type.typeArguments = source.typeArguments; + return type; + } + // Get type from reference to class or interface function getTypeFromClassOrInterfaceReference(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference, symbol: Symbol): Type { const type = getDeclaredTypeOfSymbol(getMergedSymbol(symbol)); @@ -5234,17 +5220,44 @@ namespace ts { return links.resolvedType; } - function createTupleType(elementTypes: Type[], thisType?: Type) { - const id = getTypeListId(elementTypes) + "," + (thisType ? thisType.id : 0); - return tupleTypes[id] || (tupleTypes[id] = createNewTupleType(elementTypes, thisType)); + function createTupleTypeOfArity(arity: number): TupleType { + const typeParameters: TypeParameter[] = []; + const properties: Symbol[] = []; + for (let i = 0; i < arity; i++) { + const typeParameter = createType(TypeFlags.TypeParameter); + typeParameters.push(typeParameter); + const property = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "" + i); + property.type = typeParameter; + properties.push(property); + } + const type = createObjectType(TypeFlags.Tuple | TypeFlags.Reference); + type.typeParameters = typeParameters; + type.outerTypeParameters = undefined; + type.localTypeParameters = typeParameters; + type.instantiations = createMap(); + type.instantiations[getTypeListId(type.typeParameters)] = type; + type.target = type; + type.typeArguments = type.typeParameters; + type.thisType = createType(TypeFlags.TypeParameter | TypeFlags.ThisType); + type.thisType.constraint = type; + type.declaredProperties = properties; + type.declaredCallSignatures = emptyArray; + type.declaredConstructSignatures = emptyArray; + type.declaredStringIndexInfo = undefined; + type.declaredNumberIndexInfo = undefined; + return type; } - function createNewTupleType(elementTypes: Type[], thisType?: Type) { - const propagatedFlags = getPropagatingFlagsOfTypes(elementTypes, /*excludeKinds*/ 0); - const type = createObjectType(TypeFlags.Tuple | propagatedFlags); - type.elementTypes = elementTypes; - type.thisType = thisType; - return type; + function getTupleTypeOfArity(arity: number): TupleType { + return tupleTypes[arity] || (tupleTypes[arity] = createTupleTypeOfArity(arity)); + } + + function getTypeReferenceArity(type: TypeReference): number { + return type.target.typeParameters.length; + } + + function createTupleType(elementTypes: Type[]) { + return createTypeReference(getTupleTypeOfArity(elementTypes.length), elementTypes); } function getTypeFromTupleTypeNode(node: TupleTypeNode): Type { @@ -5845,9 +5858,6 @@ namespace ts { if (type.flags & TypeFlags.Reference) { return createTypeReference((type).target, instantiateList((type).typeArguments, mapper, instantiateType)); } - if (type.flags & TypeFlags.Tuple) { - return createTupleType(instantiateList((type).elementTypes, mapper, instantiateType)); - } if (type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Primitive)) { return getUnionType(instantiateList((type).types, mapper, instantiateType), /*subtypeReduction*/ false, type.aliasSymbol, mapper.targetTypes); } @@ -7168,8 +7178,8 @@ namespace ts { * Check if a Type was written as a tuple type literal. * Prefer using isTupleLikeType() unless the use of `elementTypes` is required. */ - function isTupleType(type: Type): type is TupleType { - return !!(type.flags & TypeFlags.Tuple); + function isTupleType(type: Type): boolean { + return !!(type.flags & TypeFlags.Reference && (type).target.flags & TypeFlags.Tuple); } function getFalsyFlagsOfTypes(types: Type[]): TypeFlags { @@ -7301,11 +7311,8 @@ namespace ts { if (type.flags & TypeFlags.Union) { return getUnionType(map((type).types, getWidenedConstituentType)); } - if (isArrayType(type)) { - return createArrayType(getWidenedType((type).typeArguments[0])); - } - if (isTupleType(type)) { - return createTupleType(map(type.elementTypes, getWidenedType)); + if (isArrayType(type) || isTupleType(type)) { + return createTypeReference((type).target, map((type).typeArguments, getWidenedType)); } } return type; @@ -7331,11 +7338,8 @@ namespace ts { } } } - if (isArrayType(type)) { - return reportWideningErrorsInType((type).typeArguments[0]); - } - if (isTupleType(type)) { - for (const t of type.elementTypes) { + if (isArrayType(type) || isTupleType(type)) { + for (const t of (type).typeArguments) { if (reportWideningErrorsInType(t)) { errorReported = true; } @@ -7445,7 +7449,6 @@ namespace ts { function couldContainTypeParameters(type: Type): boolean { return !!(type.flags & TypeFlags.TypeParameter || type.flags & TypeFlags.Reference && forEach((type).typeArguments, couldContainTypeParameters) || - type.flags & TypeFlags.Tuple && forEach((type).elementTypes, couldContainTypeParameters) || type.flags & TypeFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.Class) || type.flags & TypeFlags.UnionOrIntersection && couldUnionOrIntersectionContainTypeParameters(type)); } @@ -7548,14 +7551,6 @@ namespace ts { inferFromTypes(sourceTypes[i], targetTypes[i]); } } - else if (source.flags & TypeFlags.Tuple && target.flags & TypeFlags.Tuple && (source).elementTypes.length === (target).elementTypes.length) { - // If source and target are tuples of the same size, infer from element types - const sourceTypes = (source).elementTypes; - const targetTypes = (target).elementTypes; - for (let i = 0; i < sourceTypes.length; i++) { - inferFromTypes(sourceTypes[i], targetTypes[i]); - } - } else if (target.flags & TypeFlags.UnionOrIntersection) { const targetTypes = (target).types; let typeParameterCount = 0; @@ -9913,7 +9908,7 @@ namespace ts { // If array literal is actually a destructuring pattern, mark it as an implied type. We do this such // that we get the same behavior for "var [x, y] = []" and "[x, y] = []". if (inDestructuringPattern && elementTypes.length) { - const type = createNewTupleType(elementTypes); + const type = cloneTypeReference(createTupleType(elementTypes)); type.pattern = node; return type; } @@ -9927,7 +9922,7 @@ namespace ts { for (let i = elementTypes.length; i < patternElements.length; i++) { const patternElement = patternElements[i]; if (hasDefaultValue(patternElement)) { - elementTypes.push((contextualType).elementTypes[i]); + elementTypes.push((contextualType).typeArguments[i]); } else { if (patternElement.kind !== SyntaxKind.OmittedExpression) { @@ -13038,7 +13033,7 @@ namespace ts { // such as NodeCheckFlags.LexicalThis on "this"expression. checkExpression(element); if (isTupleType(sourceType)) { - error(element, Diagnostics.Tuple_type_0_with_length_1_cannot_be_assigned_to_tuple_with_length_2, typeToString(sourceType), (sourceType).elementTypes.length, elements.length); + error(element, Diagnostics.Tuple_type_0_with_length_1_cannot_be_assigned_to_tuple_with_length_2, typeToString(sourceType), getTypeReferenceArity(sourceType), elements.length); } else { error(element, Diagnostics.Type_0_has_no_property_1, typeToString(sourceType), propName); @@ -18515,7 +18510,7 @@ namespace ts { else if (isTypeOfKind(type, TypeFlags.StringLike)) { return TypeReferenceSerializationKind.StringLikeType; } - else if (isTypeOfKind(type, TypeFlags.Tuple)) { + else if (isTupleType(type)) { return TypeReferenceSerializationKind.ArrayLikeType; } else if (isTypeOfKind(type, TypeFlags.ESSymbol)) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 7594d2c3fb8..f5d71d0935b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2385,10 +2385,7 @@ namespace ts { instantiations: Map; // Generic instantiation cache } - export interface TupleType extends ObjectType { - elementTypes: Type[]; // Element types - thisType?: Type; // This-type of tuple (only needed for tuples that are constraints of type parameters) - } + export interface TupleType extends GenericType { } export interface UnionOrIntersectionType extends Type { types: Type[]; // Constituent types From 7d82c22bc376d230e5c896aeeadf2ac922c058ca Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 21 Aug 2016 16:53:34 -0700 Subject: [PATCH 199/233] Add comments + minor changes --- src/compiler/checker.ts | 25 ++++++++++++++++--------- src/compiler/types.ts | 4 +--- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d87dbd1696c..9efaf72c03a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -106,7 +106,7 @@ namespace ts { isOptionalParameter }; - const tupleTypes: TupleType[] = []; + const tupleTypes: GenericType[] = []; const unionTypes = createMap(); const intersectionTypes = createMap(); const stringLiteralTypes = createMap(); @@ -2213,7 +2213,7 @@ namespace ts { } else if (type.target.flags & TypeFlags.Tuple) { writePunctuation(writer, SyntaxKind.OpenBracketToken); - writeTypeList(type.typeArguments.slice(0, type.target.typeParameters.length), SyntaxKind.CommaToken); + writeTypeList(type.typeArguments.slice(0, getTypeReferenceArity(type)), SyntaxKind.CommaToken); writePunctuation(writer, SyntaxKind.CloseBracketToken); } else { @@ -4978,6 +4978,10 @@ namespace ts { return type; } + function getTypeReferenceArity(type: TypeReference): number { + return type.target.typeParameters.length; + } + // Get type from reference to class or interface function getTypeFromClassOrInterfaceReference(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference, symbol: Symbol): Type { const type = getDeclaredTypeOfSymbol(getMergedSymbol(symbol)); @@ -5220,7 +5224,14 @@ namespace ts { return links.resolvedType; } - function createTupleTypeOfArity(arity: number): TupleType { + // We represent tuple types as type references to synthesized generic interface types created by + // this function. The types are of the form: + // + // interface Tuple extends Array { 0: T0, 1: T1, 2: T2, ... } + // + // Note that the generic type created by this function has no symbol associated with it. The same + // is true for each of the synthesized type parameters. + function createTupleTypeOfArity(arity: number): GenericType { const typeParameters: TypeParameter[] = []; const properties: Symbol[] = []; for (let i = 0; i < arity; i++) { @@ -5230,7 +5241,7 @@ namespace ts { property.type = typeParameter; properties.push(property); } - const type = createObjectType(TypeFlags.Tuple | TypeFlags.Reference); + const type = createObjectType(TypeFlags.Tuple | TypeFlags.Reference); type.typeParameters = typeParameters; type.outerTypeParameters = undefined; type.localTypeParameters = typeParameters; @@ -5248,14 +5259,10 @@ namespace ts { return type; } - function getTupleTypeOfArity(arity: number): TupleType { + function getTupleTypeOfArity(arity: number): GenericType { return tupleTypes[arity] || (tupleTypes[arity] = createTupleTypeOfArity(arity)); } - function getTypeReferenceArity(type: TypeReference): number { - return type.target.typeParameters.length; - } - function createTupleType(elementTypes: Type[]) { return createTypeReference(getTupleTypeOfArity(elementTypes.length), elementTypes); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f5d71d0935b..656f155784a 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2263,7 +2263,7 @@ namespace ts { Class = 1 << 15, // Class Interface = 1 << 16, // Interface Reference = 1 << 17, // Generic type reference - Tuple = 1 << 18, // Tuple + Tuple = 1 << 18, // Synthesized generic tuple type Union = 1 << 19, // Union (T | U) Intersection = 1 << 20, // Intersection (T & U) Anonymous = 1 << 21, // Anonymous @@ -2385,8 +2385,6 @@ namespace ts { instantiations: Map; // Generic instantiation cache } - export interface TupleType extends GenericType { } - export interface UnionOrIntersectionType extends Type { types: Type[]; // Constituent types /* @internal */ From 2e8d11e9c09e8ffb20ff8d8a69399f652fddc510 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 21 Aug 2016 17:01:07 -0700 Subject: [PATCH 200/233] Accept new baselines --- .../arityAndOrderCompatibility01.errors.txt | 6 ++--- .../reference/arrayLiterals3.errors.txt | 6 ++--- .../reference/castingTuple.errors.txt | 16 +++++-------- .../contextualTypeWithTuple.errors.txt | 20 +++++++--------- ...TypedBindingInitializerNegative.errors.txt | 6 ++--- .../declarationsAndAssignments.errors.txt | 16 +++++-------- ...rayBindingPatternAndAssignment2.errors.txt | 6 ++--- ...tructuringParameterDeclaration2.errors.txt | 24 +++++++------------ ...structuringParameterProperties2.errors.txt | 6 ++--- ...structuringParameterProperties5.errors.txt | 10 ++++---- ...structuringVariableDeclaration2.errors.txt | 18 +++++--------- .../genericCallWithTupleType.errors.txt | 12 ++++------ .../iterableArrayPattern29.errors.txt | 6 ++--- .../optionalBindingParameters1.errors.txt | 6 ++--- ...alBindingParametersInOverloads1.errors.txt | 6 ++--- .../baselines/reference/tupleTypes.errors.txt | 18 +++++--------- 16 files changed, 64 insertions(+), 118 deletions(-) diff --git a/tests/baselines/reference/arityAndOrderCompatibility01.errors.txt b/tests/baselines/reference/arityAndOrderCompatibility01.errors.txt index d60f0b7d03c..a6a685069d6 100644 --- a/tests/baselines/reference/arityAndOrderCompatibility01.errors.txt +++ b/tests/baselines/reference/arityAndOrderCompatibility01.errors.txt @@ -39,8 +39,7 @@ tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(26,5): error tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(27,5): error TS2322: Type '{ 0: string; 1: number; }' is not assignable to type '[string]'. Property 'length' is missing in type '{ 0: string; 1: number; }'. tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(28,5): error TS2322: Type '[string, number]' is not assignable to type '[number, string]'. - Types of property '0' are incompatible. - Type 'string' is not assignable to type 'number'. + Type 'string' is not assignable to type 'number'. tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(29,5): error TS2322: Type 'StrNum' is not assignable to type '[number, string]'. Types of property '0' are incompatible. Type 'string' is not assignable to type 'number'. @@ -136,8 +135,7 @@ tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(30,5): error var n1: [number, string] = x; ~~ !!! error TS2322: Type '[string, number]' is not assignable to type '[number, string]'. -!!! error TS2322: Types of property '0' are incompatible. -!!! error TS2322: Type 'string' is not assignable to type 'number'. +!!! error TS2322: Type 'string' is not assignable to type 'number'. var n2: [number, string] = y; ~~ !!! error TS2322: Type 'StrNum' is not assignable to type '[number, string]'. diff --git a/tests/baselines/reference/arrayLiterals3.errors.txt b/tests/baselines/reference/arrayLiterals3.errors.txt index 8e6c7eccf81..f221fd96fab 100644 --- a/tests/baselines/reference/arrayLiterals3.errors.txt +++ b/tests/baselines/reference/arrayLiterals3.errors.txt @@ -1,8 +1,7 @@ tests/cases/conformance/expressions/arrayLiterals/arrayLiterals3.ts(10,5): error TS2322: Type 'undefined[]' is not assignable to type '[any, any, any]'. Property '0' is missing in type 'undefined[]'. tests/cases/conformance/expressions/arrayLiterals/arrayLiterals3.ts(11,5): error TS2322: Type '[string, number, boolean]' is not assignable to type '[boolean, string, number]'. - Types of property '0' are incompatible. - Type 'string' is not assignable to type 'boolean'. + Type 'string' is not assignable to type 'boolean'. tests/cases/conformance/expressions/arrayLiterals/arrayLiterals3.ts(17,5): error TS2322: Type '[number, number, string, boolean]' is not assignable to type '[number, number]'. Types of property 'pop' are incompatible. Type '() => string | number | boolean' is not assignable to type '() => number'. @@ -37,8 +36,7 @@ tests/cases/conformance/expressions/arrayLiterals/arrayLiterals3.ts(34,5): error var a1: [boolean, string, number] = ["string", 1, true]; // Error ~~ !!! error TS2322: Type '[string, number, boolean]' is not assignable to type '[boolean, string, number]'. -!!! error TS2322: Types of property '0' are incompatible. -!!! error TS2322: Type 'string' is not assignable to type 'boolean'. +!!! error TS2322: Type 'string' is not assignable to type 'boolean'. // The resulting type an array literal expression is determined as follows: // - If the array literal contains no spread elements and is an array assignment pattern in a destructuring assignment (section 4.17.1), diff --git a/tests/baselines/reference/castingTuple.errors.txt b/tests/baselines/reference/castingTuple.errors.txt index 6d5549c6bf2..2f9eeedf9ef 100644 --- a/tests/baselines/reference/castingTuple.errors.txt +++ b/tests/baselines/reference/castingTuple.errors.txt @@ -1,10 +1,8 @@ tests/cases/conformance/types/tuple/castingTuple.ts(28,10): error TS2352: Type '[number, string]' cannot be converted to type '[number, number]'. - Types of property '1' are incompatible. - Type 'string' is not comparable to type 'number'. + Type 'string' is not comparable to type 'number'. tests/cases/conformance/types/tuple/castingTuple.ts(29,10): error TS2352: Type '[C, D]' cannot be converted to type '[A, I]'. - Types of property '0' are incompatible. - Type 'C' is not comparable to type 'A'. - Property 'a' is missing in type 'C'. + Type 'C' is not comparable to type 'A'. + Property 'a' is missing in type 'C'. tests/cases/conformance/types/tuple/castingTuple.ts(30,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'array1' must be of type '{}[]', but here has type 'number[]'. tests/cases/conformance/types/tuple/castingTuple.ts(31,1): error TS2304: Cannot find name 't4'. @@ -40,14 +38,12 @@ tests/cases/conformance/types/tuple/castingTuple.ts(31,1): error TS2304: Cannot var t3 = <[number, number]>numStrTuple; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2352: Type '[number, string]' cannot be converted to type '[number, number]'. -!!! error TS2352: Types of property '1' are incompatible. -!!! error TS2352: Type 'string' is not comparable to type 'number'. +!!! error TS2352: Type 'string' is not comparable to type 'number'. var t9 = <[A, I]>classCDTuple; ~~~~~~~~~~~~~~~~~~~~ !!! error TS2352: Type '[C, D]' cannot be converted to type '[A, I]'. -!!! error TS2352: Types of property '0' are incompatible. -!!! error TS2352: Type 'C' is not comparable to type 'A'. -!!! error TS2352: Property 'a' is missing in type 'C'. +!!! error TS2352: Type 'C' is not comparable to type 'A'. +!!! error TS2352: Property 'a' is missing in type 'C'. var array1 = numStrTuple; ~~~~~~ !!! error TS2403: Subsequent variable declarations must have the same type. Variable 'array1' must be of type '{}[]', but here has type 'number[]'. diff --git a/tests/baselines/reference/contextualTypeWithTuple.errors.txt b/tests/baselines/reference/contextualTypeWithTuple.errors.txt index e0580ca2379..a488d8c7648 100644 --- a/tests/baselines/reference/contextualTypeWithTuple.errors.txt +++ b/tests/baselines/reference/contextualTypeWithTuple.errors.txt @@ -5,9 +5,8 @@ tests/cases/conformance/types/tuple/contextualTypeWithTuple.ts(3,5): error TS232 Type 'true' is not assignable to type 'string | number'. tests/cases/conformance/types/tuple/contextualTypeWithTuple.ts(15,1): error TS2322: Type '[number, string, boolean]' is not assignable to type '[number, string]'. tests/cases/conformance/types/tuple/contextualTypeWithTuple.ts(18,1): error TS2322: Type '[{}, number]' is not assignable to type '[{ a: string; }, number]'. - Types of property '0' are incompatible. - Type '{}' is not assignable to type '{ a: string; }'. - Property 'a' is missing in type '{}'. + Type '{}' is not assignable to type '{ a: string; }'. + Property 'a' is missing in type '{}'. tests/cases/conformance/types/tuple/contextualTypeWithTuple.ts(19,1): error TS2322: Type '[number, string]' is not assignable to type '[number, string, boolean]'. Property '2' is missing in type '[number, string]'. tests/cases/conformance/types/tuple/contextualTypeWithTuple.ts(20,5): error TS2322: Type '[string, string, number]' is not assignable to type '[string, string]'. @@ -18,9 +17,8 @@ tests/cases/conformance/types/tuple/contextualTypeWithTuple.ts(20,5): error TS23 tests/cases/conformance/types/tuple/contextualTypeWithTuple.ts(24,1): error TS2322: Type '[C, string | number]' is not assignable to type '[C, string | number, D]'. Property '2' is missing in type '[C, string | number]'. tests/cases/conformance/types/tuple/contextualTypeWithTuple.ts(25,1): error TS2322: Type '[number, string | number]' is not assignable to type '[number, string]'. - Types of property '1' are incompatible. - Type 'string | number' is not assignable to type 'string'. - Type 'number' is not assignable to type 'string'. + Type 'string | number' is not assignable to type 'string'. + Type 'number' is not assignable to type 'string'. ==== tests/cases/conformance/types/tuple/contextualTypeWithTuple.ts (7 errors) ==== @@ -52,9 +50,8 @@ tests/cases/conformance/types/tuple/contextualTypeWithTuple.ts(25,1): error TS23 objNumTuple = [ {}, 5]; ~~~~~~~~~~~ !!! error TS2322: Type '[{}, number]' is not assignable to type '[{ a: string; }, number]'. -!!! error TS2322: Types of property '0' are incompatible. -!!! error TS2322: Type '{}' is not assignable to type '{ a: string; }'. -!!! error TS2322: Property 'a' is missing in type '{}'. +!!! error TS2322: Type '{}' is not assignable to type '{ a: string; }'. +!!! error TS2322: Property 'a' is missing in type '{}'. numStrBoolTuple = numStrTuple; ~~~~~~~~~~~~~~~ !!! error TS2322: Type '[number, string]' is not assignable to type '[number, string, boolean]'. @@ -76,6 +73,5 @@ tests/cases/conformance/types/tuple/contextualTypeWithTuple.ts(25,1): error TS23 numStrTuple = unionTuple3; ~~~~~~~~~~~ !!! error TS2322: Type '[number, string | number]' is not assignable to type '[number, string]'. -!!! error TS2322: Types of property '1' are incompatible. -!!! error TS2322: Type 'string | number' is not assignable to type 'string'. -!!! error TS2322: Type 'number' is not assignable to type 'string'. \ No newline at end of file +!!! error TS2322: Type 'string | number' is not assignable to type 'string'. +!!! error TS2322: Type 'number' is not assignable to type 'string'. \ No newline at end of file diff --git a/tests/baselines/reference/contextuallyTypedBindingInitializerNegative.errors.txt b/tests/baselines/reference/contextuallyTypedBindingInitializerNegative.errors.txt index 42d617887e0..447ccec5ddf 100644 --- a/tests/baselines/reference/contextuallyTypedBindingInitializerNegative.errors.txt +++ b/tests/baselines/reference/contextuallyTypedBindingInitializerNegative.errors.txt @@ -11,8 +11,7 @@ tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTyp tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializerNegative.ts(16,23): error TS2322: Type '(arg: string) => number' is not assignable to type '(s: string) => string'. Type 'number' is not assignable to type 'string'. tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializerNegative.ts(21,14): error TS2322: Type '[number, number]' is not assignable to type '[string, number]'. - Types of property '0' are incompatible. - Type 'number' is not assignable to type 'string'. + Type 'number' is not assignable to type 'string'. tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializerNegative.ts(26,14): error TS2322: Type '"baz"' is not assignable to type '"foo" | "bar"'. @@ -57,8 +56,7 @@ tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTyp function g({ prop = [101, 1234] }: Tuples) {} ~~~~ !!! error TS2322: Type '[number, number]' is not assignable to type '[string, number]'. -!!! error TS2322: Types of property '0' are incompatible. -!!! error TS2322: Type 'number' is not assignable to type 'string'. +!!! error TS2322: Type 'number' is not assignable to type 'string'. interface StringUnion { prop: "foo" | "bar"; diff --git a/tests/baselines/reference/declarationsAndAssignments.errors.txt b/tests/baselines/reference/declarationsAndAssignments.errors.txt index 517eaafe4ab..3ed9e241c39 100644 --- a/tests/baselines/reference/declarationsAndAssignments.errors.txt +++ b/tests/baselines/reference/declarationsAndAssignments.errors.txt @@ -18,11 +18,9 @@ tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts(73,14): tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts(74,11): error TS2459: Type 'undefined[]' has no property 'a' and no string index signature. tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts(74,14): error TS2459: Type 'undefined[]' has no property 'b' and no string index signature. tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts(106,5): error TS2345: Argument of type '[number, [string, { y: boolean; }]]' is not assignable to parameter of type '[number, [string, { x: any; y?: boolean; }]]'. - Types of property '1' are incompatible. - Type '[string, { y: boolean; }]' is not assignable to type '[string, { x: any; y?: boolean; }]'. - Types of property '1' are incompatible. - Type '{ y: boolean; }' is not assignable to type '{ x: any; y?: boolean; }'. - Property 'x' is missing in type '{ y: boolean; }'. + Type '[string, { y: boolean; }]' is not assignable to type '[string, { x: any; y?: boolean; }]'. + Type '{ y: boolean; }' is not assignable to type '{ x: any; y?: boolean; }'. + Property 'x' is missing in type '{ y: boolean; }'. tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts(138,6): error TS2322: Type 'string' is not assignable to type 'number'. tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts(138,9): error TS2322: Type 'number' is not assignable to type 'string'. @@ -174,11 +172,9 @@ tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts(138,9): f14([2, ["abc", { y: false }]]); // Error, no x ~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2345: Argument of type '[number, [string, { y: boolean; }]]' is not assignable to parameter of type '[number, [string, { x: any; y?: boolean; }]]'. -!!! error TS2345: Types of property '1' are incompatible. -!!! error TS2345: Type '[string, { y: boolean; }]' is not assignable to type '[string, { x: any; y?: boolean; }]'. -!!! error TS2345: Types of property '1' are incompatible. -!!! error TS2345: Type '{ y: boolean; }' is not assignable to type '{ x: any; y?: boolean; }'. -!!! error TS2345: Property 'x' is missing in type '{ y: boolean; }'. +!!! error TS2345: Type '[string, { y: boolean; }]' is not assignable to type '[string, { x: any; y?: boolean; }]'. +!!! error TS2345: Type '{ y: boolean; }' is not assignable to type '{ x: any; y?: boolean; }'. +!!! error TS2345: Property 'x' is missing in type '{ y: boolean; }'. module M { export var [a, b] = [1, 2]; diff --git a/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment2.errors.txt b/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment2.errors.txt index 3348effe822..e22b23122d3 100644 --- a/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment2.errors.txt +++ b/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment2.errors.txt @@ -2,8 +2,7 @@ tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAss tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAssignment2.ts(3,12): error TS2525: Initializer provides no value for this binding element and the binding element has no default value. tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAssignment2.ts(4,5): error TS2461: Type 'undefined' is not an array type. tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAssignment2.ts(9,5): error TS2322: Type '[number, number, string]' is not assignable to type '[number, boolean, string]'. - Types of property '1' are incompatible. - Type 'number' is not assignable to type 'boolean'. + Type 'number' is not assignable to type 'boolean'. tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAssignment2.ts(17,6): error TS2322: Type 'string' is not assignable to type 'Number'. tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAssignment2.ts(22,5): error TS2322: Type 'number[]' is not assignable to type '[number, number]'. Property '0' is missing in type 'number[]'. @@ -30,8 +29,7 @@ tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAss var [b0, b1, b2]: [number, boolean, string] = [1, 2, "string"]; // Error ~~~~~~~~~~~~ !!! error TS2322: Type '[number, number, string]' is not assignable to type '[number, boolean, string]'. -!!! error TS2322: Types of property '1' are incompatible. -!!! error TS2322: Type 'number' is not assignable to type 'boolean'. +!!! error TS2322: Type 'number' is not assignable to type 'boolean'. interface J extends Array { 2: number; } diff --git a/tests/baselines/reference/destructuringParameterDeclaration2.errors.txt b/tests/baselines/reference/destructuringParameterDeclaration2.errors.txt index 878bb38abf0..d76a4efe5f1 100644 --- a/tests/baselines/reference/destructuringParameterDeclaration2.errors.txt +++ b/tests/baselines/reference/destructuringParameterDeclaration2.errors.txt @@ -1,6 +1,5 @@ tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration2.ts(7,4): error TS2345: Argument of type '[number, string, string[][]]' is not assignable to parameter of type '[number, number, string[][]]'. - Types of property '1' are incompatible. - Type 'string' is not assignable to type 'number'. + Type 'string' is not assignable to type 'number'. tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration2.ts(7,29): error TS1005: ',' expected. tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration2.ts(8,4): error TS2345: Argument of type '[number, number, string[][], string]' is not assignable to parameter of type '[number, number, string[][]]'. Types of property 'pop' are incompatible. @@ -32,12 +31,9 @@ tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration2.ts( Types of property '2' are incompatible. Type 'boolean' is not assignable to type '[[any]]'. tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration2.ts(40,4): error TS2345: Argument of type '[number, number, [[string]]]' is not assignable to parameter of type '[any, any, [[number]]]'. - Types of property '2' are incompatible. - Type '[[string]]' is not assignable to type '[[number]]'. - Types of property '0' are incompatible. - Type '[string]' is not assignable to type '[number]'. - Types of property '0' are incompatible. - Type 'string' is not assignable to type 'number'. + Type '[[string]]' is not assignable to type '[[number]]'. + Type '[string]' is not assignable to type '[number]'. + Type 'string' is not assignable to type 'number'. tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration2.ts(46,13): error TS2463: A binding pattern parameter cannot be optional in an implementation signature. tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration2.ts(47,13): error TS2463: A binding pattern parameter cannot be optional in an implementation signature. tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration2.ts(55,7): error TS2420: Class 'C4' incorrectly implements interface 'F2'. @@ -62,8 +58,7 @@ tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration2.ts( a0([1, "string", [["world"]]); // Error ~~~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2345: Argument of type '[number, string, string[][]]' is not assignable to parameter of type '[number, number, string[][]]'. -!!! error TS2345: Types of property '1' are incompatible. -!!! error TS2345: Type 'string' is not assignable to type 'number'. +!!! error TS2345: Type 'string' is not assignable to type 'number'. ~ !!! error TS1005: ',' expected. a0([1, 2, [["world"]], "string"]); // Error @@ -142,12 +137,9 @@ tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration2.ts( c6([1, 2, [["string"]]]); // Error, implied type is [any, any, [[number]]] // Use initializer ~~~~~~~~~~~~~~~~~~~~ !!! error TS2345: Argument of type '[number, number, [[string]]]' is not assignable to parameter of type '[any, any, [[number]]]'. -!!! error TS2345: Types of property '2' are incompatible. -!!! error TS2345: Type '[[string]]' is not assignable to type '[[number]]'. -!!! error TS2345: Types of property '0' are incompatible. -!!! error TS2345: Type '[string]' is not assignable to type '[number]'. -!!! error TS2345: Types of property '0' are incompatible. -!!! error TS2345: Type 'string' is not assignable to type 'number'. +!!! error TS2345: Type '[[string]]' is not assignable to type '[[number]]'. +!!! error TS2345: Type '[string]' is not assignable to type '[number]'. +!!! error TS2345: Type 'string' is not assignable to type 'number'. // A parameter can be marked optional by following its name or binding pattern with a question mark (?) // or by including an initializer. Initializers (including binding property or element initializers) are diff --git a/tests/baselines/reference/destructuringParameterProperties2.errors.txt b/tests/baselines/reference/destructuringParameterProperties2.errors.txt index ec0f400631f..deb039aa67d 100644 --- a/tests/baselines/reference/destructuringParameterProperties2.errors.txt +++ b/tests/baselines/reference/destructuringParameterProperties2.errors.txt @@ -6,8 +6,7 @@ tests/cases/conformance/es6/destructuring/destructuringParameterProperties2.ts(9 tests/cases/conformance/es6/destructuring/destructuringParameterProperties2.ts(13,21): error TS2339: Property 'b' does not exist on type 'C1'. tests/cases/conformance/es6/destructuring/destructuringParameterProperties2.ts(17,21): error TS2339: Property 'c' does not exist on type 'C1'. tests/cases/conformance/es6/destructuring/destructuringParameterProperties2.ts(21,27): error TS2345: Argument of type '[number, undefined, string]' is not assignable to parameter of type '[number, string, boolean]'. - Types of property '2' are incompatible. - Type 'string' is not assignable to type 'boolean'. + Type 'string' is not assignable to type 'boolean'. ==== tests/cases/conformance/es6/destructuring/destructuringParameterProperties2.ts (8 errors) ==== @@ -48,8 +47,7 @@ tests/cases/conformance/es6/destructuring/destructuringParameterProperties2.ts(2 var x = new C1(undefined, [0, undefined, ""]); ~~~~~~~~~~~~~~~~~~ !!! error TS2345: Argument of type '[number, undefined, string]' is not assignable to parameter of type '[number, string, boolean]'. -!!! error TS2345: Types of property '2' are incompatible. -!!! error TS2345: Type 'string' is not assignable to type 'boolean'. +!!! error TS2345: Type 'string' is not assignable to type 'boolean'. var [x_a, x_b, x_c] = [x.getA(), x.getB(), x.getC()]; var y = new C1(10, [0, "", true]); diff --git a/tests/baselines/reference/destructuringParameterProperties5.errors.txt b/tests/baselines/reference/destructuringParameterProperties5.errors.txt index 6f5801b7898..8eff6ad0feb 100644 --- a/tests/baselines/reference/destructuringParameterProperties5.errors.txt +++ b/tests/baselines/reference/destructuringParameterProperties5.errors.txt @@ -8,9 +8,8 @@ tests/cases/conformance/es6/destructuring/destructuringParameterProperties5.ts(7 tests/cases/conformance/es6/destructuring/destructuringParameterProperties5.ts(7,62): error TS2339: Property 'y' does not exist on type 'C1'. tests/cases/conformance/es6/destructuring/destructuringParameterProperties5.ts(7,72): error TS2339: Property 'z' does not exist on type 'C1'. tests/cases/conformance/es6/destructuring/destructuringParameterProperties5.ts(11,19): error TS2345: Argument of type '[{ x1: number; x2: string; x3: boolean; }, string, boolean]' is not assignable to parameter of type '[{ x: number; y: string; z: boolean; }, number, string]'. - Types of property '0' are incompatible. - Type '{ x1: number; x2: string; x3: boolean; }' is not assignable to type '{ x: number; y: string; z: boolean; }'. - Object literal may only specify known properties, and 'x1' does not exist in type '{ x: number; y: string; z: boolean; }'. + Type '{ x1: number; x2: string; x3: boolean; }' is not assignable to type '{ x: number; y: string; z: boolean; }'. + Object literal may only specify known properties, and 'x1' does not exist in type '{ x: number; y: string; z: boolean; }'. ==== tests/cases/conformance/es6/destructuring/destructuringParameterProperties5.ts (10 errors) ==== @@ -45,7 +44,6 @@ tests/cases/conformance/es6/destructuring/destructuringParameterProperties5.ts(1 var a = new C1([{ x1: 10, x2: "", x3: true }, "", false]); ~~~~~~ !!! error TS2345: Argument of type '[{ x1: number; x2: string; x3: boolean; }, string, boolean]' is not assignable to parameter of type '[{ x: number; y: string; z: boolean; }, number, string]'. -!!! error TS2345: Types of property '0' are incompatible. -!!! error TS2345: Type '{ x1: number; x2: string; x3: boolean; }' is not assignable to type '{ x: number; y: string; z: boolean; }'. -!!! error TS2345: Object literal may only specify known properties, and 'x1' does not exist in type '{ x: number; y: string; z: boolean; }'. +!!! error TS2345: Type '{ x1: number; x2: string; x3: boolean; }' is not assignable to type '{ x: number; y: string; z: boolean; }'. +!!! error TS2345: Object literal may only specify known properties, and 'x1' does not exist in type '{ x: number; y: string; z: boolean; }'. var [a_x1, a_x2, a_x3, a_y, a_z] = [a.x1, a.x2, a.x3, a.y, a.z]; \ No newline at end of file diff --git a/tests/baselines/reference/destructuringVariableDeclaration2.errors.txt b/tests/baselines/reference/destructuringVariableDeclaration2.errors.txt index 1a655837743..bdbda6f9a96 100644 --- a/tests/baselines/reference/destructuringVariableDeclaration2.errors.txt +++ b/tests/baselines/reference/destructuringVariableDeclaration2.errors.txt @@ -2,12 +2,9 @@ tests/cases/conformance/es6/destructuring/destructuringVariableDeclaration2.ts(3 Types of property 'a1' are incompatible. Type 'boolean' is not assignable to type 'number'. tests/cases/conformance/es6/destructuring/destructuringVariableDeclaration2.ts(4,5): error TS2322: Type '[number, [[boolean]], boolean]' is not assignable to type '[number, [[string]], boolean]'. - Types of property '1' are incompatible. - Type '[[boolean]]' is not assignable to type '[[string]]'. - Types of property '0' are incompatible. - Type '[boolean]' is not assignable to type '[string]'. - Types of property '0' are incompatible. - Type 'boolean' is not assignable to type 'string'. + Type '[[boolean]]' is not assignable to type '[[string]]'. + Type '[boolean]' is not assignable to type '[string]'. + Type 'boolean' is not assignable to type 'string'. tests/cases/conformance/es6/destructuring/destructuringVariableDeclaration2.ts(9,25): error TS2322: Type '{ t1: boolean; t2: string; }' is not assignable to type '{ t1: boolean; t2: number; }'. Types of property 't2' are incompatible. Type 'string' is not assignable to type 'number'. @@ -28,12 +25,9 @@ tests/cases/conformance/es6/destructuring/destructuringVariableDeclaration2.ts(1 var [a3, [[a4]], a5]: [number, [[string]], boolean] = [1, [[false]], true]; // Error ~~~~~~~~~~~~~~~~ !!! error TS2322: Type '[number, [[boolean]], boolean]' is not assignable to type '[number, [[string]], boolean]'. -!!! error TS2322: Types of property '1' are incompatible. -!!! error TS2322: Type '[[boolean]]' is not assignable to type '[[string]]'. -!!! error TS2322: Types of property '0' are incompatible. -!!! error TS2322: Type '[boolean]' is not assignable to type '[string]'. -!!! error TS2322: Types of property '0' are incompatible. -!!! error TS2322: Type 'boolean' is not assignable to type 'string'. +!!! error TS2322: Type '[[boolean]]' is not assignable to type '[[string]]'. +!!! error TS2322: Type '[boolean]' is not assignable to type '[string]'. +!!! error TS2322: Type 'boolean' is not assignable to type 'string'. // The type T associated with a destructuring variable declaration is determined as follows: // Otherwise, if the declaration includes an initializer expression, T is the type of that initializer expression. diff --git a/tests/baselines/reference/genericCallWithTupleType.errors.txt b/tests/baselines/reference/genericCallWithTupleType.errors.txt index 0194dc61311..617c4f7159b 100644 --- a/tests/baselines/reference/genericCallWithTupleType.errors.txt +++ b/tests/baselines/reference/genericCallWithTupleType.errors.txt @@ -6,11 +6,9 @@ tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTup tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTupleType.ts(14,1): error TS2322: Type '{ a: string; }' is not assignable to type 'string | number'. Type '{ a: string; }' is not assignable to type 'number'. tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTupleType.ts(22,1): error TS2322: Type '[number, string]' is not assignable to type '[string, number]'. - Types of property '0' are incompatible. - Type 'number' is not assignable to type 'string'. + Type 'number' is not assignable to type 'string'. tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTupleType.ts(23,1): error TS2322: Type '[{}, {}]' is not assignable to type '[string, number]'. - Types of property '0' are incompatible. - Type '{}' is not assignable to type 'string'. + Type '{}' is not assignable to type 'string'. tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTupleType.ts(24,1): error TS2322: Type '[{}]' is not assignable to type '[{}, {}]'. Property '1' is missing in type '[{}]'. @@ -49,13 +47,11 @@ tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithTup i1.tuple1 = [5, "foo"]; ~~~~~~~~~ !!! error TS2322: Type '[number, string]' is not assignable to type '[string, number]'. -!!! error TS2322: Types of property '0' are incompatible. -!!! error TS2322: Type 'number' is not assignable to type 'string'. +!!! error TS2322: Type 'number' is not assignable to type 'string'. i1.tuple1 = [{}, {}]; ~~~~~~~~~ !!! error TS2322: Type '[{}, {}]' is not assignable to type '[string, number]'. -!!! error TS2322: Types of property '0' are incompatible. -!!! error TS2322: Type '{}' is not assignable to type 'string'. +!!! error TS2322: Type '{}' is not assignable to type 'string'. i2.tuple1 = [{}]; ~~~~~~~~~ !!! error TS2322: Type '[{}]' is not assignable to type '[{}, {}]'. diff --git a/tests/baselines/reference/iterableArrayPattern29.errors.txt b/tests/baselines/reference/iterableArrayPattern29.errors.txt index b34d0317d57..29e5d0d2a3f 100644 --- a/tests/baselines/reference/iterableArrayPattern29.errors.txt +++ b/tests/baselines/reference/iterableArrayPattern29.errors.txt @@ -1,7 +1,6 @@ tests/cases/conformance/es6/destructuring/iterableArrayPattern29.ts(1,33): error TS2501: A rest element cannot contain a binding pattern. tests/cases/conformance/es6/destructuring/iterableArrayPattern29.ts(2,21): error TS2345: Argument of type '[string, boolean]' is not assignable to parameter of type '[string, number]'. - Types of property '1' are incompatible. - Type 'boolean' is not assignable to type 'number'. + Type 'boolean' is not assignable to type 'number'. ==== tests/cases/conformance/es6/destructuring/iterableArrayPattern29.ts (2 errors) ==== @@ -11,5 +10,4 @@ tests/cases/conformance/es6/destructuring/iterableArrayPattern29.ts(2,21): error takeFirstTwoEntries(...new Map([["", true], ["hello", true]])); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2345: Argument of type '[string, boolean]' is not assignable to parameter of type '[string, number]'. -!!! error TS2345: Types of property '1' are incompatible. -!!! error TS2345: Type 'boolean' is not assignable to type 'number'. \ No newline at end of file +!!! error TS2345: Type 'boolean' is not assignable to type 'number'. \ No newline at end of file diff --git a/tests/baselines/reference/optionalBindingParameters1.errors.txt b/tests/baselines/reference/optionalBindingParameters1.errors.txt index 78fc1cbddf1..0780c626baf 100644 --- a/tests/baselines/reference/optionalBindingParameters1.errors.txt +++ b/tests/baselines/reference/optionalBindingParameters1.errors.txt @@ -1,7 +1,6 @@ tests/cases/conformance/es6/destructuring/optionalBindingParameters1.ts(2,14): error TS2463: A binding pattern parameter cannot be optional in an implementation signature. tests/cases/conformance/es6/destructuring/optionalBindingParameters1.ts(8,5): error TS2345: Argument of type '[boolean, number, string]' is not assignable to parameter of type '[string, number, boolean]'. - Types of property '0' are incompatible. - Type 'boolean' is not assignable to type 'string'. + Type 'boolean' is not assignable to type 'string'. ==== tests/cases/conformance/es6/destructuring/optionalBindingParameters1.ts (2 errors) ==== @@ -17,5 +16,4 @@ tests/cases/conformance/es6/destructuring/optionalBindingParameters1.ts(8,5): er foo([false, 0, ""]); ~~~~~~~~~~~~~~ !!! error TS2345: Argument of type '[boolean, number, string]' is not assignable to parameter of type '[string, number, boolean]'. -!!! error TS2345: Types of property '0' are incompatible. -!!! error TS2345: Type 'boolean' is not assignable to type 'string'. \ No newline at end of file +!!! error TS2345: Type 'boolean' is not assignable to type 'string'. \ No newline at end of file diff --git a/tests/baselines/reference/optionalBindingParametersInOverloads1.errors.txt b/tests/baselines/reference/optionalBindingParametersInOverloads1.errors.txt index 312603d2c2e..30ec4037c88 100644 --- a/tests/baselines/reference/optionalBindingParametersInOverloads1.errors.txt +++ b/tests/baselines/reference/optionalBindingParametersInOverloads1.errors.txt @@ -1,6 +1,5 @@ tests/cases/conformance/es6/destructuring/optionalBindingParametersInOverloads1.ts(9,5): error TS2345: Argument of type '[boolean, number, string]' is not assignable to parameter of type '[string, number, boolean]'. - Types of property '0' are incompatible. - Type 'boolean' is not assignable to type 'string'. + Type 'boolean' is not assignable to type 'string'. ==== tests/cases/conformance/es6/destructuring/optionalBindingParametersInOverloads1.ts (1 errors) ==== @@ -15,5 +14,4 @@ tests/cases/conformance/es6/destructuring/optionalBindingParametersInOverloads1. foo([false, 0, ""]); ~~~~~~~~~~~~~~ !!! error TS2345: Argument of type '[boolean, number, string]' is not assignable to parameter of type '[string, number, boolean]'. -!!! error TS2345: Types of property '0' are incompatible. -!!! error TS2345: Type 'boolean' is not assignable to type 'string'. \ No newline at end of file +!!! error TS2345: Type 'boolean' is not assignable to type 'string'. \ No newline at end of file diff --git a/tests/baselines/reference/tupleTypes.errors.txt b/tests/baselines/reference/tupleTypes.errors.txt index 75f413eb170..3b55476e4b1 100644 --- a/tests/baselines/reference/tupleTypes.errors.txt +++ b/tests/baselines/reference/tupleTypes.errors.txt @@ -4,8 +4,7 @@ tests/cases/compiler/tupleTypes.ts(14,1): error TS2322: Type 'undefined[]' is no tests/cases/compiler/tupleTypes.ts(15,1): error TS2322: Type '[number]' is not assignable to type '[number, string]'. Property '1' is missing in type '[number]'. tests/cases/compiler/tupleTypes.ts(17,1): error TS2322: Type '[string, number]' is not assignable to type '[number, string]'. - Types of property '0' are incompatible. - Type 'string' is not assignable to type 'number'. + Type 'string' is not assignable to type 'number'. tests/cases/compiler/tupleTypes.ts(41,1): error TS2322: Type 'undefined[]' is not assignable to type '[number, string]'. tests/cases/compiler/tupleTypes.ts(47,1): error TS2322: Type '[number, string]' is not assignable to type 'number[]'. Types of property 'pop' are incompatible. @@ -18,11 +17,9 @@ tests/cases/compiler/tupleTypes.ts(49,1): error TS2322: Type '[number, {}]' is n Type 'number | {}' is not assignable to type 'number'. Type '{}' is not assignable to type 'number'. tests/cases/compiler/tupleTypes.ts(50,1): error TS2322: Type '[number, number]' is not assignable to type '[number, string]'. - Types of property '1' are incompatible. - Type 'number' is not assignable to type 'string'. + Type 'number' is not assignable to type 'string'. tests/cases/compiler/tupleTypes.ts(51,1): error TS2322: Type '[number, {}]' is not assignable to type '[number, string]'. - Types of property '1' are incompatible. - Type '{}' is not assignable to type 'string'. + Type '{}' is not assignable to type 'string'. ==== tests/cases/compiler/tupleTypes.ts (9 errors) ==== @@ -53,8 +50,7 @@ tests/cases/compiler/tupleTypes.ts(51,1): error TS2322: Type '[number, {}]' is n t = ["hello", 1]; // Error ~ !!! error TS2322: Type '[string, number]' is not assignable to type '[number, string]'. -!!! error TS2322: Types of property '0' are incompatible. -!!! error TS2322: Type 'string' is not assignable to type 'number'. +!!! error TS2322: Type 'string' is not assignable to type 'number'. t = [1, "hello", 2]; // Ok var tf: [string, (x: string) => number] = ["hello", x => x.length]; @@ -104,13 +100,11 @@ tests/cases/compiler/tupleTypes.ts(51,1): error TS2322: Type '[number, {}]' is n a1 = a2; // Error ~~ !!! error TS2322: Type '[number, number]' is not assignable to type '[number, string]'. -!!! error TS2322: Types of property '1' are incompatible. -!!! error TS2322: Type 'number' is not assignable to type 'string'. +!!! error TS2322: Type 'number' is not assignable to type 'string'. a1 = a3; // Error ~~ !!! error TS2322: Type '[number, {}]' is not assignable to type '[number, string]'. -!!! error TS2322: Types of property '1' are incompatible. -!!! error TS2322: Type '{}' is not assignable to type 'string'. +!!! error TS2322: Type '{}' is not assignable to type 'string'. a3 = a1; a3 = a2; \ No newline at end of file From 05fef61e75a1d35c228b40e3096209118819dd66 Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Mon, 22 Aug 2016 09:15:05 -0700 Subject: [PATCH 201/233] Add .types extension --- src/harness/rwcRunner.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/harness/rwcRunner.ts b/src/harness/rwcRunner.ts index d56e8d6d35f..ba1ab71ec19 100644 --- a/src/harness/rwcRunner.ts +++ b/src/harness/rwcRunner.ts @@ -158,13 +158,13 @@ namespace RWC { it("has the expected emitted code", () => { - Harness.Baseline.runBaseline(baseName + ".output.js", () => { + Harness.Baseline.runBaseline(`${baseName}.output.js`, () => { return Harness.Compiler.collateOutputs(compilerResult.files); }, baselineOpts); }); it("has the expected declaration file content", () => { - Harness.Baseline.runBaseline(baseName + ".d.ts", () => { + Harness.Baseline.runBaseline(`${baseName}.d.ts`, () => { if (!compilerResult.declFilesCode.length) { return null; } @@ -174,7 +174,7 @@ namespace RWC { }); it("has the expected source maps", () => { - Harness.Baseline.runBaseline(baseName + ".map", () => { + Harness.Baseline.runBaseline(`${baseName}.map`, () => { if (!compilerResult.sourceMaps.length) { return null; } @@ -192,7 +192,7 @@ namespace RWC { });*/ it("has the expected errors", () => { - Harness.Baseline.runBaseline(baseName + ".errors.txt", () => { + Harness.Baseline.runBaseline(`${baseName}.errors.txt`, () => { if (compilerResult.errors.length === 0) { return null; } @@ -207,7 +207,7 @@ namespace RWC { // declaration file errors as part of the baseline. it("has the expected errors in generated declaration files", () => { if (compilerOptions.declaration && !compilerResult.errors.length) { - Harness.Baseline.runBaseline(baseName + ".dts.errors.txt", () => { + Harness.Baseline.runBaseline(`${baseName}.dts.errors.txt`, () => { const declFileCompilationResult = Harness.Compiler.compileDeclarationFiles( inputFiles, otherFiles, compilerResult, /*harnessSettings*/ undefined, compilerOptions, currentDirectory); @@ -223,7 +223,7 @@ namespace RWC { }); it("has the expected types", () => { - Harness.Compiler.doTypeAndSymbolBaseline(baseName, compilerResult, inputFiles + Harness.Compiler.doTypeAndSymbolBaseline(`${baseName}.types`, compilerResult, inputFiles .concat(otherFiles) .filter(file => !!compilerResult.program.getSourceFile(file.unitName)) .filter(e => !Harness.isDefaultLibraryFile(e.unitName)), baselineOpts); From 4e56fc0d272e39706ea98bdff0ded79dc70ca1d6 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 22 Aug 2016 09:49:26 -0700 Subject: [PATCH 202/233] Properly guard for undefined in getTypeReferenceArity --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9efaf72c03a..3abf271a57b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4979,7 +4979,7 @@ namespace ts { } function getTypeReferenceArity(type: TypeReference): number { - return type.target.typeParameters.length; + return type.target.typeParameters ? type.target.typeParameters.length : 0; } // Get type from reference to class or interface From 2c814f4413cbc83a88c711823b0aea5125e8d343 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 22 Aug 2016 10:08:57 -0700 Subject: [PATCH 203/233] Add jsdoc nullable union test case to fourslash --- tests/cases/fourslash/jsdocNullableUnion.ts | 23 +++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/cases/fourslash/jsdocNullableUnion.ts diff --git a/tests/cases/fourslash/jsdocNullableUnion.ts b/tests/cases/fourslash/jsdocNullableUnion.ts new file mode 100644 index 00000000000..19dc9818d69 --- /dev/null +++ b/tests/cases/fourslash/jsdocNullableUnion.ts @@ -0,0 +1,23 @@ +/// +// @allowNonTsExtensions: true +// @Filename: Foo.js +//// +//// * @param {never | {x: string}} p1 +//// * @param {undefined | {y: number}} p2 +//// * @param {null | {z: boolean}} p3 +//// * @returns {void} nothing +//// */ +////function f(p1, p2, p3) { +//// p1./*1*/ +//// p2./*2*/ +//// p3./*3*/ +////} + +goTo.marker('1'); +verify.memberListContains("x"); + +goTo.marker('2'); +verify.memberListContains("y"); + +goTo.marker('3'); +verify.memberListContains("z"); From 201305859f9a9a9fc7e93de59238e647c49ced51 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 22 Aug 2016 11:21:06 -0700 Subject: [PATCH 204/233] Fix class/interface merging issue + lint error --- src/compiler/checker.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3abf271a57b..418f5e40a89 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3564,11 +3564,13 @@ namespace ts { if (type.flags & TypeFlags.Tuple) { type.resolvedBaseTypes = [createArrayType(getUnionType(type.typeParameters))]; } - else if (type.symbol.flags & SymbolFlags.Class) { - resolveBaseTypesOfClass(type); - } - else if (type.symbol.flags & SymbolFlags.Interface) { - resolveBaseTypesOfInterface(type); + else if (type.symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { + if (type.symbol.flags & SymbolFlags.Class) { + resolveBaseTypesOfClass(type); + } + if (type.symbol.flags & SymbolFlags.Interface) { + resolveBaseTypesOfInterface(type); + } } else { Debug.fail("type must be class or interface"); @@ -4972,7 +4974,7 @@ namespace ts { } function cloneTypeReference(source: TypeReference): TypeReference { - let type = createObjectType(source.flags, source.symbol); + const type = createObjectType(source.flags, source.symbol); type.target = source.target; type.typeArguments = source.typeArguments; return type; From 92eb8df68cd264cce733d6ad43ef2680f3f6ffcc Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 22 Aug 2016 13:03:49 -0700 Subject: [PATCH 205/233] Allow "typings" in a package.json to be missing its extension (but also allow it to have an extension) --- src/compiler/program.ts | 5 +- tests/baselines/reference/typingsLookup4.js | 36 +++++++ .../reference/typingsLookup4.symbols | 27 ++++++ .../reference/typingsLookup4.trace.json | 93 +++++++++++++++++++ .../baselines/reference/typingsLookup4.types | 30 ++++++ .../conformance/typings/typingsLookup4.ts | 31 +++++++ 6 files changed, 220 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/typingsLookup4.js create mode 100644 tests/baselines/reference/typingsLookup4.symbols create mode 100644 tests/baselines/reference/typingsLookup4.trace.json create mode 100644 tests/baselines/reference/typingsLookup4.types create mode 100644 tests/cases/conformance/typings/typingsLookup4.ts diff --git a/src/compiler/program.ts b/src/compiler/program.ts index afd2ddad9e1..f099d858119 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -720,8 +720,9 @@ namespace ts { const typesFile = tryReadTypesSection(packageJsonPath, candidate, state); if (typesFile) { const onlyRecordFailures = !directoryProbablyExists(getDirectoryPath(typesFile), state.host); - // The package.json "typings" property must specify the file with extension, so just try that exact filename. - const result = tryFile(typesFile, failedLookupLocation, onlyRecordFailures, state); + // A package.json "typings" may specify an exact filename, or may choose to omit an extension. + const result = tryFile(typesFile, failedLookupLocation, onlyRecordFailures, state) || + tryAddingExtensions(typesFile, extensions, failedLookupLocation, onlyRecordFailures, state); if (result) { return result; } diff --git a/tests/baselines/reference/typingsLookup4.js b/tests/baselines/reference/typingsLookup4.js new file mode 100644 index 00000000000..c11bc13c613 --- /dev/null +++ b/tests/baselines/reference/typingsLookup4.js @@ -0,0 +1,36 @@ +//// [tests/cases/conformance/typings/typingsLookup4.ts] //// + +//// [package.json] +{ "typings": "jquery.d.ts" } + +//// [jquery.d.ts] +export const j: number; + +//// [package.json] +{ "typings": "kquery" } + +//// [kquery.d.ts] +export const k: number; + +//// [package.json] +{ "typings": "lquery" } + +//// [lquery.ts] +export const l = 2; + +//// [a.ts] +import { j } from "jquery"; +import { k } from "kquery"; +import { l } from "lquery"; +j + k + l; + + +//// [lquery.js] +"use strict"; +exports.l = 2; +//// [a.js] +"use strict"; +var jquery_1 = require("jquery"); +var kquery_1 = require("kquery"); +var lquery_1 = require("lquery"); +jquery_1.j + kquery_1.k + lquery_1.l; diff --git a/tests/baselines/reference/typingsLookup4.symbols b/tests/baselines/reference/typingsLookup4.symbols new file mode 100644 index 00000000000..144548c6452 --- /dev/null +++ b/tests/baselines/reference/typingsLookup4.symbols @@ -0,0 +1,27 @@ +=== /a.ts === +import { j } from "jquery"; +>j : Symbol(j, Decl(a.ts, 0, 8)) + +import { k } from "kquery"; +>k : Symbol(k, Decl(a.ts, 1, 8)) + +import { l } from "lquery"; +>l : Symbol(l, Decl(a.ts, 2, 8)) + +j + k + l; +>j : Symbol(j, Decl(a.ts, 0, 8)) +>k : Symbol(k, Decl(a.ts, 1, 8)) +>l : Symbol(l, Decl(a.ts, 2, 8)) + +=== /node_modules/@types/jquery/jquery.d.ts === +export const j: number; +>j : Symbol(j, Decl(jquery.d.ts, 0, 12)) + +=== /node_modules/@types/kquery/kquery.d.ts === +export const k: number; +>k : Symbol(k, Decl(kquery.d.ts, 0, 12)) + +=== /node_modules/@types/lquery/lquery.ts === +export const l = 2; +>l : Symbol(l, Decl(lquery.ts, 0, 12)) + diff --git a/tests/baselines/reference/typingsLookup4.trace.json b/tests/baselines/reference/typingsLookup4.trace.json new file mode 100644 index 00000000000..4bca8456f58 --- /dev/null +++ b/tests/baselines/reference/typingsLookup4.trace.json @@ -0,0 +1,93 @@ +[ + "======== Resolving module 'jquery' from '/a.ts'. ========", + "Module resolution kind is not specified, using 'NodeJs'.", + "Loading module 'jquery' from 'node_modules' folder.", + "File '/node_modules/jquery.ts' does not exist.", + "File '/node_modules/jquery.tsx' does not exist.", + "File '/node_modules/jquery.d.ts' does not exist.", + "File '/node_modules/jquery/package.json' does not exist.", + "File '/node_modules/jquery/index.ts' does not exist.", + "File '/node_modules/jquery/index.tsx' does not exist.", + "File '/node_modules/jquery/index.d.ts' does not exist.", + "File '/node_modules/@types/jquery.ts' does not exist.", + "File '/node_modules/@types/jquery.tsx' does not exist.", + "File '/node_modules/@types/jquery.d.ts' does not exist.", + "Found 'package.json' at '/node_modules/@types/jquery/package.json'.", + "'package.json' has 'typings' field 'jquery.d.ts' that references '/node_modules/@types/jquery/jquery.d.ts'.", + "File '/node_modules/@types/jquery/jquery.d.ts' exist - use it as a name resolution result.", + "Resolving real path for '/node_modules/@types/jquery/jquery.d.ts', result '/node_modules/@types/jquery/jquery.d.ts'", + "======== Module name 'jquery' was successfully resolved to '/node_modules/@types/jquery/jquery.d.ts'. ========", + "======== Resolving module 'kquery' from '/a.ts'. ========", + "Module resolution kind is not specified, using 'NodeJs'.", + "Loading module 'kquery' from 'node_modules' folder.", + "File '/node_modules/kquery.ts' does not exist.", + "File '/node_modules/kquery.tsx' does not exist.", + "File '/node_modules/kquery.d.ts' does not exist.", + "File '/node_modules/kquery/package.json' does not exist.", + "File '/node_modules/kquery/index.ts' does not exist.", + "File '/node_modules/kquery/index.tsx' does not exist.", + "File '/node_modules/kquery/index.d.ts' does not exist.", + "File '/node_modules/@types/kquery.ts' does not exist.", + "File '/node_modules/@types/kquery.tsx' does not exist.", + "File '/node_modules/@types/kquery.d.ts' does not exist.", + "Found 'package.json' at '/node_modules/@types/kquery/package.json'.", + "'package.json' has 'typings' field 'kquery' that references '/node_modules/@types/kquery/kquery'.", + "File '/node_modules/@types/kquery/kquery' does not exist.", + "File '/node_modules/@types/kquery/kquery.ts' does not exist.", + "File '/node_modules/@types/kquery/kquery.tsx' does not exist.", + "File '/node_modules/@types/kquery/kquery.d.ts' exist - use it as a name resolution result.", + "Resolving real path for '/node_modules/@types/kquery/kquery.d.ts', result '/node_modules/@types/kquery/kquery.d.ts'", + "======== Module name 'kquery' was successfully resolved to '/node_modules/@types/kquery/kquery.d.ts'. ========", + "======== Resolving module 'lquery' from '/a.ts'. ========", + "Module resolution kind is not specified, using 'NodeJs'.", + "Loading module 'lquery' from 'node_modules' folder.", + "File '/node_modules/lquery.ts' does not exist.", + "File '/node_modules/lquery.tsx' does not exist.", + "File '/node_modules/lquery.d.ts' does not exist.", + "File '/node_modules/lquery/package.json' does not exist.", + "File '/node_modules/lquery/index.ts' does not exist.", + "File '/node_modules/lquery/index.tsx' does not exist.", + "File '/node_modules/lquery/index.d.ts' does not exist.", + "File '/node_modules/@types/lquery.ts' does not exist.", + "File '/node_modules/@types/lquery.tsx' does not exist.", + "File '/node_modules/@types/lquery.d.ts' does not exist.", + "Found 'package.json' at '/node_modules/@types/lquery/package.json'.", + "'package.json' has 'typings' field 'lquery' that references '/node_modules/@types/lquery/lquery'.", + "File '/node_modules/@types/lquery/lquery' does not exist.", + "File '/node_modules/@types/lquery/lquery.ts' exist - use it as a name resolution result.", + "Resolving real path for '/node_modules/@types/lquery/lquery.ts', result '/node_modules/@types/lquery/lquery.ts'", + "======== Module name 'lquery' was successfully resolved to '/node_modules/@types/lquery/lquery.ts'. ========", + "======== Resolving type reference directive 'jquery', containing file '/__inferred type names__.ts', root directory '/node_modules/@types'. ========", + "Resolving with primary search path '/node_modules/@types'", + "Found 'package.json' at '/node_modules/@types/jquery/package.json'.", + "'package.json' has 'typings' field 'jquery.d.ts' that references '/node_modules/@types/jquery/jquery.d.ts'.", + "File '/node_modules/@types/jquery/jquery.d.ts' exist - use it as a name resolution result.", + "======== Type reference directive 'jquery' was successfully resolved to '/node_modules/@types/jquery/jquery.d.ts', primary: true. ========", + "======== Resolving type reference directive 'kquery', containing file '/__inferred type names__.ts', root directory '/node_modules/@types'. ========", + "Resolving with primary search path '/node_modules/@types'", + "Found 'package.json' at '/node_modules/@types/kquery/package.json'.", + "'package.json' has 'typings' field 'kquery' that references '/node_modules/@types/kquery/kquery'.", + "File '/node_modules/@types/kquery/kquery' does not exist.", + "File '/node_modules/@types/kquery/kquery.d.ts' exist - use it as a name resolution result.", + "======== Type reference directive 'kquery' was successfully resolved to '/node_modules/@types/kquery/kquery.d.ts', primary: true. ========", + "======== Resolving type reference directive 'lquery', containing file '/__inferred type names__.ts', root directory '/node_modules/@types'. ========", + "Resolving with primary search path '/node_modules/@types'", + "Found 'package.json' at '/node_modules/@types/lquery/package.json'.", + "'package.json' has 'typings' field 'lquery' that references '/node_modules/@types/lquery/lquery'.", + "File '/node_modules/@types/lquery/lquery' does not exist.", + "File '/node_modules/@types/lquery/lquery.d.ts' does not exist.", + "File '/node_modules/@types/lquery/index.d.ts' does not exist.", + "Looking up in 'node_modules' folder, initial location '/'", + "File '/node_modules/lquery.ts' does not exist.", + "File '/node_modules/lquery.d.ts' does not exist.", + "File '/node_modules/lquery/package.json' does not exist.", + "File '/node_modules/lquery/index.ts' does not exist.", + "File '/node_modules/lquery/index.d.ts' does not exist.", + "File '/node_modules/@types/lquery.ts' does not exist.", + "File '/node_modules/@types/lquery.d.ts' does not exist.", + "Found 'package.json' at '/node_modules/@types/lquery/package.json'.", + "'package.json' has 'typings' field 'lquery' that references '/node_modules/@types/lquery/lquery'.", + "File '/node_modules/@types/lquery/lquery' does not exist.", + "File '/node_modules/@types/lquery/lquery.ts' exist - use it as a name resolution result.", + "======== Type reference directive 'lquery' was successfully resolved to '/node_modules/@types/lquery/lquery.ts', primary: false. ========" +] \ No newline at end of file diff --git a/tests/baselines/reference/typingsLookup4.types b/tests/baselines/reference/typingsLookup4.types new file mode 100644 index 00000000000..d922c7b1dfa --- /dev/null +++ b/tests/baselines/reference/typingsLookup4.types @@ -0,0 +1,30 @@ +=== /a.ts === +import { j } from "jquery"; +>j : number + +import { k } from "kquery"; +>k : number + +import { l } from "lquery"; +>l : number + +j + k + l; +>j + k + l : number +>j + k : number +>j : number +>k : number +>l : number + +=== /node_modules/@types/jquery/jquery.d.ts === +export const j: number; +>j : number + +=== /node_modules/@types/kquery/kquery.d.ts === +export const k: number; +>k : number + +=== /node_modules/@types/lquery/lquery.ts === +export const l = 2; +>l : number +>2 : number + diff --git a/tests/cases/conformance/typings/typingsLookup4.ts b/tests/cases/conformance/typings/typingsLookup4.ts new file mode 100644 index 00000000000..234090aebad --- /dev/null +++ b/tests/cases/conformance/typings/typingsLookup4.ts @@ -0,0 +1,31 @@ +// @traceResolution: true +// @noImplicitReferences: true +// @currentDirectory: / +// A file extension is optional in typings entries. + +// @filename: /tsconfig.json +{} + +// @filename: /node_modules/@types/jquery/package.json +{ "typings": "jquery.d.ts" } + +// @filename: /node_modules/@types/jquery/jquery.d.ts +export const j: number; + +// @filename: /node_modules/@types/kquery/package.json +{ "typings": "kquery" } + +// @filename: /node_modules/@types/kquery/kquery.d.ts +export const k: number; + +// @filename: /node_modules/@types/lquery/package.json +{ "typings": "lquery" } + +// @filename: /node_modules/@types/lquery/lquery.ts +export const l = 2; + +// @filename: /a.ts +import { j } from "jquery"; +import { k } from "kquery"; +import { l } from "lquery"; +j + k + l; From 5aafc2c848ec209ec395428b76eb8a51341305d9 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 22 Aug 2016 14:08:34 -0700 Subject: [PATCH 206/233] Contextually type this in getDeclFromSig, not checkThisExpr --- src/compiler/checker.ts | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 17cfdf8976f..81a9c639feb 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3063,9 +3063,14 @@ namespace ts { } } // Use contextual parameter type if one is available - const type = declaration.symbol.name === "this" - ? getContextuallyTypedThisType(func) - : getContextuallyTypedParameterType(declaration); + let type: Type; + if (declaration.symbol.name === "this") { + const thisParameter = getContextuallyTypedThisParameter(func); + type = thisParameter ? getTypeOfSymbol(thisParameter) : undefined; + } + else { + type = getContextuallyTypedParameterType(declaration); + } if (type) { return addOptionality(type, /*optional*/ declaration.questionToken && includeOptionality); } @@ -4689,6 +4694,9 @@ namespace ts { if (isJSConstructSignature) { minArgumentCount--; } + if (!thisParameter && isObjectLiteralMethod(declaration)) { + thisParameter = getContextuallyTypedThisParameter(declaration); + } const classType = declaration.kind === SyntaxKind.Constructor ? getDeclaredTypeOfClassOrInterface(getMergedSymbol((declaration.parent).symbol)) @@ -9087,10 +9095,6 @@ namespace ts { if (thisType) { return thisType; } - const type = getContextuallyTypedThisType(container); - if (type) { - return type; - } } if (isClassLike(container.parent)) { const symbol = getSymbolOfNode(container.parent); @@ -9326,11 +9330,11 @@ namespace ts { } } - function getContextuallyTypedThisType(func: FunctionLikeDeclaration): Type { + function getContextuallyTypedThisParameter(func: FunctionLikeDeclaration): Symbol { if (isContextSensitiveFunctionOrObjectLiteralMethod(func) && func.kind !== SyntaxKind.ArrowFunction) { const contextualSignature = getContextualSignature(func); if (contextualSignature) { - return getThisTypeOfSignature(contextualSignature); + return contextualSignature.thisParameter; } } From 4a58e68d0040fb98e1be51ec9480d52a06bb8694 Mon Sep 17 00:00:00 2001 From: Yui Date: Mon, 22 Aug 2016 14:38:07 -0700 Subject: [PATCH 207/233] Update parser comment with es7 grammar (#10459) * Use ES7 term of ExponentiationExpression * Update timeout for mac OS * Address PR: add space --- Gulpfile.ts | 2 +- src/compiler/parser.ts | 42 +++++++++++++++++++++++++++++++----------- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/Gulpfile.ts b/Gulpfile.ts index 1b902562832..295a7ce03d9 100644 --- a/Gulpfile.ts +++ b/Gulpfile.ts @@ -551,7 +551,7 @@ function restoreSavedNodeEnv() { process.env.NODE_ENV = savedNodeEnv; } -let testTimeout = 20000; +let testTimeout = 40000; function runConsoleTests(defaultReporter: string, runInParallel: boolean, done: (e?: any) => void) { const lintFlag = cmdLineOptions["lint"]; cleanTestDirs((err) => { diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index d6a2cf88ed8..f373d339553 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -912,7 +912,7 @@ namespace ts { // Note: it is not actually necessary to save/restore the context flags here. That's // because the saving/restoring of these flags happens naturally through the recursive // descent nature of our parser. However, we still store this here just so we can - // assert that that invariant holds. + // assert that invariant holds. const saveContextFlags = contextFlags; // If we're only looking ahead, then tell the scanner to only lookahead as well. @@ -2765,7 +2765,7 @@ namespace ts { // Note: for ease of implementation we treat productions '2' and '3' as the same thing. // (i.e. they're both BinaryExpressions with an assignment operator in it). - // First, do the simple check if we have a YieldExpression (production '5'). + // First, do the simple check if we have a YieldExpression (production '6'). if (isYieldExpression()) { return parseYieldExpression(); } @@ -3373,24 +3373,44 @@ namespace ts { } /** - * Parse ES7 unary expression and await expression + * Parse ES7 exponential expression and await expression + * + * ES7 ExponentiationExpression: + * 1) UnaryExpression[?Yield] + * 2) UpdateExpression[?Yield] ** ExponentiationExpression[?Yield] * - * ES7 UnaryExpression: - * 1) SimpleUnaryExpression[?yield] - * 2) IncrementExpression[?yield] ** UnaryExpression[?yield] */ function parseUnaryExpressionOrHigher(): UnaryExpression | BinaryExpression { if (isAwaitExpression()) { return parseAwaitExpression(); } - if (isIncrementExpression()) { + /** + * ES7 UpdateExpression: + * 1) LeftHandSideExpression[?Yield] + * 2) LeftHandSideExpression[?Yield][no LineTerminator here]++ + * 3) LeftHandSideExpression[?Yield][no LineTerminator here]-- + * 4) ++UnaryExpression[?Yield] + * 5) --UnaryExpression[?Yield] + */ + if (isUpdateExpression()) { const incrementExpression = parseIncrementExpression(); return token() === SyntaxKind.AsteriskAsteriskToken ? parseBinaryExpressionRest(getBinaryOperatorPrecedence(), incrementExpression) : incrementExpression; } + /** + * ES7 UnaryExpression: + * 1) UpdateExpression[?yield] + * 2) delete UpdateExpression[?yield] + * 3) void UpdateExpression[?yield] + * 4) typeof UpdateExpression[?yield] + * 5) + UpdateExpression[?yield] + * 6) - UpdateExpression[?yield] + * 7) ~ UpdateExpression[?yield] + * 8) ! UpdateExpression[?yield] + */ const unaryOperator = token(); const simpleUnaryExpression = parseSimpleUnaryExpression(); if (token() === SyntaxKind.AsteriskAsteriskToken) { @@ -3408,8 +3428,8 @@ namespace ts { /** * Parse ES7 simple-unary expression or higher: * - * ES7 SimpleUnaryExpression: - * 1) IncrementExpression[?yield] + * ES7 UnaryExpression: + * 1) UpdateExpression[?yield] * 2) delete UnaryExpression[?yield] * 3) void UnaryExpression[?yield] * 4) typeof UnaryExpression[?yield] @@ -3447,14 +3467,14 @@ namespace ts { /** * Check if the current token can possibly be an ES7 increment expression. * - * ES7 IncrementExpression: + * ES7 UpdateExpression: * LeftHandSideExpression[?Yield] * LeftHandSideExpression[?Yield][no LineTerminator here]++ * LeftHandSideExpression[?Yield][no LineTerminator here]-- * ++LeftHandSideExpression[?Yield] * --LeftHandSideExpression[?Yield] */ - function isIncrementExpression(): boolean { + function isUpdateExpression(): boolean { // This function is called inside parseUnaryExpression to decide // whether to call parseSimpleUnaryExpression or call parseIncrementExpression directly switch (token()) { From 046b55e9f152917e801f4279ded73d467c93ae8b Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 22 Aug 2016 16:27:44 -0700 Subject: [PATCH 208/233] allowSyntheticDefaultImports resolves to modules instead of variables Fixes #10429 by improving the fix in #10096 --- src/compiler/checker.ts | 10 +++--- .../baselines/reference/tsxDefaultImports.js | 34 +++++++++++++++++++ .../reference/tsxDefaultImports.symbols | 29 ++++++++++++++++ .../reference/tsxDefaultImports.types | 29 ++++++++++++++++ tests/cases/compiler/tsxDefaultImports.ts | 12 +++++++ 5 files changed, 109 insertions(+), 5 deletions(-) create mode 100644 tests/baselines/reference/tsxDefaultImports.js create mode 100644 tests/baselines/reference/tsxDefaultImports.symbols create mode 100644 tests/baselines/reference/tsxDefaultImports.types create mode 100644 tests/cases/compiler/tsxDefaultImports.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 193deded4c5..aab284b0ceb 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1126,13 +1126,13 @@ namespace ts { else { symbolFromVariable = getPropertyOfVariable(targetSymbol, name.text); } - // If the export member we're looking for is default, and there is no real default but allowSyntheticDefaultImports is on, return the entire module as the default - if (!symbolFromVariable && allowSyntheticDefaultImports && name.text === "default") { - symbolFromVariable = resolveExternalModuleSymbol(moduleSymbol) || resolveSymbol(moduleSymbol); - } // if symbolFromVariable is export - get its final target symbolFromVariable = resolveSymbol(symbolFromVariable); - const symbolFromModule = getExportOfModule(targetSymbol, name.text); + let symbolFromModule = getExportOfModule(targetSymbol, name.text); + // If the export member we're looking for is default, and there is no real default but allowSyntheticDefaultImports is on, return the entire module as the default + if (!symbolFromModule && allowSyntheticDefaultImports && name.text === "default") { + symbolFromModule = resolveExternalModuleSymbol(moduleSymbol) || resolveSymbol(moduleSymbol); + } const symbol = symbolFromModule && symbolFromVariable ? combineValueAndTypeSymbols(symbolFromVariable, symbolFromModule) : symbolFromModule || symbolFromVariable; diff --git a/tests/baselines/reference/tsxDefaultImports.js b/tests/baselines/reference/tsxDefaultImports.js new file mode 100644 index 00000000000..79f5d2b8fa1 --- /dev/null +++ b/tests/baselines/reference/tsxDefaultImports.js @@ -0,0 +1,34 @@ +//// [tests/cases/compiler/tsxDefaultImports.ts] //// + +//// [a.ts] + +enum SomeEnum { + one, +} +export default class SomeClass { + public static E = SomeEnum; +} + +//// [b.ts] +import {default as Def} from "./a" +let a = Def.E.one; + + +//// [a.js] +"use strict"; +var SomeEnum; +(function (SomeEnum) { + SomeEnum[SomeEnum["one"] = 0] = "one"; +})(SomeEnum || (SomeEnum = {})); +var SomeClass = (function () { + function SomeClass() { + } + SomeClass.E = SomeEnum; + return SomeClass; +}()); +exports.__esModule = true; +exports["default"] = SomeClass; +//// [b.js] +"use strict"; +var a_1 = require("./a"); +var a = a_1["default"].E.one; diff --git a/tests/baselines/reference/tsxDefaultImports.symbols b/tests/baselines/reference/tsxDefaultImports.symbols new file mode 100644 index 00000000000..e42392e24aa --- /dev/null +++ b/tests/baselines/reference/tsxDefaultImports.symbols @@ -0,0 +1,29 @@ +=== tests/cases/compiler/a.ts === + +enum SomeEnum { +>SomeEnum : Symbol(SomeEnum, Decl(a.ts, 0, 0)) + + one, +>one : Symbol(SomeEnum.one, Decl(a.ts, 1, 15)) +} +export default class SomeClass { +>SomeClass : Symbol(SomeClass, Decl(a.ts, 3, 1)) + + public static E = SomeEnum; +>E : Symbol(SomeClass.E, Decl(a.ts, 4, 32)) +>SomeEnum : Symbol(SomeEnum, Decl(a.ts, 0, 0)) +} + +=== tests/cases/compiler/b.ts === +import {default as Def} from "./a" +>default : Symbol(Def, Decl(b.ts, 0, 8)) +>Def : Symbol(Def, Decl(b.ts, 0, 8)) + +let a = Def.E.one; +>a : Symbol(a, Decl(b.ts, 1, 3)) +>Def.E.one : Symbol(SomeEnum.one, Decl(a.ts, 1, 15)) +>Def.E : Symbol(Def.E, Decl(a.ts, 4, 32)) +>Def : Symbol(Def, Decl(b.ts, 0, 8)) +>E : Symbol(Def.E, Decl(a.ts, 4, 32)) +>one : Symbol(SomeEnum.one, Decl(a.ts, 1, 15)) + diff --git a/tests/baselines/reference/tsxDefaultImports.types b/tests/baselines/reference/tsxDefaultImports.types new file mode 100644 index 00000000000..a9bdedf3efd --- /dev/null +++ b/tests/baselines/reference/tsxDefaultImports.types @@ -0,0 +1,29 @@ +=== tests/cases/compiler/a.ts === + +enum SomeEnum { +>SomeEnum : SomeEnum + + one, +>one : SomeEnum +} +export default class SomeClass { +>SomeClass : SomeClass + + public static E = SomeEnum; +>E : typeof SomeEnum +>SomeEnum : typeof SomeEnum +} + +=== tests/cases/compiler/b.ts === +import {default as Def} from "./a" +>default : typeof Def +>Def : typeof Def + +let a = Def.E.one; +>a : SomeEnum +>Def.E.one : SomeEnum +>Def.E : typeof SomeEnum +>Def : typeof Def +>E : typeof SomeEnum +>one : SomeEnum + diff --git a/tests/cases/compiler/tsxDefaultImports.ts b/tests/cases/compiler/tsxDefaultImports.ts new file mode 100644 index 00000000000..4e313b33eac --- /dev/null +++ b/tests/cases/compiler/tsxDefaultImports.ts @@ -0,0 +1,12 @@ +// @Filename: a.ts + +enum SomeEnum { + one, +} +export default class SomeClass { + public static E = SomeEnum; +} + +// @Filename: b.ts +import {default as Def} from "./a" +let a = Def.E.one; From fc1d6a8437439b4caf232503baf07d1f5a4bf794 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 22 Aug 2016 16:36:38 -0700 Subject: [PATCH 209/233] Rename getContextuallyTypedThisParameter to getContextualThisParameter --- src/compiler/checker.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 81a9c639feb..2f2c3002299 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3065,7 +3065,7 @@ namespace ts { // Use contextual parameter type if one is available let type: Type; if (declaration.symbol.name === "this") { - const thisParameter = getContextuallyTypedThisParameter(func); + const thisParameter = getContextualThisParameter(func); type = thisParameter ? getTypeOfSymbol(thisParameter) : undefined; } else { @@ -4695,7 +4695,7 @@ namespace ts { minArgumentCount--; } if (!thisParameter && isObjectLiteralMethod(declaration)) { - thisParameter = getContextuallyTypedThisParameter(declaration); + thisParameter = getContextualThisParameter(declaration); } const classType = declaration.kind === SyntaxKind.Constructor ? @@ -9330,7 +9330,7 @@ namespace ts { } } - function getContextuallyTypedThisParameter(func: FunctionLikeDeclaration): Symbol { + function getContextualThisParameter(func: FunctionLikeDeclaration): Symbol { if (isContextSensitiveFunctionOrObjectLiteralMethod(func) && func.kind !== SyntaxKind.ArrowFunction) { const contextualSignature = getContextualSignature(func); if (contextualSignature) { From 36130ffa64ea95c6d924951ff2a2090504a0e94c Mon Sep 17 00:00:00 2001 From: Yui Date: Mon, 22 Aug 2016 16:37:04 -0700 Subject: [PATCH 210/233] Fix 10472: Invalid emitted code for await expression (#10483) * Properly emit await expression with yield expression * Add tests and update baselines * Move parsing await expression into parse unary-expression * Update incorrect comment --- src/compiler/emitter.ts | 4 ++ src/compiler/parser.ts | 11 ++-- .../reference/await_unaryExpression_es6.js | 47 ++++++++++++++++ .../await_unaryExpression_es6.symbols | 25 +++++++++ .../reference/await_unaryExpression_es6.types | 37 ++++++++++++ .../reference/await_unaryExpression_es6_1.js | 56 +++++++++++++++++++ .../await_unaryExpression_es6_1.symbols | 31 ++++++++++ .../await_unaryExpression_es6_1.types | 46 +++++++++++++++ .../reference/await_unaryExpression_es6_2.js | 38 +++++++++++++ .../await_unaryExpression_es6_2.symbols | 19 +++++++ .../await_unaryExpression_es6_2.types | 28 ++++++++++ .../await_unaryExpression_es6_3.errors.txt | 27 +++++++++ .../reference/await_unaryExpression_es6_3.js | 53 ++++++++++++++++++ tests/baselines/reference/castOfAwait.js | 6 +- .../async/es6/await_unaryExpression_es6.ts | 17 ++++++ .../async/es6/await_unaryExpression_es6_1.ts | 21 +++++++ .../async/es6/await_unaryExpression_es6_2.ts | 13 +++++ .../async/es6/await_unaryExpression_es6_3.ts | 19 +++++++ 18 files changed, 489 insertions(+), 9 deletions(-) create mode 100644 tests/baselines/reference/await_unaryExpression_es6.js create mode 100644 tests/baselines/reference/await_unaryExpression_es6.symbols create mode 100644 tests/baselines/reference/await_unaryExpression_es6.types create mode 100644 tests/baselines/reference/await_unaryExpression_es6_1.js create mode 100644 tests/baselines/reference/await_unaryExpression_es6_1.symbols create mode 100644 tests/baselines/reference/await_unaryExpression_es6_1.types create mode 100644 tests/baselines/reference/await_unaryExpression_es6_2.js create mode 100644 tests/baselines/reference/await_unaryExpression_es6_2.symbols create mode 100644 tests/baselines/reference/await_unaryExpression_es6_2.types create mode 100644 tests/baselines/reference/await_unaryExpression_es6_3.errors.txt create mode 100644 tests/baselines/reference/await_unaryExpression_es6_3.js create mode 100644 tests/cases/conformance/async/es6/await_unaryExpression_es6.ts create mode 100644 tests/cases/conformance/async/es6/await_unaryExpression_es6_1.ts create mode 100644 tests/cases/conformance/async/es6/await_unaryExpression_es6_2.ts create mode 100644 tests/cases/conformance/async/es6/await_unaryExpression_es6_3.ts diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 357a15507a4..d0f7918dc89 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1817,6 +1817,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge else if (node.parent.kind === SyntaxKind.ConditionalExpression && (node.parent).condition === node) { return true; } + else if (node.parent.kind === SyntaxKind.PrefixUnaryExpression || node.parent.kind === SyntaxKind.DeleteExpression || + node.parent.kind === SyntaxKind.TypeOfExpression || node.parent.kind === SyntaxKind.VoidExpression) { + return true; + } return false; } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index f373d339553..b5ba89887a2 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3381,10 +3381,6 @@ namespace ts { * */ function parseUnaryExpressionOrHigher(): UnaryExpression | BinaryExpression { - if (isAwaitExpression()) { - return parseAwaitExpression(); - } - /** * ES7 UpdateExpression: * 1) LeftHandSideExpression[?Yield] @@ -3452,13 +3448,15 @@ namespace ts { return parseTypeOfExpression(); case SyntaxKind.VoidKeyword: return parseVoidExpression(); - case SyntaxKind.AwaitKeyword: - return parseAwaitExpression(); case SyntaxKind.LessThanToken: // This is modified UnaryExpression grammar in TypeScript // UnaryExpression (modified): // < type > UnaryExpression return parseTypeAssertion(); + case SyntaxKind.AwaitKeyword: + if (isAwaitExpression()) { + return parseAwaitExpression(); + } default: return parseIncrementExpression(); } @@ -3485,6 +3483,7 @@ namespace ts { case SyntaxKind.DeleteKeyword: case SyntaxKind.TypeOfKeyword: case SyntaxKind.VoidKeyword: + case SyntaxKind.AwaitKeyword: return false; case SyntaxKind.LessThanToken: // If we are not in JSX context, we are parsing TypeAssertion which is an UnaryExpression diff --git a/tests/baselines/reference/await_unaryExpression_es6.js b/tests/baselines/reference/await_unaryExpression_es6.js new file mode 100644 index 00000000000..46065bdada9 --- /dev/null +++ b/tests/baselines/reference/await_unaryExpression_es6.js @@ -0,0 +1,47 @@ +//// [await_unaryExpression_es6.ts] + +async function bar() { + !await 42; // OK +} + +async function bar1() { + +await 42; // OK +} + +async function bar3() { + -await 42; // OK +} + +async function bar4() { + ~await 42; // OK +} + +//// [await_unaryExpression_es6.js] +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments)).next()); + }); +}; +function bar() { + return __awaiter(this, void 0, void 0, function* () { + !(yield 42); // OK + }); +} +function bar1() { + return __awaiter(this, void 0, void 0, function* () { + +(yield 42); // OK + }); +} +function bar3() { + return __awaiter(this, void 0, void 0, function* () { + -(yield 42); // OK + }); +} +function bar4() { + return __awaiter(this, void 0, void 0, function* () { + ~(yield 42); // OK + }); +} diff --git a/tests/baselines/reference/await_unaryExpression_es6.symbols b/tests/baselines/reference/await_unaryExpression_es6.symbols new file mode 100644 index 00000000000..81edd4b981b --- /dev/null +++ b/tests/baselines/reference/await_unaryExpression_es6.symbols @@ -0,0 +1,25 @@ +=== tests/cases/conformance/async/es6/await_unaryExpression_es6.ts === + +async function bar() { +>bar : Symbol(bar, Decl(await_unaryExpression_es6.ts, 0, 0)) + + !await 42; // OK +} + +async function bar1() { +>bar1 : Symbol(bar1, Decl(await_unaryExpression_es6.ts, 3, 1)) + + +await 42; // OK +} + +async function bar3() { +>bar3 : Symbol(bar3, Decl(await_unaryExpression_es6.ts, 7, 1)) + + -await 42; // OK +} + +async function bar4() { +>bar4 : Symbol(bar4, Decl(await_unaryExpression_es6.ts, 11, 1)) + + ~await 42; // OK +} diff --git a/tests/baselines/reference/await_unaryExpression_es6.types b/tests/baselines/reference/await_unaryExpression_es6.types new file mode 100644 index 00000000000..2a4b7354d3e --- /dev/null +++ b/tests/baselines/reference/await_unaryExpression_es6.types @@ -0,0 +1,37 @@ +=== tests/cases/conformance/async/es6/await_unaryExpression_es6.ts === + +async function bar() { +>bar : () => Promise + + !await 42; // OK +>!await 42 : boolean +>await 42 : number +>42 : number +} + +async function bar1() { +>bar1 : () => Promise + + +await 42; // OK +>+await 42 : number +>await 42 : number +>42 : number +} + +async function bar3() { +>bar3 : () => Promise + + -await 42; // OK +>-await 42 : number +>await 42 : number +>42 : number +} + +async function bar4() { +>bar4 : () => Promise + + ~await 42; // OK +>~await 42 : number +>await 42 : number +>42 : number +} diff --git a/tests/baselines/reference/await_unaryExpression_es6_1.js b/tests/baselines/reference/await_unaryExpression_es6_1.js new file mode 100644 index 00000000000..c6f5f1142c0 --- /dev/null +++ b/tests/baselines/reference/await_unaryExpression_es6_1.js @@ -0,0 +1,56 @@ +//// [await_unaryExpression_es6_1.ts] + +async function bar() { + !await 42; // OK +} + +async function bar1() { + delete await 42; // OK +} + +async function bar2() { + delete await 42; // OK +} + +async function bar3() { + void await 42; +} + +async function bar4() { + +await 42; +} + +//// [await_unaryExpression_es6_1.js] +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments)).next()); + }); +}; +function bar() { + return __awaiter(this, void 0, void 0, function* () { + !(yield 42); // OK + }); +} +function bar1() { + return __awaiter(this, void 0, void 0, function* () { + delete (yield 42); // OK + }); +} +function bar2() { + return __awaiter(this, void 0, void 0, function* () { + delete (yield 42); // OK + }); +} +function bar3() { + return __awaiter(this, void 0, void 0, function* () { + void (yield 42); + }); +} +function bar4() { + return __awaiter(this, void 0, void 0, function* () { + +(yield 42); + }); +} diff --git a/tests/baselines/reference/await_unaryExpression_es6_1.symbols b/tests/baselines/reference/await_unaryExpression_es6_1.symbols new file mode 100644 index 00000000000..ed7d7e4ed02 --- /dev/null +++ b/tests/baselines/reference/await_unaryExpression_es6_1.symbols @@ -0,0 +1,31 @@ +=== tests/cases/conformance/async/es6/await_unaryExpression_es6_1.ts === + +async function bar() { +>bar : Symbol(bar, Decl(await_unaryExpression_es6_1.ts, 0, 0)) + + !await 42; // OK +} + +async function bar1() { +>bar1 : Symbol(bar1, Decl(await_unaryExpression_es6_1.ts, 3, 1)) + + delete await 42; // OK +} + +async function bar2() { +>bar2 : Symbol(bar2, Decl(await_unaryExpression_es6_1.ts, 7, 1)) + + delete await 42; // OK +} + +async function bar3() { +>bar3 : Symbol(bar3, Decl(await_unaryExpression_es6_1.ts, 11, 1)) + + void await 42; +} + +async function bar4() { +>bar4 : Symbol(bar4, Decl(await_unaryExpression_es6_1.ts, 15, 1)) + + +await 42; +} diff --git a/tests/baselines/reference/await_unaryExpression_es6_1.types b/tests/baselines/reference/await_unaryExpression_es6_1.types new file mode 100644 index 00000000000..a5c3740b677 --- /dev/null +++ b/tests/baselines/reference/await_unaryExpression_es6_1.types @@ -0,0 +1,46 @@ +=== tests/cases/conformance/async/es6/await_unaryExpression_es6_1.ts === + +async function bar() { +>bar : () => Promise + + !await 42; // OK +>!await 42 : boolean +>await 42 : number +>42 : number +} + +async function bar1() { +>bar1 : () => Promise + + delete await 42; // OK +>delete await 42 : boolean +>await 42 : number +>42 : number +} + +async function bar2() { +>bar2 : () => Promise + + delete await 42; // OK +>delete await 42 : boolean +>await 42 : number +>42 : number +} + +async function bar3() { +>bar3 : () => Promise + + void await 42; +>void await 42 : undefined +>await 42 : number +>42 : number +} + +async function bar4() { +>bar4 : () => Promise + + +await 42; +>+await 42 : number +>await 42 : number +>42 : number +} diff --git a/tests/baselines/reference/await_unaryExpression_es6_2.js b/tests/baselines/reference/await_unaryExpression_es6_2.js new file mode 100644 index 00000000000..3c341018ed1 --- /dev/null +++ b/tests/baselines/reference/await_unaryExpression_es6_2.js @@ -0,0 +1,38 @@ +//// [await_unaryExpression_es6_2.ts] + +async function bar1() { + delete await 42; +} + +async function bar2() { + delete await 42; +} + +async function bar3() { + void await 42; +} + +//// [await_unaryExpression_es6_2.js] +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments)).next()); + }); +}; +function bar1() { + return __awaiter(this, void 0, void 0, function* () { + delete (yield 42); + }); +} +function bar2() { + return __awaiter(this, void 0, void 0, function* () { + delete (yield 42); + }); +} +function bar3() { + return __awaiter(this, void 0, void 0, function* () { + void (yield 42); + }); +} diff --git a/tests/baselines/reference/await_unaryExpression_es6_2.symbols b/tests/baselines/reference/await_unaryExpression_es6_2.symbols new file mode 100644 index 00000000000..574ea4d433a --- /dev/null +++ b/tests/baselines/reference/await_unaryExpression_es6_2.symbols @@ -0,0 +1,19 @@ +=== tests/cases/conformance/async/es6/await_unaryExpression_es6_2.ts === + +async function bar1() { +>bar1 : Symbol(bar1, Decl(await_unaryExpression_es6_2.ts, 0, 0)) + + delete await 42; +} + +async function bar2() { +>bar2 : Symbol(bar2, Decl(await_unaryExpression_es6_2.ts, 3, 1)) + + delete await 42; +} + +async function bar3() { +>bar3 : Symbol(bar3, Decl(await_unaryExpression_es6_2.ts, 7, 1)) + + void await 42; +} diff --git a/tests/baselines/reference/await_unaryExpression_es6_2.types b/tests/baselines/reference/await_unaryExpression_es6_2.types new file mode 100644 index 00000000000..b438f063add --- /dev/null +++ b/tests/baselines/reference/await_unaryExpression_es6_2.types @@ -0,0 +1,28 @@ +=== tests/cases/conformance/async/es6/await_unaryExpression_es6_2.ts === + +async function bar1() { +>bar1 : () => Promise + + delete await 42; +>delete await 42 : boolean +>await 42 : number +>42 : number +} + +async function bar2() { +>bar2 : () => Promise + + delete await 42; +>delete await 42 : boolean +>await 42 : number +>42 : number +} + +async function bar3() { +>bar3 : () => Promise + + void await 42; +>void await 42 : undefined +>await 42 : number +>42 : number +} diff --git a/tests/baselines/reference/await_unaryExpression_es6_3.errors.txt b/tests/baselines/reference/await_unaryExpression_es6_3.errors.txt new file mode 100644 index 00000000000..5518f84983f --- /dev/null +++ b/tests/baselines/reference/await_unaryExpression_es6_3.errors.txt @@ -0,0 +1,27 @@ +tests/cases/conformance/async/es6/await_unaryExpression_es6_3.ts(3,7): error TS1109: Expression expected. +tests/cases/conformance/async/es6/await_unaryExpression_es6_3.ts(7,7): error TS1109: Expression expected. + + +==== tests/cases/conformance/async/es6/await_unaryExpression_es6_3.ts (2 errors) ==== + + async function bar1() { + ++await 42; // Error + ~~~~~ +!!! error TS1109: Expression expected. + } + + async function bar2() { + --await 42; // Error + ~~~~~ +!!! error TS1109: Expression expected. + } + + async function bar3() { + var x = 42; + await x++; // OK but shouldn't need parenthesis + } + + async function bar4() { + var x = 42; + await x--; // OK but shouldn't need parenthesis + } \ No newline at end of file diff --git a/tests/baselines/reference/await_unaryExpression_es6_3.js b/tests/baselines/reference/await_unaryExpression_es6_3.js new file mode 100644 index 00000000000..077e264c450 --- /dev/null +++ b/tests/baselines/reference/await_unaryExpression_es6_3.js @@ -0,0 +1,53 @@ +//// [await_unaryExpression_es6_3.ts] + +async function bar1() { + ++await 42; // Error +} + +async function bar2() { + --await 42; // Error +} + +async function bar3() { + var x = 42; + await x++; // OK but shouldn't need parenthesis +} + +async function bar4() { + var x = 42; + await x--; // OK but shouldn't need parenthesis +} + +//// [await_unaryExpression_es6_3.js] +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments)).next()); + }); +}; +function bar1() { + return __awaiter(this, void 0, void 0, function* () { + ++; + yield 42; // Error + }); +} +function bar2() { + return __awaiter(this, void 0, void 0, function* () { + --; + yield 42; // Error + }); +} +function bar3() { + return __awaiter(this, void 0, void 0, function* () { + var x = 42; + yield x++; // OK but shouldn't need parenthesis + }); +} +function bar4() { + return __awaiter(this, void 0, void 0, function* () { + var x = 42; + yield x--; // OK but shouldn't need parenthesis + }); +} diff --git a/tests/baselines/reference/castOfAwait.js b/tests/baselines/reference/castOfAwait.js index 95817b7f0e6..26e9812bf43 100644 --- a/tests/baselines/reference/castOfAwait.js +++ b/tests/baselines/reference/castOfAwait.js @@ -20,9 +20,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge function f() { return __awaiter(this, void 0, void 0, function* () { yield 0; - typeof yield 0; - void yield 0; - yield void typeof void yield 0; + typeof (yield 0); + void (yield 0); + yield void typeof void (yield 0); yield yield 0; }); } diff --git a/tests/cases/conformance/async/es6/await_unaryExpression_es6.ts b/tests/cases/conformance/async/es6/await_unaryExpression_es6.ts new file mode 100644 index 00000000000..09cf0a87a5a --- /dev/null +++ b/tests/cases/conformance/async/es6/await_unaryExpression_es6.ts @@ -0,0 +1,17 @@ +// @target: es6 + +async function bar() { + !await 42; // OK +} + +async function bar1() { + +await 42; // OK +} + +async function bar3() { + -await 42; // OK +} + +async function bar4() { + ~await 42; // OK +} \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/await_unaryExpression_es6_1.ts b/tests/cases/conformance/async/es6/await_unaryExpression_es6_1.ts new file mode 100644 index 00000000000..5ccf1a1c35a --- /dev/null +++ b/tests/cases/conformance/async/es6/await_unaryExpression_es6_1.ts @@ -0,0 +1,21 @@ +// @target: es6 + +async function bar() { + !await 42; // OK +} + +async function bar1() { + delete await 42; // OK +} + +async function bar2() { + delete await 42; // OK +} + +async function bar3() { + void await 42; +} + +async function bar4() { + +await 42; +} \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/await_unaryExpression_es6_2.ts b/tests/cases/conformance/async/es6/await_unaryExpression_es6_2.ts new file mode 100644 index 00000000000..24683765d19 --- /dev/null +++ b/tests/cases/conformance/async/es6/await_unaryExpression_es6_2.ts @@ -0,0 +1,13 @@ +// @target: es6 + +async function bar1() { + delete await 42; +} + +async function bar2() { + delete await 42; +} + +async function bar3() { + void await 42; +} \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/await_unaryExpression_es6_3.ts b/tests/cases/conformance/async/es6/await_unaryExpression_es6_3.ts new file mode 100644 index 00000000000..b595ab5e748 --- /dev/null +++ b/tests/cases/conformance/async/es6/await_unaryExpression_es6_3.ts @@ -0,0 +1,19 @@ +// @target: es6 + +async function bar1() { + ++await 42; // Error +} + +async function bar2() { + --await 42; // Error +} + +async function bar3() { + var x = 42; + await x++; // OK but shouldn't need parenthesis +} + +async function bar4() { + var x = 42; + await x--; // OK but shouldn't need parenthesis +} \ No newline at end of file From 0f83fc130e2e4376cc64c7af48e8401c7eb2c68b Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Mon, 22 Aug 2016 23:10:05 -0700 Subject: [PATCH 211/233] Added tests. --- .../es6/templates/taggedTemplateWithConstructableTag01.ts | 3 +++ .../es6/templates/taggedTemplateWithConstructableTag02.ts | 6 ++++++ 2 files changed, 9 insertions(+) create mode 100644 tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag01.ts create mode 100644 tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag02.ts diff --git a/tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag01.ts b/tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag01.ts new file mode 100644 index 00000000000..996f5b80298 --- /dev/null +++ b/tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag01.ts @@ -0,0 +1,3 @@ +class CtorTag { } + +CtorTag `Hello world!`; \ No newline at end of file diff --git a/tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag02.ts b/tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag02.ts new file mode 100644 index 00000000000..7c6f009bf23 --- /dev/null +++ b/tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag02.ts @@ -0,0 +1,6 @@ +interface I { + new (...args: any[]): string; + new (): number; +} +var tag: I; +tag `Hello world!`; \ No newline at end of file From e7798c002ef4bb86d29bf7262aadb03b398aadba Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Mon, 22 Aug 2016 23:10:16 -0700 Subject: [PATCH 212/233] Accepted baselines. --- .../taggedTemplateWithConstructableTag01.js | 13 +++++++++++++ ...aggedTemplateWithConstructableTag01.symbols | 7 +++++++ .../taggedTemplateWithConstructableTag01.types | 9 +++++++++ .../taggedTemplateWithConstructableTag02.js | 12 ++++++++++++ ...aggedTemplateWithConstructableTag02.symbols | 16 ++++++++++++++++ .../taggedTemplateWithConstructableTag02.types | 18 ++++++++++++++++++ 6 files changed, 75 insertions(+) create mode 100644 tests/baselines/reference/taggedTemplateWithConstructableTag01.js create mode 100644 tests/baselines/reference/taggedTemplateWithConstructableTag01.symbols create mode 100644 tests/baselines/reference/taggedTemplateWithConstructableTag01.types create mode 100644 tests/baselines/reference/taggedTemplateWithConstructableTag02.js create mode 100644 tests/baselines/reference/taggedTemplateWithConstructableTag02.symbols create mode 100644 tests/baselines/reference/taggedTemplateWithConstructableTag02.types diff --git a/tests/baselines/reference/taggedTemplateWithConstructableTag01.js b/tests/baselines/reference/taggedTemplateWithConstructableTag01.js new file mode 100644 index 00000000000..dbcb70edf00 --- /dev/null +++ b/tests/baselines/reference/taggedTemplateWithConstructableTag01.js @@ -0,0 +1,13 @@ +//// [taggedTemplateWithConstructableTag01.ts] +class CtorTag { } + +CtorTag `Hello world!`; + +//// [taggedTemplateWithConstructableTag01.js] +var CtorTag = (function () { + function CtorTag() { + } + return CtorTag; +}()); +(_a = ["Hello world!"], _a.raw = ["Hello world!"], CtorTag(_a)); +var _a; diff --git a/tests/baselines/reference/taggedTemplateWithConstructableTag01.symbols b/tests/baselines/reference/taggedTemplateWithConstructableTag01.symbols new file mode 100644 index 00000000000..340d5887808 --- /dev/null +++ b/tests/baselines/reference/taggedTemplateWithConstructableTag01.symbols @@ -0,0 +1,7 @@ +=== tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag01.ts === +class CtorTag { } +>CtorTag : Symbol(CtorTag, Decl(taggedTemplateWithConstructableTag01.ts, 0, 0)) + +CtorTag `Hello world!`; +>CtorTag : Symbol(CtorTag, Decl(taggedTemplateWithConstructableTag01.ts, 0, 0)) + diff --git a/tests/baselines/reference/taggedTemplateWithConstructableTag01.types b/tests/baselines/reference/taggedTemplateWithConstructableTag01.types new file mode 100644 index 00000000000..05e5aba7cde --- /dev/null +++ b/tests/baselines/reference/taggedTemplateWithConstructableTag01.types @@ -0,0 +1,9 @@ +=== tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag01.ts === +class CtorTag { } +>CtorTag : CtorTag + +CtorTag `Hello world!`; +>CtorTag `Hello world!` : any +>CtorTag : typeof CtorTag +>`Hello world!` : string + diff --git a/tests/baselines/reference/taggedTemplateWithConstructableTag02.js b/tests/baselines/reference/taggedTemplateWithConstructableTag02.js new file mode 100644 index 00000000000..6c38d508bd2 --- /dev/null +++ b/tests/baselines/reference/taggedTemplateWithConstructableTag02.js @@ -0,0 +1,12 @@ +//// [taggedTemplateWithConstructableTag02.ts] +interface I { + new (...args: any[]): string; + new (): number; +} +var tag: I; +tag `Hello world!`; + +//// [taggedTemplateWithConstructableTag02.js] +var tag; +(_a = ["Hello world!"], _a.raw = ["Hello world!"], tag(_a)); +var _a; diff --git a/tests/baselines/reference/taggedTemplateWithConstructableTag02.symbols b/tests/baselines/reference/taggedTemplateWithConstructableTag02.symbols new file mode 100644 index 00000000000..2b2ccdbcbcf --- /dev/null +++ b/tests/baselines/reference/taggedTemplateWithConstructableTag02.symbols @@ -0,0 +1,16 @@ +=== tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag02.ts === +interface I { +>I : Symbol(I, Decl(taggedTemplateWithConstructableTag02.ts, 0, 0)) + + new (...args: any[]): string; +>args : Symbol(args, Decl(taggedTemplateWithConstructableTag02.ts, 1, 9)) + + new (): number; +} +var tag: I; +>tag : Symbol(tag, Decl(taggedTemplateWithConstructableTag02.ts, 4, 3)) +>I : Symbol(I, Decl(taggedTemplateWithConstructableTag02.ts, 0, 0)) + +tag `Hello world!`; +>tag : Symbol(tag, Decl(taggedTemplateWithConstructableTag02.ts, 4, 3)) + diff --git a/tests/baselines/reference/taggedTemplateWithConstructableTag02.types b/tests/baselines/reference/taggedTemplateWithConstructableTag02.types new file mode 100644 index 00000000000..1b96c3324dd --- /dev/null +++ b/tests/baselines/reference/taggedTemplateWithConstructableTag02.types @@ -0,0 +1,18 @@ +=== tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag02.ts === +interface I { +>I : I + + new (...args: any[]): string; +>args : any[] + + new (): number; +} +var tag: I; +>tag : I +>I : I + +tag `Hello world!`; +>tag `Hello world!` : any +>tag : I +>`Hello world!` : string + From 3292631b421fd85a870e65aaf82fab8aabf27dfe Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Mon, 22 Aug 2016 23:24:13 -0700 Subject: [PATCH 213/233] Added test for untyped tag. --- .../conformance/es6/templates/taggedTemplateUntypedTagCall01.ts | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 tests/cases/conformance/es6/templates/taggedTemplateUntypedTagCall01.ts diff --git a/tests/cases/conformance/es6/templates/taggedTemplateUntypedTagCall01.ts b/tests/cases/conformance/es6/templates/taggedTemplateUntypedTagCall01.ts new file mode 100644 index 00000000000..b77e04b3095 --- /dev/null +++ b/tests/cases/conformance/es6/templates/taggedTemplateUntypedTagCall01.ts @@ -0,0 +1,2 @@ +var tag: Function; +tag `Hello world!`; \ No newline at end of file From 310e9c3a5110fb07bd987f7fcce1b87d7d591180 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Mon, 22 Aug 2016 23:25:37 -0700 Subject: [PATCH 214/233] Accepted baselines. --- .../reference/taggedTemplateUntypedTagCall01.js | 8 ++++++++ .../reference/taggedTemplateUntypedTagCall01.symbols | 8 ++++++++ .../reference/taggedTemplateUntypedTagCall01.types | 10 ++++++++++ 3 files changed, 26 insertions(+) create mode 100644 tests/baselines/reference/taggedTemplateUntypedTagCall01.js create mode 100644 tests/baselines/reference/taggedTemplateUntypedTagCall01.symbols create mode 100644 tests/baselines/reference/taggedTemplateUntypedTagCall01.types diff --git a/tests/baselines/reference/taggedTemplateUntypedTagCall01.js b/tests/baselines/reference/taggedTemplateUntypedTagCall01.js new file mode 100644 index 00000000000..e0d16eec39d --- /dev/null +++ b/tests/baselines/reference/taggedTemplateUntypedTagCall01.js @@ -0,0 +1,8 @@ +//// [taggedTemplateUntypedTagCall01.ts] +var tag: Function; +tag `Hello world!`; + +//// [taggedTemplateUntypedTagCall01.js] +var tag; +(_a = ["Hello world!"], _a.raw = ["Hello world!"], tag(_a)); +var _a; diff --git a/tests/baselines/reference/taggedTemplateUntypedTagCall01.symbols b/tests/baselines/reference/taggedTemplateUntypedTagCall01.symbols new file mode 100644 index 00000000000..07af0a996e5 --- /dev/null +++ b/tests/baselines/reference/taggedTemplateUntypedTagCall01.symbols @@ -0,0 +1,8 @@ +=== tests/cases/conformance/es6/templates/taggedTemplateUntypedTagCall01.ts === +var tag: Function; +>tag : Symbol(tag, Decl(taggedTemplateUntypedTagCall01.ts, 0, 3)) +>Function : Symbol(Function, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + +tag `Hello world!`; +>tag : Symbol(tag, Decl(taggedTemplateUntypedTagCall01.ts, 0, 3)) + diff --git a/tests/baselines/reference/taggedTemplateUntypedTagCall01.types b/tests/baselines/reference/taggedTemplateUntypedTagCall01.types new file mode 100644 index 00000000000..3949869550c --- /dev/null +++ b/tests/baselines/reference/taggedTemplateUntypedTagCall01.types @@ -0,0 +1,10 @@ +=== tests/cases/conformance/es6/templates/taggedTemplateUntypedTagCall01.ts === +var tag: Function; +>tag : Function +>Function : Function + +tag `Hello world!`; +>tag `Hello world!` : any +>tag : Function +>`Hello world!` : string + From 590755b8a0c2396969b0a7de17d6fe745a220694 Mon Sep 17 00:00:00 2001 From: Yuichi Nukiyama Date: Tue, 23 Aug 2016 21:54:01 +0900 Subject: [PATCH 215/233] change error message --- src/compiler/checker.ts | 2 +- src/compiler/diagnosticMessages.json | 2 +- .../reference/apparentTypeSubtyping.errors.txt | 4 ++-- .../reference/apparentTypeSupertype.errors.txt | 4 ++-- tests/baselines/reference/arrayLiterals3.errors.txt | 4 ++-- .../reference/assignFromBooleanInterface.errors.txt | 4 ++-- .../reference/assignFromBooleanInterface2.errors.txt | 4 ++-- .../reference/assignFromNumberInterface.errors.txt | 4 ++-- .../reference/assignFromNumberInterface2.errors.txt | 4 ++-- .../reference/assignFromStringInterface.errors.txt | 4 ++-- .../reference/assignFromStringInterface2.errors.txt | 4 ++-- .../reference/nativeToBoxedTypes.errors.txt | 12 ++++++------ .../baselines/reference/primitiveMembers.errors.txt | 4 ++-- tests/baselines/reference/symbolType15.errors.txt | 4 ++-- 14 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3a6608c71d1..a6cb6fbde44 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6275,7 +6275,7 @@ namespace ts { (globalNumberType === source && numberType === target) || (globalBooleanType === source && booleanType === target) || (getGlobalESSymbolType() === source && esSymbolType === target)) { - reportError(Diagnostics._0_is_a_primitive_type_while_1_is_a_boxed_object_Prefer_using_0_when_possible, targetType, sourceType); + reportError(Diagnostics._0_is_a_primitive_but_1_is_a_wrapper_object_Prefer_using_0_when_possible, targetType, sourceType); } } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index fb8bc21ec95..b8978b32571 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1955,7 +1955,7 @@ "category": "Error", "code": 2691 }, - "'{0}' is a primitive type while '{1}' is a boxed object. Prefer using '{0}' when possible.": { + "'{0}' is a primitive, but '{1}' is a wrapper object. Prefer using '{0}' when possible.": { "category": "Error", "code": 2692 }, diff --git a/tests/baselines/reference/apparentTypeSubtyping.errors.txt b/tests/baselines/reference/apparentTypeSubtyping.errors.txt index c19cfa44360..01fffeb3824 100644 --- a/tests/baselines/reference/apparentTypeSubtyping.errors.txt +++ b/tests/baselines/reference/apparentTypeSubtyping.errors.txt @@ -1,7 +1,7 @@ tests/cases/conformance/types/typeRelationships/apparentType/apparentTypeSubtyping.ts(9,7): error TS2415: Class 'Derived' incorrectly extends base class 'Base'. Types of property 'x' are incompatible. Type 'String' is not assignable to type 'string'. - 'string' is a primitive type while 'String' is a boxed object. Prefer using 'string' when possible. + 'string' is a primitive, but 'String' is a wrapper object. Prefer using 'string' when possible. ==== tests/cases/conformance/types/typeRelationships/apparentType/apparentTypeSubtyping.ts (1 errors) ==== @@ -18,7 +18,7 @@ tests/cases/conformance/types/typeRelationships/apparentType/apparentTypeSubtypi !!! error TS2415: Class 'Derived' incorrectly extends base class 'Base'. !!! error TS2415: Types of property 'x' are incompatible. !!! error TS2415: Type 'String' is not assignable to type 'string'. -!!! error TS2415: 'string' is a primitive type while 'String' is a boxed object. Prefer using 'string' when possible. +!!! error TS2415: 'string' is a primitive, but 'String' is a wrapper object. Prefer using 'string' when possible. x: String; } diff --git a/tests/baselines/reference/apparentTypeSupertype.errors.txt b/tests/baselines/reference/apparentTypeSupertype.errors.txt index b7195557e8d..c7610a80f2f 100644 --- a/tests/baselines/reference/apparentTypeSupertype.errors.txt +++ b/tests/baselines/reference/apparentTypeSupertype.errors.txt @@ -2,7 +2,7 @@ tests/cases/conformance/types/typeRelationships/apparentType/apparentTypeSuperty Types of property 'x' are incompatible. Type 'U' is not assignable to type 'string'. Type 'String' is not assignable to type 'string'. - 'string' is a primitive type while 'String' is a boxed object. Prefer using 'string' when possible. + 'string' is a primitive, but 'String' is a wrapper object. Prefer using 'string' when possible. ==== tests/cases/conformance/types/typeRelationships/apparentType/apparentTypeSupertype.ts (1 errors) ==== @@ -20,6 +20,6 @@ tests/cases/conformance/types/typeRelationships/apparentType/apparentTypeSuperty !!! error TS2415: Types of property 'x' are incompatible. !!! error TS2415: Type 'U' is not assignable to type 'string'. !!! error TS2415: Type 'String' is not assignable to type 'string'. -!!! error TS2415: 'string' is a primitive type while 'String' is a boxed object. Prefer using 'string' when possible. +!!! error TS2415: 'string' is a primitive, but 'String' is a wrapper object. Prefer using 'string' when possible. x: U; } \ No newline at end of file diff --git a/tests/baselines/reference/arrayLiterals3.errors.txt b/tests/baselines/reference/arrayLiterals3.errors.txt index eb0c6dc51b7..7ced9e3933e 100644 --- a/tests/baselines/reference/arrayLiterals3.errors.txt +++ b/tests/baselines/reference/arrayLiterals3.errors.txt @@ -18,7 +18,7 @@ tests/cases/conformance/expressions/arrayLiterals/arrayLiterals3.ts(34,5): error Types of parameters 'items' and 'items' are incompatible. Type 'Number' is not assignable to type 'string | number'. Type 'Number' is not assignable to type 'number'. - 'number' is a primitive type while 'Number' is a boxed object. Prefer using 'number' when possible. + 'number' is a primitive, but 'Number' is a wrapper object. Prefer using 'number' when possible. ==== tests/cases/conformance/expressions/arrayLiterals/arrayLiterals3.ts (6 errors) ==== @@ -82,5 +82,5 @@ tests/cases/conformance/expressions/arrayLiterals/arrayLiterals3.ts(34,5): error !!! error TS2322: Types of parameters 'items' and 'items' are incompatible. !!! error TS2322: Type 'Number' is not assignable to type 'string | number'. !!! error TS2322: Type 'Number' is not assignable to type 'number'. -!!! error TS2322: 'number' is a primitive type while 'Number' is a boxed object. Prefer using 'number' when possible. +!!! error TS2322: 'number' is a primitive, but 'Number' is a wrapper object. Prefer using 'number' when possible. \ No newline at end of file diff --git a/tests/baselines/reference/assignFromBooleanInterface.errors.txt b/tests/baselines/reference/assignFromBooleanInterface.errors.txt index f5c92a47569..555b645cd7d 100644 --- a/tests/baselines/reference/assignFromBooleanInterface.errors.txt +++ b/tests/baselines/reference/assignFromBooleanInterface.errors.txt @@ -1,5 +1,5 @@ tests/cases/conformance/types/primitives/boolean/assignFromBooleanInterface.ts(3,1): error TS2322: Type 'Boolean' is not assignable to type 'boolean'. - 'boolean' is a primitive type while 'Boolean' is a boxed object. Prefer using 'boolean' when possible. + 'boolean' is a primitive, but 'Boolean' is a wrapper object. Prefer using 'boolean' when possible. ==== tests/cases/conformance/types/primitives/boolean/assignFromBooleanInterface.ts (1 errors) ==== @@ -8,5 +8,5 @@ tests/cases/conformance/types/primitives/boolean/assignFromBooleanInterface.ts(3 x = a; ~ !!! error TS2322: Type 'Boolean' is not assignable to type 'boolean'. -!!! error TS2322: 'boolean' is a primitive type while 'Boolean' is a boxed object. Prefer using 'boolean' when possible. +!!! error TS2322: 'boolean' is a primitive, but 'Boolean' is a wrapper object. Prefer using 'boolean' when possible. a = x; \ No newline at end of file diff --git a/tests/baselines/reference/assignFromBooleanInterface2.errors.txt b/tests/baselines/reference/assignFromBooleanInterface2.errors.txt index bfbf56eec5a..6c7ebbe5ee8 100644 --- a/tests/baselines/reference/assignFromBooleanInterface2.errors.txt +++ b/tests/baselines/reference/assignFromBooleanInterface2.errors.txt @@ -3,7 +3,7 @@ tests/cases/conformance/types/primitives/boolean/assignFromBooleanInterface2.ts( Type '() => Object' is not assignable to type '() => boolean'. Type 'Object' is not assignable to type 'boolean'. tests/cases/conformance/types/primitives/boolean/assignFromBooleanInterface2.ts(19,1): error TS2322: Type 'Boolean' is not assignable to type 'boolean'. - 'boolean' is a primitive type while 'Boolean' is a boxed object. Prefer using 'boolean' when possible. + 'boolean' is a primitive, but 'Boolean' is a wrapper object. Prefer using 'boolean' when possible. tests/cases/conformance/types/primitives/boolean/assignFromBooleanInterface2.ts(20,1): error TS2322: Type 'NotBoolean' is not assignable to type 'boolean'. @@ -34,7 +34,7 @@ tests/cases/conformance/types/primitives/boolean/assignFromBooleanInterface2.ts( x = a; // expected error ~ !!! error TS2322: Type 'Boolean' is not assignable to type 'boolean'. -!!! error TS2322: 'boolean' is a primitive type while 'Boolean' is a boxed object. Prefer using 'boolean' when possible. +!!! error TS2322: 'boolean' is a primitive, but 'Boolean' is a wrapper object. Prefer using 'boolean' when possible. x = b; // expected error ~ !!! error TS2322: Type 'NotBoolean' is not assignable to type 'boolean'. diff --git a/tests/baselines/reference/assignFromNumberInterface.errors.txt b/tests/baselines/reference/assignFromNumberInterface.errors.txt index e68ec76f68f..1a70ef342d2 100644 --- a/tests/baselines/reference/assignFromNumberInterface.errors.txt +++ b/tests/baselines/reference/assignFromNumberInterface.errors.txt @@ -1,5 +1,5 @@ tests/cases/conformance/types/primitives/number/assignFromNumberInterface.ts(3,1): error TS2322: Type 'Number' is not assignable to type 'number'. - 'number' is a primitive type while 'Number' is a boxed object. Prefer using 'number' when possible. + 'number' is a primitive, but 'Number' is a wrapper object. Prefer using 'number' when possible. ==== tests/cases/conformance/types/primitives/number/assignFromNumberInterface.ts (1 errors) ==== @@ -8,5 +8,5 @@ tests/cases/conformance/types/primitives/number/assignFromNumberInterface.ts(3,1 x = a; ~ !!! error TS2322: Type 'Number' is not assignable to type 'number'. -!!! error TS2322: 'number' is a primitive type while 'Number' is a boxed object. Prefer using 'number' when possible. +!!! error TS2322: 'number' is a primitive, but 'Number' is a wrapper object. Prefer using 'number' when possible. a = x; \ No newline at end of file diff --git a/tests/baselines/reference/assignFromNumberInterface2.errors.txt b/tests/baselines/reference/assignFromNumberInterface2.errors.txt index 5cae4654760..3297501d612 100644 --- a/tests/baselines/reference/assignFromNumberInterface2.errors.txt +++ b/tests/baselines/reference/assignFromNumberInterface2.errors.txt @@ -1,5 +1,5 @@ tests/cases/conformance/types/primitives/number/assignFromNumberInterface2.ts(24,1): error TS2322: Type 'Number' is not assignable to type 'number'. - 'number' is a primitive type while 'Number' is a boxed object. Prefer using 'number' when possible. + 'number' is a primitive, but 'Number' is a wrapper object. Prefer using 'number' when possible. tests/cases/conformance/types/primitives/number/assignFromNumberInterface2.ts(25,1): error TS2322: Type 'NotNumber' is not assignable to type 'number'. @@ -30,7 +30,7 @@ tests/cases/conformance/types/primitives/number/assignFromNumberInterface2.ts(25 x = a; // expected error ~ !!! error TS2322: Type 'Number' is not assignable to type 'number'. -!!! error TS2322: 'number' is a primitive type while 'Number' is a boxed object. Prefer using 'number' when possible. +!!! error TS2322: 'number' is a primitive, but 'Number' is a wrapper object. Prefer using 'number' when possible. x = b; // expected error ~ !!! error TS2322: Type 'NotNumber' is not assignable to type 'number'. diff --git a/tests/baselines/reference/assignFromStringInterface.errors.txt b/tests/baselines/reference/assignFromStringInterface.errors.txt index 80268f4b06a..7e7af4d1b9d 100644 --- a/tests/baselines/reference/assignFromStringInterface.errors.txt +++ b/tests/baselines/reference/assignFromStringInterface.errors.txt @@ -1,5 +1,5 @@ tests/cases/conformance/types/primitives/string/assignFromStringInterface.ts(3,1): error TS2322: Type 'String' is not assignable to type 'string'. - 'string' is a primitive type while 'String' is a boxed object. Prefer using 'string' when possible. + 'string' is a primitive, but 'String' is a wrapper object. Prefer using 'string' when possible. ==== tests/cases/conformance/types/primitives/string/assignFromStringInterface.ts (1 errors) ==== @@ -8,5 +8,5 @@ tests/cases/conformance/types/primitives/string/assignFromStringInterface.ts(3,1 x = a; ~ !!! error TS2322: Type 'String' is not assignable to type 'string'. -!!! error TS2322: 'string' is a primitive type while 'String' is a boxed object. Prefer using 'string' when possible. +!!! error TS2322: 'string' is a primitive, but 'String' is a wrapper object. Prefer using 'string' when possible. a = x; \ No newline at end of file diff --git a/tests/baselines/reference/assignFromStringInterface2.errors.txt b/tests/baselines/reference/assignFromStringInterface2.errors.txt index d20bdaaba32..0fc3284bf00 100644 --- a/tests/baselines/reference/assignFromStringInterface2.errors.txt +++ b/tests/baselines/reference/assignFromStringInterface2.errors.txt @@ -1,5 +1,5 @@ tests/cases/conformance/types/primitives/string/assignFromStringInterface2.ts(47,1): error TS2322: Type 'String' is not assignable to type 'string'. - 'string' is a primitive type while 'String' is a boxed object. Prefer using 'string' when possible. + 'string' is a primitive, but 'String' is a wrapper object. Prefer using 'string' when possible. tests/cases/conformance/types/primitives/string/assignFromStringInterface2.ts(48,1): error TS2322: Type 'NotString' is not assignable to type 'string'. @@ -53,7 +53,7 @@ tests/cases/conformance/types/primitives/string/assignFromStringInterface2.ts(48 x = a; // expected error ~ !!! error TS2322: Type 'String' is not assignable to type 'string'. -!!! error TS2322: 'string' is a primitive type while 'String' is a boxed object. Prefer using 'string' when possible. +!!! error TS2322: 'string' is a primitive, but 'String' is a wrapper object. Prefer using 'string' when possible. x = b; // expected error ~ !!! error TS2322: Type 'NotString' is not assignable to type 'string'. diff --git a/tests/baselines/reference/nativeToBoxedTypes.errors.txt b/tests/baselines/reference/nativeToBoxedTypes.errors.txt index 255e0414fd9..03186a6d7e3 100644 --- a/tests/baselines/reference/nativeToBoxedTypes.errors.txt +++ b/tests/baselines/reference/nativeToBoxedTypes.errors.txt @@ -1,9 +1,9 @@ tests/cases/compiler/nativeToBoxedTypes.ts(3,1): error TS2322: Type 'Number' is not assignable to type 'number'. - 'number' is a primitive type while 'Number' is a boxed object. Prefer using 'number' when possible. + 'number' is a primitive, but 'Number' is a wrapper object. Prefer using 'number' when possible. tests/cases/compiler/nativeToBoxedTypes.ts(7,1): error TS2322: Type 'String' is not assignable to type 'string'. - 'string' is a primitive type while 'String' is a boxed object. Prefer using 'string' when possible. + 'string' is a primitive, but 'String' is a wrapper object. Prefer using 'string' when possible. tests/cases/compiler/nativeToBoxedTypes.ts(11,1): error TS2322: Type 'Boolean' is not assignable to type 'boolean'. - 'boolean' is a primitive type while 'Boolean' is a boxed object. Prefer using 'boolean' when possible. + 'boolean' is a primitive, but 'Boolean' is a wrapper object. Prefer using 'boolean' when possible. tests/cases/compiler/nativeToBoxedTypes.ts(14,10): error TS2304: Cannot find name 'Symbol'. @@ -13,21 +13,21 @@ tests/cases/compiler/nativeToBoxedTypes.ts(14,10): error TS2304: Cannot find nam n = N; ~ !!! error TS2322: Type 'Number' is not assignable to type 'number'. -!!! error TS2322: 'number' is a primitive type while 'Number' is a boxed object. Prefer using 'number' when possible. +!!! error TS2322: 'number' is a primitive, but 'Number' is a wrapper object. Prefer using 'number' when possible. var S = new String(); var s = "foge"; s = S; ~ !!! error TS2322: Type 'String' is not assignable to type 'string'. -!!! error TS2322: 'string' is a primitive type while 'String' is a boxed object. Prefer using 'string' when possible. +!!! error TS2322: 'string' is a primitive, but 'String' is a wrapper object. Prefer using 'string' when possible. var B = new Boolean(); var b = true; b = B; ~ !!! error TS2322: Type 'Boolean' is not assignable to type 'boolean'. -!!! error TS2322: 'boolean' is a primitive type while 'Boolean' is a boxed object. Prefer using 'boolean' when possible. +!!! error TS2322: 'boolean' is a primitive, but 'Boolean' is a wrapper object. Prefer using 'boolean' when possible. var sym: symbol; var Sym: Symbol; diff --git a/tests/baselines/reference/primitiveMembers.errors.txt b/tests/baselines/reference/primitiveMembers.errors.txt index 24f02486d00..49ef8c4e3cf 100644 --- a/tests/baselines/reference/primitiveMembers.errors.txt +++ b/tests/baselines/reference/primitiveMembers.errors.txt @@ -1,6 +1,6 @@ tests/cases/compiler/primitiveMembers.ts(5,3): error TS2339: Property 'toBAZ' does not exist on type 'number'. tests/cases/compiler/primitiveMembers.ts(11,1): error TS2322: Type 'Number' is not assignable to type 'number'. - 'number' is a primitive type while 'Number' is a boxed object. Prefer using 'number' when possible. + 'number' is a primitive, but 'Number' is a wrapper object. Prefer using 'number' when possible. ==== tests/cases/compiler/primitiveMembers.ts (2 errors) ==== @@ -19,7 +19,7 @@ tests/cases/compiler/primitiveMembers.ts(11,1): error TS2322: Type 'Number' is n n = N; // should not work, as 'number' has a different brand ~ !!! error TS2322: Type 'Number' is not assignable to type 'number'. -!!! error TS2322: 'number' is a primitive type while 'Number' is a boxed object. Prefer using 'number' when possible. +!!! error TS2322: 'number' is a primitive, but 'Number' is a wrapper object. Prefer using 'number' when possible. N = n; // should work var o: Object = {} diff --git a/tests/baselines/reference/symbolType15.errors.txt b/tests/baselines/reference/symbolType15.errors.txt index 4a27ea24c59..205a2a999d0 100644 --- a/tests/baselines/reference/symbolType15.errors.txt +++ b/tests/baselines/reference/symbolType15.errors.txt @@ -1,5 +1,5 @@ tests/cases/conformance/es6/Symbols/symbolType15.ts(5,1): error TS2322: Type 'Symbol' is not assignable to type 'symbol'. - 'symbol' is a primitive type while 'Symbol' is a boxed object. Prefer using 'symbol' when possible. + 'symbol' is a primitive, but 'Symbol' is a wrapper object. Prefer using 'symbol' when possible. ==== tests/cases/conformance/es6/Symbols/symbolType15.ts (1 errors) ==== @@ -10,4 +10,4 @@ tests/cases/conformance/es6/Symbols/symbolType15.ts(5,1): error TS2322: Type 'Sy sym = symObj; ~~~ !!! error TS2322: Type 'Symbol' is not assignable to type 'symbol'. -!!! error TS2322: 'symbol' is a primitive type while 'Symbol' is a boxed object. Prefer using 'symbol' when possible. \ No newline at end of file +!!! error TS2322: 'symbol' is a primitive, but 'Symbol' is a wrapper object. Prefer using 'symbol' when possible. \ No newline at end of file From c21d16a3bbb58b9beac271b1735fa782243d447a Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Tue, 23 Aug 2016 11:14:05 -0700 Subject: [PATCH 216/233] Added test for decorators. --- .../decorators/class/constructableDecoratorOnClass01.ts | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/cases/conformance/decorators/class/constructableDecoratorOnClass01.ts diff --git a/tests/cases/conformance/decorators/class/constructableDecoratorOnClass01.ts b/tests/cases/conformance/decorators/class/constructableDecoratorOnClass01.ts new file mode 100644 index 00000000000..a9f9fa1699d --- /dev/null +++ b/tests/cases/conformance/decorators/class/constructableDecoratorOnClass01.ts @@ -0,0 +1,8 @@ +// @experimentalDecorators: true + +class CtorDtor {} + +@CtorDtor +class C { + +} From d6ec5f29796ff094267df0e11b3b17bc6b95a28a Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Tue, 23 Aug 2016 11:14:33 -0700 Subject: [PATCH 217/233] Accepted baselines. --- .../constructableDecoratorOnClass01.js | 30 +++++++++++++++++++ .../constructableDecoratorOnClass01.symbols | 13 ++++++++ .../constructableDecoratorOnClass01.types | 13 ++++++++ 3 files changed, 56 insertions(+) create mode 100644 tests/baselines/reference/constructableDecoratorOnClass01.js create mode 100644 tests/baselines/reference/constructableDecoratorOnClass01.symbols create mode 100644 tests/baselines/reference/constructableDecoratorOnClass01.types diff --git a/tests/baselines/reference/constructableDecoratorOnClass01.js b/tests/baselines/reference/constructableDecoratorOnClass01.js new file mode 100644 index 00000000000..26c5bf4a1c4 --- /dev/null +++ b/tests/baselines/reference/constructableDecoratorOnClass01.js @@ -0,0 +1,30 @@ +//// [constructableDecoratorOnClass01.ts] + +class CtorDtor {} + +@CtorDtor +class C { + +} + + +//// [constructableDecoratorOnClass01.js] +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var CtorDtor = (function () { + function CtorDtor() { + } + return CtorDtor; +}()); +var C = (function () { + function C() { + } + C = __decorate([ + CtorDtor + ], C); + return C; +}()); diff --git a/tests/baselines/reference/constructableDecoratorOnClass01.symbols b/tests/baselines/reference/constructableDecoratorOnClass01.symbols new file mode 100644 index 00000000000..46de420b826 --- /dev/null +++ b/tests/baselines/reference/constructableDecoratorOnClass01.symbols @@ -0,0 +1,13 @@ +=== tests/cases/conformance/decorators/class/constructableDecoratorOnClass01.ts === + +class CtorDtor {} +>CtorDtor : Symbol(CtorDtor, Decl(constructableDecoratorOnClass01.ts, 0, 0)) + +@CtorDtor +>CtorDtor : Symbol(CtorDtor, Decl(constructableDecoratorOnClass01.ts, 0, 0)) + +class C { +>C : Symbol(C, Decl(constructableDecoratorOnClass01.ts, 1, 17)) + +} + diff --git a/tests/baselines/reference/constructableDecoratorOnClass01.types b/tests/baselines/reference/constructableDecoratorOnClass01.types new file mode 100644 index 00000000000..d66778a27e6 --- /dev/null +++ b/tests/baselines/reference/constructableDecoratorOnClass01.types @@ -0,0 +1,13 @@ +=== tests/cases/conformance/decorators/class/constructableDecoratorOnClass01.ts === + +class CtorDtor {} +>CtorDtor : CtorDtor + +@CtorDtor +>CtorDtor : typeof CtorDtor + +class C { +>C : C + +} + From 7ecbfb21481fd81464404802bd7a5d713cdf8a09 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Tue, 23 Aug 2016 11:30:44 -0700 Subject: [PATCH 218/233] Unify untyped call checking between decorators and template tags. --- src/compiler/checker.ts | 43 +++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 370802478bc..bde11422b95 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11943,18 +11943,12 @@ namespace ts { // Function interface, since they have none by default. This is a bit of a leap of faith // that the user will not add any. const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call); - const constructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct); - // TS 1.0 spec: 4.12 - // If FuncExpr is of type Any, or of an object type that has no call or construct signatures - // but is a subtype of the Function interface, the call is an untyped function call. In an - // untyped function call no TypeArgs are permitted, Args can be any argument list, no contextual + + // TS 1.0 Spec: 4.12 + // In an untyped function call no TypeArgs are permitted, Args can be any argument list, no contextual // types are provided for the argument expressions, and the result is always of type Any. - // We exclude union types because we may have a union of function types that happen to have - // no common signatures. - if (isTypeAny(funcType) || - (isTypeAny(apparentType) && funcType.flags & TypeFlags.TypeParameter) || - (!callSignatures.length && !constructSignatures.length && !(funcType.flags & TypeFlags.Union) && isTypeAssignableTo(funcType, globalFunctionType))) { + if (isUntypedFunctionCall(funcType, apparentType, callSignatures.length, constructSignatures.length)) { // The unknownType indicates that an error already occurred (and was reported). No // need to report another error in this case. if (funcType !== unknownType && node.typeArguments) { @@ -11977,6 +11971,29 @@ namespace ts { return resolveCall(node, callSignatures, candidatesOutArray); } + /** + * TS 1.0 spec: 4.12 + * If FuncExpr is of type Any, or of an object type that has no call or construct signatures + * but is a subtype of the Function interface, the call is an untyped function call. + */ + function isUntypedFunctionCall(funcType: Type, apparentFuncType: Type, numCallSignatures: number, numConstructSignatures: number) { + if (isTypeAny(funcType)) { + return true; + } + if (isTypeAny(apparentFuncType) && funcType.flags & TypeFlags.TypeParameter) { + return true; + } + if (!numCallSignatures && !numConstructSignatures) { + // We exclude union types because we may have a union of function types that happen to have + // no common signatures. + if (funcType.flags & TypeFlags.Union) { + return false; + } + return isTypeAssignableTo(funcType, globalFunctionType); + } + return false; + } + function resolveNewExpression(node: NewExpression, candidatesOutArray: Signature[]): Signature { if (node.arguments && languageVersion < ScriptTarget.ES5) { const spreadIndex = getSpreadArgumentIndex(node.arguments); @@ -12102,8 +12119,9 @@ namespace ts { } const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call); + const constructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct); - if (isTypeAny(tagType) || (!callSignatures.length && !(tagType.flags & TypeFlags.Union) && isTypeAssignableTo(tagType, globalFunctionType))) { + if (isUntypedFunctionCall(tagType, apparentType, callSignatures.length, constructSignatures.length)) { return resolveUntypedCall(node); } @@ -12148,7 +12166,8 @@ namespace ts { } const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call); - if (funcType === anyType || (!callSignatures.length && !(funcType.flags & TypeFlags.Union) && isTypeAssignableTo(funcType, globalFunctionType))) { + const constructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct); + if (isUntypedFunctionCall(funcType, apparentType, callSignatures.length, constructSignatures.length)) { return resolveUntypedCall(node); } From 5ce285c367c8baf70576ce6b1562015328aa118a Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Tue, 23 Aug 2016 11:30:57 -0700 Subject: [PATCH 219/233] Accepted baselines. --- .../constructableDecoratorOnClass01.errors.txt | 16 ++++++++++++++++ ...ggedTemplateWithConstructableTag01.errors.txt | 9 +++++++++ ...ggedTemplateWithConstructableTag02.errors.txt | 12 ++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 tests/baselines/reference/constructableDecoratorOnClass01.errors.txt create mode 100644 tests/baselines/reference/taggedTemplateWithConstructableTag01.errors.txt create mode 100644 tests/baselines/reference/taggedTemplateWithConstructableTag02.errors.txt diff --git a/tests/baselines/reference/constructableDecoratorOnClass01.errors.txt b/tests/baselines/reference/constructableDecoratorOnClass01.errors.txt new file mode 100644 index 00000000000..f390aaf8550 --- /dev/null +++ b/tests/baselines/reference/constructableDecoratorOnClass01.errors.txt @@ -0,0 +1,16 @@ +tests/cases/conformance/decorators/class/constructableDecoratorOnClass01.ts(4,1): error TS1238: Unable to resolve signature of class decorator when called as an expression. + Cannot invoke an expression whose type lacks a call signature. + + +==== tests/cases/conformance/decorators/class/constructableDecoratorOnClass01.ts (1 errors) ==== + + class CtorDtor {} + + @CtorDtor + ~~~~~~~~~ +!!! error TS1238: Unable to resolve signature of class decorator when called as an expression. +!!! error TS1238: Cannot invoke an expression whose type lacks a call signature. + class C { + + } + \ No newline at end of file diff --git a/tests/baselines/reference/taggedTemplateWithConstructableTag01.errors.txt b/tests/baselines/reference/taggedTemplateWithConstructableTag01.errors.txt new file mode 100644 index 00000000000..b114acc5277 --- /dev/null +++ b/tests/baselines/reference/taggedTemplateWithConstructableTag01.errors.txt @@ -0,0 +1,9 @@ +tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag01.ts(3,1): error TS2349: Cannot invoke an expression whose type lacks a call signature. + + +==== tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag01.ts (1 errors) ==== + class CtorTag { } + + CtorTag `Hello world!`; + ~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2349: Cannot invoke an expression whose type lacks a call signature. \ No newline at end of file diff --git a/tests/baselines/reference/taggedTemplateWithConstructableTag02.errors.txt b/tests/baselines/reference/taggedTemplateWithConstructableTag02.errors.txt new file mode 100644 index 00000000000..9bc4414b24f --- /dev/null +++ b/tests/baselines/reference/taggedTemplateWithConstructableTag02.errors.txt @@ -0,0 +1,12 @@ +tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag02.ts(6,1): error TS2349: Cannot invoke an expression whose type lacks a call signature. + + +==== tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag02.ts (1 errors) ==== + interface I { + new (...args: any[]): string; + new (): number; + } + var tag: I; + tag `Hello world!`; + ~~~~~~~~~~~~~~~~~~ +!!! error TS2349: Cannot invoke an expression whose type lacks a call signature. \ No newline at end of file From 73c3961355322515567875286141c4da723fb8ae Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Wed, 24 Aug 2016 18:50:04 -0700 Subject: [PATCH 220/233] Adding display parts to definition items to support FindAllReferences --- src/services/services.ts | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index babb5cf4574..7718be5a3d4 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -300,8 +300,8 @@ namespace ts { } } // For syntactic classifications, all trivia are classcified together, including jsdoc comments. - // For that to work, the jsdoc comments should still be the leading trivia of the first child. - // Restoring the scanner position ensures that. + // For that to work, the jsdoc comments should still be the leading trivia of the first child. + // Restoring the scanner position ensures that. pos = this.pos; forEachChild(this, processNode, processNodes); if (pos < this.end) { @@ -1374,8 +1374,13 @@ namespace ts { containerName: string; } + export interface ReferencedSymbolDefinitionInfo extends DefinitionInfo { + // For more complex definitions where kind and name are insufficient to properly colorize the text + displayParts?: SymbolDisplayPart[]; + } + export interface ReferencedSymbol { - definition: DefinitionInfo; + definition: ReferencedSymbolDefinitionInfo; references: ReferenceEntry[]; } @@ -6108,7 +6113,7 @@ namespace ts { return result; - function getDefinition(symbol: Symbol): DefinitionInfo { + function getDefinition(symbol: Symbol): ReferencedSymbolDefinitionInfo { const info = getSymbolDisplayPartsDocumentationAndSymbolKind(symbol, node.getSourceFile(), getContainerNode(node), node); const name = map(info.displayParts, p => p.text).join(""); const declarations = symbol.declarations; @@ -6122,7 +6127,8 @@ namespace ts { name, kind: info.symbolKind, fileName: declarations[0].getSourceFile().fileName, - textSpan: createTextSpan(declarations[0].getStart(), 0) + textSpan: createTextSpan(declarations[0].getStart(), 0), + displayParts: info.displayParts }; } @@ -6317,7 +6323,7 @@ namespace ts { } }); - const definition: DefinitionInfo = { + const definition: ReferencedSymbolDefinitionInfo = { containerKind: "", containerName: "", fileName: targetLabel.getSourceFile().fileName, @@ -6634,6 +6640,11 @@ namespace ts { getReferencesForStringLiteralInFile(sourceFile, type, possiblePositions, references); } + const symbol = typeChecker.getSymbolAtLocation(node); + + const displayParts = symbol ? getSymbolDisplayPartsDocumentationAndSymbolKind( + symbol, node.getSourceFile(), getContainerNode(node), node).displayParts : undefined; + return [{ definition: { containerKind: "", @@ -6641,7 +6652,8 @@ namespace ts { fileName: node.getSourceFile().fileName, kind: ScriptElementKind.variableElement, name: type.text, - textSpan: createTextSpanFromBounds(node.getStart(), node.getEnd()) + textSpan: createTextSpanFromBounds(node.getStart(), node.getEnd()), + displayParts }, references: references }]; From bab4a529834d94ddf8a7b84852d77140593a02fe Mon Sep 17 00:00:00 2001 From: Zhengbo Li Date: Thu, 25 Aug 2016 06:21:02 -0700 Subject: [PATCH 221/233] strip comments when re-emitting tsconfig.json (#10529) --- src/harness/unittests/tsconfigParsing.ts | 1 + src/services/utilities.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/harness/unittests/tsconfigParsing.ts b/src/harness/unittests/tsconfigParsing.ts index 2c9bcdb8423..eccf3537643 100644 --- a/src/harness/unittests/tsconfigParsing.ts +++ b/src/harness/unittests/tsconfigParsing.ts @@ -186,6 +186,7 @@ namespace ts { const content = `{ "compilerOptions": { "allowJs": true + // Some comments "outDir": "bin" } "files": ["file1.ts"] diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 858f889bc6c..9b3469643ba 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -930,7 +930,8 @@ namespace ts { const options: TranspileOptions = { fileName: "config.js", compilerOptions: { - target: ScriptTarget.ES6 + target: ScriptTarget.ES6, + removeComments: true }, reportDiagnostics: true }; From c0309fa78e5dbf7dd699cf21ea59ed9cb24268ad Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 25 Aug 2016 09:21:31 -0700 Subject: [PATCH 222/233] Fix crash when checking module exports for export= Also make maxNodeModuleJsDepth default to 0 so that incorrect tsconfigs now let the compiler spend less time compiling JS that is found in node_modules (especially since most people will already have the d.ts and want ignore the JS anyway). jsconfig still defaults to 2. --- src/compiler/checker.ts | 6 ++-- src/compiler/commandLineParser.ts | 2 +- src/compiler/program.ts | 2 +- .../convertCompilerOptionsFromJson.ts | 8 +++-- ...ForConflictingExportEqualsValue.errors.txt | 9 +++++ .../errorForConflictingExportEqualsValue.js | 8 +++++ ...NodeModuleJsDepthDefaultsToZero.errors.txt | 26 ++++++++++++++ ...NodeModuleJsDepthDefaultsToZero.trace.json | 28 +++++++++++++++ .../errorForConflictingExportEqualsValue.ts | 2 ++ .../maxNodeModuleJsDepthDefaultsToZero.ts | 36 +++++++++++++++++++ .../importHigher/tsconfig.json | 3 +- 11 files changed, 123 insertions(+), 7 deletions(-) create mode 100644 tests/baselines/reference/errorForConflictingExportEqualsValue.errors.txt create mode 100644 tests/baselines/reference/errorForConflictingExportEqualsValue.js create mode 100644 tests/baselines/reference/maxNodeModuleJsDepthDefaultsToZero.errors.txt create mode 100644 tests/baselines/reference/maxNodeModuleJsDepthDefaultsToZero.trace.json create mode 100644 tests/cases/compiler/errorForConflictingExportEqualsValue.ts create mode 100644 tests/cases/compiler/maxNodeModuleJsDepthDefaultsToZero.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 370802478bc..9e8bcf40be7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1023,8 +1023,8 @@ namespace ts { } } - function getDeclarationOfAliasSymbol(symbol: Symbol): Declaration { - return findMap(symbol.declarations, d => isAliasSymbolDeclaration(d) ? d : undefined); + function getDeclarationOfAliasSymbol(symbol: Symbol): Declaration | undefined { + return forEach(symbol.declarations, d => isAliasSymbolDeclaration(d) ? d : undefined); } function getTargetOfImportEqualsDeclaration(node: ImportEqualsDeclaration): Symbol { @@ -1191,6 +1191,7 @@ namespace ts { if (!links.target) { links.target = resolvingSymbol; const node = getDeclarationOfAliasSymbol(symbol); + Debug.assert(!!node); const target = getTargetOfAliasDeclaration(node); if (links.target === resolvingSymbol) { links.target = target || unknownSymbol; @@ -1226,6 +1227,7 @@ namespace ts { if (!links.referenced) { links.referenced = true; const node = getDeclarationOfAliasSymbol(symbol); + Debug.assert(!!node); if (node.kind === SyntaxKind.ExportAssignment) { // export default checkExpressionCached((node).expression); diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 6406455d713..e1175c42438 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -885,7 +885,7 @@ namespace ts { function convertCompilerOptionsFromJsonWorker(jsonOptions: any, basePath: string, errors: Diagnostic[], configFileName?: string): CompilerOptions { - const options: CompilerOptions = getBaseFileName(configFileName) === "jsconfig.json" ? { allowJs: true } : {}; + const options: CompilerOptions = getBaseFileName(configFileName) === "jsconfig.json" ? { allowJs: true, maxNodeModuleJsDepth: 2 } : {}; convertOptionsFromJson(optionDeclarations, jsonOptions, basePath, options, Diagnostics.Unknown_compiler_option_0, errors); return options; } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index f099d858119..49f61981d72 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1101,7 +1101,7 @@ namespace ts { // - This calls resolveModuleNames, and then calls findSourceFile for each resolved module. // As all these operations happen - and are nested - within the createProgram call, they close over the below variables. // The current resolution depth is tracked by incrementing/decrementing as the depth first search progresses. - const maxNodeModulesJsDepth = typeof options.maxNodeModuleJsDepth === "number" ? options.maxNodeModuleJsDepth : 2; + const maxNodeModulesJsDepth = typeof options.maxNodeModuleJsDepth === "number" ? options.maxNodeModuleJsDepth : 0; let currentNodeModulesDepth = 0; // If a module has some of its imports skipped due to being at the depth limit under node_modules, then track diff --git a/src/harness/unittests/convertCompilerOptionsFromJson.ts b/src/harness/unittests/convertCompilerOptionsFromJson.ts index d308bb2a6e6..9de18850477 100644 --- a/src/harness/unittests/convertCompilerOptionsFromJson.ts +++ b/src/harness/unittests/convertCompilerOptionsFromJson.ts @@ -403,6 +403,7 @@ namespace ts { { compilerOptions: { allowJs: true, + maxNodeModuleJsDepth: 2, module: ModuleKind.CommonJS, target: ScriptTarget.ES5, noImplicitAny: false, @@ -429,6 +430,7 @@ namespace ts { { compilerOptions: { allowJs: false, + maxNodeModuleJsDepth: 2, module: ModuleKind.CommonJS, target: ScriptTarget.ES5, noImplicitAny: false, @@ -450,7 +452,8 @@ namespace ts { { compilerOptions: { - allowJs: true + allowJs: true, + maxNodeModuleJsDepth: 2 }, errors: [{ file: undefined, @@ -469,7 +472,8 @@ namespace ts { { compilerOptions: { - allowJs: true + allowJs: true, + maxNodeModuleJsDepth: 2 }, errors: [] } diff --git a/tests/baselines/reference/errorForConflictingExportEqualsValue.errors.txt b/tests/baselines/reference/errorForConflictingExportEqualsValue.errors.txt new file mode 100644 index 00000000000..9a5858f46ab --- /dev/null +++ b/tests/baselines/reference/errorForConflictingExportEqualsValue.errors.txt @@ -0,0 +1,9 @@ +tests/cases/compiler/errorForConflictingExportEqualsValue.ts(2,1): error TS2309: An export assignment cannot be used in a module with other exported elements. + + +==== tests/cases/compiler/errorForConflictingExportEqualsValue.ts (1 errors) ==== + export var x; + export = {}; + ~~~~~~~~~~~~ +!!! error TS2309: An export assignment cannot be used in a module with other exported elements. + \ No newline at end of file diff --git a/tests/baselines/reference/errorForConflictingExportEqualsValue.js b/tests/baselines/reference/errorForConflictingExportEqualsValue.js new file mode 100644 index 00000000000..88762e7e846 --- /dev/null +++ b/tests/baselines/reference/errorForConflictingExportEqualsValue.js @@ -0,0 +1,8 @@ +//// [errorForConflictingExportEqualsValue.ts] +export var x; +export = {}; + + +//// [errorForConflictingExportEqualsValue.js] +"use strict"; +module.exports = {}; diff --git a/tests/baselines/reference/maxNodeModuleJsDepthDefaultsToZero.errors.txt b/tests/baselines/reference/maxNodeModuleJsDepthDefaultsToZero.errors.txt new file mode 100644 index 00000000000..4859ca45b7c --- /dev/null +++ b/tests/baselines/reference/maxNodeModuleJsDepthDefaultsToZero.errors.txt @@ -0,0 +1,26 @@ +c:/root/index.ts(4,5): error TS2339: Property 'y' does not exist on type 'typeof "shortid"'. + + +==== c:/root/index.ts (1 errors) ==== + /// + import * as foo from "shortid"; + foo.x // found in index.d.ts + foo.y // ignored from shortid/index.ts + ~ +!!! error TS2339: Property 'y' does not exist on type 'typeof "shortid"'. + + +==== c:/root/node_modules/shortid/node_modules/z/index.js (0 errors) ==== + // z will not be found because maxNodeModulesJsDepth: 0 + module.exports = { z: 'no' }; + +==== c:/root/node_modules/shortid/index.js (0 errors) ==== + var z = require('z'); + var y = { y: 'foo' }; + module.exports = y; + +==== c:/root/typings/index.d.ts (0 errors) ==== + declare module "shortid" { + export var x: number; + } + \ No newline at end of file diff --git a/tests/baselines/reference/maxNodeModuleJsDepthDefaultsToZero.trace.json b/tests/baselines/reference/maxNodeModuleJsDepthDefaultsToZero.trace.json new file mode 100644 index 00000000000..d9fabbfa5df --- /dev/null +++ b/tests/baselines/reference/maxNodeModuleJsDepthDefaultsToZero.trace.json @@ -0,0 +1,28 @@ +[ + "======== Resolving module 'shortid' from 'c:/root/index.ts'. ========", + "Explicitly specified module resolution kind: 'NodeJs'.", + "Loading module 'shortid' from 'node_modules' folder.", + "File 'c:/root/node_modules/shortid.ts' does not exist.", + "File 'c:/root/node_modules/shortid.tsx' does not exist.", + "File 'c:/root/node_modules/shortid.d.ts' does not exist.", + "File 'c:/root/node_modules/shortid.js' does not exist.", + "File 'c:/root/node_modules/shortid.jsx' does not exist.", + "File 'c:/root/node_modules/shortid/package.json' does not exist.", + "File 'c:/root/node_modules/shortid/index.ts' does not exist.", + "File 'c:/root/node_modules/shortid/index.tsx' does not exist.", + "File 'c:/root/node_modules/shortid/index.d.ts' does not exist.", + "File 'c:/root/node_modules/shortid/index.js' exist - use it as a name resolution result.", + "File 'c:/root/node_modules/@types/shortid.ts' does not exist.", + "File 'c:/root/node_modules/@types/shortid.tsx' does not exist.", + "File 'c:/root/node_modules/@types/shortid.d.ts' does not exist.", + "File 'c:/root/node_modules/@types/shortid.js' does not exist.", + "File 'c:/root/node_modules/@types/shortid.jsx' does not exist.", + "File 'c:/root/node_modules/@types/shortid/package.json' does not exist.", + "File 'c:/root/node_modules/@types/shortid/index.ts' does not exist.", + "File 'c:/root/node_modules/@types/shortid/index.tsx' does not exist.", + "File 'c:/root/node_modules/@types/shortid/index.d.ts' does not exist.", + "File 'c:/root/node_modules/@types/shortid/index.js' does not exist.", + "File 'c:/root/node_modules/@types/shortid/index.jsx' does not exist.", + "Resolving real path for 'c:/root/node_modules/shortid/index.js', result 'c:/root/node_modules/shortid/index.js'", + "======== Module name 'shortid' was successfully resolved to 'c:/root/node_modules/shortid/index.js'. ========" +] \ No newline at end of file diff --git a/tests/cases/compiler/errorForConflictingExportEqualsValue.ts b/tests/cases/compiler/errorForConflictingExportEqualsValue.ts new file mode 100644 index 00000000000..59af1f46690 --- /dev/null +++ b/tests/cases/compiler/errorForConflictingExportEqualsValue.ts @@ -0,0 +1,2 @@ +export var x; +export = {}; diff --git a/tests/cases/compiler/maxNodeModuleJsDepthDefaultsToZero.ts b/tests/cases/compiler/maxNodeModuleJsDepthDefaultsToZero.ts new file mode 100644 index 00000000000..119f8277bf2 --- /dev/null +++ b/tests/cases/compiler/maxNodeModuleJsDepthDefaultsToZero.ts @@ -0,0 +1,36 @@ +// @module: commonjs +// @moduleResolution: node +// @allowJs: true +// @traceResolution: true +// @noEmit: true + +// @filename: c:/root/tsconfig.json +{ + "compileOnSave": true, + "compilerOptions": { + "module": "commonjs", + "moduleResolution": "node", + "outDir": "bin" + }, + "exclude": [ "node_modules" ] +} +// @filename: c:/root/node_modules/shortid/node_modules/z/index.js +// z will not be found because maxNodeModulesJsDepth: 0 +module.exports = { z: 'no' }; + +// @filename: c:/root/node_modules/shortid/index.js +var z = require('z'); +var y = { y: 'foo' }; +module.exports = y; + +// @filename: c:/root/typings/index.d.ts +declare module "shortid" { + export var x: number; +} + +// @filename: c:/root/index.ts +/// +import * as foo from "shortid"; +foo.x // found in index.d.ts +foo.y // ignored from shortid/index.ts + diff --git a/tests/cases/projects/NodeModulesSearch/importHigher/tsconfig.json b/tests/cases/projects/NodeModulesSearch/importHigher/tsconfig.json index c7b95984b42..301db9c870a 100644 --- a/tests/cases/projects/NodeModulesSearch/importHigher/tsconfig.json +++ b/tests/cases/projects/NodeModulesSearch/importHigher/tsconfig.json @@ -2,6 +2,7 @@ "compilerOptions": { "allowJs": true, "declaration": false, - "moduleResolution": "node" + "moduleResolution": "node", + "maxNodeModuleJsDepth": 2 } } From 751b0a65dec5ddecf8a4d53942776e32b88115bc Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 25 Aug 2016 10:25:17 -0700 Subject: [PATCH 223/233] Clean up maxNodeModuleJsDepth test --- ...NodeModuleJsDepthDefaultsToZero.errors.txt | 14 +++--- ...NodeModuleJsDepthDefaultsToZero.trace.json | 48 +++++++++---------- .../maxNodeModuleJsDepthDefaultsToZero.ts | 14 +++--- 3 files changed, 38 insertions(+), 38 deletions(-) diff --git a/tests/baselines/reference/maxNodeModuleJsDepthDefaultsToZero.errors.txt b/tests/baselines/reference/maxNodeModuleJsDepthDefaultsToZero.errors.txt index 4859ca45b7c..4fa15a71fa8 100644 --- a/tests/baselines/reference/maxNodeModuleJsDepthDefaultsToZero.errors.txt +++ b/tests/baselines/reference/maxNodeModuleJsDepthDefaultsToZero.errors.txt @@ -1,25 +1,25 @@ -c:/root/index.ts(4,5): error TS2339: Property 'y' does not exist on type 'typeof "shortid"'. +tests/cases/compiler/index.ts(4,5): error TS2339: Property 'y' does not exist on type 'typeof "shortid"'. -==== c:/root/index.ts (1 errors) ==== - /// +==== tests/cases/compiler/index.ts (1 errors) ==== + /// import * as foo from "shortid"; foo.x // found in index.d.ts - foo.y // ignored from shortid/index.ts + foo.y // ignored from shortid/index.js ~ !!! error TS2339: Property 'y' does not exist on type 'typeof "shortid"'. -==== c:/root/node_modules/shortid/node_modules/z/index.js (0 errors) ==== +==== tests/cases/compiler/node_modules/shortid/node_modules/z/index.js (0 errors) ==== // z will not be found because maxNodeModulesJsDepth: 0 module.exports = { z: 'no' }; -==== c:/root/node_modules/shortid/index.js (0 errors) ==== +==== tests/cases/compiler/node_modules/shortid/index.js (0 errors) ==== var z = require('z'); var y = { y: 'foo' }; module.exports = y; -==== c:/root/typings/index.d.ts (0 errors) ==== +==== tests/cases/compiler/typings/index.d.ts (0 errors) ==== declare module "shortid" { export var x: number; } diff --git a/tests/baselines/reference/maxNodeModuleJsDepthDefaultsToZero.trace.json b/tests/baselines/reference/maxNodeModuleJsDepthDefaultsToZero.trace.json index d9fabbfa5df..f3db007a2ba 100644 --- a/tests/baselines/reference/maxNodeModuleJsDepthDefaultsToZero.trace.json +++ b/tests/baselines/reference/maxNodeModuleJsDepthDefaultsToZero.trace.json @@ -1,28 +1,28 @@ [ - "======== Resolving module 'shortid' from 'c:/root/index.ts'. ========", + "======== Resolving module 'shortid' from '/media/nathansa/src2/ts/tests/cases/compiler/index.ts'. ========", "Explicitly specified module resolution kind: 'NodeJs'.", "Loading module 'shortid' from 'node_modules' folder.", - "File 'c:/root/node_modules/shortid.ts' does not exist.", - "File 'c:/root/node_modules/shortid.tsx' does not exist.", - "File 'c:/root/node_modules/shortid.d.ts' does not exist.", - "File 'c:/root/node_modules/shortid.js' does not exist.", - "File 'c:/root/node_modules/shortid.jsx' does not exist.", - "File 'c:/root/node_modules/shortid/package.json' does not exist.", - "File 'c:/root/node_modules/shortid/index.ts' does not exist.", - "File 'c:/root/node_modules/shortid/index.tsx' does not exist.", - "File 'c:/root/node_modules/shortid/index.d.ts' does not exist.", - "File 'c:/root/node_modules/shortid/index.js' exist - use it as a name resolution result.", - "File 'c:/root/node_modules/@types/shortid.ts' does not exist.", - "File 'c:/root/node_modules/@types/shortid.tsx' does not exist.", - "File 'c:/root/node_modules/@types/shortid.d.ts' does not exist.", - "File 'c:/root/node_modules/@types/shortid.js' does not exist.", - "File 'c:/root/node_modules/@types/shortid.jsx' does not exist.", - "File 'c:/root/node_modules/@types/shortid/package.json' does not exist.", - "File 'c:/root/node_modules/@types/shortid/index.ts' does not exist.", - "File 'c:/root/node_modules/@types/shortid/index.tsx' does not exist.", - "File 'c:/root/node_modules/@types/shortid/index.d.ts' does not exist.", - "File 'c:/root/node_modules/@types/shortid/index.js' does not exist.", - "File 'c:/root/node_modules/@types/shortid/index.jsx' does not exist.", - "Resolving real path for 'c:/root/node_modules/shortid/index.js', result 'c:/root/node_modules/shortid/index.js'", - "======== Module name 'shortid' was successfully resolved to 'c:/root/node_modules/shortid/index.js'. ========" + "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/shortid.ts' does not exist.", + "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/shortid.tsx' does not exist.", + "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/shortid.d.ts' does not exist.", + "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/shortid.js' does not exist.", + "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/shortid.jsx' does not exist.", + "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/shortid/package.json' does not exist.", + "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/shortid/index.ts' does not exist.", + "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/shortid/index.tsx' does not exist.", + "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/shortid/index.d.ts' does not exist.", + "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/shortid/index.js' exist - use it as a name resolution result.", + "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/@types/shortid.ts' does not exist.", + "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/@types/shortid.tsx' does not exist.", + "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/@types/shortid.d.ts' does not exist.", + "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/@types/shortid.js' does not exist.", + "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/@types/shortid.jsx' does not exist.", + "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/@types/shortid/package.json' does not exist.", + "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/@types/shortid/index.ts' does not exist.", + "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/@types/shortid/index.tsx' does not exist.", + "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/@types/shortid/index.d.ts' does not exist.", + "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/@types/shortid/index.js' does not exist.", + "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/@types/shortid/index.jsx' does not exist.", + "Resolving real path for '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/shortid/index.js', result '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/shortid/index.js'", + "======== Module name 'shortid' was successfully resolved to '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/shortid/index.js'. ========" ] \ No newline at end of file diff --git a/tests/cases/compiler/maxNodeModuleJsDepthDefaultsToZero.ts b/tests/cases/compiler/maxNodeModuleJsDepthDefaultsToZero.ts index 119f8277bf2..0c8ac7c5fa6 100644 --- a/tests/cases/compiler/maxNodeModuleJsDepthDefaultsToZero.ts +++ b/tests/cases/compiler/maxNodeModuleJsDepthDefaultsToZero.ts @@ -4,7 +4,7 @@ // @traceResolution: true // @noEmit: true -// @filename: c:/root/tsconfig.json +// @filename: tsconfig.json { "compileOnSave": true, "compilerOptions": { @@ -14,23 +14,23 @@ }, "exclude": [ "node_modules" ] } -// @filename: c:/root/node_modules/shortid/node_modules/z/index.js +// @filename: node_modules/shortid/node_modules/z/index.js // z will not be found because maxNodeModulesJsDepth: 0 module.exports = { z: 'no' }; -// @filename: c:/root/node_modules/shortid/index.js +// @filename: node_modules/shortid/index.js var z = require('z'); var y = { y: 'foo' }; module.exports = y; -// @filename: c:/root/typings/index.d.ts +// @filename: typings/index.d.ts declare module "shortid" { export var x: number; } -// @filename: c:/root/index.ts -/// +// @filename: index.ts +/// import * as foo from "shortid"; foo.x // found in index.d.ts -foo.y // ignored from shortid/index.ts +foo.y // ignored from shortid/index.js From 6ee749f8b33bfdcaee2b1e41eeed618f49a1e25a Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 25 Aug 2016 11:02:26 -0700 Subject: [PATCH 224/233] Get rid of absolute paths --- ...NodeModuleJsDepthDefaultsToZero.errors.txt | 12 ++--- ...NodeModuleJsDepthDefaultsToZero.trace.json | 48 +++++++++---------- .../maxNodeModuleJsDepthDefaultsToZero.ts | 12 ++--- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/tests/baselines/reference/maxNodeModuleJsDepthDefaultsToZero.errors.txt b/tests/baselines/reference/maxNodeModuleJsDepthDefaultsToZero.errors.txt index 4fa15a71fa8..2218e7910ae 100644 --- a/tests/baselines/reference/maxNodeModuleJsDepthDefaultsToZero.errors.txt +++ b/tests/baselines/reference/maxNodeModuleJsDepthDefaultsToZero.errors.txt @@ -1,8 +1,8 @@ -tests/cases/compiler/index.ts(4,5): error TS2339: Property 'y' does not exist on type 'typeof "shortid"'. +/index.ts(4,5): error TS2339: Property 'y' does not exist on type 'typeof "shortid"'. -==== tests/cases/compiler/index.ts (1 errors) ==== - /// +==== /index.ts (1 errors) ==== + /// import * as foo from "shortid"; foo.x // found in index.d.ts foo.y // ignored from shortid/index.js @@ -10,16 +10,16 @@ tests/cases/compiler/index.ts(4,5): error TS2339: Property 'y' does not exist on !!! error TS2339: Property 'y' does not exist on type 'typeof "shortid"'. -==== tests/cases/compiler/node_modules/shortid/node_modules/z/index.js (0 errors) ==== +==== /node_modules/shortid/node_modules/z/index.js (0 errors) ==== // z will not be found because maxNodeModulesJsDepth: 0 module.exports = { z: 'no' }; -==== tests/cases/compiler/node_modules/shortid/index.js (0 errors) ==== +==== /node_modules/shortid/index.js (0 errors) ==== var z = require('z'); var y = { y: 'foo' }; module.exports = y; -==== tests/cases/compiler/typings/index.d.ts (0 errors) ==== +==== /typings/index.d.ts (0 errors) ==== declare module "shortid" { export var x: number; } diff --git a/tests/baselines/reference/maxNodeModuleJsDepthDefaultsToZero.trace.json b/tests/baselines/reference/maxNodeModuleJsDepthDefaultsToZero.trace.json index f3db007a2ba..e53b2a0934f 100644 --- a/tests/baselines/reference/maxNodeModuleJsDepthDefaultsToZero.trace.json +++ b/tests/baselines/reference/maxNodeModuleJsDepthDefaultsToZero.trace.json @@ -1,28 +1,28 @@ [ - "======== Resolving module 'shortid' from '/media/nathansa/src2/ts/tests/cases/compiler/index.ts'. ========", + "======== Resolving module 'shortid' from '/index.ts'. ========", "Explicitly specified module resolution kind: 'NodeJs'.", "Loading module 'shortid' from 'node_modules' folder.", - "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/shortid.ts' does not exist.", - "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/shortid.tsx' does not exist.", - "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/shortid.d.ts' does not exist.", - "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/shortid.js' does not exist.", - "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/shortid.jsx' does not exist.", - "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/shortid/package.json' does not exist.", - "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/shortid/index.ts' does not exist.", - "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/shortid/index.tsx' does not exist.", - "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/shortid/index.d.ts' does not exist.", - "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/shortid/index.js' exist - use it as a name resolution result.", - "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/@types/shortid.ts' does not exist.", - "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/@types/shortid.tsx' does not exist.", - "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/@types/shortid.d.ts' does not exist.", - "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/@types/shortid.js' does not exist.", - "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/@types/shortid.jsx' does not exist.", - "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/@types/shortid/package.json' does not exist.", - "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/@types/shortid/index.ts' does not exist.", - "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/@types/shortid/index.tsx' does not exist.", - "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/@types/shortid/index.d.ts' does not exist.", - "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/@types/shortid/index.js' does not exist.", - "File '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/@types/shortid/index.jsx' does not exist.", - "Resolving real path for '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/shortid/index.js', result '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/shortid/index.js'", - "======== Module name 'shortid' was successfully resolved to '/media/nathansa/src2/ts/tests/cases/compiler/node_modules/shortid/index.js'. ========" + "File '/node_modules/shortid.ts' does not exist.", + "File '/node_modules/shortid.tsx' does not exist.", + "File '/node_modules/shortid.d.ts' does not exist.", + "File '/node_modules/shortid.js' does not exist.", + "File '/node_modules/shortid.jsx' does not exist.", + "File '/node_modules/shortid/package.json' does not exist.", + "File '/node_modules/shortid/index.ts' does not exist.", + "File '/node_modules/shortid/index.tsx' does not exist.", + "File '/node_modules/shortid/index.d.ts' does not exist.", + "File '/node_modules/shortid/index.js' exist - use it as a name resolution result.", + "File '/node_modules/@types/shortid.ts' does not exist.", + "File '/node_modules/@types/shortid.tsx' does not exist.", + "File '/node_modules/@types/shortid.d.ts' does not exist.", + "File '/node_modules/@types/shortid.js' does not exist.", + "File '/node_modules/@types/shortid.jsx' does not exist.", + "File '/node_modules/@types/shortid/package.json' does not exist.", + "File '/node_modules/@types/shortid/index.ts' does not exist.", + "File '/node_modules/@types/shortid/index.tsx' does not exist.", + "File '/node_modules/@types/shortid/index.d.ts' does not exist.", + "File '/node_modules/@types/shortid/index.js' does not exist.", + "File '/node_modules/@types/shortid/index.jsx' does not exist.", + "Resolving real path for '/node_modules/shortid/index.js', result '/node_modules/shortid/index.js'", + "======== Module name 'shortid' was successfully resolved to '/node_modules/shortid/index.js'. ========" ] \ No newline at end of file diff --git a/tests/cases/compiler/maxNodeModuleJsDepthDefaultsToZero.ts b/tests/cases/compiler/maxNodeModuleJsDepthDefaultsToZero.ts index 0c8ac7c5fa6..9d154ec1e59 100644 --- a/tests/cases/compiler/maxNodeModuleJsDepthDefaultsToZero.ts +++ b/tests/cases/compiler/maxNodeModuleJsDepthDefaultsToZero.ts @@ -4,7 +4,7 @@ // @traceResolution: true // @noEmit: true -// @filename: tsconfig.json +// @filename: /tsconfig.json { "compileOnSave": true, "compilerOptions": { @@ -14,22 +14,22 @@ }, "exclude": [ "node_modules" ] } -// @filename: node_modules/shortid/node_modules/z/index.js +// @filename: /node_modules/shortid/node_modules/z/index.js // z will not be found because maxNodeModulesJsDepth: 0 module.exports = { z: 'no' }; -// @filename: node_modules/shortid/index.js +// @filename: /node_modules/shortid/index.js var z = require('z'); var y = { y: 'foo' }; module.exports = y; -// @filename: typings/index.d.ts +// @filename: /typings/index.d.ts declare module "shortid" { export var x: number; } -// @filename: index.ts -/// +// @filename: /index.ts +/// import * as foo from "shortid"; foo.x // found in index.d.ts foo.y // ignored from shortid/index.js From e0a36840c058210d7d29cea9e9bd039e4cf4210a Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Thu, 25 Aug 2016 11:53:44 -0700 Subject: [PATCH 225/233] PR feedback --- src/services/services.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 7718be5a3d4..6da12696937 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1375,8 +1375,7 @@ namespace ts { } export interface ReferencedSymbolDefinitionInfo extends DefinitionInfo { - // For more complex definitions where kind and name are insufficient to properly colorize the text - displayParts?: SymbolDisplayPart[]; + displayParts: SymbolDisplayPart[]; } export interface ReferencedSymbol { @@ -6640,11 +6639,6 @@ namespace ts { getReferencesForStringLiteralInFile(sourceFile, type, possiblePositions, references); } - const symbol = typeChecker.getSymbolAtLocation(node); - - const displayParts = symbol ? getSymbolDisplayPartsDocumentationAndSymbolKind( - symbol, node.getSourceFile(), getContainerNode(node), node).displayParts : undefined; - return [{ definition: { containerKind: "", @@ -6653,7 +6647,7 @@ namespace ts { kind: ScriptElementKind.variableElement, name: type.text, textSpan: createTextSpanFromBounds(node.getStart(), node.getEnd()), - displayParts + displayParts: [displayPart(getTextOfNode(node), SymbolDisplayPartKind.stringLiteral)] }, references: references }]; From 15ff0f79568f6e2dd7f1c685ee11b537ca27b951 Mon Sep 17 00:00:00 2001 From: Yui Date: Mon, 8 Aug 2016 14:45:01 -0700 Subject: [PATCH 226/233] [Release 2.0] fix10179: 2.0 emits optional class properties defined in constructors, which breaks compatibility (#10212) * Do not emit "?" for in property declaration in .d.ts when the property is declared as optional parameter property declaration * Update baselines --- src/compiler/declarationEmitter.ts | 6 ++++-- tests/baselines/reference/declFileConstructors.js | 4 ++-- tests/baselines/reference/optionalMethods.js | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index 3642ee7f10a..ab1ca518256 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -1132,8 +1132,10 @@ namespace ts { // it if it's not a well known symbol. In that case, the text of the name will be exactly // what we want, namely the name expression enclosed in brackets. writeTextOfNode(currentText, node.name); - // If optional property emit ? - if ((node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature || node.kind === SyntaxKind.Parameter) && hasQuestionToken(node)) { + // If optional property emit ? but in the case of parameterProperty declaration with "?" indicating optional parameter for the constructor + // we don't want to emit property declaration with "?" + if ((node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature || + (node.kind === SyntaxKind.Parameter && !isParameterPropertyDeclaration(node))) && hasQuestionToken(node)) { write("?"); } if ((node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature) && node.parent.kind === SyntaxKind.TypeLiteral) { diff --git a/tests/baselines/reference/declFileConstructors.js b/tests/baselines/reference/declFileConstructors.js index 8e92b1a36ae..44e816712fb 100644 --- a/tests/baselines/reference/declFileConstructors.js +++ b/tests/baselines/reference/declFileConstructors.js @@ -247,7 +247,7 @@ export declare class ConstructorWithPrivateParameterProperty { constructor(x: string); } export declare class ConstructorWithOptionalParameterProperty { - x?: string; + x: string; constructor(x?: string); } export declare class ConstructorWithParameterInitializer { @@ -281,7 +281,7 @@ declare class GlobalConstructorWithPrivateParameterProperty { constructor(x: string); } declare class GlobalConstructorWithOptionalParameterProperty { - x?: string; + x: string; constructor(x?: string); } declare class GlobalConstructorWithParameterInitializer { diff --git a/tests/baselines/reference/optionalMethods.js b/tests/baselines/reference/optionalMethods.js index 42decf512e4..98629f5718b 100644 --- a/tests/baselines/reference/optionalMethods.js +++ b/tests/baselines/reference/optionalMethods.js @@ -126,7 +126,7 @@ interface Foo { } declare function test1(x: Foo): void; declare class Bar { - d?: number; + d: number; e: number; a: number; b?: number; From 8d5aaf8eda96e9305fa172f600dac9fe39e54dc2 Mon Sep 17 00:00:00 2001 From: Zhengbo Li Date: Thu, 25 Aug 2016 14:26:58 -0700 Subject: [PATCH 227/233] avoid creating syntax nodes for jsdoc comment tags (#10526) --- src/services/services.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index babb5cf4574..a55f2cf61b0 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -280,11 +280,14 @@ namespace ts { let pos = this.pos; const useJSDocScanner = this.kind >= SyntaxKind.FirstJSDocTagNode && this.kind <= SyntaxKind.LastJSDocTagNode; const processNode = (node: Node) => { - if (pos < node.pos) { + const isJSDocTagNode = isJSDocTag(node); + if (!isJSDocTagNode && pos < node.pos) { pos = this.addSyntheticNodes(children, pos, node.pos, useJSDocScanner); } children.push(node); - pos = node.end; + if (!isJSDocTagNode) { + pos = node.end; + } }; const processNodes = (nodes: NodeArray) => { if (pos < nodes.pos) { @@ -299,10 +302,6 @@ namespace ts { processNode(jsDocComment); } } - // For syntactic classifications, all trivia are classcified together, including jsdoc comments. - // For that to work, the jsdoc comments should still be the leading trivia of the first child. - // Restoring the scanner position ensures that. - pos = this.pos; forEachChild(this, processNode, processNodes); if (pos < this.end) { this.addSyntheticNodes(children, pos, this.end); From 0116abdcf2cc57174a839deb29e6c4419c576dfd Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Fri, 26 Aug 2016 08:49:43 -0700 Subject: [PATCH 228/233] First in UMD global wins Fixes #9771 --- src/compiler/checker.ts | 8 ++++- .../baselines/reference/umdGlobalConflict.js | 23 +++++++++++++++ .../reference/umdGlobalConflict.symbols | 29 +++++++++++++++++++ .../reference/umdGlobalConflict.types | 29 +++++++++++++++++++ tests/cases/compiler/umdGlobalConflict.ts | 15 ++++++++++ 5 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/umdGlobalConflict.js create mode 100644 tests/baselines/reference/umdGlobalConflict.symbols create mode 100644 tests/baselines/reference/umdGlobalConflict.types create mode 100644 tests/cases/compiler/umdGlobalConflict.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2341a0f7dfd..8c1e401912d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18763,7 +18763,13 @@ namespace ts { (augmentations || (augmentations = [])).push(file.moduleAugmentations); } if (file.symbol && file.symbol.globalExports) { - mergeSymbolTable(globals, file.symbol.globalExports); + // Merge in UMD exports with first-in-wins semantics (see #9771) + const source = file.symbol.globalExports; + for (const id in source) { + if (!(id in globals)) { + globals[id] = source[id]; + } + } } }); diff --git a/tests/baselines/reference/umdGlobalConflict.js b/tests/baselines/reference/umdGlobalConflict.js new file mode 100644 index 00000000000..e8a820e2333 --- /dev/null +++ b/tests/baselines/reference/umdGlobalConflict.js @@ -0,0 +1,23 @@ +//// [tests/cases/compiler/umdGlobalConflict.ts] //// + +//// [index.d.ts] +export as namespace Alpha; +export var x: string; + +//// [index.d.ts] +export as namespace Alpha; +export var y: number; + +//// [consumer.ts] +import * as v1 from './v1'; +import * as v2 from './v2'; + +//// [global.ts] +// Should be OK, first in wins +const p: string = Alpha.x; + +//// [consumer.js] +"use strict"; +//// [global.js] +// Should be OK, first in wins +var p = Alpha.x; diff --git a/tests/baselines/reference/umdGlobalConflict.symbols b/tests/baselines/reference/umdGlobalConflict.symbols new file mode 100644 index 00000000000..b13fb0f25b1 --- /dev/null +++ b/tests/baselines/reference/umdGlobalConflict.symbols @@ -0,0 +1,29 @@ +=== tests/cases/compiler/v1/index.d.ts === +export as namespace Alpha; +>Alpha : Symbol(Alpha, Decl(index.d.ts, 0, 0)) + +export var x: string; +>x : Symbol(x, Decl(index.d.ts, 1, 10)) + +=== tests/cases/compiler/v2/index.d.ts === +export as namespace Alpha; +>Alpha : Symbol(Alpha, Decl(index.d.ts, 0, 0)) + +export var y: number; +>y : Symbol(y, Decl(index.d.ts, 1, 10)) + +=== tests/cases/compiler/consumer.ts === +import * as v1 from './v1'; +>v1 : Symbol(v1, Decl(consumer.ts, 0, 6)) + +import * as v2 from './v2'; +>v2 : Symbol(v2, Decl(consumer.ts, 1, 6)) + +=== tests/cases/compiler/global.ts === +// Should be OK, first in wins +const p: string = Alpha.x; +>p : Symbol(p, Decl(global.ts, 1, 5)) +>Alpha.x : Symbol(Alpha.x, Decl(index.d.ts, 1, 10)) +>Alpha : Symbol(Alpha, Decl(index.d.ts, 0, 0)) +>x : Symbol(Alpha.x, Decl(index.d.ts, 1, 10)) + diff --git a/tests/baselines/reference/umdGlobalConflict.types b/tests/baselines/reference/umdGlobalConflict.types new file mode 100644 index 00000000000..fa74e26874f --- /dev/null +++ b/tests/baselines/reference/umdGlobalConflict.types @@ -0,0 +1,29 @@ +=== tests/cases/compiler/v1/index.d.ts === +export as namespace Alpha; +>Alpha : typeof Alpha + +export var x: string; +>x : string + +=== tests/cases/compiler/v2/index.d.ts === +export as namespace Alpha; +>Alpha : typeof + +export var y: number; +>y : number + +=== tests/cases/compiler/consumer.ts === +import * as v1 from './v1'; +>v1 : typeof v1 + +import * as v2 from './v2'; +>v2 : typeof v2 + +=== tests/cases/compiler/global.ts === +// Should be OK, first in wins +const p: string = Alpha.x; +>p : string +>Alpha.x : string +>Alpha : typeof Alpha +>x : string + diff --git a/tests/cases/compiler/umdGlobalConflict.ts b/tests/cases/compiler/umdGlobalConflict.ts new file mode 100644 index 00000000000..58b4ea6be02 --- /dev/null +++ b/tests/cases/compiler/umdGlobalConflict.ts @@ -0,0 +1,15 @@ +//@filename: v1/index.d.ts +export as namespace Alpha; +export var x: string; + +//@filename: v2/index.d.ts +export as namespace Alpha; +export var y: number; + +//@filename: consumer.ts +import * as v1 from './v1'; +import * as v2 from './v2'; + +//@filename: global.ts +// Should be OK, first in wins +const p: string = Alpha.x; \ No newline at end of file From 0a985ee28771c1aee386c7a2cb74c36ce8721e81 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Fri, 26 Aug 2016 13:25:19 -0700 Subject: [PATCH 229/233] Parse untyped object type members separated by ',' Previously if the first member was untyped and the separator was ',', then parsing would fail. --- src/compiler/parser.ts | 1 + .../reference/parseObjectLiteralsWithoutTypes.js | 10 ++++++++++ .../parseObjectLiteralsWithoutTypes.symbols | 16 ++++++++++++++++ .../parseObjectLiteralsWithoutTypes.types | 16 ++++++++++++++++ .../compiler/parseObjectLiteralsWithoutTypes.ts | 3 +++ 5 files changed, 46 insertions(+) create mode 100644 tests/baselines/reference/parseObjectLiteralsWithoutTypes.js create mode 100644 tests/baselines/reference/parseObjectLiteralsWithoutTypes.symbols create mode 100644 tests/baselines/reference/parseObjectLiteralsWithoutTypes.types create mode 100644 tests/cases/compiler/parseObjectLiteralsWithoutTypes.ts diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index b5ba89887a2..b28f13e438f 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2339,6 +2339,7 @@ namespace ts { token() === SyntaxKind.LessThanToken || token() === SyntaxKind.QuestionToken || token() === SyntaxKind.ColonToken || + token() === SyntaxKind.CommaToken || canParseSemicolon(); } return false; diff --git a/tests/baselines/reference/parseObjectLiteralsWithoutTypes.js b/tests/baselines/reference/parseObjectLiteralsWithoutTypes.js new file mode 100644 index 00000000000..4872cb8773b --- /dev/null +++ b/tests/baselines/reference/parseObjectLiteralsWithoutTypes.js @@ -0,0 +1,10 @@ +//// [parseObjectLiteralsWithoutTypes.ts] +let x: { foo, bar } +let y: { foo: number, bar } +let z: { foo, bar: number } + + +//// [parseObjectLiteralsWithoutTypes.js] +var x; +var y; +var z; diff --git a/tests/baselines/reference/parseObjectLiteralsWithoutTypes.symbols b/tests/baselines/reference/parseObjectLiteralsWithoutTypes.symbols new file mode 100644 index 00000000000..1e0bd775d10 --- /dev/null +++ b/tests/baselines/reference/parseObjectLiteralsWithoutTypes.symbols @@ -0,0 +1,16 @@ +=== tests/cases/compiler/parseObjectLiteralsWithoutTypes.ts === +let x: { foo, bar } +>x : Symbol(x, Decl(parseObjectLiteralsWithoutTypes.ts, 0, 3)) +>foo : Symbol(foo, Decl(parseObjectLiteralsWithoutTypes.ts, 0, 8)) +>bar : Symbol(bar, Decl(parseObjectLiteralsWithoutTypes.ts, 0, 13)) + +let y: { foo: number, bar } +>y : Symbol(y, Decl(parseObjectLiteralsWithoutTypes.ts, 1, 3)) +>foo : Symbol(foo, Decl(parseObjectLiteralsWithoutTypes.ts, 1, 8)) +>bar : Symbol(bar, Decl(parseObjectLiteralsWithoutTypes.ts, 1, 21)) + +let z: { foo, bar: number } +>z : Symbol(z, Decl(parseObjectLiteralsWithoutTypes.ts, 2, 3)) +>foo : Symbol(foo, Decl(parseObjectLiteralsWithoutTypes.ts, 2, 8)) +>bar : Symbol(bar, Decl(parseObjectLiteralsWithoutTypes.ts, 2, 13)) + diff --git a/tests/baselines/reference/parseObjectLiteralsWithoutTypes.types b/tests/baselines/reference/parseObjectLiteralsWithoutTypes.types new file mode 100644 index 00000000000..dcd9cb79d19 --- /dev/null +++ b/tests/baselines/reference/parseObjectLiteralsWithoutTypes.types @@ -0,0 +1,16 @@ +=== tests/cases/compiler/parseObjectLiteralsWithoutTypes.ts === +let x: { foo, bar } +>x : { foo: any; bar: any; } +>foo : any +>bar : any + +let y: { foo: number, bar } +>y : { foo: number; bar: any; } +>foo : number +>bar : any + +let z: { foo, bar: number } +>z : { foo: any; bar: number; } +>foo : any +>bar : number + diff --git a/tests/cases/compiler/parseObjectLiteralsWithoutTypes.ts b/tests/cases/compiler/parseObjectLiteralsWithoutTypes.ts new file mode 100644 index 00000000000..7f00e4b15e7 --- /dev/null +++ b/tests/cases/compiler/parseObjectLiteralsWithoutTypes.ts @@ -0,0 +1,3 @@ +let x: { foo, bar } +let y: { foo: number, bar } +let z: { foo, bar: number } From 3f8cd8230163b9b33f5b511bc639398a101b8bc0 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Fri, 26 Aug 2016 13:30:59 -0700 Subject: [PATCH 230/233] Update other tests and baselines --- ...ShorthandPropertiesAssignmentError.errors.txt | 13 ++----------- ...tLiteralShorthandPropertiesAssignmentError.js | 5 ++--- ...signmentErrorFromMissingIdentifier.errors.txt | 16 ++++------------ ...ertiesAssignmentErrorFromMissingIdentifier.js | 8 ++++---- ...tLiteralShorthandPropertiesAssignmentError.ts | 2 +- ...ertiesAssignmentErrorFromMissingIdentifier.ts | 4 ++-- 6 files changed, 15 insertions(+), 33 deletions(-) diff --git a/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentError.errors.txt b/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentError.errors.txt index 50839d5e56b..6f405facceb 100644 --- a/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentError.errors.txt +++ b/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentError.errors.txt @@ -1,8 +1,5 @@ tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentError.ts(4,43): error TS2322: Type '{ name: string; id: number; }' is not assignable to type '{ b: string; id: number; }'. Object literal may only specify known properties, and 'name' does not exist in type '{ b: string; id: number; }'. -tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentError.ts(5,16): error TS1131: Property or signature expected. -tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentError.ts(5,22): error TS2403: Subsequent variable declarations must have the same type. Variable 'id' must be of type 'number', but here has type 'any'. -tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentError.ts(5,25): error TS1128: Declaration or statement expected. tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentError.ts(6,79): error TS2322: Type '{ name: string; id: number; }' is not assignable to type '{ id: string; name: number; }'. Types of property 'id' are incompatible. Type 'number' is not assignable to type 'string'. @@ -11,7 +8,7 @@ tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPr Type 'number' is not assignable to type 'boolean'. -==== tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentError.ts (6 errors) ==== +==== tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentError.ts (3 errors) ==== var id: number = 10000; var name: string = "my name"; @@ -19,13 +16,7 @@ tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPr ~~~~ !!! error TS2322: Type '{ name: string; id: number; }' is not assignable to type '{ b: string; id: number; }'. !!! error TS2322: Object literal may only specify known properties, and 'name' does not exist in type '{ b: string; id: number; }'. - var person1: { name, id }; // error: can't use short-hand property assignment in type position - ~~~~ -!!! error TS1131: Property or signature expected. - ~~ -!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'id' must be of type 'number', but here has type 'any'. - ~ -!!! error TS1128: Declaration or statement expected. + var person1: { name, id }; // ok function foo(name: string, id: number): { id: string, name: number } { return { name, id }; } // error ~~~~~~~~~~~~ !!! error TS2322: Type '{ name: string; id: number; }' is not assignable to type '{ id: string; name: number; }'. diff --git a/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentError.js b/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentError.js index 8e2d7f80356..4d01f799348 100644 --- a/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentError.js +++ b/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentError.js @@ -3,7 +3,7 @@ var id: number = 10000; var name: string = "my name"; var person: { b: string; id: number } = { name, id }; // error -var person1: { name, id }; // error: can't use short-hand property assignment in type position +var person1: { name, id }; // ok function foo(name: string, id: number): { id: string, name: number } { return { name, id }; } // error function bar(obj: { name: string; id: boolean }) { } bar({ name, id }); // error @@ -14,8 +14,7 @@ bar({ name, id }); // error var id = 10000; var name = "my name"; var person = { name: name, id: id }; // error -var person1 = name, id; -; // error: can't use short-hand property assignment in type position +var person1; // ok function foo(name, id) { return { name: name, id: id }; } // error function bar(obj) { } bar({ name: name, id: id }); // error diff --git a/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.errors.txt b/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.errors.txt index b4f695c835d..92750fd29a0 100644 --- a/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.errors.txt +++ b/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.errors.txt @@ -3,15 +3,12 @@ tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPr tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.ts(5,79): error TS2322: Type '{ name: string; id: number; }' is not assignable to type '{ name: number; id: string; }'. Types of property 'name' are incompatible. Type 'string' is not assignable to type 'number'. -tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.ts(7,16): error TS1131: Property or signature expected. -tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.ts(7,22): error TS2403: Subsequent variable declarations must have the same type. Variable 'id' must be of type 'number', but here has type 'any'. -tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.ts(7,25): error TS1128: Declaration or statement expected. tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.ts(8,5): error TS2322: Type '{ name: number; id: string; }' is not assignable to type '{ name: string; id: number; }'. Types of property 'name' are incompatible. Type 'number' is not assignable to type 'string'. -==== tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.ts (6 errors) ==== +==== tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.ts (3 errors) ==== var id: number = 10000; var name: string = "my name"; @@ -25,15 +22,10 @@ tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPr !!! error TS2322: Types of property 'name' are incompatible. !!! error TS2322: Type 'string' is not assignable to type 'number'. function foo(name: string, id: number): { name: string, id: number } { return { name, id }; } // error - var person1: { name, id }; // error : Can't use shorthand in the type position - ~~~~ -!!! error TS1131: Property or signature expected. - ~~ -!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'id' must be of type 'number', but here has type 'any'. - ~ -!!! error TS1128: Declaration or statement expected. + var person1: { name, id }; // ok var person2: { name: string, id: number } = bar("hello", 5); ~~~~~~~ !!! error TS2322: Type '{ name: number; id: string; }' is not assignable to type '{ name: string; id: number; }'. !!! error TS2322: Types of property 'name' are incompatible. -!!! error TS2322: Type 'number' is not assignable to type 'string'. \ No newline at end of file +!!! error TS2322: Type 'number' is not assignable to type 'string'. + \ No newline at end of file diff --git a/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.js b/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.js index 71449746add..7800cbff53e 100644 --- a/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.js +++ b/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.js @@ -5,8 +5,9 @@ var name: string = "my name"; var person: { b: string; id: number } = { name, id }; // error function bar(name: string, id: number): { name: number, id: string } { return { name, id }; } // error function foo(name: string, id: number): { name: string, id: number } { return { name, id }; } // error -var person1: { name, id }; // error : Can't use shorthand in the type position -var person2: { name: string, id: number } = bar("hello", 5); +var person1: { name, id }; // ok +var person2: { name: string, id: number } = bar("hello", 5); + //// [objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.js] var id = 10000; @@ -14,6 +15,5 @@ var name = "my name"; var person = { name: name, id: id }; // error function bar(name, id) { return { name: name, id: id }; } // error function foo(name, id) { return { name: name, id: id }; } // error -var person1 = name, id; -; // error : Can't use shorthand in the type position +var person1; // ok var person2 = bar("hello", 5); diff --git a/tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentError.ts b/tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentError.ts index b745b93b3ca..55f10aec932 100644 --- a/tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentError.ts +++ b/tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentError.ts @@ -2,7 +2,7 @@ var name: string = "my name"; var person: { b: string; id: number } = { name, id }; // error -var person1: { name, id }; // error: can't use short-hand property assignment in type position +var person1: { name, id }; // ok function foo(name: string, id: number): { id: string, name: number } { return { name, id }; } // error function bar(obj: { name: string; id: boolean }) { } bar({ name, id }); // error diff --git a/tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.ts b/tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.ts index 6b0943a8d77..104be41a839 100644 --- a/tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.ts +++ b/tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.ts @@ -4,5 +4,5 @@ var name: string = "my name"; var person: { b: string; id: number } = { name, id }; // error function bar(name: string, id: number): { name: number, id: string } { return { name, id }; } // error function foo(name: string, id: number): { name: string, id: number } { return { name, id }; } // error -var person1: { name, id }; // error : Can't use shorthand in the type position -var person2: { name: string, id: number } = bar("hello", 5); \ No newline at end of file +var person1: { name, id }; // ok +var person2: { name: string, id: number } = bar("hello", 5); From 4c847eb257434ac129a8a7b62bdb9a17a272028e Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Fri, 26 Aug 2016 15:04:08 -0700 Subject: [PATCH 231/233] Fixing errors and adding a fourslash test --- src/harness/fourslash.ts | 62 +++++++++++++------ src/services/services.ts | 11 +++- .../findReferencesDefinitionDisplayParts.ts | 23 +++++++ tests/cases/fourslash/fourslash.ts | 1 + 4 files changed, 75 insertions(+), 22 deletions(-) create mode 100644 tests/cases/fourslash/findReferencesDefinitionDisplayParts.ts diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 63731417f48..694dcf6394e 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -206,6 +206,24 @@ namespace FourSlash { private inputFiles = ts.createMap(); // Map between inputFile's fileName and its content for easily looking up when resolving references + private static getDisplayPartsJson(displayParts: ts.SymbolDisplayPart[]) { + let result = ""; + ts.forEach(displayParts, part => { + if (result) { + result += ",\n "; + } + else { + result = "[\n "; + } + result += JSON.stringify(part); + }); + if (result) { + result += "\n]"; + } + + return result; + } + // Add input file which has matched file name with the given reference-file path. // This is necessary when resolveReference flag is specified private addMatchedInputFile(referenceFilePath: string, extensions: string[]) { @@ -777,6 +795,20 @@ namespace FourSlash { ts.forEachProperty(this.rangesByText(), ranges => this.verifyRangesReferenceEachOther(ranges)); } + public verifyDisplayPartsOfReferencedSymbol(expected: ts.SymbolDisplayPart[]) { + const referencedSymbols = this.findReferencesAtCaret(); + + if (referencedSymbols.length === 0) { + this.raiseError("No referenced symbols found at current caret position"); + } + else if (referencedSymbols.length > 1) { + this.raiseError("More than one referenced symbol found"); + } + + assert.equal(TestState.getDisplayPartsJson(referencedSymbols[0].definition.displayParts), + TestState.getDisplayPartsJson(expected), this.messageAtLastKnownMarker("referenced symbol definition display parts")); + } + private verifyReferencesWorker(references: ts.ReferenceEntry[], fileName: string, start: number, end: number, isWriteAccess?: boolean, isDefinition?: boolean) { for (let i = 0; i < references.length; i++) { const reference = references[i]; @@ -811,6 +843,10 @@ namespace FourSlash { return this.languageService.getReferencesAtPosition(this.activeFile.fileName, this.currentCaretPosition); } + private findReferencesAtCaret() { + return this.languageService.findReferences(this.activeFile.fileName, this.currentCaretPosition); + } + public getSyntacticDiagnostics(expected: string) { const diagnostics = this.languageService.getSyntacticDiagnostics(this.activeFile.fileName); this.testDiagnostics(expected, diagnostics); @@ -856,30 +892,12 @@ namespace FourSlash { displayParts: ts.SymbolDisplayPart[], documentation: ts.SymbolDisplayPart[]) { - function getDisplayPartsJson(displayParts: ts.SymbolDisplayPart[]) { - let result = ""; - ts.forEach(displayParts, part => { - if (result) { - result += ",\n "; - } - else { - result = "[\n "; - } - result += JSON.stringify(part); - }); - if (result) { - result += "\n]"; - } - - return result; - } - const actualQuickInfo = this.languageService.getQuickInfoAtPosition(this.activeFile.fileName, this.currentCaretPosition); assert.equal(actualQuickInfo.kind, kind, this.messageAtLastKnownMarker("QuickInfo kind")); assert.equal(actualQuickInfo.kindModifiers, kindModifiers, this.messageAtLastKnownMarker("QuickInfo kindModifiers")); assert.equal(JSON.stringify(actualQuickInfo.textSpan), JSON.stringify(textSpan), this.messageAtLastKnownMarker("QuickInfo textSpan")); - assert.equal(getDisplayPartsJson(actualQuickInfo.displayParts), getDisplayPartsJson(displayParts), this.messageAtLastKnownMarker("QuickInfo displayParts")); - assert.equal(getDisplayPartsJson(actualQuickInfo.documentation), getDisplayPartsJson(documentation), this.messageAtLastKnownMarker("QuickInfo documentation")); + assert.equal(TestState.getDisplayPartsJson(actualQuickInfo.displayParts), TestState.getDisplayPartsJson(displayParts), this.messageAtLastKnownMarker("QuickInfo displayParts")); + assert.equal(TestState.getDisplayPartsJson(actualQuickInfo.documentation), TestState.getDisplayPartsJson(documentation), this.messageAtLastKnownMarker("QuickInfo documentation")); } public verifyRenameLocations(findInStrings: boolean, findInComments: boolean, ranges?: Range[]) { @@ -2947,6 +2965,10 @@ namespace FourSlashInterface { this.state.verifyRangesReferenceEachOther(ranges); } + public findReferencesDefinitionDisplayPartsAtCaretAre(expected: ts.SymbolDisplayPart[]) { + this.state.verifyDisplayPartsOfReferencedSymbol(expected); + } + public rangesWithSameTextReferenceEachOther() { this.state.verifyRangesWithSameTextReferenceEachOther(); } diff --git a/src/services/services.ts b/src/services/services.ts index 6da12696937..8794446e83e 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -6328,7 +6328,8 @@ namespace ts { fileName: targetLabel.getSourceFile().fileName, kind: ScriptElementKind.label, name: labelName, - textSpan: createTextSpanFromBounds(targetLabel.getStart(), targetLabel.getEnd()) + textSpan: createTextSpanFromBounds(targetLabel.getStart(), targetLabel.getEnd()), + displayParts: [displayPart(labelName, SymbolDisplayPartKind.text)] }; return [{ definition, references }]; @@ -6568,6 +6569,11 @@ namespace ts { getThisReferencesInFile(sourceFile, searchSpaceNode, possiblePositions, references); } + const thisOrSuperSymbol = typeChecker.getSymbolAtLocation(thisOrSuperKeyword); + + const { displayParts } = getSymbolDisplayPartsDocumentationAndSymbolKind( + thisOrSuperSymbol, thisOrSuperKeyword.getSourceFile(), getContainerNode(thisOrSuperKeyword), thisOrSuperKeyword); + return [{ definition: { containerKind: "", @@ -6575,7 +6581,8 @@ namespace ts { fileName: node.getSourceFile().fileName, kind: ScriptElementKind.variableElement, name: "this", - textSpan: createTextSpanFromBounds(node.getStart(), node.getEnd()) + textSpan: createTextSpanFromBounds(node.getStart(), node.getEnd()), + displayParts }, references: references }]; diff --git a/tests/cases/fourslash/findReferencesDefinitionDisplayParts.ts b/tests/cases/fourslash/findReferencesDefinitionDisplayParts.ts new file mode 100644 index 00000000000..d424bd0c500 --- /dev/null +++ b/tests/cases/fourslash/findReferencesDefinitionDisplayParts.ts @@ -0,0 +1,23 @@ +/// + +//// class Gre/*1*/eter { +//// someFunction() { th/*2*/is; } +//// } +//// +//// type Options = "opt/*3*/ion 1" | "option 2"; +//// let myOption: Options = "option 1"; +//// +//// some/*4*/Label: +//// break someLabel; + +goTo.marker("1"); +verify.findReferencesDefinitionDisplayPartsAtCaretAre([{ text: "class", kind: "keyword" }, { text: " ", kind: "space" }, { text: "Greeter", kind: "className" }]); + +goTo.marker("2"); +verify.findReferencesDefinitionDisplayPartsAtCaretAre([{ text: "this", kind: "keyword" }, { text: ":", kind: "punctuation" }, { text: " ", kind: "space" }, { text: "this", kind: "keyword" }]); + +goTo.marker("3"); +verify.findReferencesDefinitionDisplayPartsAtCaretAre([{ text: "\"option 1\"", kind: "stringLiteral" }]); + +goTo.marker("4"); +verify.findReferencesDefinitionDisplayPartsAtCaretAre([{ text: "someLabel", kind: "text" }]); \ No newline at end of file diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 9ce3197a46f..59037e0de67 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -171,6 +171,7 @@ declare namespace FourSlashInterface { * If `ranges` is omitted, this is `test.ranges()`. */ rangesReferenceEachOther(ranges?: Range[]): void; + findReferencesDefinitionDisplayPartsAtCaretAre(expected: ts.SymbolDisplayPart[]): void; rangesWithSameTextReferenceEachOther(): void; currentParameterHelpArgumentNameIs(name: string): void; currentParameterSpanIs(parameter: string): void; From e62f1181b6a94161f8bfa88a84c2cf4c0f66bc3a Mon Sep 17 00:00:00 2001 From: Yui Date: Fri, 26 Aug 2016 16:46:10 -0700 Subject: [PATCH 232/233] fix typo of missing node.kind (#10569) --- src/compiler/emitter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index d0f7918dc89..f02cf266744 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -6587,7 +6587,7 @@ const _super = (function (geti, seti) { // import { x, y } from "foo" // import d, * as x from "foo" // import d, { x, y } from "foo" - const isNakedImport = SyntaxKind.ImportDeclaration && !(node).importClause; + const isNakedImport = node.kind === SyntaxKind.ImportDeclaration && !(node).importClause; if (!isNakedImport) { write(varOrConst); write(getGeneratedNameForNode(node)); From d72ed3c0b0fcf1f3c5f2b06d430c5e9c2ea6ca6f Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Fri, 26 Aug 2016 16:52:25 -0700 Subject: [PATCH 233/233] Check for this symbol before getting display parts --- src/services/services.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index dd99c0dba52..c19eb487d75 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -6570,8 +6570,8 @@ namespace ts { const thisOrSuperSymbol = typeChecker.getSymbolAtLocation(thisOrSuperKeyword); - const { displayParts } = getSymbolDisplayPartsDocumentationAndSymbolKind( - thisOrSuperSymbol, thisOrSuperKeyword.getSourceFile(), getContainerNode(thisOrSuperKeyword), thisOrSuperKeyword); + const displayParts = thisOrSuperSymbol && getSymbolDisplayPartsDocumentationAndSymbolKind( + thisOrSuperSymbol, thisOrSuperKeyword.getSourceFile(), getContainerNode(thisOrSuperKeyword), thisOrSuperKeyword).displayParts; return [{ definition: {