From cbc112a7617528c9449ba3c4ababacdb4baa725a Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Mon, 21 Mar 2016 08:26:56 -0700 Subject: [PATCH 001/197] 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 6957be484f35a..dc0ff55b89aa0 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 27b0839720559..63d2afe72eba6 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 0000000000000..117fc5be9bec5 --- /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 0000000000000..d6daa320c5a87 --- /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 0000000000000..47004d53b044f --- /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/197] 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 63d2afe72eba6..49efcc7dc0b61 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/197] 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 117fc5be9bec5..19f7a39b6150d 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 0000000000000..1148d5f320eb2 --- /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/197] 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 10d6b05c62203..e54261b117f39 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 cbc15c01e9e6d..4e9606d9c5380 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 0000000000000..d84a297566582 --- /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 0000000000000..79ee0b8830ffc --- /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 6988416d4de3f..b9ecd8e72a8b4 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 0000000000000..a964bd96dba0d --- /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/197] 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 899ee479578b2..f84988e68ba8d 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 d84a297566582..0000000000000 --- 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 0000000000000..ee7fe9f7edd9f --- /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 0000000000000..ef0a3f2c25b63 --- /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/197] 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 f84988e68ba8d..a6d1680a6e313 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 e54261b117f39..f7e6475b971ca 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 4e9606d9c5380..85358df666ede 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 b9ecd8e72a8b4..ea99d03bd7cc5 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/197] 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 aea8c982a9563..08ee6bf4b8a38 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/197] 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 0000000000000..0233180873dbc --- /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 0000000000000..34a477b706a99 --- /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 0000000000000..060d35be21af8 --- /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 0000000000000..f25e66ecfb8b9 --- /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 448a480aa8d592378732b1052c645b5c681fa7e3 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 12 Jul 2016 08:37:52 -0700 Subject: [PATCH 009/197] 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 988714be2ab1b..5ddf5f7d065c5 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; - } - 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; + /** 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); } - 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 b3f2102d903de..0c312f4701a66 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 a695482e1fba3..86d8baba6f780 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 104b6dd9443d6..7b3456015c832 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 0000000000000..d1d6270ae136f --- /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 0000000000000..2a56947ce198a --- /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 df12a3531bcfa..16d176872cac1 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 e9934946a64fd..eaaa1d51cc097 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 7dc9e8c104b8c..5060006ac56bd 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 fbc0091ee3b95..f59eda1a81e79 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 da6f0fde1433a..f36851b2b97e8 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 0809d1af5f059..b3514e00aad87 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 25909b6add4c2..1717bc663f270 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 0000000000000..18b042d905fd9 --- /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 3adb8667be578..ed34dbd535f27 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 6ad3565862337..429289546c5ea 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 010/197] 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 1cebd4693885b..88565bcba92c3 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 926ee38a79514..9b12f6a7f6861 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 8ffb02a89743e..d85dc20afdfdd 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 7219a5bbd24ab..bfe8e4bd657b8 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 d1d6270ae136f..de105f7353965 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 2a56947ce198a..8a912f33011f6 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 18b042d905fd9..29d81589cd92c 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 011/197] 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 774cea60e09a8..8545273afecc9 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 0000000000000..8833a6aa97224 --- /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 0000000000000..b6ee77ac793f9 --- /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 0000000000000..7b6d8ef035bb7 --- /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 0000000000000..64e96be70f327 --- /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 012/197] 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 8545273afecc9..61c7698c59d0d 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 8833a6aa97224..9d1399d9d65f3 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 b6ee77ac793f9..d0bca9f38c713 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 7b6d8ef035bb7..ba1590fe03d95 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 0000000000000..abafe67878430 --- /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 0000000000000..652cf28e9e1f0 --- /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 64e96be70f327..6a152226ea215 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 0000000000000..9a85e6b21c27d --- /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 013/197] 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 61c7698c59d0d..9c54de6ba1384 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 0000000000000..5a04051139c9f --- /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 0000000000000..14fb3a11505da --- /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 0000000000000..5a405804b0013 --- /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 014/197] 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 9c54de6ba1384..68753fdb63867 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 5a04051139c9f..0000000000000 --- 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 14fb3a11505da..0000000000000 --- 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 9d1399d9d65f3..95817b7f0e604 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 d0bca9f38c713..574ee21f7731b 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 ba1590fe03d95..ad3597f6c018a 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 5a405804b0013..0000000000000 --- 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 6a152226ea215..fcd8f999e9fa9 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 a6642d68c92ce7ddd16e8da71a1f2eb909fde74d Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 27 Jul 2016 13:21:42 -0700 Subject: [PATCH 015/197] 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 6da456afcbc4d..321bfcc74a3d8 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 774cea60e09a8..2e76093cde242 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 dc332b245678b..17e8a25038d71 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 0000000000000..f74faf3519e1a --- /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 0000000000000..0414efc24d95f --- /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 0000000000000..c7b87857d18ea --- /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 0000000000000..d2a0d6c644da2 --- /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 016/197] 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 f74faf3519e1a..e1210690f915c 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 0414efc24d95f..83f0f4b1e6597 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 c7b87857d18ea..834ec65b91601 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 017/197] 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 206b8b8f9bf40..dcfe9a0b92e19 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 018/197] 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 dcfe9a0b92e19..42e4a8c2b57a1 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 019/197] 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 83f0f4b1e6597..982a153606921 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 834ec65b91601..0400416a25992 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 f8103b596091d717753ee8200f9f45a3c8ca6ef7 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 27 Jul 2016 15:58:13 -0700 Subject: [PATCH 020/197] 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 17e8a25038d71..d5d5546a0cde7 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 021/197] 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 6da456afcbc4d..38079c1bea02f 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 0000000000000..f885323efae32 --- /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 0000000000000..15e8bbdbf41fe --- /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 0000000000000..07b6050503a57 --- /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 0000000000000..a1fce75c54e9c --- /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 0000000000000..9300b7c054921 --- /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 0000000000000..2aeb2ee0b1dc0 --- /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 0000000000000..f8fe0a323c660 --- /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 022/197] 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 38079c1bea02f..c8b03876c46f1 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 97bbbd729e049ee644e954f4dff0c3756a0677db Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 28 Jul 2016 07:20:51 -0700 Subject: [PATCH 023/197] 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 04c12eb613c5d..6d670c70541f6 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 d93a8a0aed027..220244c55afea 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 072209aa49640..983ed25c84891 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 4ae0005e9c5e7..acace78396ba8 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 0000000000000..efb4ee8bff394 --- /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 0000000000000..2bc00e48feca9 --- /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 0000000000000..82e6277b2926d --- /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 0000000000000..4df2eb692a8a8 --- /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 024/197] 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 8d8666a67abac..502cb39e8fe55 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 6d670c70541f6..21d74bb24b856 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 709a331e02281..cd332b6bef3d4 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 acace78396ba8..466cf8c2b770c 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 efb4ee8bff394..10ba6d6419a60 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; +} -//// [exportDefaultProperty.js] +//// [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; + + +//// [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"] = "".length; +exports["default"] = A.B; +//// [b.js] +"use strict"; +exports.__esModule = true; +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 2bc00e48feca9..f9edcd154cc05 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 82e6277b2926d..47cfabfbc165e 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 0000000000000..2fd8a8c851178 --- /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 0000000000000..43c9ed3251868 --- /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 0000000000000..a92af53b12bd4 --- /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 4df2eb692a8a8..4a4b413902586 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 0000000000000..0d14815a5bdf0 --- /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 c50ccbf9616bbb06e7f73203a7e0134ab852f0d6 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Fri, 29 Jul 2016 12:31:02 -0700 Subject: [PATCH 025/197] 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 21d74bb24b856..ca66534603675 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 e8066158eba22aff78d6899d0bc0dc67b20404d4 Mon Sep 17 00:00:00 2001 From: Yuichi Nukiyama Date: Mon, 1 Aug 2016 15:15:11 +0900 Subject: [PATCH 026/197] 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 7b85189b784e4..4b12e2e61f5a0 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 8126d5c605ea7..c1eead94f1456 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 0000000000000..0581199313f86 --- /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 0000000000000..d0b608a6d8796 --- /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 0000000000000..cb3b3e9555679 --- /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 0000000000000..2bb04fde088a2 --- /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 0000000000000..61c4374c60a33 --- /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 0000000000000..b9e05fbc96700 --- /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 0f134ed69efb8d59919bcec6a1e702f403665af6 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 2 Aug 2016 06:58:26 -0700 Subject: [PATCH 027/197] 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 7eab69f1df66c..290fa257daf9b 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 7d6aea6246216..d77036660b0a7 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 ff534847cda44..93bd6c608f002 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 d6667d2b8b20e..b92037b7e5b53 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 de105f7353965..2a9b74a1af674 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 8a912f33011f6..d9d918f5600df 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 "./x.ts"; +import y from "./y.tsx"; -import x from "./m.ts"; +// 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 29d81589cd92c..683f6630d9f30 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 "./x.ts"; +import y from "./y.tsx"; -import x from "./m.ts"; +// 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 028/197] 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 ca66534603675..d8b3785a89e9b 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 220244c55afea..c93784cd9e0c3 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 983ed25c84891..804989570d80b 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 466cf8c2b770c..f946e4b614f97 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 029/197] 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 d8b3785a89e9b..dc0c8a6a415e8 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 030/197] 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 0a45b61660a1f..c1626ff0b7584 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 0000000000000..981f89214e2ca --- /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 0000000000000..746997c4c8941 --- /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 0000000000000..51cc63e6e11e7 --- /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 0000000000000..51155dd69dfd8 --- /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 0000000000000..69ce39e6dc6e6 --- /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 0000000000000..acb584f8fd9e0 --- /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 0000000000000..3c3c5fa8d28c7 --- /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 0000000000000..15810ccaaf703 --- /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 0000000000000..51155dd69dfd8 --- /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 0000000000000..69ce39e6dc6e6 --- /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 0000000000000..6b50bae96d8fe --- /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 0000000000000..2da05e4678c7f --- /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 0000000000000..3a213ad80235f --- /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 0000000000000..8da66f33adf56 --- /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 031/197] 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 dc0c8a6a415e8..ca85a85e279ba 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 f946e4b614f97..affbcbe7a9818 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 032/197] 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 ca85a85e279ba..dd9b56a5b586b 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 033/197] 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 dd9b56a5b586b..0c65050a327a5 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 034/197] 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 04c12eb613c5d..2d4ce970172e0 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 a70c07d38dc1b..e40b831552f3d 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 1d85b31c9988f..e4d2bb9f184c0 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 716491ea58baa..e706f9f6a4ef9 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 c9e9be92f8e7e..dc1cf97fe5991 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 035/197] 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 | 32 +++++++++++-------- .../reference/moduleResolutionNoTs.js | 11 ++++--- tests/cases/compiler/moduleResolutionNoTs.ts | 9 ++++-- 9 files changed, 46 insertions(+), 43 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 290fa257daf9b..6dd2747e33f96 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 d77036660b0a7..869cddb2d4d0b 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 93bd6c608f002..8975e5daa04cf 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 a2674d7978326..d35ce644120c4 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 b92037b7e5b53..22c9355072524 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 ecdc18394b079..b8467adda5ce4 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 2a9b74a1af674..c054330731158 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 - /// +==== 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 should not end with a '.ts' extension. Consider importing './x' instead. +!!! 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 should not end with a '.tsx' extension. Consider importing './y' instead. +!!! 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"; - -==== tests/cases/compiler/x.ts (0 errors) ==== - export default 0; - -==== tests/cases/compiler/y.tsx (0 errors) ==== - export default 0; + 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 d9d918f5600df..9ff33d688ea24 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 683f6630d9f30..2051bc259bf0e 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 036/197] 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 709a331e02281..6c87ad82955c3 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 5fe5027978c78..c8bd8072b2400 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 0000000000000..190d1693c248c --- /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 0000000000000..ce5446317a4d5 --- /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 0000000000000..17037852f3b13 --- /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 0000000000000..248df036bfde9 --- /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 037/197] 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 0a45b61660a1f..69c93a75323a3 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 072209aa49640..b9de9ce1635ff 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 038/197] 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 0900d9fc745af..10a30a6d75c94 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 f86a9e66fa285..0a6524dd26f23 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 8e9928896a19d..520d045d9e0d1 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 34d3b296a48db..661ce9fbc55b1 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 984ca454e76af..cd9ae40932ac9 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 039/197] 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 69c93a75323a3..4e5da63af2f2f 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 040/197] 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 | 58 +++++++++++-------- 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, 183 insertions(+), 29 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 8d8666a67abac..6f0def32702d3 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); } + else { + if (node.name) { + node.name.parent = 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; + // 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; - } - }); + 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))); + 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); + 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 072209aa49640..4c08d7a487c41 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 5f5ac29e90964..e5aae416663c4 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 a256943dd506c..7e4bcd53c8a99 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 7c0a3de70c9c6..2a7cac18205ab 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 6899be2ec89a2..9ead1eeb0a5e9 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 041/197] 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 2d4ce970172e0..278d27a23476d 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 e706f9f6a4ef9..79985378bf5bd 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 042/197] 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 278d27a23476d..5e9fb85682399 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 043/197] 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 9ead1eeb0a5e9..ba2e14e11e934 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 044/197] [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 4b9948c3ba0b4..7d40b2f3219fc 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 b9de9ce1635ff..28ededebb0de1 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 6ec95343e330d..ab1e30a44a607 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 271d6f2d6e205..45c4b284ae744 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 d8dfb57c2a66f..a23f0ef0ca56e 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 d8dfb57c2a66f..fb3a2bb7da48d 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 3e9d7dba1d21d..e23517976b0e4 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 c26f7d2763d7f..64cdd8091832f 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 48fb49e6c7fb9..fd83c1431b438 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 775af9c5283f3..a621081993126 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 55c17b219ecaa..a33a2aba4082d 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 05080e05651dd..be789a08ddc5c 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 e17e498b9784d..13c84c7ae5e9b 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 61971ba44b2ad..1eb796d03fd25 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 2d93e22bdcf23..8b56459cd697f 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 efdb1e8312b6f..f1abe27f05f36 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 124c31274ace4..f9dede73267bd 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 31a01a0b8e4bc..44218683a5a99 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 4c2729ab389b3..1baf0bdac9d28 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 ac7346895ef7d..dfa87b651387b 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 bb24b82b32466..e81ae663e24f6 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 7154963f1efa3..edf2ece7e06dc 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 79d42fa701853..e9335b0708209 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 c7725a3aab1ab..bed69cbf35739 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 610c7173c898b..1ad1aa522885b 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 92b4b259ba4a9..419643d9d0d90 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 555d4569af34d..150deef992ac1 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 045/197] 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 046/197] 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 0000000000000..c240c125dbe66 --- /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 047/197] 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 01aeb7ca74473..ddc188ace9209 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 9136105457ee1..07f013e4f318e 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 4f6806d11a578..45187b784b020 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 e1210690f915c..1a017160302c9 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 0000000000000..9f01fe782aa62 --- /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 0400416a25992..b6c9e522ac35c 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 982a153606921..0000000000000 --- 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 d2a0d6c644da2..bd0d2d562f9a5 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 c240c125dbe66..11ae699eab8d8 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 048/197] 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 0599eb4bbbc60..84d5cd08a2bb1 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 d7aa46bc05ace..42dabc741ab92 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 abfb54e000c7e..4bde09074b552 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 dd16ade10e31e..83fb7b32f5620 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 049/197] 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 6f0def32702d3..5e2cd3f868084 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 e5aae416663c4..7fb7e0bca026f 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 7e4bcd53c8a99..9e49c2fc00cc4 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 2a7cac18205ab..900d03195d486 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 050/197] 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 5e2cd3f868084..b771a3b67be14 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 051/197] 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 84d5cd08a2bb1..9583fadba6230 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 4ae0005e9c5e7..2cfaf1f5cfbc4 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 ab1e30a44a607..aea4d5f872e03 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 645511350a91c..70715649894c1 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 31ac43fe85557..59b577141e055 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 bd6584d3d7473..a31ed61d531e9 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 20bba09190314..24fe6df3c3cc5 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 3193e83cd1979..3e2df7c8a89ac 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 854ebceff379c..39bc28cbd2451 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 3f2cc913d8610..7be5d278bf26d 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 71783d0301260..246270aa5a60f 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 9b964456bcf02..0307a9b78d375 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 1767f3b5a899d..1379bba8d5d1c 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 165fd81597aba..c1fd3d6d74fbc 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 85ee6bafe5ec1..23149e58d76cc 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 8403187198bf3..d266997770bd3 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 579599f56619b..18794258517d3 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 d08507f1ea2e7..958875b79ebd3 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 7318a43057a4e..5b1469cbd6c3c 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 0b3ef17fb7b72..19b56ed30f3ba 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 60782543710bc..d83e039b674e1 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 8c38f267a2ae9..97da693b38f12 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 0e66a49b9630e..ae3bdfd5fc89e 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 0000000000000..4177e154532b8 --- /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 0000000000000..085ea33b87446 --- /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 e3f57ac7b4182..e0408af656fc3 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 0000000000000..94aabcdbde522 --- /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 0000000000000..0daa4087b0d61 --- /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 052/197] 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 4c08d7a487c41..f5cee2f8f8f29 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 053/197] 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 478e31c439831..cb9bf42225a8c 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 5606a423a0e10..9f3122c33748e 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 054/197] 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 6c91ef52cbb49..b9b5d2068b214 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 174be5e702f52..a5650a56b16dc 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 746283720bf32..af04a74d55755 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 055/197] 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 7e2c6eb8d334e..c1408187d88ba 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 8126d5c605ea7..e3ac2d78c6375 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 0000000000000..d27fad84e5031 --- /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 0000000000000..70bbc6bd8dc94 --- /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 0000000000000..5bd6ecf1d74a7 --- /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 056/197] 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 84d5cd08a2bb1..3158a1902445a 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 28ededebb0de1..a6e860450c669 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 057/197] 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 0000000000000..41593c4b2245d --- /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 0000000000000..b75a6277735f7 --- /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 0000000000000..e493f6bf77042 --- /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 058/197] 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 c8bd8072b2400..6666b7df2351c 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 6b3a06cf7c369..5a72e173475e2 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 03fb45b806e3d..91e6c506c2b50 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 059/197] 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 9583fadba6230..7dbd6c1ab8bdc 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 060/197] 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 e76cfbf4caba9..5251973005fcd 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 0bf5ab524b906..d24462316b4da 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 e00fd18163dd6..e95a7364f9340 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 fb1632559ea90..c984b8a78f9ba 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 05be633813b49..fbe71ff07c188 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 061/197] 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 0000000000000..7c7833927ca04 --- /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 0000000000000..64b567c1548d4 --- /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 0000000000000..362715914f994 --- /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 0000000000000..229a00236baf2 --- /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 062/197] 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 b771a3b67be14..849f503ef85e9 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 f5cee2f8f8f29..70bd3c3c07035 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 063/197] 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 7dbd6c1ab8bdc..09402b9411926 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 064/197] 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 229a00236baf2..56f3ff97e4c8e 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 0000000000000..c21ab7ec8f49a --- /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 065/197] 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 7c7833927ca04..3f2287b46285a 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 64b567c1548d4..3189482799013 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 362715914f994..e52d1a25b1d0d 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 0000000000000..66cfe056ab711 --- /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 0000000000000..4f4128239364f --- /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 0000000000000..f7675b1475601 --- /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 066/197] 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 cb9bf42225a8c..4126683eb625d 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 067/197] 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 a42abbbc60909..366891c0d308e 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 068/197] 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 366891c0d308e..751db7b971a7a 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 069/197] Make baselines faster by not writing out unneeded files --- Gulpfile.ts | 115 ++++++++++++++++++++----------------- Jakefile.js | 17 +++--- package.json | 1 + scripts/types/ambient.d.ts | 2 +- src/harness/harness.ts | 19 +++--- 5 files changed, 81 insertions(+), 73 deletions(-) diff --git a/Gulpfile.ts b/Gulpfile.ts index b9b5d2068b214..e2d37277bcf7c 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"] }, - - // 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") } + // 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 + 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 a5650a56b16dc..8062d8a292808 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 9f3122c33748e..1f1c52b42edd0 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 e77e3fe8c5a6d..16ec9385cb1e2 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 f27e7e1c1749a..18a440c789b8a 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 070/197] 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 ada4947acde52..b05346a366a4a 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 071/197] 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 fb60852faf32c..bec8686ab935a 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 0000000000000..d0f11d056ca53 --- /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 0000000000000..9dd7453ba4f17 --- /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 0000000000000..25207e28c79a9 --- /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 0000000000000..f999cfe70f000 --- /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 0000000000000..e369cd9fb505d --- /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 0000000000000..2bb931c4f4f1a --- /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 072/197] 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 751db7b971a7a..d0643db4967a8 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 073/197] Remove use strict --- Jakefile.js | 1 - 1 file changed, 1 deletion(-) diff --git a/Jakefile.js b/Jakefile.js index 8062d8a292808..0c08c0faf4c7a 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 074/197] 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 fb60852faf32c..e2e09b80a7293 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 075/197] 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 e2e09b80a7293..d4599a47f4fd1 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 076/197] 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 0000000000000..564f7a9c22eaa --- /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 077/197] 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 5251973005fcd..217e11dc95d0a 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 d24462316b4da..7e92fcdbbd153 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 0000000000000..97be21377229d --- /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 0000000000000..f2eb40eea47a1 --- /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 0000000000000..d98a602579642 --- /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 078/197] 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 f27e7e1c1749a..e8e0a11f615aa 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 079/197] 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 1f2eaea833df0..8b31ef91930ec 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 0000000000000..c63fbf6199124 --- /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 0000000000000..4d2bef22c55fe --- /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 0000000000000..7daafe02bfc7e --- /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 0000000000000..b6d0d338d85c5 --- /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 080/197] 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 8b31ef91930ec..546d8412c986e 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 081/197] Ignore null, undefined, void when checking for discriminant property --- src/compiler/checker.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1f2eaea833df0..c88ab16acd7c7 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)); - } - return (prop).isDiscriminantProperty; + 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 false; From 6c0bca0ae583c6b68ae9e007c081338ce9047d51 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 9 Aug 2016 13:39:12 -0700 Subject: [PATCH 082/197] 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 0000000000000..153950f8ab5d9 --- /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 0000000000000..3f95fcf3a3fef --- /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 0000000000000..7a2918ab83b42 --- /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 0000000000000..8346fa8adeadf --- /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 083/197] 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 546d8412c986e..1ed8cb8c6eea6 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 a6e860450c669..25630059bf094 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 084/197] 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 1ed8cb8c6eea6..8fcd22f0ffbde 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 085/197] 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 1f2eaea833df0..ac4ac3d506df0 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 10ad325464005bc7d94808dfd3ec6c8c8d69320c Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Tue, 9 Aug 2016 16:52:51 -0700 Subject: [PATCH 086/197] wip - fix error --- src/compiler/types.ts | 2 +- src/compiler/utilities.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d92845ea1ff87..38b4c90220c13 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -585,7 +585,7 @@ namespace ts { // @kind(SyntaxKind.ConstructSignature) export interface ConstructSignatureDeclaration extends SignatureDeclaration, TypeElement { } - export type BindingName = Identifier | ObjectBindingPattern | ArrayBindingPattern; + export type BindingName = Identifier | BindingPattern; // @kind(SyntaxKind.VariableDeclaration) export interface VariableDeclaration extends Declaration { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index a5848668bf0f0..10b5911e33a28 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3623,7 +3623,7 @@ namespace ts { return node.kind === SyntaxKind.Identifier; } - export function isGeneratedIdentifier(node: Node): node is Identifier { + export function isGeneratedIdentifier(node: Node): boolean { // Using `>` here catches both `GeneratedIdentifierKind.None` and `undefined`. return isIdentifier(node) && node.autoGenerateKind > GeneratedIdentifierKind.None; } From 8975cd7024dc1057794af93fcb74f36844f8e1a5 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 9 Aug 2016 23:00:47 -0400 Subject: [PATCH 087/197] 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 e1b9d99762bce..de339e2bf2cc4 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 088/197] 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 4126683eb625d..97c772f45ac07 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 9d42f9c73e8482e7a2f510493832a1fe4d8820f8 Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Wed, 10 Aug 2016 11:18:07 -0700 Subject: [PATCH 089/197] Fix error from using merging master --- src/compiler/emitter.ts | 2 +- src/compiler/transformers/es6.ts | 4 +++- src/compiler/transformers/generators.ts | 2 +- src/compiler/types.ts | 2 +- src/compiler/utilities.ts | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 8b91a7c19d944..216cd3c5b7977 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2609,7 +2609,7 @@ const _super = (function (geti, seti) { function getTextOfNode(node: Node, includeTrivia?: boolean): string { if (isGeneratedIdentifier(node)) { - return getGeneratedIdentifier(node); + return getGeneratedIdentifier(node); } else if (isIdentifier(node) && (nodeIsSynthesized(node) || !node.parent)) { return unescapeIdentifier(node.text); diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index b63df6d5d6d0a..b3f96db701873 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -1815,7 +1815,9 @@ namespace ts { ); } else { - assignment.end = initializer.end; + // Currently there is not way to check that assignment is binary expression of destructing assignment + // so we have to cast never type to binaryExpression + (assignment).end = initializer.end; statements.push(createStatement(assignment, /*location*/ moveRangeEnd(initializer, -1))); } } diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index 1dcb2446e0b63..3c4ad65c0ce30 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -1912,7 +1912,7 @@ namespace ts { function cacheExpression(node: Expression): Identifier { let temp: Identifier; if (isGeneratedIdentifier(node)) { - return node; + return node; } temp = createTempVariable(hoistVariableDeclaration); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d92845ea1ff87..38b4c90220c13 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -585,7 +585,7 @@ namespace ts { // @kind(SyntaxKind.ConstructSignature) export interface ConstructSignatureDeclaration extends SignatureDeclaration, TypeElement { } - export type BindingName = Identifier | ObjectBindingPattern | ArrayBindingPattern; + export type BindingName = Identifier | BindingPattern; // @kind(SyntaxKind.VariableDeclaration) export interface VariableDeclaration extends Declaration { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index a5848668bf0f0..10b5911e33a28 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3623,7 +3623,7 @@ namespace ts { return node.kind === SyntaxKind.Identifier; } - export function isGeneratedIdentifier(node: Node): node is Identifier { + export function isGeneratedIdentifier(node: Node): boolean { // Using `>` here catches both `GeneratedIdentifierKind.None` and `undefined`. return isIdentifier(node) && node.autoGenerateKind > GeneratedIdentifierKind.None; } From 56cb07ae919372d96e98d9195375b7264555380e Mon Sep 17 00:00:00 2001 From: zhengbli Date: Wed, 10 Aug 2016 12:34:27 -0700 Subject: [PATCH 090/197] 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 c528554432914..d5e6daa5d6088 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 091/197] 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 8fcd22f0ffbde..b8d7146efb706 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 32fb60b72a48a298e2a8d6802525d8c724a5001d Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Wed, 10 Aug 2016 14:24:40 -0700 Subject: [PATCH 092/197] Update baselines --- .../allowSyntheticDefaultImports7.js | 21 ++++++++++--------- .../allowSyntheticDefaultImports8.js | 10 ++++----- .../emitCapturingThisInTupleDestructuring2.js | 2 +- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/tests/baselines/reference/allowSyntheticDefaultImports7.js b/tests/baselines/reference/allowSyntheticDefaultImports7.js index d1e61ee1cc8b9..6d0ea5b3be07a 100644 --- a/tests/baselines/reference/allowSyntheticDefaultImports7.js +++ b/tests/baselines/reference/allowSyntheticDefaultImports7.js @@ -1,28 +1,29 @@ //// [tests/cases/compiler/allowSyntheticDefaultImports7.ts] //// //// [b.d.ts] -export function foo(); - -export function bar(); +export function foo(); + +export function bar(); //// [a.ts] -import { default as Foo } from "./b"; -Foo.bar(); +import { default as Foo } from "./b"; +Foo.bar(); Foo.foo(); //// [a.js] -System.register(["./b"], function(exports_1, context_1) { +System.register(["./b"], function (exports_1, context_1) { "use strict"; var __moduleName = context_1 && context_1.id; var b_1; return { - setters:[ + setters: [ function (b_1_1) { b_1 = b_1_1; - }], - execute: function() { + } + ], + execute: function () { b_1["default"].bar(); b_1["default"].foo(); } - } + }; }); diff --git a/tests/baselines/reference/allowSyntheticDefaultImports8.js b/tests/baselines/reference/allowSyntheticDefaultImports8.js index 4a611671c24b6..5a77e51e6a241 100644 --- a/tests/baselines/reference/allowSyntheticDefaultImports8.js +++ b/tests/baselines/reference/allowSyntheticDefaultImports8.js @@ -1,13 +1,13 @@ //// [tests/cases/compiler/allowSyntheticDefaultImports8.ts] //// //// [b.d.ts] -export function foo(); - -export function bar(); +export function foo(); + +export function bar(); //// [a.ts] -import { default as Foo } from "./b"; -Foo.bar(); +import { default as Foo } from "./b"; +Foo.bar(); Foo.foo(); //// [a.js] diff --git a/tests/baselines/reference/emitCapturingThisInTupleDestructuring2.js b/tests/baselines/reference/emitCapturingThisInTupleDestructuring2.js index f999cfe70f000..890b9d4c9b843 100644 --- a/tests/baselines/reference/emitCapturingThisInTupleDestructuring2.js +++ b/tests/baselines/reference/emitCapturingThisInTupleDestructuring2.js @@ -17,7 +17,7 @@ var B = (function () { } 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" + (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; }()); From 68e1079727d488f17ded5b8fd5f7bd8078483944 Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Wed, 10 Aug 2016 14:24:46 -0700 Subject: [PATCH 093/197] Fix linting --- 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 38b4c90220c13..f6edf6ac8c961 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -585,7 +585,7 @@ namespace ts { // @kind(SyntaxKind.ConstructSignature) export interface ConstructSignatureDeclaration extends SignatureDeclaration, TypeElement { } - export type BindingName = Identifier | BindingPattern; + export type BindingName = Identifier | BindingPattern; // @kind(SyntaxKind.VariableDeclaration) export interface VariableDeclaration extends Declaration { From 65e1293b2e542165f668191a26dae2d446c54b74 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 10 Aug 2016 16:47:06 -0700 Subject: [PATCH 094/197] 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 8d8666a67abac..c7450ac88fb09 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 c88ab16acd7c7..6b3fb1076a158 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 7e2c6eb8d334e..eaf17f00dd065 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 6c87ad82955c3..db88305d2c5a7 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 d93a8a0aed027..113b79f33d434 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 c8bd8072b2400..1d62a4a4f4660 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 014d092621368..6f38783d5edeb 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 89db876ae5e48..e496353fde8e6 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 7d40b2f3219fc..c26e748da9ab9 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 6dd84298008ce..134dc489b2ae8 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 29ae2c60af165..350d75429b754 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 10538d0c009ee..3b588edd101ca 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 a6e860450c669..dad60775bafc2 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 2cfaf1f5cfbc4..e0b3700c6f1f1 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 ecdc18394b079..5d4bdf7db3259 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 a42abbbc60909..fed7751eb4a26 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 e8e0a11f615aa..5ae33303d5572 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 d7ed04b627f4f..4f95f19e14441 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 038b2dbe35953..be3030a6dd68f 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 e0ce09391fb86..a1e010b97dfa5 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 5f63889b08498..4b5ef9f24f15e 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 8b2b15c15b30d..76015667232a6 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 c528554432914..8e7faa94a7a84 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 7a375c50b8a36..74b9e303cf7e7 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 f04dbd8dc0253..3547ac52602de 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 6e9adad7f472f..a14c42f471e70 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 7e1ca81e2af8c..13b0e6d6ce4ac 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 3b013a4a924ac..05de0061d3978 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 f105a9fa6e23b..ebd6936ea8543 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 3d20337eca775..cff3f591f8adf 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 aea4d5f872e03..850e29030d801 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 095/197] 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 7573f2ba99ea8..cb7fc3a4c5c92 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 34baa04c85035..07922bd35c779 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 096/197] 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 26632ba6bab3c..431cf46018050 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 097/197] 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 97c772f45ac07..bfc07e2b5100c 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 098/197] 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 b9b5d2068b214..d677b042250e6 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 a5650a56b16dc..0624c92f16228 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 9f3122c33748e..25d8f0c42eab1 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 099/197] 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 43ec22df40307..2ae238b917596 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 100/197] 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 e77e3fe8c5a6d..e83489801d790 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 101/197] Optimize getTypeWithFacts --- src/compiler/checker.ts | 45 ++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2ae238b917596..e4f650691d5be 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) { + 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) { - if (!firstType) { - firstType = t; - } - else { - if (!types) { - types = [firstType]; - } - types.push(t); - } + filtered.push(t); } + i++; } - return types ? getUnionType(types) : - firstType ? firstType : neverType; + 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 102/197] Filter out nullable and primitive types in isDiscriminantProperty --- 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 e4f650691d5be..0ba44b34f8936 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,17 +7849,24 @@ 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 103/197] 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 0ba44b34f8936..b6d037378103c 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 104/197] 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 0000000000000..1d11781a70784 --- /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 0000000000000..c84f32cd99073 --- /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 0000000000000..d3e1b70e59911 --- /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 0000000000000..6352d741808ab --- /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 105/197] 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 549410159b035..1d89d0092f322 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 106/197] 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 b6d037378103c..0fb626e6281b5 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 107/197] 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 0fb626e6281b5..4e04c5fc11abb 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 108/197] Allow an @types direcotry to have a package.json which specifies `"typings": null` to disclude it from automatically included typings. --- src/compiler/program.ts | 85 ++++++++++--------- 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, 84 insertions(+), 51 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 7d40b2f3219fc..67a93606cc691 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 = {}; - } - - 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); + 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; } - } - } - // 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); + else { + if (state.traceEnabled) { + trace(state.host, Diagnostics.Expected_type_of_0_field_in_package_json_to_be_string_got_1, fieldName, typeof typesFile); + } } } } - 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 ecdc18394b079..35e09c9c0d006 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 a42abbbc60909..620eee584e740 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 0000000000000..3e816526af252 --- /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 0000000000000..7223c8589a640 --- /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 0000000000000..0637a088a01e8 --- /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 0000000000000..7223c8589a640 --- /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 0000000000000..90e1e463f0ab3 --- /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 109/197] 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 e2d37277bcf7c..f0e274f3f1ea8 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 18a440c789b8a..81567265d3f7c 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 110/197] 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 0624c92f16228..01aac82a47ad8 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 111/197] Simplifies performance API --- src/compiler/binder.ts | 5 +- src/compiler/checker.ts | 7 +-- src/compiler/parser.ts | 6 +- src/compiler/performance.ts | 110 ++++++++++++++++-------------------- src/compiler/program.ts | 21 ++++--- src/compiler/sourcemap.ts | 10 +++- 6 files changed, 77 insertions(+), 82 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index c355d6c91865b..ff5a8b77cfd98 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 1b7b659e1a6b3..f1fff6737a376 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 6f38783d5edeb..cbf767a5410fd 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 e496353fde8e6..e8f064e81cdf3 100644 --- a/src/compiler/performance.ts +++ b/src/compiler/performance.ts @@ -6,104 +6,90 @@ 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. + * Adds a performance measurement with the specified name. * - * @param counterName The name of the counter. + * @param measureName The name of the performance measurement. + * @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 increment(counterName: string) { - if (counters) { - counters[counterName] = (getProperty(counters, counterName) || 0) + 1; + 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); } } /** - * Gets the value of the counter with the specified name. + * Gets the number of times a marker was encountered. * - * @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. + * @param markName The name of the mark. */ - export function mark() { - return measures ? timestamp() : 0; + export function getCount(markName: string) { + return counts && counts[markName] || 0; } /** - * Adds a performance measurement with the specified name. + * Gets the total duration of all measurements with the supplied name. * - * @param measureName The name of the performance measurement. - * @param marker The timestamp of the starting mark. + * @param measureName The name of the measure whose durations should be accumulated. */ - export function measure(measureName: string, marker: number) { - if (measures) { - measures[measureName] = (getProperty(measures, measureName) || 0) + (timestamp() - marker); - } + export function getDuration(measureName: string) { + 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) { - return forEachKey(measures, key => cb(key, measures[key])); - } - - /** - * Gets the total duration of all measurements with the supplied name. - * - * @param measureName The name of the measure whose durations should be accumulated. - */ - export function getDuration(measureName: string) { - return measures && getProperty(measures, measureName) || 0; + 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 eb50548750df0..a632eed7fd2b9 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 2d8c36a3d0252..52e8975db76b8 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 112/197] 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 aaa1b0e53d5b8..1d3166924686a 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 113/197] 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 1b7b659e1a6b3..85e33c5e4a1dd 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 114/197] 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 b81dd26652b0e..0817954c35c5c 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 115/197] 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 c574118f91dc3..b67f73288560f 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 7e6b332447062..40ef6587e75f7 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 116/197] 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 0000000000000..dfa60a415f91a --- /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 0000000000000..4fbfc46060a80 --- /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 0000000000000..473bd349b5f91 --- /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 117/197] 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 0000000000000..e435b98050de6 --- /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 0000000000000..5808ed7682683 --- /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 0000000000000..3e152b0faf456 --- /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 0000000000000..4cf1ca174aa02 --- /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 0000000000000..94f41becdade8 --- /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 0000000000000..958a3cfd70daf --- /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 118/197] 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 eb50548750df0..5befec5ebc08a 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 0000000000000..b3be036b4ae85 --- /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 0000000000000..e641afb183b4b --- /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 0000000000000..83b0e91d6c7c1 --- /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 0000000000000..f57a974077e06 --- /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 0000000000000..a0bb15476b982 --- /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 119/197] 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 ac5f046c1ddbf..4d2cb7d46722a 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 120/197] 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 85e33c5e4a1dd..bf721a96bc37b 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 875a656202d11..0000000000000 --- 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 ab05d2a9979c1..0000000000000 --- 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 bf7de709ef28e..0000000000000 --- 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 121/197] 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 583ae5a1f3528..37f13dbf807fe 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 36db85061b00b..d2ea69b304dd7 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 0598c34bd7bff..8b32d5351eaf8 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 122/197] 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 d677b042250e6..2d17325c4d880 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 01aac82a47ad8..e4aaf330dc72f 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 = {}; - -function lintWatchFile(filename) { - fs.watch(filename, {persistent: true}, function(event) { - if (event !== "change") { - return; - } + var workerCount = (process.env.workerCount && +process.env.workerCount) || os.cpus().length; - 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 0000000000000..a9aec06c2dfa0 --- /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 123/197] Fix the style fix (#10344) --- Gulpfile.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gulpfile.ts b/Gulpfile.ts index 2d17325c4d880..2718669b1f7b3 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 124/197] 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 ff5a8b77cfd98..b5ab2306989f3 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 f1fff6737a376..b2b6b9442ac4a 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 cbf767a5410fd..aca7d745730cd 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 a632eed7fd2b9..767fcb488bbf9 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 52e8975db76b8..4046867faf108 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 125/197] 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 ab76950ec4aaa..6f5d2dce1218a 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 126/197] 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 6f5d2dce1218a..6e51e418ee433 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 127/197] Migrated more MapLikes to Maps --- src/compiler/checker.ts | 24 +- src/compiler/commandLineParser.ts | 33 ++- src/compiler/core.ts | 251 +++++++++++++----- 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, 301 insertions(+), 234 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b2b6b9442ac4a..6a7cc10862556 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 eaf17f00dd065..44befb3943d6f 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 168a201c388a6..7bb5dbef2c52f 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,80 +352,190 @@ 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 true; + return false; } - export function clone(object: T): T { - const result: any = {}; - for (const id in object) { - result[id] = (object)[id]; + /** + * 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]; } - 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]; - } + /** + * 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]; } - 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; + /** + * 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; } - export function forEachKey(map: MapLike, callback: (key: string) => U): U { - let result: U; - for (const id in map) { - if (result = callback(id)) break; + /** + * 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; } - export function lookUp(map: MapLike, key: string): T { - return hasProperty(map, key) ? map[key] : undefined; + /** + * 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; } - export function copyMap(source: MapLike, target: MapLike): void { - for (const p in source) { - target[p] = source[p]; + /** + * 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; } /** @@ -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(); + 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; + } - forEach(array, value => { - result[makeKey(value)] = value; - }); + 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; } - /** - * 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 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 56ff206ab0176..3642ee7f10a50 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 1d62a4a4f4660..47495cc69bbae 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 767fcb488bbf9..0bf67b0ddb0ad 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 3b588edd101ca..73cf8e9c4e20c 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 fcda8f0ce1a57..779e479cc43d2 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 e7e076a9eae0c..345ce19f6aebc 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 5ae33303d5572..1f7c48d21bcfd 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 4f95f19e14441..b03d8788165b6 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 a1e010b97dfa5..61ab38b2d8f20 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 76015667232a6..9d8b176a6a8d7 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 74b9e303cf7e7..1e76a636c9a44 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 3547ac52602de..88177e91fad7b 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 a14c42f471e70..9ba243d97048c 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 05de0061d3978..bac33a6bdfbdd 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 e9afb6925b53f..ccded188e618e 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 ebd6936ea8543..dda9ef485d2a2 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 850e29030d801..0c1f5929fbfc2 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 45c4b284ae744..ff57dd9cf7ab4 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 50378aa64b1a1..211e55b23ba5e 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 a46e58f8c296c..f058eab3dd658 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 128/197] 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 980f610ca7d11..15fbb1b77c5ee 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 129/197] 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 1b7b659e1a6b3..8a32e23e1ff34 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 d144c984a3fc2..cca94c0adc1a6 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 130/197] 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 0000000000000..95ff60d71f809 --- /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 0000000000000..a5faf5b125354 --- /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 0000000000000..97996789124f6 --- /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 131/197] 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 6a7cc10862556..c309b9e36c0a7 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 44befb3943d6f..35140855d81d3 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 7bb5dbef2c52f..d10d76b34928d 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 47495cc69bbae..5c834ad0ac2ef 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 134dc489b2ae8..c1431ca23ed50 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 73cf8e9c4e20c..8b445bcb62653 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 d144c984a3fc2..83d299583d20c 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 779e479cc43d2..0a1f43203ce80 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 fe8986984d39b..66396293dc284 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 345ce19f6aebc..ba1994c8141ae 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 1f7c48d21bcfd..e3bcb0018633d 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 b03d8788165b6..b37a6a9089970 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 be3030a6dd68f..76f042834bb99 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 61ab38b2d8f20..2885b310605a0 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 4b5ef9f24f15e..ff0151c40a5e5 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 9d8b176a6a8d7..9f2b0c343c229 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 e8e06da7badc0..cf7486acb40da 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 1e76a636c9a44..9887573436edf 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 13b0e6d6ce4ac..9383a54bd48f7 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 bac33a6bdfbdd..4f0e06244d26f 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 cff3f591f8adf..b4f67ca056ddb 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 0c1f5929fbfc2..87093645e2a94 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 132/197] 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 984cedbf5add2..25a35c307f4c0 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 0000000000000..81d42e49059f6 --- /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 0000000000000..d920179458cc0 --- /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 0000000000000..1e4418e7fba37 --- /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 133/197] 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 8a32e23e1ff34..685b32f796fd5 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 cca94c0adc1a6..8e9953ee64d3f 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 134/197] 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 35140855d81d3..616bf5e70c05a 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 d10d76b34928d..40341929c02f7 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 ba1994c8141ae..f9fdd5a115935 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 b37a6a9089970..9412443278002 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 ff0151c40a5e5..09febd2300a11 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 9f2b0c343c229..60687d34c55af 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 9887573436edf..49f033b9defcb 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 9ba243d97048c..db9bdd5043bf6 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 87093645e2a94..1153e2e9e3d20 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 135/197] 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 c309b9e36c0a7..73ae7fc750daa 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 136/197] 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 496df578c1911..e4cbe323c681a 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 0000000000000..6f4a0ef9d6df7 --- /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 0000000000000..255b52e16c989 --- /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 0000000000000..d621cf460bcb1 --- /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 137/197] Fix link to blog --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fca2890bc7783..d16bc363b269e 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 138/197] 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 850e29030d801..5876da74f153e 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 139/197] 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 aea4d5f872e03..80eb0b3f46d44 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 140/197] 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 c1eead94f1456..c6a3698ea16a3 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 767fcb488bbf9..3f698e827583c 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 a3729bc3a9953..c23b49c61de89 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 141/197] 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 004f41026670f..1c491aeeaac9f 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 142/197] 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 ee82a88d7a57a..5aaf6f5018399 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 143/197] 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 5aaf6f5018399..d8017d601adb0 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 144/197] 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 3d7fb7eb7e1dd..24fe429c6e38a 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 145/197] 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 80eb0b3f46d44..1dd44577d7f11 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 146/197] 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 0000000000000..ab1148413ef30 --- /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 0000000000000..74426576d5a2c --- /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 0000000000000..9611fba2fbf30 --- /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 0000000000000..90d7912dec532 --- /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 147/197] 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 1aeeb1f2c1764..17cfdf8976f86 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 0233180873dbc..176df15985f3a 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 34a477b706a99..1db5b30dd0b30 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 060d35be21af8..5cdce6b99fe18 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 8a0be7427ed4c..ad16785f28830 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 24c4fb87bafb1..863ecbce628b7 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 cdc7fb321d46f..5cc26e3ad43a3 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 371b5f5cafc17..25fb2f28a6063 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 f25e66ecfb8b9..f06f06ee7a1c5 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 148/197] 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 992839a8661f7..441f6aef4f935 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 51dc2b498f19d..6406455d71328 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 1c491aeeaac9f..e732fe218726e 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 8b445bcb62653..62b58609086e6 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 0bbbd99bdd5d1..7594d2c3fb8af 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 3b9025c27c362..a21faf9dd0745 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 0000000000000..059f07e01152b --- /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 0000000000000..ea891967cb510 --- /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 0000000000000..abe135b4f1687 --- /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 0000000000000..e28b66c8c2bf9 --- /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 0000000000000..5273b3cb7c8ed --- /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 0000000000000..fa9cb6cad8411 --- /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 0000000000000..ea891967cb510 --- /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 0000000000000..3ff8208d9d64c --- /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 0000000000000..b1740ac4c12c3 --- /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 149/197] 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 01f75dc12da14..62a252b6d8d48 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 b05451340d14e..f78346fd5cd2b 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 0bbbd99bdd5d1..1ada1e547f2c6 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 0000000000000..e57b7091a3bea --- /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 0000000000000..a57636ed34402 --- /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 0000000000000..fe57cc298e2cb --- /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 0000000000000..c095bc1c920d4 --- /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 150/197] 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 f78346fd5cd2b..ea69a0270c100 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 d1ca42f38612e..a9987ef3176e6 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 151/197] 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 15220438d2330..292a77a309fb6 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 152/197] 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 0000000000000..c00462c8255c9 --- /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 153/197] 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 ea85caf1332ae..9ef14315cb6e9 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 154/197] 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 148d3af427ca2..c4b42f942268d 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 155/197] 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 0000000000000..88f0fbb4e20f9 --- /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 0000000000000..7ac99d53a0898 --- /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 0000000000000..8dff23f1c8916 --- /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 0000000000000..283fa3996a75a --- /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 0000000000000..a765e95bdf39a --- /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 0000000000000..90b239e7bb6ed --- /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 0000000000000..180a58a22d49c --- /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 0000000000000..6a07837334a21 --- /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 156/197] 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 9ed817eae499e..db8c30ddcc938 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 157/197] 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 d2ea69b304dd7..808f8a538df64 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 8b32d5351eaf8..faf210e461fd8 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 158/197] 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 68753fdb63867..361e293011b54 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 159/197] 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 1266ffa5d3783..7ea191e3c8340 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 160/197] 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 ea85caf1332ae..64b872d14a774 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 0000000000000..243975fde2d45 --- /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 161/197] 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 88f0fbb4e20f9..2b0a5dce526d7 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 7ac99d53a0898..d3d7519aa94ef 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 8dff23f1c8916..2fc9d3a475057 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 283fa3996a75a..b5d91431722e3 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 a765e95bdf39a..df7eda76661cf 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 90b239e7bb6ed..4594cbfd01541 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 180a58a22d49c..0db617c93e1ee 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 6a07837334a21..61f117f162938 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 162/197] 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 ff57dd9cf7ab4..9a581ed6be18d 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 163/197] 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 d954ea51170a2..8568cd5feed2f 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 0000000000000..1c4f02d6a756f --- /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 0000000000000..c3f7e410d0a7c --- /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 0000000000000..44fbe35978942 --- /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 164/197] 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 66396293dc284..88e81ffcf348d 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 b5fa53763cb9d..63731417f480a 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 db8c30ddcc938..3375b8e47e7dc 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 76f042834bb99..080c6edfa879f 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 7ea191e3c8340..17346016fbbd0 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 c44b2286b832d..66cf474824b14 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 059f07e01152b..cb995212a944a 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 547d10b9fbc02..2dd9c89a1cb51 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 165/197] Remove needless call to basename --- Jakefile.js | 317 +++++++++++++++++++++++++++------------------------- 1 file changed, 164 insertions(+), 153 deletions(-) diff --git a/Jakefile.js b/Jakefile.js index 441f6aef4f935..4c751945c8766 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"] }, - - // 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") } + // 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 + 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); +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) { - if (files[i].substr(files[i].length - deleteEnding.length) === deleteEnding) { - var filename = path.basename(files[i]); + var filename = files[i]; + if (filename.substr(filename.length - deleteEnding.length) === deleteEnding) { filename = filename.substr(0, filename.length - deleteEnding.length); - fs.unlink(path.join(refBaseline, filename)); + fs.unlinkSync(path.join(targetFolder, filename)); } else { - jake.cpR(files[i], refBaseline); + 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 166/197] 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 88e81ffcf348d..a64435b7ddcbe 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 3375b8e47e7dc..14cb3147c323d 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 17346016fbbd0..5720f9b541c46 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 167/197] 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 64b872d14a774..94b69113da799 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 168/197] 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 94b69113da799..c59551457ba8d 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 169/197] 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 14cb3147c323d..9e27f500b80f9 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 5720f9b541c46..d56e8d6d35f63 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 170/197] 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 8568cd5feed2f..bc8af9e63228a 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 6210a20afa64c..fb8bc21ec957c 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 e1a53ef6a73cc..c19cfa443603e 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 5fe5c92a5b3e8..b7195557e8d35 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 8e6c7eccf81f8..eb0c6dc51b763 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 4b06603a9bf02..f5c92a4756999 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 c03eab60c7477..bfbf56eec5a80 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 0c8c4a5f5aca0..e68ec76f68f6d 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 85639cdeeaf18..5cae465476025 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 6ec8afad12072..80268f4b06a5e 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 4e09eb6eacbf6..d20bdaaba3257 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 0000000000000..c471e6b0e7b70 --- /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 0000000000000..074b39fa08d2e --- /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 da8b75f9bc577..24f02486d00ad 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 0000000000000..6cd51251b065b --- /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 171/197] 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 bc8af9e63228a..e279b07bb1fd5 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 172/197] 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 e279b07bb1fd5..279517bc1ec28 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 eb63e5798d5e2..4a27ea24c598f 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 6cd51251b065b..a34dd5cdd3b1d 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 173/197] 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 279517bc1ec28..7ff5b5b19edde 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 c471e6b0e7b70..255e0414fd92a 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 074b39fa08d2e..db47c72fa10f0 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 174/197] 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 0000000000000..2c5e53ea224cc --- /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 175/197] 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 7ff5b5b19edde..3a6608c71d171 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 176/197] 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 d2938f6e983dc..ee657f64a65c3 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 c61a1a8b8a600..483348f3f750e 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 177/197] 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 0000000000000..d8ac2f12d8b32 --- /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 178/197] 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 0a1f43203ce80..f1573f9b58af2 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 c4b42f942268d..22f5a74eb5010 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 179/197] 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 db9bdd5043bf6..0d84fa54a4a01 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 9383a54bd48f7..00468330ffbcb 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 180/197] 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 49f033b9defcb..d4ed84191b41f 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 181/197] Use TS parser to tolerate more errors in tsconfig.json --- src/harness/unittests/tsconfigParsing.ts | 20 +++ src/server/editorServices.ts | 165 ++++++++++++----------- src/services/utilities.ts | 20 +++ 3 files changed, 128 insertions(+), 77 deletions(-) diff --git a/src/harness/unittests/tsconfigParsing.ts b/src/harness/unittests/tsconfigParsing.ts index 557379dff3b7d..2c9bcdb84233e 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 0d84fa54a4a01..255ad8547ea48 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); - - // 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 }; - } + 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 { project, errors }; } + } - 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)); - } + 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)); + } + project.finishGraph(); + project.projectFileWatcher = this.host.watchFile(configFilename, _ => this.watchedProjectConfigFileChanged(project)); - const configDirectoryPath = ts.getDirectoryPath(configFilename); + 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 - ); + 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 - ); - } + 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 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 7f41cdbb3a42e..858f889bc6c91 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 182/197] 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 8568cd5feed2f..d87dbd1696c2a 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 7594d2c3fb8af..f5d71d0935b2e 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 183/197] 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 d87dbd1696c2a..9efaf72c03a03 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 f5d71d0935b2e..656f155784a47 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 184/197] 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 d60f0b7d03c47..a6a685069d6b0 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 8e6c7eccf81f8..f221fd96fab63 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 6d5549c6bf2ae..2f9eeedf9efd5 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 e0580ca237961..a488d8c764829 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 42d617887e015..447ccec5ddf91 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 517eaafe4abbe..3ed9e241c3992 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 3348effe8225d..e22b23122d34d 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 878bb38abf0fc..d76a4efe5f15a 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 ec0f400631fe4..deb039aa67db5 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 6f5801b7898c8..8eff6ad0febee 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 1a6558377434e..bdbda6f9a96eb 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 0194dc61311ee..617c4f7159b54 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 b34d0317d5768..29e5d0d2a3f98 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 78fc1cbddf1f7..0780c626baf8b 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 312603d2c2e62..30ec4037c88b6 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 75f413eb170f4..3b55476e4b1d5 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 185/197] 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 d56e8d6d35f63..ba1ab71ec19d3 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 186/197] 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 9efaf72c03a03..3abf271a57b28 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 187/197] 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 0000000000000..19dc9818d6950 --- /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 188/197] 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 3abf271a57b28..418f5e40a892c 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 189/197] 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 afd2ddad9e157..f099d8581191a 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 0000000000000..c11bc13c6138b --- /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 0000000000000..144548c645265 --- /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 0000000000000..4bca8456f5846 --- /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 0000000000000..d922c7b1dfa53 --- /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 0000000000000..234090aebad52 --- /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 190/197] 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 17cfdf8976f86..81a9c639feb52 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 191/197] 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 1b90256283264..295a7ce03d9d7 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 d6a2cf88ed816..f373d33955347 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 192/197] 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 193deded4c5fa..aab284b0cebdc 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 0000000000000..79f5d2b8fa1bc --- /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 0000000000000..e42392e24aafb --- /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 0000000000000..a9bdedf3efd21 --- /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 0000000000000..4e313b33eac78 --- /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 193/197] 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 81a9c639feb52..2f2c3002299b5 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 194/197] 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 357a15507a457..d0f7918dc8900 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 f373d33955347..b5ba89887a26c 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 0000000000000..46065bdada925 --- /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 0000000000000..81edd4b981b1b --- /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 0000000000000..2a4b7354d3ebf --- /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 0000000000000..c6f5f1142c085 --- /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 0000000000000..ed7d7e4ed02e9 --- /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 0000000000000..a5c3740b677f1 --- /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 0000000000000..3c341018ed16c --- /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 0000000000000..574ea4d433a7a --- /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 0000000000000..b438f063add31 --- /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 0000000000000..5518f84983f78 --- /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 0000000000000..077e264c450e4 --- /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 95817b7f0e604..26e9812bf431e 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 0000000000000..09cf0a87a5adc --- /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 0000000000000..5ccf1a1c35a87 --- /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 0000000000000..24683765d1996 --- /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 0000000000000..b595ab5e748ad --- /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 590755b8a0c2396969b0a7de17d6fe745a220694 Mon Sep 17 00:00:00 2001 From: Yuichi Nukiyama Date: Tue, 23 Aug 2016 21:54:01 +0900 Subject: [PATCH 195/197] 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 3a6608c71d171..a6cb6fbde44b1 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 fb8bc21ec957c..b8978b32571c2 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 c19cfa443603e..01fffeb382494 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 b7195557e8d35..c7610a80f2f37 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 eb0c6dc51b763..7ced9e3933e50 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 f5c92a4756999..555b645cd7dc1 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 bfbf56eec5a80..6c7ebbe5ee83f 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 e68ec76f68f6d..1a70ef342d2b8 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 5cae465476025..3297501d61208 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 80268f4b06a5e..7e7af4d1b9daf 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 d20bdaaba3257..0fc3284bf0046 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 255e0414fd92a..03186a6d7e340 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 24f02486d00ad..49ef8c4e3cf0e 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 4a27ea24c598f..205a2a999d0d4 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 371602c517b4bd0b130e23f0d2016ed55ee8f484 Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Wed, 24 Aug 2016 18:42:57 -0700 Subject: [PATCH 196/197] Fix broken build from merging with master --- src/compiler/checker.ts | 9 ++++---- src/compiler/comments.ts | 23 ++++++++----------- src/compiler/core.ts | 11 +++++++++ src/compiler/emitter.ts | 14 ++++------- src/compiler/sourcemap.ts | 20 ++++++++-------- src/compiler/sys.ts | 2 +- src/compiler/transformer.ts | 6 ++--- src/compiler/transformers/es6.ts | 6 ++--- src/compiler/transformers/generators.ts | 8 +++---- src/compiler/transformers/jsx.ts | 4 ++-- src/compiler/transformers/module/module.ts | 8 +++---- src/compiler/transformers/module/system.ts | 2 +- src/compiler/transformers/ts.ts | 2 +- src/compiler/utilities.ts | 8 +++---- src/compiler/visitor.ts | 8 +++---- src/harness/compilerRunner.ts | 2 +- src/harness/harness.ts | 2 +- src/harness/unittests/jsDocParsing.ts | 4 ++-- ...eExpressions.parsesCorrectly.keyword2.json | 11 ++------- ...eExpressions.parsesCorrectly.keyword3.json | 11 ++------- tests/baselines/reference/castOfYield.js | 2 +- .../decoratorOnClassConstructor2.errors.txt | 21 +++++++++++++++++ ...sDeclarationWithPropertyAssignmentInES6.js | 4 ++-- .../baselines/reference/tsxDefaultImports.js | 2 +- 24 files changed, 101 insertions(+), 89 deletions(-) create mode 100644 tests/baselines/reference/decoratorOnClassConstructor2.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 31185bf8aad6c..81f22e2f1921d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1270,7 +1270,7 @@ namespace ts { } // Resolves a qualified name and any involved aliases - function resolveEntityName(name: EntityNameOrEntityNameExpression, meaning: SymbolFlags, ignoreErrors?: boolean, dontResolveAlias?: boolean): Symbol | undefined { + function resolveEntityName(name: EntityNameOrEntityNameExpression, meaning: SymbolFlags, ignoreErrors?: boolean, dontResolveAlias?: boolean, location?: Node): Symbol | undefined { if (nodeIsMissing(name)) { return undefined; } @@ -1368,10 +1368,10 @@ namespace ts { 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)); + error(errorNode, diag, tsExtension, removeExtension(moduleName, tsExtension)); } else { - error(moduleReferenceLiteral, moduleNotFoundError, moduleName); + error(errorNode, moduleNotFoundError, moduleName); } } return undefined; @@ -9169,10 +9169,11 @@ namespace ts { return thisType; } } + if (isClassLike(container.parent)) { const symbol = getSymbolOfNode(container.parent); const type = hasModifier(container, ModifierFlags.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)) { diff --git a/src/compiler/comments.ts b/src/compiler/comments.ts index f60260a19a128..1bca13a4bdbc6 100644 --- a/src/compiler/comments.ts +++ b/src/compiler/comments.ts @@ -53,9 +53,8 @@ namespace ts { } } else { - let commentStart: number; if (extendedDiagnostics) { - commentStart = performance.mark(); + performance.mark("preEmitNodeWithComment"); } const isEmittedNode = node.kind !== SyntaxKind.NotEmittedStatement; @@ -88,7 +87,7 @@ namespace ts { } if (extendedDiagnostics) { - performance.measure("commentTime", commentStart); + performance.measure("commentTime", "preEmitNodeWithComment"); } if (emitFlags & NodeEmitFlags.NoNestedComments) { @@ -99,7 +98,7 @@ namespace ts { } if (extendedDiagnostics) { - commentStart = performance.mark(); + performance.mark("beginEmitNodeWithComment"); } // Restore previous container state. @@ -114,16 +113,15 @@ namespace ts { } if (extendedDiagnostics) { - performance.measure("commentTime", commentStart); + performance.measure("commentTime", "beginEmitNodeWithComment"); } } } } function emitBodyWithDetachedComments(node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void) { - let commentStart: number; if (extendedDiagnostics) { - commentStart = performance.mark(); + performance.mark("preEmitBodyWithDetachedComments"); } const { pos, end } = detachedRange; @@ -136,7 +134,7 @@ namespace ts { } if (extendedDiagnostics) { - performance.measure("commentTime", commentStart); + performance.measure("commentTime", "preEmitBodyWithDetachedComments"); } if (emitFlags & NodeEmitFlags.NoNestedComments) { @@ -147,7 +145,7 @@ namespace ts { } if (extendedDiagnostics) { - commentStart = performance.mark(); + performance.mark("beginEmitBodyWithDetachedCommetns"); } if (!skipTrailingComments) { @@ -155,7 +153,7 @@ namespace ts { } if (extendedDiagnostics) { - performance.measure("commentTime", commentStart); + performance.measure("commentTime", "beginEmitBodyWithDetachedCommetns"); } } @@ -227,15 +225,14 @@ namespace ts { return; } - let commentStart: number; if (extendedDiagnostics) { - commentStart = performance.mark(); + performance.mark("beforeEmitTrailingCommentsOfPosition"); } forEachTrailingCommentToEmit(pos, emitTrailingCommentOfPosition); if (extendedDiagnostics) { - performance.measure("commentTime", commentStart); + performance.measure("commentTime", "beforeEmitTrailingCommentsOfPosition"); } } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index a3186925548ed..e74ee64515df9 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -128,6 +128,7 @@ namespace ts { } return true; + } /** Works like Array.prototype.find, returning `undefined` if no element satisfying the predicate is found. */ export function find(array: T[], predicate: (element: T, index: number) => boolean): T | undefined { @@ -199,6 +200,7 @@ namespace ts { } export function filter(array: T[], f: (x: T) => x is U): U[]; + export function filter(array: T[], f: (x: T) => boolean): T[] export function filter(array: T[], f: (x: T) => boolean): T[] { if (array) { const len = array.length; @@ -708,6 +710,15 @@ namespace ts { return result; } + export function isEmpty(map: Map) { + for (const id in map) { + if (hasProperty(map, id)) { + return false; + } + } + return true; + } + export function cloneMap(map: Map) { const clone = createMap(); copyProperties(map, clone); diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 216cd3c5b7977..37641aa507981 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -182,8 +182,7 @@ const _super = (function (geti, seti) { let isOwnFileEmit: boolean; let emitSkipped = false; - performance.emit("beforeTransform"); - const transformStart = performance.mark(); + performance.mark("beforeTransform"); // Transform the source files const transformed = transformFiles( @@ -192,8 +191,7 @@ const _super = (function (geti, seti) { getSourceFilesToEmit(host, targetSourceFile), transformers); - performance.measure("transformTime", transformStart); - performance.emit("afterTransform"); + performance.measure("transformTime", "beforeTransform"); // Extract helpers from the result const { @@ -204,8 +202,7 @@ const _super = (function (geti, seti) { onEmitNode } = transformed; - performance.emit("beforePrint"); - const printStart = performance.mark(); + performance.mark("beforePrint"); // Emit each output file forEachTransformedEmitFile(host, transformed.getSourceFiles(), emitFile); @@ -213,8 +210,7 @@ const _super = (function (geti, seti) { // Clean up after transformation transformed.dispose(); - performance.measure("printTime", printStart); - performance.emit("afterPrint"); + performance.measure("printTime", "beforePrint"); return { emitSkipped, @@ -251,7 +247,7 @@ const _super = (function (geti, seti) { sourceMap.initialize(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit); nodeIdToGeneratedName = []; autoGeneratedIdToGeneratedName = []; - generatedNameSet = {}; + generatedNameSet = createMap();; isOwnFileEmit = !isBundledEmit; // Emit helpers from all the files diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index 49657101b648b..eb369be39d777 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -788,30 +788,30 @@ namespace ts { getSourceMapData, setSourceFile, emitPos(pos: number): void { - const sourcemapStart = performance.mark(); + performance.mark("sourcemapStart"); emitPos(pos); - performance.measure("sourceMapTime", sourcemapStart); + performance.measure("sourceMapTime", "sourcemapStart"); }, emitStart(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean, ignoreChildrenCallback?: (node: Node) => boolean, getTextRangeCallback?: (node: Node) => TextRange): void { - const sourcemapStart = performance.mark(); + performance.mark("emitSourcemap:emitStart"); emitStart(range, contextNode, ignoreNodeCallback, ignoreChildrenCallback, getTextRangeCallback); - performance.measure("sourceMapTime", sourcemapStart); + performance.measure("sourceMapTime", "emitSourcemap:emitStart"); }, emitEnd(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean, ignoreChildrenCallback?: (node: Node) => boolean, getTextRangeCallback?: (node: Node) => TextRange): void { - const sourcemapStart = performance.mark(); + performance.mark("emitSourcemap:emitEnd"); emitEnd(range, contextNode, ignoreNodeCallback, ignoreChildrenCallback, getTextRangeCallback); - performance.measure("sourceMapTime", sourcemapStart); + performance.measure("sourceMapTime", "emitSourcemap:emitEnd"); }, emitTokenStart(token: SyntaxKind, tokenStartPos: number, contextNode?: Node, ignoreTokenCallback?: (node: Node) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind) => TextRange): number { - const sourcemapStart = performance.mark(); + performance.mark("emitSourcemap:emitTokenStart"); tokenStartPos = emitTokenStart(token, tokenStartPos, contextNode, ignoreTokenCallback, getTokenTextRangeCallback); - performance.measure("sourceMapTime", sourcemapStart); + performance.measure("sourceMapTime", "emitSourcemap:emitTokenStart"); return tokenStartPos; }, emitTokenEnd(token: SyntaxKind, tokenEndPos: number, contextNode?: Node, ignoreTokenCallback?: (node: Node) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind) => TextRange): number { - const sourcemapStart = performance.mark(); + performance.mark("emitSourcemap:emitTokenEnd"); tokenEndPos = emitTokenEnd(token, tokenEndPos, contextNode, ignoreTokenCallback, getTokenTextRangeCallback); - performance.measure("sourceMapTime", sourcemapStart); + performance.measure("sourceMapTime", "emitSourcemap:emitTokenEnd"); return tokenEndPos; }, changeEmitSourcePos, diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 1abce27bbf15e..b4b776c498c3f 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -438,7 +438,7 @@ namespace ts { } function getDirectories(path: string): string[] { - return filter(_fs.readdirSync(path), p => fileSystemEntryExists(combinePaths(path, p), FileSystemEntryKind.Directory)); + return filter(_fs.readdirSync(path), dir => fileSystemEntryExists(combinePaths(path, dir), FileSystemEntryKind.Directory)); } const nodeSystem: System = { diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index 84cd00ab79afb..6242a2f23474c 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -10,14 +10,14 @@ /* @internal */ namespace ts { - const moduleTransformerMap: Map = { + const moduleTransformerMap = createMap({ [ModuleKind.ES6]: transformES6Module, [ModuleKind.System]: transformSystemModule, [ModuleKind.AMD]: transformModule, [ModuleKind.CommonJS]: transformModule, [ModuleKind.UMD]: transformModule, [ModuleKind.None]: transformModule, - }; + }); const enum SyntaxKindFeatureFlags { Substitution = 1 << 0, @@ -206,7 +206,7 @@ namespace ts { const transformId = nextTransformId; nextTransformId++; - const tokenSourceMapRanges: Map = { }; + const tokenSourceMapRanges = createMap(); const lexicalEnvironmentVariableDeclarationsStack: VariableDeclaration[][] = []; const lexicalEnvironmentFunctionDeclarationsStack: FunctionDeclaration[][] = []; const enabledSyntaxKindFeatures = new Array(SyntaxKind.Count); diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index b3f96db701873..4e994c1acb0fc 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -1663,7 +1663,7 @@ namespace ts { function visitLabeledStatement(node: LabeledStatement): VisitResult { if (convertedLoopState) { if (!convertedLoopState.labels) { - convertedLoopState.labels = {}; + convertedLoopState.labels = createMap(); } convertedLoopState.labels[node.label.text] = node.label.text; } @@ -2251,13 +2251,13 @@ namespace ts { 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; } diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index 3c4ad65c0ce30..9a4b567cb8017 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -216,13 +216,13 @@ namespace ts { Endfinally = 7, } - const instructionNames: Map = { + const instructionNames = createMap({ [Instruction.Return]: "return", [Instruction.Break]: "break", [Instruction.Yield]: "yield", [Instruction.YieldStar]: "yield*", [Instruction.Endfinally]: "endfinally", - }; + }); export function transformGenerators(context: TransformationContext) { const { @@ -2060,8 +2060,8 @@ namespace ts { const name = declareLocal(text); if (!renamedCatchVariables) { - renamedCatchVariables = {}; - renamedCatchVariableDeclarations = {}; + renamedCatchVariables = createMap(); + renamedCatchVariableDeclarations = createMap(); context.enableSubstitution(SyntaxKind.Identifier); } diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 90503d5a5f689..4bc523a262f94 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -259,7 +259,7 @@ namespace ts { } function createEntitiesMap(): Map { - return { + return createMap({ "quot": 0x0022, "amp": 0x0026, "apos": 0x0027, @@ -513,6 +513,6 @@ namespace ts { "clubs": 0x2663, "hearts": 0x2665, "diams": 0x2666 - }; + }); } } \ No newline at end of file diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index bf908d4dbc5f9..144420b7ae958 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -4,12 +4,12 @@ /*@internal*/ namespace ts { export function transformModule(context: TransformationContext) { - const transformModuleDelegates: Map<(node: SourceFile) => SourceFile> = { + const transformModuleDelegates = createMap<(node: SourceFile) => SourceFile>({ [ModuleKind.None]: transformCommonJSModule, [ModuleKind.CommonJS]: transformCommonJSModule, [ModuleKind.AMD]: transformAMDModule, [ModuleKind.UMD]: transformUMDModule, - }; + }); const { startLexicalEnvironment, @@ -43,7 +43,7 @@ namespace ts { let bindingNameExportSpecifiersMap: Map; // Subset of exportSpecifiers that is a binding-name. // This is to reduce amount of memory we have to keep around even after we done with module-transformer - const bindingNameExportSpecifiersForFileMap: Map> = {}; + const bindingNameExportSpecifiersForFileMap = createMap>(); let hasExportStarsToExportValues: boolean; return transformSourceFile; @@ -667,7 +667,7 @@ namespace ts { if (!exportEquals && exportSpecifiers && hasProperty(exportSpecifiers, name.text)) { const sourceFileId = getOriginalNodeId(currentSourceFile); if (!bindingNameExportSpecifiersForFileMap[sourceFileId]) { - bindingNameExportSpecifiersForFileMap[sourceFileId] = {}; + bindingNameExportSpecifiersForFileMap[sourceFileId] = createMap(); } bindingNameExportSpecifiersForFileMap[sourceFileId][name.text] = exportSpecifiers[name.text]; addExportMemberAssignments(resultStatements, name); diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 1f7897b760c1c..d303dd580ef75 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -1324,7 +1324,7 @@ namespace ts { } function collectDependencyGroups(externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]) { - const groupIndices: Map = {}; + const groupIndices = createMap(); const dependencyGroups: DependencyGroup[] = []; for (let i = 0; i < externalImports.length; i++) { const externalImport = externalImports[i]; diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index f66e4d6d9980f..6390c8748859b 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -3152,7 +3152,7 @@ namespace ts { context.enableSubstitution(SyntaxKind.Identifier); // Keep track of class aliases. - classAliases = {}; + classAliases = createMap(); } } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index a19b834b22df5..030a3a33871ad 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2177,7 +2177,7 @@ namespace ts { export function createDiagnosticCollection(): DiagnosticCollection { let nonFileDiagnostics: Diagnostic[] = []; - const fileDiagnostics: Map = {}; + const fileDiagnostics = createMap(); let diagnosticsModified = false; let modificationCount = 0; @@ -3317,7 +3317,7 @@ namespace ts { return false; } - const syntaxKindCache: Map = {}; + const syntaxKindCache = createMap(); export function formatSyntaxKind(kind: SyntaxKind): string { const syntaxKindEnum = (ts).SyntaxKind; @@ -3465,7 +3465,7 @@ namespace ts { export function collectExternalModuleInfo(sourceFile: SourceFile, resolver: EmitResolver) { const externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = []; - const exportSpecifiers: Map = {}; + const exportSpecifiers = createMap(); let exportEquals: ExportAssignment = undefined; let hasExportStarsToExportValues = false; for (const node of sourceFile.statements) { @@ -3506,7 +3506,7 @@ namespace ts { // 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; diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 8e6e2ec27652e..648fd235c44d1 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -46,7 +46,7 @@ namespace ts { * supplant the existing `forEachChild` implementation if performance is not * significantly impacted. */ - const nodeEdgeTraversalMap: Map = { + const nodeEdgeTraversalMap = createMap({ [SyntaxKind.QualifiedName]: [ { name: "left", test: isEntityName }, { name: "right", test: isIdentifier } @@ -93,7 +93,7 @@ namespace ts { { name: "name", test: isPropertyName }, { name: "initializer", test: isExpression, optional: true, parenthesize: parenthesizeExpressionForList } ] - }; + }); function reduceNode(node: Node, f: (memo: T, node: Node) => T, initial: T) { return node ? f(initial, node) : initial; @@ -523,7 +523,7 @@ namespace ts { const edgeTraversalPath = nodeEdgeTraversalMap[kind]; if (edgeTraversalPath) { for (const edge of edgeTraversalPath) { - const value = (>node)[edge.name]; + const value = (>node)[edge.name]; if (value !== undefined) { result = isArray(value) ? reduceLeft(>value, f, result) @@ -1140,7 +1140,7 @@ namespace ts { visitNode((node).expression, visitor, isExpression)); default: - let updated: Node & Map; + let updated: Node & MapLike; const edgeTraversalPath = nodeEdgeTraversalMap[kind]; if (edgeTraversalPath) { for (const edge of edgeTraversalPath) { diff --git a/src/harness/compilerRunner.ts b/src/harness/compilerRunner.ts index a64435b7ddcbe..63e01f0f81483 100644 --- a/src/harness/compilerRunner.ts +++ b/src/harness/compilerRunner.ts @@ -154,7 +154,7 @@ class CompilerBaselineRunner extends RunnerBase { if (options.sourceMap || options.inlineSourceMap) { Harness.Baseline.runBaseline(justName.replace(/\.tsx?$/, ".sourcemap.txt"), () => { const record = result.getSourceMapRecord(); - if (options.noEmitOnError && result.errors.length !== 0 && record === undefined) { + 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. /* tslint:disable:no-null-keyword */ return null; diff --git a/src/harness/harness.ts b/src/harness/harness.ts index fbbcb9eab5709..fb0634676982d 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1542,7 +1542,7 @@ namespace Harness { } Harness.Baseline.runBaseline(baselinePath.replace(/\.tsx?/, ".js.map"), () => { - if (options.noEmitOnError && result.errors.length !== 0 && result.sourceMaps.length === 0) { + 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 */ diff --git a/src/harness/unittests/jsDocParsing.ts b/src/harness/unittests/jsDocParsing.ts index 936ea77a1dcd6..c8f6d60e4ad8d 100644 --- a/src/harness/unittests/jsDocParsing.ts +++ b/src/harness/unittests/jsDocParsing.ts @@ -9,7 +9,7 @@ namespace ts { const typeAndDiagnostics = ts.parseJSDocTypeExpressionForTests(content); assert.isTrue(typeAndDiagnostics && typeAndDiagnostics.diagnostics.length === 0); - Harness.Baseline.runBaseline("parseCorrectly", "JSDocParsing/TypeExpressions.parsesCorrectly." + name + ".json", + Harness.Baseline.runBaseline("JSDocParsing/TypeExpressions.parsesCorrectly." + name + ".json", () => Utils.sourceFileToJSON(typeAndDiagnostics.jsDocTypeExpression.type)); }); } @@ -99,7 +99,7 @@ namespace ts { Debug.fail("Comment has at least one diagnostic: " + comment.diagnostics[0].messageText); } - Harness.Baseline.runBaseline("parseCorrectly", "JSDocParsing/DocComments.parsesCorrectly." + name + ".json", + Harness.Baseline.runBaseline("JSDocParsing/DocComments.parsesCorrectly." + name + ".json", () => JSON.stringify(comment.jsDocComment, (k, v) => v && v.pos !== undefined ? JSON.parse(Utils.sourceFileToJSON(v)) : v, 4)); }); diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.keyword2.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.keyword2.json index 9197232584782..4148937b278f5 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.keyword2.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.keyword2.json @@ -1,12 +1,5 @@ { - "kind": "JSDocTypeReference", + "kind": "NullKeyword", "pos": 1, - "end": 5, - "name": { - "kind": "Identifier", - "pos": 1, - "end": 5, - "originalKeywordKind": "NullKeyword", - "text": "null" - } + "end": 5 } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.keyword3.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.keyword3.json index 408fb1c6f469d..c61912f9eb83b 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.keyword3.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.keyword3.json @@ -1,12 +1,5 @@ { - "kind": "JSDocTypeReference", + "kind": "UndefinedKeyword", "pos": 1, - "end": 10, - "name": { - "kind": "Identifier", - "pos": 1, - "end": 10, - "originalKeywordKind": "UndefinedKeyword", - "text": "undefined" - } + "end": 10 } \ No newline at end of file diff --git a/tests/baselines/reference/castOfYield.js b/tests/baselines/reference/castOfYield.js index 652cf28e9e1f0..633f780a6a144 100644 --- a/tests/baselines/reference/castOfYield.js +++ b/tests/baselines/reference/castOfYield.js @@ -7,7 +7,7 @@ function* f() { //// [castOfYield.js] -function f() { +function* f() { (yield 0); // Unlike await, yield is not allowed to appear in a simple unary expression. ; diff --git a/tests/baselines/reference/decoratorOnClassConstructor2.errors.txt b/tests/baselines/reference/decoratorOnClassConstructor2.errors.txt new file mode 100644 index 0000000000000..c4585b8301fea --- /dev/null +++ b/tests/baselines/reference/decoratorOnClassConstructor2.errors.txt @@ -0,0 +1,21 @@ +tests/cases/conformance/decorators/class/constructor/2.ts(1,20): error TS2691: An import path cannot end with a '.ts' extension. Consider importing './0' instead. +tests/cases/conformance/decorators/class/constructor/2.ts(2,19): error TS2691: An import path cannot end with a '.ts' extension. Consider importing './0' instead. + + +==== tests/cases/conformance/decorators/class/constructor/0.ts (0 errors) ==== + + export class base { } + export function foo(target: Object, propertyKey: string | symbol, parameterIndex: number) { } + +==== tests/cases/conformance/decorators/class/constructor/2.ts (2 errors) ==== + import {base} from "./0.ts" + ~~~~~~~~ +!!! error TS2691: An import path cannot end with a '.ts' extension. Consider importing './0' instead. + import {foo} from "./0.ts" + ~~~~~~~~ +!!! error TS2691: An import path cannot end with a '.ts' extension. Consider importing './0' instead. + export class C extends base{ + constructor(@foo prop: any) { + super(); + } + } \ No newline at end of file diff --git a/tests/baselines/reference/emitClassDeclarationWithPropertyAssignmentInES6.js b/tests/baselines/reference/emitClassDeclarationWithPropertyAssignmentInES6.js index 91e6c506c2b50..03fb45b806e3d 100644 --- a/tests/baselines/reference/emitClassDeclarationWithPropertyAssignmentInES6.js +++ b/tests/baselines/reference/emitClassDeclarationWithPropertyAssignmentInES6.js @@ -37,8 +37,8 @@ class D { } } class E extends D { - constructor() { - super(...arguments); + constructor(...args) { + super(...args); this.z = true; } } diff --git a/tests/baselines/reference/tsxDefaultImports.js b/tests/baselines/reference/tsxDefaultImports.js index 79f5d2b8fa1bc..e7e0a13860d83 100644 --- a/tests/baselines/reference/tsxDefaultImports.js +++ b/tests/baselines/reference/tsxDefaultImports.js @@ -23,11 +23,11 @@ var SomeEnum; var SomeClass = (function () { function SomeClass() { } - SomeClass.E = SomeEnum; return SomeClass; }()); exports.__esModule = true; exports["default"] = SomeClass; +SomeClass.E = SomeEnum; //// [b.js] "use strict"; var a_1 = require("./a"); From 422097e19428fc845b6aba2238467f2e288cf4dd Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Fri, 26 Aug 2016 15:15:45 -0700 Subject: [PATCH 197/197] Fix linting error --- 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 37641aa507981..680c446c0d53e 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -247,7 +247,7 @@ const _super = (function (geti, seti) { sourceMap.initialize(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit); nodeIdToGeneratedName = []; autoGeneratedIdToGeneratedName = []; - generatedNameSet = createMap();; + generatedNameSet = createMap(); isOwnFileEmit = !isBundledEmit; // Emit helpers from all the files