From ef5f3c90a457ba50484da59000c39a2797150240 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 18 Oct 2016 11:53:26 -0700 Subject: [PATCH 01/49] Normalize intersection and union types --- src/compiler/checker.ts | 15 +++++++++++++++ src/compiler/core.ts | 6 ++++++ 2 files changed, 21 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d3f15f2367b..879e2dec9df 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5603,6 +5603,11 @@ namespace ts { } } + // We normalize combinations of intersection and union types based on the distributive property of the '&' + // operator. Specifically, because X & (A | B) is equivalent to X & A | X & B, we can transform intersection + // types with union type constituents into equivalent union types with intersection type constituents and + // effectively ensure that union types are always at the top level in type representations. + // // We do not perform structural deduplication on intersection types. Intersection types are created only by the & // type operator and we can't reduce those because we want to support recursive intersection types. For example, // a type alias of the form "type List = T & { next: List }" cannot be reduced during its declaration. @@ -5612,6 +5617,16 @@ namespace ts { if (types.length === 0) { return emptyObjectType; } + for (let i = 0; i < types.length; i++) { + const type = types[i]; + if (type.flags & TypeFlags.Union) { + // We are attempting to construct a type of the form X & (A | B) & Y. Transform this into a type of + // the form X & A & Y | X & B & Y and recursively reduce until no union type constituents remain. + let unionType = types[i]; + return getUnionType(map((type).types, t => getIntersectionType(replaceElement(types, i, t))), + /*subtypeReduction*/ false, aliasSymbol, aliasTypeArguments); + } + } const typeSet = [] as TypeSet; addTypesToIntersection(typeSet, types); if (typeSet.containsAny) { diff --git a/src/compiler/core.ts b/src/compiler/core.ts index befa30b6006..b9a3becba72 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -532,6 +532,12 @@ namespace ts { : undefined; } + export function replaceElement(array: T[], index: number, value: T): T[] { + const result = array.slice(0); + result[index] = value; + return result; + } + /** * Performs a binary search, finding the index at which 'value' occurs in 'array'. * If no such index is found, returns the 2's-complement of first index at which From eb7d2cbe9bbc7fd8332488fa084c9293ca29e53a Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 18 Oct 2016 11:54:26 -0700 Subject: [PATCH 02/49] Accept new baselines --- ...rrorMessagesIntersectionTypes04.errors.txt | 6 +- .../intersectionAndUnionTypes.errors.txt | 74 ++++++++++--------- ...itchCaseWithIntersectionTypes01.errors.txt | 10 ++- .../reference/typeGuardsWithInstanceOf.types | 4 +- 4 files changed, 50 insertions(+), 44 deletions(-) diff --git a/tests/baselines/reference/errorMessagesIntersectionTypes04.errors.txt b/tests/baselines/reference/errorMessagesIntersectionTypes04.errors.txt index 7582c68ecec..d2e81844da1 100644 --- a/tests/baselines/reference/errorMessagesIntersectionTypes04.errors.txt +++ b/tests/baselines/reference/errorMessagesIntersectionTypes04.errors.txt @@ -1,7 +1,8 @@ tests/cases/compiler/errorMessagesIntersectionTypes04.ts(17,5): error TS2322: Type 'A & B' is not assignable to type 'number'. tests/cases/compiler/errorMessagesIntersectionTypes04.ts(18,5): error TS2322: Type 'A & B' is not assignable to type 'boolean'. tests/cases/compiler/errorMessagesIntersectionTypes04.ts(19,5): error TS2322: Type 'A & B' is not assignable to type 'string'. -tests/cases/compiler/errorMessagesIntersectionTypes04.ts(21,5): error TS2322: Type 'number & boolean' is not assignable to type 'string'. +tests/cases/compiler/errorMessagesIntersectionTypes04.ts(21,5): error TS2322: Type '(number & true) | (number & false)' is not assignable to type 'string'. + Type 'number & true' is not assignable to type 'string'. ==== tests/cases/compiler/errorMessagesIntersectionTypes04.ts (4 errors) ==== @@ -33,5 +34,6 @@ tests/cases/compiler/errorMessagesIntersectionTypes04.ts(21,5): error TS2322: Ty str = num_and_bool; ~~~ -!!! error TS2322: Type 'number & boolean' is not assignable to type 'string'. +!!! error TS2322: Type '(number & true) | (number & false)' is not assignable to type 'string'. +!!! error TS2322: Type 'number & true' is not assignable to type 'string'. } \ No newline at end of file diff --git a/tests/baselines/reference/intersectionAndUnionTypes.errors.txt b/tests/baselines/reference/intersectionAndUnionTypes.errors.txt index 5a04d4f25d3..62ddfd497a3 100644 --- a/tests/baselines/reference/intersectionAndUnionTypes.errors.txt +++ b/tests/baselines/reference/intersectionAndUnionTypes.errors.txt @@ -30,28 +30,29 @@ tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(29,1): e Type 'A & B' is not assignable to type 'C | D'. Type 'A & B' is not assignable to type 'D'. Property 'd' is missing in type 'A & B'. -tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(31,1): error TS2322: Type 'A & B' is not assignable to type '(A | B) & (C | D)'. - Type 'A & B' is not assignable to type 'C | D'. +tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(31,1): error TS2322: Type 'A & B' is not assignable to type '(A & C) | (A & D) | (B & C) | (B & D)'. + Type 'A & B' is not assignable to type 'B & D'. Type 'A & B' is not assignable to type 'D'. -tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(32,1): error TS2322: Type 'A | B' is not assignable to type '(A | B) & (C | D)'. - Type 'A' is not assignable to type '(A | B) & (C | D)'. - Type 'A' is not assignable to type 'C | D'. - Type 'A' is not assignable to type 'D'. - Property 'd' is missing in type 'A'. -tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(33,1): error TS2322: Type 'C & D' is not assignable to type '(A | B) & (C | D)'. - Type 'C & D' is not assignable to type 'A | B'. +tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(32,1): error TS2322: Type 'A | B' is not assignable to type '(A & C) | (A & D) | (B & C) | (B & D)'. + Type 'A' is not assignable to type '(A & C) | (A & D) | (B & C) | (B & D)'. + Type 'A' is not assignable to type 'B & D'. + Type 'A' is not assignable to type 'B'. +tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(33,1): error TS2322: Type 'C & D' is not assignable to type '(A & C) | (A & D) | (B & C) | (B & D)'. + Type 'C & D' is not assignable to type 'B & D'. Type 'C & D' is not assignable to type 'B'. -tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(34,1): error TS2322: Type 'C | D' is not assignable to type '(A | B) & (C | D)'. - Type 'C' is not assignable to type '(A | B) & (C | D)'. - Type 'C' is not assignable to type 'A | B'. +tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(34,1): error TS2322: Type 'C | D' is not assignable to type '(A & C) | (A & D) | (B & C) | (B & D)'. + Type 'C' is not assignable to type '(A & C) | (A & D) | (B & C) | (B & D)'. + Type 'C' is not assignable to type 'B & D'. Type 'C' is not assignable to type 'B'. Property 'b' is missing in type 'C'. -tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(35,1): error TS2322: Type '(A | B) & (C | D)' is not assignable to type 'A & B'. - Type '(A | B) & (C | D)' is not assignable to type 'A'. - Property 'a' is missing in type '(A | B) & (C | D)'. -tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(37,1): error TS2322: Type '(A | B) & (C | D)' is not assignable to type 'C & D'. - Type '(A | B) & (C | D)' is not assignable to type 'C'. - Property 'c' is missing in type '(A | B) & (C | D)'. +tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(35,1): error TS2322: Type '(A & C) | (A & D) | (B & C) | (B & D)' is not assignable to type 'A & B'. + Type 'A & C' is not assignable to type 'A & B'. + Type 'A & C' is not assignable to type 'B'. + Property 'b' is missing in type 'A & C'. +tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(37,1): error TS2322: Type '(A & C) | (A & D) | (B & C) | (B & D)' is not assignable to type 'C & D'. + Type 'A & C' is not assignable to type 'C & D'. + Type 'A & C' is not assignable to type 'D'. + Property 'd' is missing in type 'A & C'. ==== tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts (14 errors) ==== @@ -127,38 +128,39 @@ tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(37,1): e y = anb; ~ -!!! error TS2322: Type 'A & B' is not assignable to type '(A | B) & (C | D)'. -!!! error TS2322: Type 'A & B' is not assignable to type 'C | D'. +!!! error TS2322: Type 'A & B' is not assignable to type '(A & C) | (A & D) | (B & C) | (B & D)'. +!!! error TS2322: Type 'A & B' is not assignable to type 'B & D'. !!! error TS2322: Type 'A & B' is not assignable to type 'D'. y = aob; ~ -!!! error TS2322: Type 'A | B' is not assignable to type '(A | B) & (C | D)'. -!!! error TS2322: Type 'A' is not assignable to type '(A | B) & (C | D)'. -!!! error TS2322: Type 'A' is not assignable to type 'C | D'. -!!! error TS2322: Type 'A' is not assignable to type 'D'. -!!! error TS2322: Property 'd' is missing in type 'A'. +!!! error TS2322: Type 'A | B' is not assignable to type '(A & C) | (A & D) | (B & C) | (B & D)'. +!!! error TS2322: Type 'A' is not assignable to type '(A & C) | (A & D) | (B & C) | (B & D)'. +!!! error TS2322: Type 'A' is not assignable to type 'B & D'. +!!! error TS2322: Type 'A' is not assignable to type 'B'. y = cnd; ~ -!!! error TS2322: Type 'C & D' is not assignable to type '(A | B) & (C | D)'. -!!! error TS2322: Type 'C & D' is not assignable to type 'A | B'. +!!! error TS2322: Type 'C & D' is not assignable to type '(A & C) | (A & D) | (B & C) | (B & D)'. +!!! error TS2322: Type 'C & D' is not assignable to type 'B & D'. !!! error TS2322: Type 'C & D' is not assignable to type 'B'. y = cod; ~ -!!! error TS2322: Type 'C | D' is not assignable to type '(A | B) & (C | D)'. -!!! error TS2322: Type 'C' is not assignable to type '(A | B) & (C | D)'. -!!! error TS2322: Type 'C' is not assignable to type 'A | B'. +!!! error TS2322: Type 'C | D' is not assignable to type '(A & C) | (A & D) | (B & C) | (B & D)'. +!!! error TS2322: Type 'C' is not assignable to type '(A & C) | (A & D) | (B & C) | (B & D)'. +!!! error TS2322: Type 'C' is not assignable to type 'B & D'. !!! error TS2322: Type 'C' is not assignable to type 'B'. !!! error TS2322: Property 'b' is missing in type 'C'. anb = y; ~~~ -!!! error TS2322: Type '(A | B) & (C | D)' is not assignable to type 'A & B'. -!!! error TS2322: Type '(A | B) & (C | D)' is not assignable to type 'A'. -!!! error TS2322: Property 'a' is missing in type '(A | B) & (C | D)'. +!!! error TS2322: Type '(A & C) | (A & D) | (B & C) | (B & D)' is not assignable to type 'A & B'. +!!! error TS2322: Type 'A & C' is not assignable to type 'A & B'. +!!! error TS2322: Type 'A & C' is not assignable to type 'B'. +!!! error TS2322: Property 'b' is missing in type 'A & C'. aob = y; // Ok cnd = y; ~~~ -!!! error TS2322: Type '(A | B) & (C | D)' is not assignable to type 'C & D'. -!!! error TS2322: Type '(A | B) & (C | D)' is not assignable to type 'C'. -!!! error TS2322: Property 'c' is missing in type '(A | B) & (C | D)'. +!!! error TS2322: Type '(A & C) | (A & D) | (B & C) | (B & D)' is not assignable to type 'C & D'. +!!! error TS2322: Type 'A & C' is not assignable to type 'C & D'. +!!! error TS2322: Type 'A & C' is not assignable to type 'D'. +!!! error TS2322: Property 'd' is missing in type 'A & C'. cod = y; // Ok \ No newline at end of file diff --git a/tests/baselines/reference/switchCaseWithIntersectionTypes01.errors.txt b/tests/baselines/reference/switchCaseWithIntersectionTypes01.errors.txt index ad2ca8bc123..d31a005842c 100644 --- a/tests/baselines/reference/switchCaseWithIntersectionTypes01.errors.txt +++ b/tests/baselines/reference/switchCaseWithIntersectionTypes01.errors.txt @@ -1,5 +1,6 @@ -tests/cases/conformance/types/typeRelationships/comparable/switchCaseWithIntersectionTypes01.ts(19,10): error TS2678: Type 'number & boolean' is not comparable to type 'string & number'. - Type 'number & boolean' is not comparable to type 'string'. +tests/cases/conformance/types/typeRelationships/comparable/switchCaseWithIntersectionTypes01.ts(19,10): error TS2678: Type '(number & true) | (number & false)' is not comparable to type 'string & number'. + Type 'number & false' is not comparable to type 'string & number'. + Type 'number & false' is not comparable to type 'string'. tests/cases/conformance/types/typeRelationships/comparable/switchCaseWithIntersectionTypes01.ts(23,10): error TS2678: Type 'boolean' is not comparable to type 'string & number'. @@ -24,8 +25,9 @@ tests/cases/conformance/types/typeRelationships/comparable/switchCaseWithInterse // Overlap in constituents case numAndBool: ~~~~~~~~~~ -!!! error TS2678: Type 'number & boolean' is not comparable to type 'string & number'. -!!! error TS2678: Type 'number & boolean' is not comparable to type 'string'. +!!! error TS2678: Type '(number & true) | (number & false)' is not comparable to type 'string & number'. +!!! error TS2678: Type 'number & false' is not comparable to type 'string & number'. +!!! error TS2678: Type 'number & false' is not comparable to type 'string'. break; // No relation diff --git a/tests/baselines/reference/typeGuardsWithInstanceOf.types b/tests/baselines/reference/typeGuardsWithInstanceOf.types index f54498f93c8..7fce8cd4559 100644 --- a/tests/baselines/reference/typeGuardsWithInstanceOf.types +++ b/tests/baselines/reference/typeGuardsWithInstanceOf.types @@ -25,7 +25,7 @@ if (!(result instanceof RegExp)) { } else if (!result.global) { >!result.global : boolean ->result.global : string & boolean +>result.global : (string & true) | (string & false) >result : I & RegExp ->global : string & boolean +>global : (string & true) | (string & false) } From 6a0f72916eb31ded2a011dd9dd4e701145b85d78 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 18 Oct 2016 14:13:19 -0700 Subject: [PATCH 03/49] Simplify logic in checkTypeRelatedTo --- src/compiler/checker.ts | 57 ++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 879e2dec9df..c395343ea35 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5622,7 +5622,6 @@ namespace ts { if (type.flags & TypeFlags.Union) { // We are attempting to construct a type of the form X & (A | B) & Y. Transform this into a type of // the form X & A & Y | X & B & Y and recursively reduce until no union type constituents remain. - let unionType = types[i]; return getUnionType(map((type).types, t => getIntersectionType(replaceElement(types, i, t))), /*subtypeReduction*/ false, aliasSymbol, aliasTypeArguments); } @@ -6574,7 +6573,9 @@ namespace ts { const saveErrorInfo = errorInfo; - // Note that these checks are specifically ordered to produce correct results. + // Note that these checks are specifically ordered to produce correct results. In particular, + // we need to deconstruct unions before intersections (because unions are always at the top), + // and we need to handle "each" relations before "some" relations for the same kind of type. if (source.flags & TypeFlags.Union) { if (relation === comparableRelation) { result = someTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive)); @@ -6582,44 +6583,36 @@ namespace ts { else { result = eachTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive)); } - if (result) { return result; } } + else if (target.flags & TypeFlags.Union) { + if (result = typeRelatedToSomeType(source, target, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive))) { + return result; + } + } else if (target.flags & TypeFlags.Intersection) { - result = typeRelatedToEachType(source, target as IntersectionType, reportErrors); - - if (result) { + if (result = typeRelatedToEachType(source, target as IntersectionType, reportErrors)) { return result; } } - else { - // It is necessary to try these "some" checks on both sides because there may be nested "each" checks - // on either side that need to be prioritized. For example, A | B = (A | B) & (C | D) or - // A & B = (A & B) | (C & D). - if (source.flags & TypeFlags.Intersection) { - // Check to see if any constituents of the intersection are immediately related to the target. - // - // Don't report errors though. Checking whether a constituent is related to the source is not actually - // useful and leads to some confusing error messages. Instead it is better to let the below checks - // take care of this, or to not elaborate at all. For instance, - // - // - For an object type (such as 'C = A & B'), users are usually more interested in structural errors. - // - // - For a union type (such as '(A | B) = (C & D)'), it's better to hold onto the whole intersection - // than to report that 'D' is not assignable to 'A' or 'B'. - // - // - For a primitive type or type parameter (such as 'number = A & B') there is no point in - // breaking the intersection apart. - if (result = someTypeRelatedToType(source, target, /*reportErrors*/ false)) { - return result; - } - } - if (target.flags & TypeFlags.Union) { - if (result = typeRelatedToSomeType(source, target, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive))) { - return result; - } + else if (source.flags & TypeFlags.Intersection) { + // Check to see if any constituents of the intersection are immediately related to the target. + // + // Don't report errors though. Checking whether a constituent is related to the source is not actually + // useful and leads to some confusing error messages. Instead it is better to let the below checks + // take care of this, or to not elaborate at all. For instance, + // + // - For an object type (such as 'C = A & B'), users are usually more interested in structural errors. + // + // - For a union type (such as '(A | B) = (C & D)'), it's better to hold onto the whole intersection + // than to report that 'D' is not assignable to 'A' or 'B'. + // + // - For a primitive type or type parameter (such as 'number = A & B') there is no point in + // breaking the intersection apart. + if (result = someTypeRelatedToType(source, target, /*reportErrors*/ false)) { + return result; } } From bf7f2e2999cf7f36d0f735def0ac7711f3bb2275 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 18 Oct 2016 14:13:30 -0700 Subject: [PATCH 04/49] Add tests --- .../intersectionTypeNormalization.js | 79 ++++++ .../intersectionTypeNormalization.symbols | 242 +++++++++++++++++ .../intersectionTypeNormalization.types | 246 ++++++++++++++++++ .../compiler/intersectionTypeNormalization.ts | 60 +++++ 4 files changed, 627 insertions(+) create mode 100644 tests/baselines/reference/intersectionTypeNormalization.js create mode 100644 tests/baselines/reference/intersectionTypeNormalization.symbols create mode 100644 tests/baselines/reference/intersectionTypeNormalization.types create mode 100644 tests/cases/compiler/intersectionTypeNormalization.ts diff --git a/tests/baselines/reference/intersectionTypeNormalization.js b/tests/baselines/reference/intersectionTypeNormalization.js new file mode 100644 index 00000000000..8309b17f198 --- /dev/null +++ b/tests/baselines/reference/intersectionTypeNormalization.js @@ -0,0 +1,79 @@ +//// [intersectionTypeNormalization.ts] +interface A { a: string } +interface B { b: string } +interface C { c: string } +interface D { d: string } + +// Identical ways of writing the same type +type X1 = (A | B) & (C | D); +type X2 = A & (C | D) | B & (C | D) +type X3 = A & C | A & D | B & C | B & D; + +var x: X1; +var x: X2; +var x: X3; + +interface X { x: string } +interface Y { y: string } + +// Identical ways of writing the same type +type Y1 = (A | X & Y) & (C | D); +type Y2 = A & (C | D) | X & Y & (C | D) +type Y3 = A & C | A & D | X & Y & C | X & Y & D; + +var y: Y1; +var y: Y2; +var y: Y3; + +interface M { m: string } +interface N { n: string } + +// Identical ways of writing the same type +type Z1 = (A | X & (M | N)) & (C | D); +type Z2 = A & (C | D) | X & (M | N) & (C | D) +type Z3 = A & C | A & D | X & (M | N) & C | X & (M | N) & D; +type Z4 = A & C | A & D | X & M & C | X & N & C | X & M & D | X & N & D; + +var z: Z1; +var z: Z2; +var z: Z3; +var z: Z4; + +// Repro from #9919 + +type ToString = { + toString(): string; +} + +type BoxedValue = { kind: 'int', num: number } + | { kind: 'string', str: string } + +type IntersectionFail = BoxedValue & ToString + +type IntersectionInline = { kind: 'int', num: number } & ToString + | { kind: 'string', str: string } & ToString + +function getValueAsString(value: IntersectionFail): string { + if (value.kind === 'int') { + return '' + value.num; + } + return value.str; +} + +//// [intersectionTypeNormalization.js] +var x; +var x; +var x; +var y; +var y; +var y; +var z; +var z; +var z; +var z; +function getValueAsString(value) { + if (value.kind === 'int') { + return '' + value.num; + } + return value.str; +} diff --git a/tests/baselines/reference/intersectionTypeNormalization.symbols b/tests/baselines/reference/intersectionTypeNormalization.symbols new file mode 100644 index 00000000000..a5a00c70407 --- /dev/null +++ b/tests/baselines/reference/intersectionTypeNormalization.symbols @@ -0,0 +1,242 @@ +=== tests/cases/compiler/intersectionTypeNormalization.ts === +interface A { a: string } +>A : Symbol(A, Decl(intersectionTypeNormalization.ts, 0, 0)) +>a : Symbol(A.a, Decl(intersectionTypeNormalization.ts, 0, 13)) + +interface B { b: string } +>B : Symbol(B, Decl(intersectionTypeNormalization.ts, 0, 25)) +>b : Symbol(B.b, Decl(intersectionTypeNormalization.ts, 1, 13)) + +interface C { c: string } +>C : Symbol(C, Decl(intersectionTypeNormalization.ts, 1, 25)) +>c : Symbol(C.c, Decl(intersectionTypeNormalization.ts, 2, 13)) + +interface D { d: string } +>D : Symbol(D, Decl(intersectionTypeNormalization.ts, 2, 25)) +>d : Symbol(D.d, Decl(intersectionTypeNormalization.ts, 3, 13)) + +// Identical ways of writing the same type +type X1 = (A | B) & (C | D); +>X1 : Symbol(X1, Decl(intersectionTypeNormalization.ts, 3, 25)) +>A : Symbol(A, Decl(intersectionTypeNormalization.ts, 0, 0)) +>B : Symbol(B, Decl(intersectionTypeNormalization.ts, 0, 25)) +>C : Symbol(C, Decl(intersectionTypeNormalization.ts, 1, 25)) +>D : Symbol(D, Decl(intersectionTypeNormalization.ts, 2, 25)) + +type X2 = A & (C | D) | B & (C | D) +>X2 : Symbol(X2, Decl(intersectionTypeNormalization.ts, 6, 28)) +>A : Symbol(A, Decl(intersectionTypeNormalization.ts, 0, 0)) +>C : Symbol(C, Decl(intersectionTypeNormalization.ts, 1, 25)) +>D : Symbol(D, Decl(intersectionTypeNormalization.ts, 2, 25)) +>B : Symbol(B, Decl(intersectionTypeNormalization.ts, 0, 25)) +>C : Symbol(C, Decl(intersectionTypeNormalization.ts, 1, 25)) +>D : Symbol(D, Decl(intersectionTypeNormalization.ts, 2, 25)) + +type X3 = A & C | A & D | B & C | B & D; +>X3 : Symbol(X3, Decl(intersectionTypeNormalization.ts, 7, 35)) +>A : Symbol(A, Decl(intersectionTypeNormalization.ts, 0, 0)) +>C : Symbol(C, Decl(intersectionTypeNormalization.ts, 1, 25)) +>A : Symbol(A, Decl(intersectionTypeNormalization.ts, 0, 0)) +>D : Symbol(D, Decl(intersectionTypeNormalization.ts, 2, 25)) +>B : Symbol(B, Decl(intersectionTypeNormalization.ts, 0, 25)) +>C : Symbol(C, Decl(intersectionTypeNormalization.ts, 1, 25)) +>B : Symbol(B, Decl(intersectionTypeNormalization.ts, 0, 25)) +>D : Symbol(D, Decl(intersectionTypeNormalization.ts, 2, 25)) + +var x: X1; +>x : Symbol(x, Decl(intersectionTypeNormalization.ts, 10, 3), Decl(intersectionTypeNormalization.ts, 11, 3), Decl(intersectionTypeNormalization.ts, 12, 3)) +>X1 : Symbol(X1, Decl(intersectionTypeNormalization.ts, 3, 25)) + +var x: X2; +>x : Symbol(x, Decl(intersectionTypeNormalization.ts, 10, 3), Decl(intersectionTypeNormalization.ts, 11, 3), Decl(intersectionTypeNormalization.ts, 12, 3)) +>X2 : Symbol(X2, Decl(intersectionTypeNormalization.ts, 6, 28)) + +var x: X3; +>x : Symbol(x, Decl(intersectionTypeNormalization.ts, 10, 3), Decl(intersectionTypeNormalization.ts, 11, 3), Decl(intersectionTypeNormalization.ts, 12, 3)) +>X3 : Symbol(X3, Decl(intersectionTypeNormalization.ts, 7, 35)) + +interface X { x: string } +>X : Symbol(X, Decl(intersectionTypeNormalization.ts, 12, 10)) +>x : Symbol(X.x, Decl(intersectionTypeNormalization.ts, 14, 13)) + +interface Y { y: string } +>Y : Symbol(Y, Decl(intersectionTypeNormalization.ts, 14, 25)) +>y : Symbol(Y.y, Decl(intersectionTypeNormalization.ts, 15, 13)) + +// Identical ways of writing the same type +type Y1 = (A | X & Y) & (C | D); +>Y1 : Symbol(Y1, Decl(intersectionTypeNormalization.ts, 15, 25)) +>A : Symbol(A, Decl(intersectionTypeNormalization.ts, 0, 0)) +>X : Symbol(X, Decl(intersectionTypeNormalization.ts, 12, 10)) +>Y : Symbol(Y, Decl(intersectionTypeNormalization.ts, 14, 25)) +>C : Symbol(C, Decl(intersectionTypeNormalization.ts, 1, 25)) +>D : Symbol(D, Decl(intersectionTypeNormalization.ts, 2, 25)) + +type Y2 = A & (C | D) | X & Y & (C | D) +>Y2 : Symbol(Y2, Decl(intersectionTypeNormalization.ts, 18, 32)) +>A : Symbol(A, Decl(intersectionTypeNormalization.ts, 0, 0)) +>C : Symbol(C, Decl(intersectionTypeNormalization.ts, 1, 25)) +>D : Symbol(D, Decl(intersectionTypeNormalization.ts, 2, 25)) +>X : Symbol(X, Decl(intersectionTypeNormalization.ts, 12, 10)) +>Y : Symbol(Y, Decl(intersectionTypeNormalization.ts, 14, 25)) +>C : Symbol(C, Decl(intersectionTypeNormalization.ts, 1, 25)) +>D : Symbol(D, Decl(intersectionTypeNormalization.ts, 2, 25)) + +type Y3 = A & C | A & D | X & Y & C | X & Y & D; +>Y3 : Symbol(Y3, Decl(intersectionTypeNormalization.ts, 19, 39)) +>A : Symbol(A, Decl(intersectionTypeNormalization.ts, 0, 0)) +>C : Symbol(C, Decl(intersectionTypeNormalization.ts, 1, 25)) +>A : Symbol(A, Decl(intersectionTypeNormalization.ts, 0, 0)) +>D : Symbol(D, Decl(intersectionTypeNormalization.ts, 2, 25)) +>X : Symbol(X, Decl(intersectionTypeNormalization.ts, 12, 10)) +>Y : Symbol(Y, Decl(intersectionTypeNormalization.ts, 14, 25)) +>C : Symbol(C, Decl(intersectionTypeNormalization.ts, 1, 25)) +>X : Symbol(X, Decl(intersectionTypeNormalization.ts, 12, 10)) +>Y : Symbol(Y, Decl(intersectionTypeNormalization.ts, 14, 25)) +>D : Symbol(D, Decl(intersectionTypeNormalization.ts, 2, 25)) + +var y: Y1; +>y : Symbol(y, Decl(intersectionTypeNormalization.ts, 22, 3), Decl(intersectionTypeNormalization.ts, 23, 3), Decl(intersectionTypeNormalization.ts, 24, 3)) +>Y1 : Symbol(Y1, Decl(intersectionTypeNormalization.ts, 15, 25)) + +var y: Y2; +>y : Symbol(y, Decl(intersectionTypeNormalization.ts, 22, 3), Decl(intersectionTypeNormalization.ts, 23, 3), Decl(intersectionTypeNormalization.ts, 24, 3)) +>Y2 : Symbol(Y2, Decl(intersectionTypeNormalization.ts, 18, 32)) + +var y: Y3; +>y : Symbol(y, Decl(intersectionTypeNormalization.ts, 22, 3), Decl(intersectionTypeNormalization.ts, 23, 3), Decl(intersectionTypeNormalization.ts, 24, 3)) +>Y3 : Symbol(Y3, Decl(intersectionTypeNormalization.ts, 19, 39)) + +interface M { m: string } +>M : Symbol(M, Decl(intersectionTypeNormalization.ts, 24, 10)) +>m : Symbol(M.m, Decl(intersectionTypeNormalization.ts, 26, 13)) + +interface N { n: string } +>N : Symbol(N, Decl(intersectionTypeNormalization.ts, 26, 25)) +>n : Symbol(N.n, Decl(intersectionTypeNormalization.ts, 27, 13)) + +// Identical ways of writing the same type +type Z1 = (A | X & (M | N)) & (C | D); +>Z1 : Symbol(Z1, Decl(intersectionTypeNormalization.ts, 27, 25)) +>A : Symbol(A, Decl(intersectionTypeNormalization.ts, 0, 0)) +>X : Symbol(X, Decl(intersectionTypeNormalization.ts, 12, 10)) +>M : Symbol(M, Decl(intersectionTypeNormalization.ts, 24, 10)) +>N : Symbol(N, Decl(intersectionTypeNormalization.ts, 26, 25)) +>C : Symbol(C, Decl(intersectionTypeNormalization.ts, 1, 25)) +>D : Symbol(D, Decl(intersectionTypeNormalization.ts, 2, 25)) + +type Z2 = A & (C | D) | X & (M | N) & (C | D) +>Z2 : Symbol(Z2, Decl(intersectionTypeNormalization.ts, 30, 38)) +>A : Symbol(A, Decl(intersectionTypeNormalization.ts, 0, 0)) +>C : Symbol(C, Decl(intersectionTypeNormalization.ts, 1, 25)) +>D : Symbol(D, Decl(intersectionTypeNormalization.ts, 2, 25)) +>X : Symbol(X, Decl(intersectionTypeNormalization.ts, 12, 10)) +>M : Symbol(M, Decl(intersectionTypeNormalization.ts, 24, 10)) +>N : Symbol(N, Decl(intersectionTypeNormalization.ts, 26, 25)) +>C : Symbol(C, Decl(intersectionTypeNormalization.ts, 1, 25)) +>D : Symbol(D, Decl(intersectionTypeNormalization.ts, 2, 25)) + +type Z3 = A & C | A & D | X & (M | N) & C | X & (M | N) & D; +>Z3 : Symbol(Z3, Decl(intersectionTypeNormalization.ts, 31, 45)) +>A : Symbol(A, Decl(intersectionTypeNormalization.ts, 0, 0)) +>C : Symbol(C, Decl(intersectionTypeNormalization.ts, 1, 25)) +>A : Symbol(A, Decl(intersectionTypeNormalization.ts, 0, 0)) +>D : Symbol(D, Decl(intersectionTypeNormalization.ts, 2, 25)) +>X : Symbol(X, Decl(intersectionTypeNormalization.ts, 12, 10)) +>M : Symbol(M, Decl(intersectionTypeNormalization.ts, 24, 10)) +>N : Symbol(N, Decl(intersectionTypeNormalization.ts, 26, 25)) +>C : Symbol(C, Decl(intersectionTypeNormalization.ts, 1, 25)) +>X : Symbol(X, Decl(intersectionTypeNormalization.ts, 12, 10)) +>M : Symbol(M, Decl(intersectionTypeNormalization.ts, 24, 10)) +>N : Symbol(N, Decl(intersectionTypeNormalization.ts, 26, 25)) +>D : Symbol(D, Decl(intersectionTypeNormalization.ts, 2, 25)) + +type Z4 = A & C | A & D | X & M & C | X & N & C | X & M & D | X & N & D; +>Z4 : Symbol(Z4, Decl(intersectionTypeNormalization.ts, 32, 60)) +>A : Symbol(A, Decl(intersectionTypeNormalization.ts, 0, 0)) +>C : Symbol(C, Decl(intersectionTypeNormalization.ts, 1, 25)) +>A : Symbol(A, Decl(intersectionTypeNormalization.ts, 0, 0)) +>D : Symbol(D, Decl(intersectionTypeNormalization.ts, 2, 25)) +>X : Symbol(X, Decl(intersectionTypeNormalization.ts, 12, 10)) +>M : Symbol(M, Decl(intersectionTypeNormalization.ts, 24, 10)) +>C : Symbol(C, Decl(intersectionTypeNormalization.ts, 1, 25)) +>X : Symbol(X, Decl(intersectionTypeNormalization.ts, 12, 10)) +>N : Symbol(N, Decl(intersectionTypeNormalization.ts, 26, 25)) +>C : Symbol(C, Decl(intersectionTypeNormalization.ts, 1, 25)) +>X : Symbol(X, Decl(intersectionTypeNormalization.ts, 12, 10)) +>M : Symbol(M, Decl(intersectionTypeNormalization.ts, 24, 10)) +>D : Symbol(D, Decl(intersectionTypeNormalization.ts, 2, 25)) +>X : Symbol(X, Decl(intersectionTypeNormalization.ts, 12, 10)) +>N : Symbol(N, Decl(intersectionTypeNormalization.ts, 26, 25)) +>D : Symbol(D, Decl(intersectionTypeNormalization.ts, 2, 25)) + +var z: Z1; +>z : Symbol(z, Decl(intersectionTypeNormalization.ts, 35, 3), Decl(intersectionTypeNormalization.ts, 36, 3), Decl(intersectionTypeNormalization.ts, 37, 3), Decl(intersectionTypeNormalization.ts, 38, 3)) +>Z1 : Symbol(Z1, Decl(intersectionTypeNormalization.ts, 27, 25)) + +var z: Z2; +>z : Symbol(z, Decl(intersectionTypeNormalization.ts, 35, 3), Decl(intersectionTypeNormalization.ts, 36, 3), Decl(intersectionTypeNormalization.ts, 37, 3), Decl(intersectionTypeNormalization.ts, 38, 3)) +>Z2 : Symbol(Z2, Decl(intersectionTypeNormalization.ts, 30, 38)) + +var z: Z3; +>z : Symbol(z, Decl(intersectionTypeNormalization.ts, 35, 3), Decl(intersectionTypeNormalization.ts, 36, 3), Decl(intersectionTypeNormalization.ts, 37, 3), Decl(intersectionTypeNormalization.ts, 38, 3)) +>Z3 : Symbol(Z3, Decl(intersectionTypeNormalization.ts, 31, 45)) + +var z: Z4; +>z : Symbol(z, Decl(intersectionTypeNormalization.ts, 35, 3), Decl(intersectionTypeNormalization.ts, 36, 3), Decl(intersectionTypeNormalization.ts, 37, 3), Decl(intersectionTypeNormalization.ts, 38, 3)) +>Z4 : Symbol(Z4, Decl(intersectionTypeNormalization.ts, 32, 60)) + +// Repro from #9919 + +type ToString = { +>ToString : Symbol(ToString, Decl(intersectionTypeNormalization.ts, 38, 10)) + + toString(): string; +>toString : Symbol(toString, Decl(intersectionTypeNormalization.ts, 42, 17)) +} + +type BoxedValue = { kind: 'int', num: number } +>BoxedValue : Symbol(BoxedValue, Decl(intersectionTypeNormalization.ts, 44, 1)) +>kind : Symbol(kind, Decl(intersectionTypeNormalization.ts, 46, 19)) +>num : Symbol(num, Decl(intersectionTypeNormalization.ts, 46, 32)) + + | { kind: 'string', str: string } +>kind : Symbol(kind, Decl(intersectionTypeNormalization.ts, 47, 19)) +>str : Symbol(str, Decl(intersectionTypeNormalization.ts, 47, 35)) + +type IntersectionFail = BoxedValue & ToString +>IntersectionFail : Symbol(IntersectionFail, Decl(intersectionTypeNormalization.ts, 47, 49)) +>BoxedValue : Symbol(BoxedValue, Decl(intersectionTypeNormalization.ts, 44, 1)) +>ToString : Symbol(ToString, Decl(intersectionTypeNormalization.ts, 38, 10)) + +type IntersectionInline = { kind: 'int', num: number } & ToString +>IntersectionInline : Symbol(IntersectionInline, Decl(intersectionTypeNormalization.ts, 49, 45)) +>kind : Symbol(kind, Decl(intersectionTypeNormalization.ts, 51, 27)) +>num : Symbol(num, Decl(intersectionTypeNormalization.ts, 51, 40)) +>ToString : Symbol(ToString, Decl(intersectionTypeNormalization.ts, 38, 10)) + + | { kind: 'string', str: string } & ToString +>kind : Symbol(kind, Decl(intersectionTypeNormalization.ts, 52, 27)) +>str : Symbol(str, Decl(intersectionTypeNormalization.ts, 52, 43)) +>ToString : Symbol(ToString, Decl(intersectionTypeNormalization.ts, 38, 10)) + +function getValueAsString(value: IntersectionFail): string { +>getValueAsString : Symbol(getValueAsString, Decl(intersectionTypeNormalization.ts, 52, 68)) +>value : Symbol(value, Decl(intersectionTypeNormalization.ts, 54, 26)) +>IntersectionFail : Symbol(IntersectionFail, Decl(intersectionTypeNormalization.ts, 47, 49)) + + if (value.kind === 'int') { +>value.kind : Symbol(kind, Decl(intersectionTypeNormalization.ts, 46, 19), Decl(intersectionTypeNormalization.ts, 47, 19)) +>value : Symbol(value, Decl(intersectionTypeNormalization.ts, 54, 26)) +>kind : Symbol(kind, Decl(intersectionTypeNormalization.ts, 46, 19), Decl(intersectionTypeNormalization.ts, 47, 19)) + + return '' + value.num; +>value.num : Symbol(num, Decl(intersectionTypeNormalization.ts, 46, 32)) +>value : Symbol(value, Decl(intersectionTypeNormalization.ts, 54, 26)) +>num : Symbol(num, Decl(intersectionTypeNormalization.ts, 46, 32)) + } + return value.str; +>value.str : Symbol(str, Decl(intersectionTypeNormalization.ts, 47, 35)) +>value : Symbol(value, Decl(intersectionTypeNormalization.ts, 54, 26)) +>str : Symbol(str, Decl(intersectionTypeNormalization.ts, 47, 35)) +} diff --git a/tests/baselines/reference/intersectionTypeNormalization.types b/tests/baselines/reference/intersectionTypeNormalization.types new file mode 100644 index 00000000000..10ef50ae1b4 --- /dev/null +++ b/tests/baselines/reference/intersectionTypeNormalization.types @@ -0,0 +1,246 @@ +=== tests/cases/compiler/intersectionTypeNormalization.ts === +interface A { a: string } +>A : A +>a : string + +interface B { b: string } +>B : B +>b : string + +interface C { c: string } +>C : C +>c : string + +interface D { d: string } +>D : D +>d : string + +// Identical ways of writing the same type +type X1 = (A | B) & (C | D); +>X1 : X1 +>A : A +>B : B +>C : C +>D : D + +type X2 = A & (C | D) | B & (C | D) +>X2 : X1 +>A : A +>C : C +>D : D +>B : B +>C : C +>D : D + +type X3 = A & C | A & D | B & C | B & D; +>X3 : X1 +>A : A +>C : C +>A : A +>D : D +>B : B +>C : C +>B : B +>D : D + +var x: X1; +>x : X1 +>X1 : X1 + +var x: X2; +>x : X1 +>X2 : X1 + +var x: X3; +>x : X1 +>X3 : X1 + +interface X { x: string } +>X : X +>x : string + +interface Y { y: string } +>Y : Y +>y : string + +// Identical ways of writing the same type +type Y1 = (A | X & Y) & (C | D); +>Y1 : Y1 +>A : A +>X : X +>Y : Y +>C : C +>D : D + +type Y2 = A & (C | D) | X & Y & (C | D) +>Y2 : Y1 +>A : A +>C : C +>D : D +>X : X +>Y : Y +>C : C +>D : D + +type Y3 = A & C | A & D | X & Y & C | X & Y & D; +>Y3 : Y1 +>A : A +>C : C +>A : A +>D : D +>X : X +>Y : Y +>C : C +>X : X +>Y : Y +>D : D + +var y: Y1; +>y : Y1 +>Y1 : Y1 + +var y: Y2; +>y : Y1 +>Y2 : Y1 + +var y: Y3; +>y : Y1 +>Y3 : Y1 + +interface M { m: string } +>M : M +>m : string + +interface N { n: string } +>N : N +>n : string + +// Identical ways of writing the same type +type Z1 = (A | X & (M | N)) & (C | D); +>Z1 : Z1 +>A : A +>X : X +>M : M +>N : N +>C : C +>D : D + +type Z2 = A & (C | D) | X & (M | N) & (C | D) +>Z2 : Z1 +>A : A +>C : C +>D : D +>X : X +>M : M +>N : N +>C : C +>D : D + +type Z3 = A & C | A & D | X & (M | N) & C | X & (M | N) & D; +>Z3 : Z1 +>A : A +>C : C +>A : A +>D : D +>X : X +>M : M +>N : N +>C : C +>X : X +>M : M +>N : N +>D : D + +type Z4 = A & C | A & D | X & M & C | X & N & C | X & M & D | X & N & D; +>Z4 : Z1 +>A : A +>C : C +>A : A +>D : D +>X : X +>M : M +>C : C +>X : X +>N : N +>C : C +>X : X +>M : M +>D : D +>X : X +>N : N +>D : D + +var z: Z1; +>z : Z1 +>Z1 : Z1 + +var z: Z2; +>z : Z1 +>Z2 : Z1 + +var z: Z3; +>z : Z1 +>Z3 : Z1 + +var z: Z4; +>z : Z1 +>Z4 : Z1 + +// Repro from #9919 + +type ToString = { +>ToString : { toString(): string; } + + toString(): string; +>toString : () => string +} + +type BoxedValue = { kind: 'int', num: number } +>BoxedValue : BoxedValue +>kind : "int" +>num : number + + | { kind: 'string', str: string } +>kind : "string" +>str : string + +type IntersectionFail = BoxedValue & ToString +>IntersectionFail : IntersectionFail +>BoxedValue : BoxedValue +>ToString : { toString(): string; } + +type IntersectionInline = { kind: 'int', num: number } & ToString +>IntersectionInline : IntersectionInline +>kind : "int" +>num : number +>ToString : { toString(): string; } + + | { kind: 'string', str: string } & ToString +>kind : "string" +>str : string +>ToString : { toString(): string; } + +function getValueAsString(value: IntersectionFail): string { +>getValueAsString : (value: IntersectionFail) => string +>value : IntersectionFail +>IntersectionFail : IntersectionFail + + if (value.kind === 'int') { +>value.kind === 'int' : boolean +>value.kind : "int" | "string" +>value : IntersectionFail +>kind : "int" | "string" +>'int' : "int" + + return '' + value.num; +>'' + value.num : string +>'' : "" +>value.num : number +>value : { kind: "int"; num: number; } & { toString(): string; } +>num : number + } + return value.str; +>value.str : string +>value : { kind: "string"; str: string; } & { toString(): string; } +>str : string +} diff --git a/tests/cases/compiler/intersectionTypeNormalization.ts b/tests/cases/compiler/intersectionTypeNormalization.ts new file mode 100644 index 00000000000..f31c3d49014 --- /dev/null +++ b/tests/cases/compiler/intersectionTypeNormalization.ts @@ -0,0 +1,60 @@ +interface A { a: string } +interface B { b: string } +interface C { c: string } +interface D { d: string } + +// Identical ways of writing the same type +type X1 = (A | B) & (C | D); +type X2 = A & (C | D) | B & (C | D) +type X3 = A & C | A & D | B & C | B & D; + +var x: X1; +var x: X2; +var x: X3; + +interface X { x: string } +interface Y { y: string } + +// Identical ways of writing the same type +type Y1 = (A | X & Y) & (C | D); +type Y2 = A & (C | D) | X & Y & (C | D) +type Y3 = A & C | A & D | X & Y & C | X & Y & D; + +var y: Y1; +var y: Y2; +var y: Y3; + +interface M { m: string } +interface N { n: string } + +// Identical ways of writing the same type +type Z1 = (A | X & (M | N)) & (C | D); +type Z2 = A & (C | D) | X & (M | N) & (C | D) +type Z3 = A & C | A & D | X & (M | N) & C | X & (M | N) & D; +type Z4 = A & C | A & D | X & M & C | X & N & C | X & M & D | X & N & D; + +var z: Z1; +var z: Z2; +var z: Z3; +var z: Z4; + +// Repro from #9919 + +type ToString = { + toString(): string; +} + +type BoxedValue = { kind: 'int', num: number } + | { kind: 'string', str: string } + +type IntersectionFail = BoxedValue & ToString + +type IntersectionInline = { kind: 'int', num: number } & ToString + | { kind: 'string', str: string } & ToString + +function getValueAsString(value: IntersectionFail): string { + if (value.kind === 'int') { + return '' + value.num; + } + return value.str; +} \ No newline at end of file From 17cf4357adbb363ba796a810150fef28d8bac10e Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 18 Oct 2016 12:24:41 -0700 Subject: [PATCH 05/49] Add testcase when error is reported about unused react --- .../reference/unusedImports13.errors.txt | 25 +++++++++++++ tests/baselines/reference/unusedImports13.js | 27 ++++++++++++++ tests/baselines/reference/unusedImports14.js | 27 ++++++++++++++ .../reference/unusedImports14.symbols | 36 ++++++++++++++++++ .../baselines/reference/unusedImports14.types | 37 +++++++++++++++++++ tests/cases/compiler/unusedImports13.ts | 23 ++++++++++++ tests/cases/compiler/unusedImports14.ts | 23 ++++++++++++ 7 files changed, 198 insertions(+) create mode 100644 tests/baselines/reference/unusedImports13.errors.txt create mode 100644 tests/baselines/reference/unusedImports13.js create mode 100644 tests/baselines/reference/unusedImports14.js create mode 100644 tests/baselines/reference/unusedImports14.symbols create mode 100644 tests/baselines/reference/unusedImports14.types create mode 100644 tests/cases/compiler/unusedImports13.ts create mode 100644 tests/cases/compiler/unusedImports14.ts diff --git a/tests/baselines/reference/unusedImports13.errors.txt b/tests/baselines/reference/unusedImports13.errors.txt new file mode 100644 index 00000000000..0e870795eb6 --- /dev/null +++ b/tests/baselines/reference/unusedImports13.errors.txt @@ -0,0 +1,25 @@ +tests/cases/compiler/foo.tsx(2,8): error TS6133: 'React' is declared but never used. + + +==== tests/cases/compiler/foo.tsx (1 errors) ==== + + import React = require("react"); + ~~~~~ +!!! error TS6133: 'React' is declared but never used. + + export const FooComponent =
+ +==== tests/cases/compiler/node_modules/@types/react/index.d.ts (0 errors) ==== + export = React; + export as namespace React; + + declare namespace React { + function createClass(spec); + } + declare global { + namespace JSX { + } + } + + + \ No newline at end of file diff --git a/tests/baselines/reference/unusedImports13.js b/tests/baselines/reference/unusedImports13.js new file mode 100644 index 00000000000..01e91abc14b --- /dev/null +++ b/tests/baselines/reference/unusedImports13.js @@ -0,0 +1,27 @@ +//// [tests/cases/compiler/unusedImports13.ts] //// + +//// [foo.tsx] + +import React = require("react"); + +export const FooComponent =
+ +//// [index.d.ts] +export = React; +export as namespace React; + +declare namespace React { + function createClass(spec); +} +declare global { + namespace JSX { + } +} + + + + +//// [foo.jsx] +"use strict"; +var React = require("react"); +exports.FooComponent =
; diff --git a/tests/baselines/reference/unusedImports14.js b/tests/baselines/reference/unusedImports14.js new file mode 100644 index 00000000000..f3c51590e49 --- /dev/null +++ b/tests/baselines/reference/unusedImports14.js @@ -0,0 +1,27 @@ +//// [tests/cases/compiler/unusedImports14.ts] //// + +//// [foo.tsx] + +import React = require("react"); + +export const FooComponent =
+ +//// [index.d.ts] +export = React; +export as namespace React; + +declare namespace React { + function createClass(spec); +} +declare global { + namespace JSX { + } +} + + + + +//// [foo.js] +"use strict"; +var React = require("react"); +exports.FooComponent = React.createElement("div", null); diff --git a/tests/baselines/reference/unusedImports14.symbols b/tests/baselines/reference/unusedImports14.symbols new file mode 100644 index 00000000000..3ac44d17533 --- /dev/null +++ b/tests/baselines/reference/unusedImports14.symbols @@ -0,0 +1,36 @@ +=== tests/cases/compiler/foo.tsx === + +import React = require("react"); +>React : Symbol(React, Decl(foo.tsx, 0, 0)) + +export const FooComponent =
+>FooComponent : Symbol(FooComponent, Decl(foo.tsx, 3, 12)) +>div : Symbol(unknown) +>div : Symbol(unknown) + +=== tests/cases/compiler/node_modules/@types/react/index.d.ts === +export = React; +>React : Symbol(React, Decl(index.d.ts, 1, 26)) + +export as namespace React; +>React : Symbol(React, Decl(index.d.ts, 0, 15)) + +declare namespace React { +>React : Symbol(React, Decl(index.d.ts, 1, 26)) + + function createClass(spec); +>createClass : Symbol(createClass, Decl(index.d.ts, 3, 25)) +>P : Symbol(P, Decl(index.d.ts, 4, 25)) +>S : Symbol(S, Decl(index.d.ts, 4, 27)) +>spec : Symbol(spec, Decl(index.d.ts, 4, 31)) +} +declare global { +>global : Symbol(global, Decl(index.d.ts, 5, 1)) + + namespace JSX { +>JSX : Symbol(JSX, Decl(index.d.ts, 6, 16)) + } +} + + + diff --git a/tests/baselines/reference/unusedImports14.types b/tests/baselines/reference/unusedImports14.types new file mode 100644 index 00000000000..b19531b3e2d --- /dev/null +++ b/tests/baselines/reference/unusedImports14.types @@ -0,0 +1,37 @@ +=== tests/cases/compiler/foo.tsx === + +import React = require("react"); +>React : typeof React + +export const FooComponent =
+>FooComponent : any +>
: any +>div : any +>div : any + +=== tests/cases/compiler/node_modules/@types/react/index.d.ts === +export = React; +>React : typeof React + +export as namespace React; +>React : typeof React + +declare namespace React { +>React : typeof React + + function createClass(spec); +>createClass : (spec: any) => any +>P : P +>S : S +>spec : any +} +declare global { +>global : any + + namespace JSX { +>JSX : any + } +} + + + diff --git a/tests/cases/compiler/unusedImports13.ts b/tests/cases/compiler/unusedImports13.ts new file mode 100644 index 00000000000..875e36abab2 --- /dev/null +++ b/tests/cases/compiler/unusedImports13.ts @@ -0,0 +1,23 @@ +//@noUnusedLocals:true +//@noUnusedParameters:true +//@module: commonjs +//@jsx: preserve + +// @filename: foo.tsx +import React = require("react"); + +export const FooComponent =
+ +// @filename: node_modules/@types/react/index.d.ts +export = React; +export as namespace React; + +declare namespace React { + function createClass(spec); +} +declare global { + namespace JSX { + } +} + + diff --git a/tests/cases/compiler/unusedImports14.ts b/tests/cases/compiler/unusedImports14.ts new file mode 100644 index 00000000000..89e4b31cb13 --- /dev/null +++ b/tests/cases/compiler/unusedImports14.ts @@ -0,0 +1,23 @@ +//@noUnusedLocals:true +//@noUnusedParameters:true +//@module: commonjs +//@jsx: react + +// @filename: foo.tsx +import React = require("react"); + +export const FooComponent =
+ +// @filename: node_modules/@types/react/index.d.ts +export = React; +export as namespace React; + +declare namespace React { + function createClass(spec); +} +declare global { + namespace JSX { + } +} + + From 96a7b7b00f108cad190257c019d07d60abd83b41 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 18 Oct 2016 12:24:41 -0700 Subject: [PATCH 06/49] Mark local "react" symbol as referenced since it might not be marked if there was no error message being displayed Fixes #10312 --- src/compiler/checker.ts | 10 ++++- .../reference/unusedImports13.errors.txt | 25 ------------- .../reference/unusedImports13.symbols | 36 ++++++++++++++++++ .../baselines/reference/unusedImports13.types | 37 +++++++++++++++++++ 4 files changed, 81 insertions(+), 27 deletions(-) delete mode 100644 tests/baselines/reference/unusedImports13.errors.txt create mode 100644 tests/baselines/reference/unusedImports13.symbols create mode 100644 tests/baselines/reference/unusedImports13.types diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d3f15f2367b..a4d53c40d4d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11070,14 +11070,20 @@ namespace ts { function checkJsxOpeningLikeElement(node: JsxOpeningLikeElement) { checkGrammarJsxElement(node); checkJsxPreconditions(node); - // The reactNamespace symbol should be marked as 'used' so we don't incorrectly elide its import. And if there // is no reactNamespace symbol in scope when targeting React emit, we should issue an error. const reactRefErr = compilerOptions.jsx === JsxEmit.React ? Diagnostics.Cannot_find_name_0 : undefined; const reactNamespace = compilerOptions.reactNamespace ? compilerOptions.reactNamespace : "React"; const reactSym = resolveName(node.tagName, reactNamespace, SymbolFlags.Value, reactRefErr, reactNamespace); if (reactSym) { - getSymbolLinks(reactSym).referenced = true; + // Mark local symbol as referenced here because it might not have been marked + // if jsx emit was not react as there wont be error being emitted + reactSym.isReferenced = true; + + // If react symbol is alias, mark it as refereced + if (reactSym.flags & SymbolFlags.Alias && !isConstEnumOrConstEnumOnlyModule(resolveAlias(reactSym))) { + markAliasSymbolAsReferenced(reactSym); + } } const targetAttributesType = getJsxElementAttributesType(node); diff --git a/tests/baselines/reference/unusedImports13.errors.txt b/tests/baselines/reference/unusedImports13.errors.txt deleted file mode 100644 index 0e870795eb6..00000000000 --- a/tests/baselines/reference/unusedImports13.errors.txt +++ /dev/null @@ -1,25 +0,0 @@ -tests/cases/compiler/foo.tsx(2,8): error TS6133: 'React' is declared but never used. - - -==== tests/cases/compiler/foo.tsx (1 errors) ==== - - import React = require("react"); - ~~~~~ -!!! error TS6133: 'React' is declared but never used. - - export const FooComponent =
- -==== tests/cases/compiler/node_modules/@types/react/index.d.ts (0 errors) ==== - export = React; - export as namespace React; - - declare namespace React { - function createClass(spec); - } - declare global { - namespace JSX { - } - } - - - \ No newline at end of file diff --git a/tests/baselines/reference/unusedImports13.symbols b/tests/baselines/reference/unusedImports13.symbols new file mode 100644 index 00000000000..3ac44d17533 --- /dev/null +++ b/tests/baselines/reference/unusedImports13.symbols @@ -0,0 +1,36 @@ +=== tests/cases/compiler/foo.tsx === + +import React = require("react"); +>React : Symbol(React, Decl(foo.tsx, 0, 0)) + +export const FooComponent =
+>FooComponent : Symbol(FooComponent, Decl(foo.tsx, 3, 12)) +>div : Symbol(unknown) +>div : Symbol(unknown) + +=== tests/cases/compiler/node_modules/@types/react/index.d.ts === +export = React; +>React : Symbol(React, Decl(index.d.ts, 1, 26)) + +export as namespace React; +>React : Symbol(React, Decl(index.d.ts, 0, 15)) + +declare namespace React { +>React : Symbol(React, Decl(index.d.ts, 1, 26)) + + function createClass(spec); +>createClass : Symbol(createClass, Decl(index.d.ts, 3, 25)) +>P : Symbol(P, Decl(index.d.ts, 4, 25)) +>S : Symbol(S, Decl(index.d.ts, 4, 27)) +>spec : Symbol(spec, Decl(index.d.ts, 4, 31)) +} +declare global { +>global : Symbol(global, Decl(index.d.ts, 5, 1)) + + namespace JSX { +>JSX : Symbol(JSX, Decl(index.d.ts, 6, 16)) + } +} + + + diff --git a/tests/baselines/reference/unusedImports13.types b/tests/baselines/reference/unusedImports13.types new file mode 100644 index 00000000000..b19531b3e2d --- /dev/null +++ b/tests/baselines/reference/unusedImports13.types @@ -0,0 +1,37 @@ +=== tests/cases/compiler/foo.tsx === + +import React = require("react"); +>React : typeof React + +export const FooComponent =
+>FooComponent : any +>
: any +>div : any +>div : any + +=== tests/cases/compiler/node_modules/@types/react/index.d.ts === +export = React; +>React : typeof React + +export as namespace React; +>React : typeof React + +declare namespace React { +>React : typeof React + + function createClass(spec); +>createClass : (spec: any) => any +>P : P +>S : S +>spec : any +} +declare global { +>global : any + + namespace JSX { +>JSX : any + } +} + + + From 23e9e0ba63095a38f6bfa6ebb412610aad1300b2 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 18 Oct 2016 12:24:41 -0700 Subject: [PATCH 07/49] Adding testcases for reactnamespace --- tests/baselines/reference/unusedImports15.js | 27 ++++++++++++++ .../reference/unusedImports15.symbols | 36 ++++++++++++++++++ .../baselines/reference/unusedImports15.types | 37 +++++++++++++++++++ tests/baselines/reference/unusedImports16.js | 27 ++++++++++++++ .../reference/unusedImports16.symbols | 36 ++++++++++++++++++ .../baselines/reference/unusedImports16.types | 37 +++++++++++++++++++ tests/cases/compiler/unusedImports13.ts | 1 - tests/cases/compiler/unusedImports14.ts | 1 - tests/cases/compiler/unusedImports15.ts | 23 ++++++++++++ tests/cases/compiler/unusedImports16.ts | 23 ++++++++++++ 10 files changed, 246 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/unusedImports15.js create mode 100644 tests/baselines/reference/unusedImports15.symbols create mode 100644 tests/baselines/reference/unusedImports15.types create mode 100644 tests/baselines/reference/unusedImports16.js create mode 100644 tests/baselines/reference/unusedImports16.symbols create mode 100644 tests/baselines/reference/unusedImports16.types create mode 100644 tests/cases/compiler/unusedImports15.ts create mode 100644 tests/cases/compiler/unusedImports16.ts diff --git a/tests/baselines/reference/unusedImports15.js b/tests/baselines/reference/unusedImports15.js new file mode 100644 index 00000000000..01be756e0e5 --- /dev/null +++ b/tests/baselines/reference/unusedImports15.js @@ -0,0 +1,27 @@ +//// [tests/cases/compiler/unusedImports15.ts] //// + +//// [foo.tsx] + +import Element = require("react"); + +export const FooComponent =
+ +//// [index.d.ts] +export = React; +export as namespace React; + +declare namespace React { + function createClass(spec); +} +declare global { + namespace JSX { + } +} + + + + +//// [foo.jsx] +"use strict"; +var Element = require("react"); +exports.FooComponent =
; diff --git a/tests/baselines/reference/unusedImports15.symbols b/tests/baselines/reference/unusedImports15.symbols new file mode 100644 index 00000000000..a325dc7ed76 --- /dev/null +++ b/tests/baselines/reference/unusedImports15.symbols @@ -0,0 +1,36 @@ +=== tests/cases/compiler/foo.tsx === + +import Element = require("react"); +>Element : Symbol(Element, Decl(foo.tsx, 0, 0)) + +export const FooComponent =
+>FooComponent : Symbol(FooComponent, Decl(foo.tsx, 3, 12)) +>div : Symbol(unknown) +>div : Symbol(unknown) + +=== tests/cases/compiler/node_modules/@types/react/index.d.ts === +export = React; +>React : Symbol(React, Decl(index.d.ts, 1, 26)) + +export as namespace React; +>React : Symbol(React, Decl(index.d.ts, 0, 15)) + +declare namespace React { +>React : Symbol(React, Decl(index.d.ts, 1, 26)) + + function createClass(spec); +>createClass : Symbol(createClass, Decl(index.d.ts, 3, 25)) +>P : Symbol(P, Decl(index.d.ts, 4, 25)) +>S : Symbol(S, Decl(index.d.ts, 4, 27)) +>spec : Symbol(spec, Decl(index.d.ts, 4, 31)) +} +declare global { +>global : Symbol(global, Decl(index.d.ts, 5, 1)) + + namespace JSX { +>JSX : Symbol(JSX, Decl(index.d.ts, 6, 16)) + } +} + + + diff --git a/tests/baselines/reference/unusedImports15.types b/tests/baselines/reference/unusedImports15.types new file mode 100644 index 00000000000..c222eaa03a3 --- /dev/null +++ b/tests/baselines/reference/unusedImports15.types @@ -0,0 +1,37 @@ +=== tests/cases/compiler/foo.tsx === + +import Element = require("react"); +>Element : typeof Element + +export const FooComponent =
+>FooComponent : any +>
: any +>div : any +>div : any + +=== tests/cases/compiler/node_modules/@types/react/index.d.ts === +export = React; +>React : typeof React + +export as namespace React; +>React : typeof React + +declare namespace React { +>React : typeof React + + function createClass(spec); +>createClass : (spec: any) => any +>P : P +>S : S +>spec : any +} +declare global { +>global : any + + namespace JSX { +>JSX : any + } +} + + + diff --git a/tests/baselines/reference/unusedImports16.js b/tests/baselines/reference/unusedImports16.js new file mode 100644 index 00000000000..1ce802d869a --- /dev/null +++ b/tests/baselines/reference/unusedImports16.js @@ -0,0 +1,27 @@ +//// [tests/cases/compiler/unusedImports16.ts] //// + +//// [foo.tsx] + +import Element = require("react"); + +export const FooComponent =
+ +//// [index.d.ts] +export = React; +export as namespace React; + +declare namespace React { + function createClass(spec); +} +declare global { + namespace JSX { + } +} + + + + +//// [foo.js] +"use strict"; +var Element = require("react"); +exports.FooComponent = Element.createElement("div", null); diff --git a/tests/baselines/reference/unusedImports16.symbols b/tests/baselines/reference/unusedImports16.symbols new file mode 100644 index 00000000000..a325dc7ed76 --- /dev/null +++ b/tests/baselines/reference/unusedImports16.symbols @@ -0,0 +1,36 @@ +=== tests/cases/compiler/foo.tsx === + +import Element = require("react"); +>Element : Symbol(Element, Decl(foo.tsx, 0, 0)) + +export const FooComponent =
+>FooComponent : Symbol(FooComponent, Decl(foo.tsx, 3, 12)) +>div : Symbol(unknown) +>div : Symbol(unknown) + +=== tests/cases/compiler/node_modules/@types/react/index.d.ts === +export = React; +>React : Symbol(React, Decl(index.d.ts, 1, 26)) + +export as namespace React; +>React : Symbol(React, Decl(index.d.ts, 0, 15)) + +declare namespace React { +>React : Symbol(React, Decl(index.d.ts, 1, 26)) + + function createClass(spec); +>createClass : Symbol(createClass, Decl(index.d.ts, 3, 25)) +>P : Symbol(P, Decl(index.d.ts, 4, 25)) +>S : Symbol(S, Decl(index.d.ts, 4, 27)) +>spec : Symbol(spec, Decl(index.d.ts, 4, 31)) +} +declare global { +>global : Symbol(global, Decl(index.d.ts, 5, 1)) + + namespace JSX { +>JSX : Symbol(JSX, Decl(index.d.ts, 6, 16)) + } +} + + + diff --git a/tests/baselines/reference/unusedImports16.types b/tests/baselines/reference/unusedImports16.types new file mode 100644 index 00000000000..c222eaa03a3 --- /dev/null +++ b/tests/baselines/reference/unusedImports16.types @@ -0,0 +1,37 @@ +=== tests/cases/compiler/foo.tsx === + +import Element = require("react"); +>Element : typeof Element + +export const FooComponent =
+>FooComponent : any +>
: any +>div : any +>div : any + +=== tests/cases/compiler/node_modules/@types/react/index.d.ts === +export = React; +>React : typeof React + +export as namespace React; +>React : typeof React + +declare namespace React { +>React : typeof React + + function createClass(spec); +>createClass : (spec: any) => any +>P : P +>S : S +>spec : any +} +declare global { +>global : any + + namespace JSX { +>JSX : any + } +} + + + diff --git a/tests/cases/compiler/unusedImports13.ts b/tests/cases/compiler/unusedImports13.ts index 875e36abab2..af188e70eae 100644 --- a/tests/cases/compiler/unusedImports13.ts +++ b/tests/cases/compiler/unusedImports13.ts @@ -1,5 +1,4 @@ //@noUnusedLocals:true -//@noUnusedParameters:true //@module: commonjs //@jsx: preserve diff --git a/tests/cases/compiler/unusedImports14.ts b/tests/cases/compiler/unusedImports14.ts index 89e4b31cb13..1823d4bb49b 100644 --- a/tests/cases/compiler/unusedImports14.ts +++ b/tests/cases/compiler/unusedImports14.ts @@ -1,5 +1,4 @@ //@noUnusedLocals:true -//@noUnusedParameters:true //@module: commonjs //@jsx: react diff --git a/tests/cases/compiler/unusedImports15.ts b/tests/cases/compiler/unusedImports15.ts new file mode 100644 index 00000000000..ef6b277d21d --- /dev/null +++ b/tests/cases/compiler/unusedImports15.ts @@ -0,0 +1,23 @@ +//@noUnusedLocals:true +//@module: commonjs +//@reactNamespace: Element +//@jsx: preserve + +// @filename: foo.tsx +import Element = require("react"); + +export const FooComponent =
+ +// @filename: node_modules/@types/react/index.d.ts +export = React; +export as namespace React; + +declare namespace React { + function createClass(spec); +} +declare global { + namespace JSX { + } +} + + diff --git a/tests/cases/compiler/unusedImports16.ts b/tests/cases/compiler/unusedImports16.ts new file mode 100644 index 00000000000..ff0c38bb75e --- /dev/null +++ b/tests/cases/compiler/unusedImports16.ts @@ -0,0 +1,23 @@ +//@noUnusedLocals:true +//@module: commonjs +//@reactNamespace: Element +//@jsx: react + +// @filename: foo.tsx +import Element = require("react"); + +export const FooComponent =
+ +// @filename: node_modules/@types/react/index.d.ts +export = React; +export as namespace React; + +declare namespace React { + function createClass(spec); +} +declare global { + namespace JSX { + } +} + + From 0365c96e37bbdf8cb3cdce2b3ff5cef98c79dd05 Mon Sep 17 00:00:00 2001 From: Dom Chen Date: Thu, 20 Oct 2016 04:07:49 +0800 Subject: [PATCH 08/49] =?UTF-8?q?Fix=20#11660:=20wrong=20reports=20that=20?= =?UTF-8?q?block-scoped=20variable=20used=20before=20its=20=E2=80=A6=20(#1?= =?UTF-8?q?1692)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix #11660: wrong reports that block-scoped variable used before its declaration * Fix code style in checker.ts * Add unit test for #11660 * Fix the unit test for #11660 --- src/compiler/checker.ts | 12 ++++-- .../reference/useBeforeDeclaration.js | 38 ++++++++++++++++++ .../reference/useBeforeDeclaration.symbols | 34 ++++++++++++++++ .../reference/useBeforeDeclaration.types | 39 +++++++++++++++++++ tests/cases/compiler/useBeforeDeclaration.ts | 20 ++++++++++ 5 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 tests/baselines/reference/useBeforeDeclaration.js create mode 100644 tests/baselines/reference/useBeforeDeclaration.symbols create mode 100644 tests/baselines/reference/useBeforeDeclaration.types create mode 100644 tests/cases/compiler/useBeforeDeclaration.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3c7ff7112e0..a0a2fa19f19 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -591,7 +591,11 @@ namespace ts { // nodes are in different files and order cannot be determines return true; } - + // declaration is after usage + // can be legal if usage is deferred (i.e. inside function or in initializer of instance property) + if (isUsedInFunctionOrNonStaticProperty(usage)) { + return true; + } const sourceFiles = host.getSourceFiles(); return indexOf(sourceFiles, declarationFile) <= indexOf(sourceFiles, useFile); } @@ -605,7 +609,8 @@ namespace ts { // declaration is after usage // can be legal if usage is deferred (i.e. inside function or in initializer of instance property) - return isUsedInFunctionOrNonStaticProperty(declaration, usage); + const container = getEnclosingBlockScopeContainer(declaration); + return isUsedInFunctionOrNonStaticProperty(usage, container); function isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration: VariableDeclaration, usage: Node): boolean { const container = getEnclosingBlockScopeContainer(declaration); @@ -634,8 +639,7 @@ namespace ts { return false; } - function isUsedInFunctionOrNonStaticProperty(declaration: Declaration, usage: Node): boolean { - const container = getEnclosingBlockScopeContainer(declaration); + function isUsedInFunctionOrNonStaticProperty(usage: Node, container?: Node): boolean { let current = usage; while (current) { if (current === container) { diff --git a/tests/baselines/reference/useBeforeDeclaration.js b/tests/baselines/reference/useBeforeDeclaration.js new file mode 100644 index 00000000000..6eab8683aec --- /dev/null +++ b/tests/baselines/reference/useBeforeDeclaration.js @@ -0,0 +1,38 @@ +//// [tests/cases/compiler/useBeforeDeclaration.ts] //// + +//// [A.ts] + +namespace ts { + export function printVersion():void { + log("Version: " + sys.version); // the call of sys.version is deferred, should not report an error. + } + + export function log(info:string):void { + + } +} + +//// [B.ts] +namespace ts { + + export let sys:{version:string} = {version: "2.0.5"}; + +} + + + +//// [test.js] +var ts; +(function (ts) { + function printVersion() { + log("Version: " + ts.sys.version); // the call of sys.version is deferred, should not report an error. + } + ts.printVersion = printVersion; + function log(info) { + } + ts.log = log; +})(ts || (ts = {})); +var ts; +(function (ts) { + ts.sys = { version: "2.0.5" }; +})(ts || (ts = {})); diff --git a/tests/baselines/reference/useBeforeDeclaration.symbols b/tests/baselines/reference/useBeforeDeclaration.symbols new file mode 100644 index 00000000000..484255c0c7d --- /dev/null +++ b/tests/baselines/reference/useBeforeDeclaration.symbols @@ -0,0 +1,34 @@ +=== tests/cases/compiler/A.ts === + +namespace ts { +>ts : Symbol(ts, Decl(A.ts, 0, 0), Decl(B.ts, 0, 0)) + + export function printVersion():void { +>printVersion : Symbol(printVersion, Decl(A.ts, 1, 14)) + + log("Version: " + sys.version); // the call of sys.version is deferred, should not report an error. +>log : Symbol(log, Decl(A.ts, 4, 5)) +>sys.version : Symbol(version, Decl(B.ts, 2, 20)) +>sys : Symbol(sys, Decl(B.ts, 2, 14)) +>version : Symbol(version, Decl(B.ts, 2, 20)) + } + + export function log(info:string):void { +>log : Symbol(log, Decl(A.ts, 4, 5)) +>info : Symbol(info, Decl(A.ts, 6, 24)) + + } +} + +=== tests/cases/compiler/B.ts === +namespace ts { +>ts : Symbol(ts, Decl(A.ts, 0, 0), Decl(B.ts, 0, 0)) + + export let sys:{version:string} = {version: "2.0.5"}; +>sys : Symbol(sys, Decl(B.ts, 2, 14)) +>version : Symbol(version, Decl(B.ts, 2, 20)) +>version : Symbol(version, Decl(B.ts, 2, 39)) + +} + + diff --git a/tests/baselines/reference/useBeforeDeclaration.types b/tests/baselines/reference/useBeforeDeclaration.types new file mode 100644 index 00000000000..141826723cf --- /dev/null +++ b/tests/baselines/reference/useBeforeDeclaration.types @@ -0,0 +1,39 @@ +=== tests/cases/compiler/A.ts === + +namespace ts { +>ts : typeof ts + + export function printVersion():void { +>printVersion : () => void + + log("Version: " + sys.version); // the call of sys.version is deferred, should not report an error. +>log("Version: " + sys.version) : void +>log : (info: string) => void +>"Version: " + sys.version : string +>"Version: " : "Version: " +>sys.version : string +>sys : { version: string; } +>version : string + } + + export function log(info:string):void { +>log : (info: string) => void +>info : string + + } +} + +=== tests/cases/compiler/B.ts === +namespace ts { +>ts : typeof ts + + export let sys:{version:string} = {version: "2.0.5"}; +>sys : { version: string; } +>version : string +>{version: "2.0.5"} : { version: string; } +>version : string +>"2.0.5" : "2.0.5" + +} + + diff --git a/tests/cases/compiler/useBeforeDeclaration.ts b/tests/cases/compiler/useBeforeDeclaration.ts new file mode 100644 index 00000000000..6dc3b026d13 --- /dev/null +++ b/tests/cases/compiler/useBeforeDeclaration.ts @@ -0,0 +1,20 @@ +// @outFile: test.js + +// @fileName: A.ts +namespace ts { + export function printVersion():void { + log("Version: " + sys.version); // the call of sys.version is deferred, should not report an error. + } + + export function log(info:string):void { + + } +} + +// @fileName: B.ts +namespace ts { + + export let sys:{version:string} = {version: "2.0.5"}; + +} + From 4fbbbed321baa58418b9379b418278d31400fe3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=28=C2=B4=E3=83=BB=CF=89=E3=83=BB=EF=BD=80=29?= Date: Thu, 20 Oct 2016 05:10:44 +0800 Subject: [PATCH 09/49] fix #11670, support type guards in NumberConstructor (#11722) --- src/lib/es2015.core.d.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/es2015.core.d.ts b/src/lib/es2015.core.d.ts index 356512ecf58..28f2e12248b 100644 --- a/src/lib/es2015.core.d.ts +++ b/src/lib/es2015.core.d.ts @@ -205,13 +205,13 @@ interface NumberConstructor { * number. Only finite values of the type number, result in true. * @param number A numeric value. */ - isFinite(number: number): boolean; + isFinite(value: any): value is number; /** * Returns true if the value passed is an integer, false otherwise. * @param number A numeric value. */ - isInteger(number: number): boolean; + isInteger(value: any): value is number; /** * Returns a Boolean value that indicates whether a value is the reserved value NaN (not a @@ -219,13 +219,13 @@ interface NumberConstructor { * to a number. Only values of the type number, that are also NaN, result in true. * @param number A numeric value. */ - isNaN(number: number): boolean; + isNaN(value: any): value is number; /** * Returns true if the value passed is a safe integer. * @param number A numeric value. */ - isSafeInteger(number: number): boolean; + isSafeInteger(value: any): value is number; /** * The value of the largest integer n such that n and n + 1 are both exactly representable as From 5e7e5421fa1049deaef4995bcc8dd28ece334268 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 19 Oct 2016 14:15:14 -0700 Subject: [PATCH 10/49] Add noop, notImplemented, and notYetImplemented helpers --- src/compiler/core.ts | 13 ++++++++ src/compiler/declarationEmitter.ts | 2 +- src/compiler/program.ts | 3 +- src/compiler/utilities.ts | 8 ++--- src/compiler/visitor.ts | 6 ++-- src/harness/harness.ts | 4 +-- src/harness/harnessLanguageService.ts | 20 +++++------ src/harness/runner.ts | 2 +- .../unittests/cachingInServerLSHost.ts | 33 +++++++++---------- src/harness/unittests/moduleResolution.ts | 20 ++++------- .../unittests/reuseProgramStructure.ts | 14 ++++---- src/harness/unittests/session.ts | 22 ++++++------- .../unittests/tsserverProjectSystem.ts | 8 ++--- src/server/client.ts | 28 ++++++++-------- src/server/typingsCache.ts | 6 ++-- .../typingsInstaller/typingsInstaller.ts | 2 +- src/services/services.ts | 2 +- src/services/utilities.ts | 4 +-- 18 files changed, 95 insertions(+), 102 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 884f4c94267..cb469255d0a 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -869,6 +869,19 @@ namespace ts { return Array.isArray ? Array.isArray(value) : value instanceof Array; } + /** Does nothing. */ + export function noop(): void {} + + /** A function not intended to be implemented. */ + export function notImplemented(): never { + throw new Error("Not implemented"); + } + + /** A function which isn't implemented, but should be eventually. */ + export function notYetImplemented(): never { + throw new Error("TODO: Not yet implemented"); + } + export function memoize(callback: () => T): () => T { let value: T; return () => { diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index 27751a1dfc2..6458183b23c 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -63,7 +63,7 @@ namespace ts { let isCurrentFileExternalModule: boolean; let reportedDeclarationError = false; let errorNameNode: DeclarationName; - const emitJsDocComments = compilerOptions.removeComments ? () => {} : writeJsDocComments; + const emitJsDocComments = compilerOptions.removeComments ? noop : writeJsDocComments; const emit = compilerOptions.stripInternal ? stripInternal : emitNode; let noDeclare: boolean; diff --git a/src/compiler/program.ts b/src/compiler/program.ts index b34791b7198..b619f005ded 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -950,8 +950,7 @@ namespace ts { return runWithCancellationToken(() => { const resolver = getDiagnosticsProducingTypeChecker().getEmitResolver(sourceFile, cancellationToken); // Don't actually write any files since we're just getting diagnostics. - const writeFile: WriteFileCallback = () => { }; - return ts.getDeclarationDiagnostics(getEmitHost(writeFile), resolver, sourceFile); + return ts.getDeclarationDiagnostics(getEmitHost(noop), resolver, sourceFile); }); } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 55374a1cd74..87f6cd5e4aa 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -63,11 +63,11 @@ namespace ts { // Completely ignore indentation for string writers. And map newlines to // a single space. writeLine: () => str += " ", - increaseIndent: () => { }, - decreaseIndent: () => { }, + increaseIndent: noop, + decreaseIndent: noop, clear: () => str = "", - trackSymbol: () => { }, - reportInaccessibleThisError: () => { } + trackSymbol: noop, + reportInaccessibleThisError: noop }; } diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 5c152c88e47..7d5558d5fb2 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -1331,18 +1331,18 @@ namespace ts { export namespace Debug { export const failNotOptional = shouldAssert(AssertionLevel.Normal) ? (message?: string) => assert(false, message || "Node not optional.") - : () => {}; + : noop; export const failBadSyntaxKind = shouldAssert(AssertionLevel.Normal) ? (node: Node, message?: string) => assert(false, message || "Unexpected node.", () => `Node ${formatSyntaxKind(node.kind)} was unexpected.`) - : () => {}; + : noop; export const assertNode = shouldAssert(AssertionLevel.Normal) ? (node: Node, test: (node: Node) => boolean, message?: string) => assert( test === undefined || test(node), message || "Unexpected node.", () => `Node ${formatSyntaxKind(node.kind)} did not pass test '${getFunctionName(test)}'.`) - : () => {}; + : noop; function getFunctionName(func: Function) { if (typeof func !== "function") { diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 7bfd94637b5..32681421d24 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -690,7 +690,7 @@ namespace Harness { export const getCurrentDirectory = () => ""; export const args = () => []; export const getExecutingFilePath = () => ""; - export const exit = () => { }; + export const exit = ts.noop; export const getDirectories = () => []; export let log = (s: string) => console.log(s); @@ -1668,7 +1668,7 @@ namespace Harness { // This does not need to exist strictly speaking, but many tests will need to be updated if it's removed export function compileString(_code: string, _unitName: string, _callback: (result: CompilerResult) => void) { // NEWTODO: Re-implement 'compileString' - throw new Error("compileString NYI"); + return ts.notYetImplemented(); } export interface GeneratedFile { diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 28b57728e1f..a2e1f52f7ea 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -313,14 +313,10 @@ namespace Harness.LanguageService { getLocalizedDiagnosticMessages(): string { return JSON.stringify({}); } readDirectory(_rootDir: string, _extension: string): string { - throw new Error("NYI"); - } - readDirectoryNames(_path: string): string { - throw new Error("Not implemented."); - } - readFileNames(_path: string): string { - throw new Error("Not implemented."); + return ts.notYetImplemented(); } + readDirectoryNames = ts.notImplemented; + readFileNames = ts.notImplemented; fileExists(fileName: string) { return this.getScriptInfo(fileName) !== undefined; } readFile(fileName: string) { const snapshot = this.nativeHost.getScriptSnapshot(fileName); @@ -339,7 +335,7 @@ namespace Harness.LanguageService { constructor(private shim: ts.ClassifierShim) { } getEncodedLexicalClassifications(_text: string, _lexState: ts.EndOfLineState, _classifyKeywordsInGenerics?: boolean): ts.Classifications { - throw new Error("NYI"); + return ts.notYetImplemented(); } getClassificationsForLine(text: string, lexState: ts.EndOfLineState, classifyKeywordsInGenerics?: boolean): ts.ClassificationResult { const result = this.shim.getClassificationsForLine(text, lexState, classifyKeywordsInGenerics).split("\n"); @@ -648,7 +644,7 @@ namespace Harness.LanguageService { } createDirectory(_directoryName: string): void { - throw new Error("Not Implemented Yet."); + return ts.notYetImplemented(); } getCurrentDirectory(): string { @@ -664,15 +660,15 @@ namespace Harness.LanguageService { } readDirectory(_path: string, _extension?: string[], _exclude?: string[], _include?: string[]): string[] { - throw new Error("Not implemented Yet."); + return ts.notYetImplemented(); } watchFile(): ts.FileWatcher { - return { close() { } }; + return { close: ts.noop }; } watchDirectory(): ts.FileWatcher { - return { close() { } }; + return { close: ts.noop }; } close(): void { diff --git a/src/harness/runner.ts b/src/harness/runner.ts index f8df1c51683..3db1da627bc 100644 --- a/src/harness/runner.ts +++ b/src/harness/runner.ts @@ -222,5 +222,5 @@ else { } if (!runUnitTests) { // patch `describe` to skip unit tests - describe = (function () { }); + describe = ts.noop as any; } diff --git a/src/harness/unittests/cachingInServerLSHost.ts b/src/harness/unittests/cachingInServerLSHost.ts index 0a522ddb22e..f930ab393d3 100644 --- a/src/harness/unittests/cachingInServerLSHost.ts +++ b/src/harness/unittests/cachingInServerLSHost.ts @@ -21,15 +21,15 @@ namespace ts { args: [], newLine: "\r\n", useCaseSensitiveFileNames: false, - write: () => { }, + write: noop, readFile: (path: string): string => { return path in fileMap ? fileMap[path].content : undefined; }, writeFile: (_path: string, _data: string, _writeByteOrderMark?: boolean) => { - throw new Error("NYI"); + return ts.notYetImplemented(); }, resolvePath: (_path: string): string => { - throw new Error("NYI"); + return ts.notYetImplemented(); }, fileExists: (path: string): boolean => { return path in fileMap; @@ -37,7 +37,7 @@ namespace ts { directoryExists: (path: string): boolean => { return existingDirectories[path] || false; }, - createDirectory: () => { }, + createDirectory: noop, getExecutingFilePath: (): string => { return ""; }, @@ -47,14 +47,14 @@ namespace ts { getDirectories: () => [], getEnvironmentVariable: () => "", readDirectory: (_path: string, _extension?: string[], _exclude?: string[], _include?: string[]): string[] => { - throw new Error("NYI"); + return ts.notYetImplemented(); }, - exit: () => { }, + exit: noop, watchFile: () => ({ - close: () => { } + close: noop }), watchDirectory: () => ({ - close: () => { } + close: noop }), setTimeout, clearTimeout, @@ -65,14 +65,14 @@ namespace ts { function createProject(rootFile: string, serverHost: server.ServerHost): { project: server.Project, rootScriptInfo: server.ScriptInfo } { const logger: server.Logger = { - close() { }, + close: noop, hasLevel: () => false, loggingEnabled: () => false, - perftrc: () => { }, - info: () => { }, - startGroup: () => { }, - endGroup: () => { }, - msg: () => { }, + perftrc: noop, + info: noop, + startGroup: noop, + endGroup: noop, + msg: noop, getLogFileName: (): string => undefined }; @@ -109,10 +109,7 @@ namespace ts { const originalFileExists = serverHost.fileExists; { // patch fileExists to make sure that disk is not touched - serverHost.fileExists = (): boolean => { - assert.isTrue(false, "fileExists should not be called"); - return false; - }; + serverHost.fileExists = notImplemented; const newContent = `import {x} from "f1" var x: string = 1;`; diff --git a/src/harness/unittests/moduleResolution.ts b/src/harness/unittests/moduleResolution.ts index c21d2df4019..b71d42265e3 100644 --- a/src/harness/unittests/moduleResolution.ts +++ b/src/harness/unittests/moduleResolution.ts @@ -290,7 +290,7 @@ namespace ts { return path in files ? createSourceFile(fileName, files[path], languageVersion) : undefined; }, getDefaultLibFileName: () => "lib.d.ts", - writeFile: (): void => { throw new Error("NotImplemented"); }, + writeFile: notImplemented, getCurrentDirectory: () => currentDirectory, getDirectories: () => [], getCanonicalFileName: fileName => fileName.toLowerCase(), @@ -300,7 +300,7 @@ namespace ts { const path = normalizePath(combinePaths(currentDirectory, fileName)); return path in files; }, - readFile: (): string => { throw new Error("NotImplemented"); } + readFile: notImplemented }; const program = createProgram(rootFiles, options, host); @@ -370,7 +370,7 @@ export = C; return path in files ? createSourceFile(fileName, files[path], languageVersion) : undefined; }, getDefaultLibFileName: () => "lib.d.ts", - writeFile: (): void => { throw new Error("NotImplemented"); }, + writeFile: notImplemented, getCurrentDirectory: () => currentDirectory, getDirectories: () => [], getCanonicalFileName, @@ -380,7 +380,7 @@ export = C; const path = getCanonicalFileName(normalizePath(combinePaths(currentDirectory, fileName))); return path in files; }, - readFile: (): string => { throw new Error("NotImplemented"); } + readFile: notImplemented }; const program = createProgram(rootFiles, options, host); const diagnostics = sortAndDeduplicateDiagnostics(program.getSemanticDiagnostics().concat(program.getOptionsDiagnostics())); @@ -911,15 +911,11 @@ import b = require("./moduleB"); }); }); - function notImplemented(name: string): () => any { - return () => assert(`${name} is not implemented and should not be called`); - } - describe("ModuleResolutionHost.directoryExists", () => { it("No 'fileExists' calls if containing directory is missing", () => { const host: ModuleResolutionHost = { - readFile: notImplemented("readFile"), - fileExists: notImplemented("fileExists"), + readFile: notImplemented, + fileExists: notImplemented, directoryExists: _ => false }; @@ -1022,9 +1018,7 @@ import b = require("./moduleB"); fileExists : fileName => fileName in sourceFiles, getSourceFile: fileName => sourceFiles[fileName], getDefaultLibFileName: () => "lib.d.ts", - writeFile(_file, _text) { - throw new Error("NYI"); - }, + writeFile: notImplemented, getCurrentDirectory: () => "/", getDirectories: () => [], getCanonicalFileName: f => f.toLowerCase(), diff --git a/src/harness/unittests/reuseProgramStructure.ts b/src/harness/unittests/reuseProgramStructure.ts index fcdfc6d08c6..29bb9a05b7c 100644 --- a/src/harness/unittests/reuseProgramStructure.ts +++ b/src/harness/unittests/reuseProgramStructure.ts @@ -111,9 +111,7 @@ namespace ts { getDefaultLibFileName(): string { return "lib.d.ts"; }, - writeFile() { - throw new Error("NYI"); - }, + writeFile: notYetImplemented, getCurrentDirectory(): string { return ""; }, @@ -244,13 +242,13 @@ namespace ts { it("fails if change affects type references", () => { const program_1 = newProgram(files, ["a.ts"], { types: ["a"] }); - updateProgram(program_1, ["a.ts"], { types: ["b"] }, () => { }); + updateProgram(program_1, ["a.ts"], { types: ["b"] }, noop); assert.isTrue(!program_1.structureIsReused); }); it("succeeds if change doesn't affect type references", () => { const program_1 = newProgram(files, ["a.ts"], { types: ["a"] }); - updateProgram(program_1, ["a.ts"], { types: ["a"] }, () => { }); + updateProgram(program_1, ["a.ts"], { types: ["a"] }, noop); assert.isTrue(program_1.structureIsReused); }); @@ -276,19 +274,19 @@ namespace ts { it("fails if module kind changes", () => { const program_1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS }); - updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.AMD }, () => { }); + updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.AMD }, noop); assert.isTrue(!program_1.structureIsReused); }); it("fails if rootdir changes", () => { const program_1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS, rootDir: "/a/b" }); - updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.CommonJS, rootDir: "/a/c" }, () => { }); + updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.CommonJS, rootDir: "/a/c" }, noop); assert.isTrue(!program_1.structureIsReused); }); it("fails if config path changes", () => { const program_1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS, configFilePath: "/a/b/tsconfig.json" }); - updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.CommonJS, configFilePath: "/a/c/tsconfig.json" }, () => { }); + updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.CommonJS, configFilePath: "/a/c/tsconfig.json" }, noop); assert.isTrue(!program_1.structureIsReused); }); diff --git a/src/harness/unittests/session.ts b/src/harness/unittests/session.ts index 0d8fb31e5d8..dd9efe51a4a 100644 --- a/src/harness/unittests/session.ts +++ b/src/harness/unittests/session.ts @@ -10,32 +10,32 @@ namespace ts.server { useCaseSensitiveFileNames: true, write(s): void { lastWrittenToHost = s; }, readFile(): string { return void 0; }, - writeFile(): void {}, + writeFile: noop, resolvePath(): string { return void 0; }, fileExists: () => false, directoryExists: () => false, getDirectories: () => [], - createDirectory(): void {}, + createDirectory: noop, getExecutingFilePath(): string { return void 0; }, getCurrentDirectory(): string { return void 0; }, getEnvironmentVariable(): string { return ""; }, readDirectory(): string[] { return []; }, - exit(): void { }, + exit: noop, setTimeout() { return 0; }, - clearTimeout() { }, + clearTimeout: noop, setImmediate: () => 0, - clearImmediate() {} + clearImmediate: noop }; const nullCancellationToken: HostCancellationToken = { isCancellationRequested: () => false }; const mockLogger: Logger = { - close(): void {}, + close: noop, hasLevel(): boolean { return false; }, loggingEnabled(): boolean { return false; }, - perftrc(): void {}, - info(): void {}, - startGroup(): void {}, - endGroup(): void {}, - msg(): void {}, + perftrc: noop, + info: noop, + startGroup: noop, + endGroup: noop, + msg: noop, getLogFileName: (): string => undefined }; diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index a1ef87c2483..ff92f556eab 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -23,10 +23,6 @@ namespace ts.projectSystem { readonly callback: TI.RequestCompletedAction; } - export function notImplemented(): any { - throw new Error("Not yet implemented"); - } - export const nullLogger: server.Logger = { close: () => void 0, hasLevel: () => void 0, @@ -525,8 +521,8 @@ namespace ts.projectSystem { readonly resolvePath = (s: string) => s; readonly getExecutingFilePath = () => this.executingFilePath; readonly getCurrentDirectory = () => this.currentDirectory; - readonly exit = notImplemented; - readonly getEnvironmentVariable = notImplemented; + readonly exit = notYetImplemented; + readonly getEnvironmentVariable = notYetImplemented; } export function makeSessionRequest(command: string, args: T) { diff --git a/src/server/client.ts b/src/server/client.ts index adc55f96471..baa47ac6381 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -242,7 +242,7 @@ namespace ts.server { } getCompletionEntrySymbol(_fileName: string, _position: number, _entryName: string): Symbol { - throw new Error("Not Implemented Yet."); + return notYetImplemented(); } getNavigateToItems(searchValue: string): NavigateToItem[] { @@ -415,7 +415,7 @@ namespace ts.server { } getEmitOutput(_fileName: string): EmitOutput { - throw new Error("Not Implemented Yet."); + return notYetImplemented(); } getSyntacticDiagnostics(fileName: string): Diagnostic[] { @@ -457,7 +457,7 @@ namespace ts.server { } getCompilerOptionsDiagnostics(): Diagnostic[] { - throw new Error("Not Implemented Yet."); + return notYetImplemented(); } getRenameInfo(fileName: string, position: number, findInStrings?: boolean, findInComments?: boolean): RenameInfo { @@ -562,11 +562,11 @@ namespace ts.server { } getNameOrDottedNameSpan(_fileName: string, _startPos: number, _endPos: number): TextSpan { - throw new Error("Not Implemented Yet."); + return notYetImplemented(); } getBreakpointStatementAtPosition(_fileName: string, _position: number): TextSpan { - throw new Error("Not Implemented Yet."); + return notYetImplemented(); } getSignatureHelpItems(fileName: string, position: number): SignatureHelpItems { @@ -656,19 +656,19 @@ namespace ts.server { } getOutliningSpans(_fileName: string): OutliningSpan[] { - throw new Error("Not Implemented Yet."); + return notYetImplemented(); } getTodoComments(_fileName: string, _descriptors: TodoCommentDescriptor[]): TodoComment[] { - throw new Error("Not Implemented Yet."); + return notYetImplemented(); } getDocCommentTemplateAtPosition(_fileName: string, _position: number): TextInsertion { - throw new Error("Not Implemented Yet."); + return notYetImplemented(); } isValidBraceCompletionAtPosition(_fileName: string, _position: number, _openingBrace: number): boolean { - throw new Error("Not Implemented Yet."); + return notYetImplemented(); } getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: number[]): CodeAction[] { @@ -735,23 +735,23 @@ namespace ts.server { } getIndentationAtPosition(_fileName: string, _position: number, _options: EditorOptions): number { - throw new Error("Not Implemented Yet."); + return notYetImplemented(); } getSyntacticClassifications(_fileName: string, _span: TextSpan): ClassifiedSpan[] { - throw new Error("Not Implemented Yet."); + return notYetImplemented(); } getSemanticClassifications(_fileName: string, _span: TextSpan): ClassifiedSpan[] { - throw new Error("Not Implemented Yet."); + return notYetImplemented(); } getEncodedSyntacticClassifications(_fileName: string, _span: TextSpan): Classifications { - throw new Error("Not Implemented Yet."); + return notYetImplemented(); } getEncodedSemanticClassifications(_fileName: string, _span: TextSpan): Classifications { - throw new Error("Not Implemented Yet."); + return notYetImplemented(); } getProgram(): Program { diff --git a/src/server/typingsCache.ts b/src/server/typingsCache.ts index d522974cc5b..04c321b46f9 100644 --- a/src/server/typingsCache.ts +++ b/src/server/typingsCache.ts @@ -9,9 +9,9 @@ namespace ts.server { } export const nullTypingsInstaller: ITypingsInstaller = { - enqueueInstallTypingsRequest: () => {}, - attach: () => {}, - onProjectClosed: () => {}, + enqueueInstallTypingsRequest: noop, + attach: noop, + onProjectClosed: noop, globalTypingsCacheLocation: undefined }; diff --git a/src/server/typingsInstaller/typingsInstaller.ts b/src/server/typingsInstaller/typingsInstaller.ts index 58d56195068..2851da3a64d 100644 --- a/src/server/typingsInstaller/typingsInstaller.ts +++ b/src/server/typingsInstaller/typingsInstaller.ts @@ -15,7 +15,7 @@ namespace ts.server.typingsInstaller { const nullLog: Log = { isEnabled: () => false, - writeLine: () => {} + writeLine: noop }; function typingToFileName(cachePath: string, packageName: string, installTypingHost: InstallTypingHost): string { diff --git a/src/services/services.ts b/src/services/services.ts index 9ed73318d8e..06e8a4615f9 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1051,7 +1051,7 @@ namespace ts { useCaseSensitiveFileNames: () => useCaseSensitivefileNames, getNewLine: () => getNewLineOrDefaultFromHost(host), getDefaultLibFileName: (options) => host.getDefaultLibFileName(options), - writeFile: () => { }, + writeFile: noop, getCurrentDirectory: () => currentDirectory, fileExists: (fileName): boolean => { // stub missing host functionality diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 45dff60dd89..8fd5f510be0 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1142,8 +1142,8 @@ namespace ts { increaseIndent: () => { indent++; }, decreaseIndent: () => { indent--; }, clear: resetWriter, - trackSymbol: () => { }, - reportInaccessibleThisError: () => { } + trackSymbol: noop, + reportInaccessibleThisError: noop }; function writeIndent() { From ca970063a396bd9aacf1670e3032a0cc7c64c5fd Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 20 Oct 2016 07:15:25 -0700 Subject: [PATCH 11/49] Just use notImplemented --- src/compiler/core.ts | 7 +---- src/harness/harness.ts | 2 +- src/harness/harnessLanguageService.ts | 8 +++--- .../unittests/cachingInServerLSHost.ts | 6 ++-- .../unittests/reuseProgramStructure.ts | 2 +- .../unittests/tsserverProjectSystem.ts | 4 +-- src/server/client.ts | 28 +++++++++---------- 7 files changed, 26 insertions(+), 31 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index cb469255d0a..395ddb40574 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -872,16 +872,11 @@ namespace ts { /** Does nothing. */ export function noop(): void {} - /** A function not intended to be implemented. */ + /** Throws an error because a function is not implemented. */ export function notImplemented(): never { throw new Error("Not implemented"); } - /** A function which isn't implemented, but should be eventually. */ - export function notYetImplemented(): never { - throw new Error("TODO: Not yet implemented"); - } - export function memoize(callback: () => T): () => T { let value: T; return () => { diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 32681421d24..409ab9e2fcc 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1668,7 +1668,7 @@ namespace Harness { // This does not need to exist strictly speaking, but many tests will need to be updated if it's removed export function compileString(_code: string, _unitName: string, _callback: (result: CompilerResult) => void) { // NEWTODO: Re-implement 'compileString' - return ts.notYetImplemented(); + return ts.notImplemented(); } export interface GeneratedFile { diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index a2e1f52f7ea..889d4f28ce9 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -313,7 +313,7 @@ namespace Harness.LanguageService { getLocalizedDiagnosticMessages(): string { return JSON.stringify({}); } readDirectory(_rootDir: string, _extension: string): string { - return ts.notYetImplemented(); + return ts.notImplemented(); } readDirectoryNames = ts.notImplemented; readFileNames = ts.notImplemented; @@ -335,7 +335,7 @@ namespace Harness.LanguageService { constructor(private shim: ts.ClassifierShim) { } getEncodedLexicalClassifications(_text: string, _lexState: ts.EndOfLineState, _classifyKeywordsInGenerics?: boolean): ts.Classifications { - return ts.notYetImplemented(); + return ts.notImplemented(); } getClassificationsForLine(text: string, lexState: ts.EndOfLineState, classifyKeywordsInGenerics?: boolean): ts.ClassificationResult { const result = this.shim.getClassificationsForLine(text, lexState, classifyKeywordsInGenerics).split("\n"); @@ -644,7 +644,7 @@ namespace Harness.LanguageService { } createDirectory(_directoryName: string): void { - return ts.notYetImplemented(); + return ts.notImplemented(); } getCurrentDirectory(): string { @@ -660,7 +660,7 @@ namespace Harness.LanguageService { } readDirectory(_path: string, _extension?: string[], _exclude?: string[], _include?: string[]): string[] { - return ts.notYetImplemented(); + return ts.notImplemented(); } watchFile(): ts.FileWatcher { diff --git a/src/harness/unittests/cachingInServerLSHost.ts b/src/harness/unittests/cachingInServerLSHost.ts index f930ab393d3..953edd98b6a 100644 --- a/src/harness/unittests/cachingInServerLSHost.ts +++ b/src/harness/unittests/cachingInServerLSHost.ts @@ -26,10 +26,10 @@ namespace ts { return path in fileMap ? fileMap[path].content : undefined; }, writeFile: (_path: string, _data: string, _writeByteOrderMark?: boolean) => { - return ts.notYetImplemented(); + return ts.notImplemented(); }, resolvePath: (_path: string): string => { - return ts.notYetImplemented(); + return ts.notImplemented(); }, fileExists: (path: string): boolean => { return path in fileMap; @@ -47,7 +47,7 @@ namespace ts { getDirectories: () => [], getEnvironmentVariable: () => "", readDirectory: (_path: string, _extension?: string[], _exclude?: string[], _include?: string[]): string[] => { - return ts.notYetImplemented(); + return ts.notImplemented(); }, exit: noop, watchFile: () => ({ diff --git a/src/harness/unittests/reuseProgramStructure.ts b/src/harness/unittests/reuseProgramStructure.ts index 29bb9a05b7c..0d3a8b3977a 100644 --- a/src/harness/unittests/reuseProgramStructure.ts +++ b/src/harness/unittests/reuseProgramStructure.ts @@ -111,7 +111,7 @@ namespace ts { getDefaultLibFileName(): string { return "lib.d.ts"; }, - writeFile: notYetImplemented, + writeFile: notImplemented, getCurrentDirectory(): string { return ""; }, diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index ff92f556eab..2abfd66b983 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -521,8 +521,8 @@ namespace ts.projectSystem { readonly resolvePath = (s: string) => s; readonly getExecutingFilePath = () => this.executingFilePath; readonly getCurrentDirectory = () => this.currentDirectory; - readonly exit = notYetImplemented; - readonly getEnvironmentVariable = notYetImplemented; + readonly exit = notImplemented; + readonly getEnvironmentVariable = notImplemented; } export function makeSessionRequest(command: string, args: T) { diff --git a/src/server/client.ts b/src/server/client.ts index baa47ac6381..3b09a926754 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -242,7 +242,7 @@ namespace ts.server { } getCompletionEntrySymbol(_fileName: string, _position: number, _entryName: string): Symbol { - return notYetImplemented(); + return notImplemented(); } getNavigateToItems(searchValue: string): NavigateToItem[] { @@ -415,7 +415,7 @@ namespace ts.server { } getEmitOutput(_fileName: string): EmitOutput { - return notYetImplemented(); + return notImplemented(); } getSyntacticDiagnostics(fileName: string): Diagnostic[] { @@ -457,7 +457,7 @@ namespace ts.server { } getCompilerOptionsDiagnostics(): Diagnostic[] { - return notYetImplemented(); + return notImplemented(); } getRenameInfo(fileName: string, position: number, findInStrings?: boolean, findInComments?: boolean): RenameInfo { @@ -562,11 +562,11 @@ namespace ts.server { } getNameOrDottedNameSpan(_fileName: string, _startPos: number, _endPos: number): TextSpan { - return notYetImplemented(); + return notImplemented(); } getBreakpointStatementAtPosition(_fileName: string, _position: number): TextSpan { - return notYetImplemented(); + return notImplemented(); } getSignatureHelpItems(fileName: string, position: number): SignatureHelpItems { @@ -656,19 +656,19 @@ namespace ts.server { } getOutliningSpans(_fileName: string): OutliningSpan[] { - return notYetImplemented(); + return notImplemented(); } getTodoComments(_fileName: string, _descriptors: TodoCommentDescriptor[]): TodoComment[] { - return notYetImplemented(); + return notImplemented(); } getDocCommentTemplateAtPosition(_fileName: string, _position: number): TextInsertion { - return notYetImplemented(); + return notImplemented(); } isValidBraceCompletionAtPosition(_fileName: string, _position: number, _openingBrace: number): boolean { - return notYetImplemented(); + return notImplemented(); } getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: number[]): CodeAction[] { @@ -735,23 +735,23 @@ namespace ts.server { } getIndentationAtPosition(_fileName: string, _position: number, _options: EditorOptions): number { - return notYetImplemented(); + return notImplemented(); } getSyntacticClassifications(_fileName: string, _span: TextSpan): ClassifiedSpan[] { - return notYetImplemented(); + return notImplemented(); } getSemanticClassifications(_fileName: string, _span: TextSpan): ClassifiedSpan[] { - return notYetImplemented(); + return notImplemented(); } getEncodedSyntacticClassifications(_fileName: string, _span: TextSpan): Classifications { - return notYetImplemented(); + return notImplemented(); } getEncodedSemanticClassifications(_fileName: string, _span: TextSpan): Classifications { - return notYetImplemented(); + return notImplemented(); } getProgram(): Program { From df2f32bf0509ad60a9274e1612604f272c584b7f Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 20 Oct 2016 11:15:44 -0700 Subject: [PATCH 12/49] Properly distinguish between Type, ObjectType, and StructuredType --- src/compiler/checker.ts | 66 ++++++++++++++++++++--------------------- src/compiler/types.ts | 3 ++ 2 files changed, 36 insertions(+), 33 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 369d61ce484..c260b86e114 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -323,9 +323,9 @@ namespace ts { "undefined": undefinedType }); - let jsxElementType: ObjectType; + let jsxElementType: Type; /** Things we lazy load from the JSX namespace */ - const jsxTypes = createMap(); + const jsxTypes = createMap(); const JsxNames = { JSX: "JSX", IntrinsicElements: "IntrinsicElements", @@ -1620,7 +1620,7 @@ namespace ts { return result || emptyArray; } - function setObjectTypeMembers(type: ObjectType, members: SymbolTable, callSignatures: Signature[], constructSignatures: Signature[], stringIndexInfo: IndexInfo, numberIndexInfo: IndexInfo): ResolvedType { + function setStructuredTypeMembers(type: StructuredType, members: SymbolTable, callSignatures: Signature[], constructSignatures: Signature[], stringIndexInfo: IndexInfo, numberIndexInfo: IndexInfo): ResolvedType { (type).members = members; (type).properties = getNamedMembers(members); (type).callSignatures = callSignatures; @@ -1631,7 +1631,7 @@ namespace ts { } function createAnonymousType(symbol: Symbol, members: SymbolTable, callSignatures: Signature[], constructSignatures: Signature[], stringIndexInfo: IndexInfo, numberIndexInfo: IndexInfo): ResolvedType { - return setObjectTypeMembers(createObjectType(TypeFlags.Anonymous, symbol), + return setStructuredTypeMembers(createObjectType(TypeFlags.Anonymous, symbol), members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); } @@ -3519,7 +3519,7 @@ namespace ts { return unknownType; } - function getTargetType(type: ObjectType): Type { + function getTargetType(type: Type): Type { return type.flags & TypeFlags.Reference ? (type).target : type; } @@ -3603,13 +3603,13 @@ namespace ts { return getClassExtendsHeritageClauseElement(type.symbol.valueDeclaration); } - function getConstructorsForTypeArguments(type: ObjectType, typeArgumentNodes: TypeNode[]): Signature[] { + function getConstructorsForTypeArguments(type: Type, typeArgumentNodes: TypeNode[]): Signature[] { const typeArgCount = typeArgumentNodes ? typeArgumentNodes.length : 0; return filter(getSignaturesOfType(type, SignatureKind.Construct), sig => (sig.typeParameters ? sig.typeParameters.length : 0) === typeArgCount); } - function getInstantiatedConstructorsForTypeArguments(type: ObjectType, typeArgumentNodes: TypeNode[]): Signature[] { + function getInstantiatedConstructorsForTypeArguments(type: Type, typeArgumentNodes: TypeNode[]): Signature[] { let signatures = getConstructorsForTypeArguments(type, typeArgumentNodes); if (typeArgumentNodes) { const typeArguments = map(typeArgumentNodes, getTypeFromTypeNodeNoAlias); @@ -3623,7 +3623,7 @@ namespace ts { // unknownType if an error occurred during resolution of the extends expression, // nullType if the extends expression is the null value, or // an object type with at least one construct signature. - function getBaseConstructorTypeOfClass(type: InterfaceType): ObjectType { + function getBaseConstructorTypeOfClass(type: InterfaceType): Type { if (!type.resolvedBaseConstructorType) { const baseTypeNode = getBaseTypeNodeOfClass(type); if (!baseTypeNode) { @@ -3636,7 +3636,7 @@ namespace ts { if (baseConstructorType.flags & TypeFlags.ObjectType) { // Resolving the members of a class requires us to resolve the base class of that class. // We force resolution here such that we catch circularities now. - resolveStructuredTypeMembers(baseConstructorType); + resolveStructuredTypeMembers(baseConstructorType); } if (!popTypeResolution()) { error(type.symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_base_expression, symbolToString(type.symbol)); @@ -3673,7 +3673,7 @@ namespace ts { function resolveBaseTypesOfClass(type: InterfaceType): void { type.resolvedBaseTypes = type.resolvedBaseTypes || emptyArray; - const baseConstructorType = getBaseConstructorTypeOfClass(type); + const baseConstructorType = getBaseConstructorTypeOfClass(type); if (!(baseConstructorType.flags & TypeFlags.ObjectType)) { return; } @@ -3711,10 +3711,10 @@ namespace ts { return; } if (type.resolvedBaseTypes === emptyArray) { - type.resolvedBaseTypes = [baseType]; + type.resolvedBaseTypes = [baseType]; } else { - type.resolvedBaseTypes.push(baseType); + type.resolvedBaseTypes.push(baseType); } } @@ -3740,10 +3740,10 @@ namespace ts { if (getTargetType(baseType).flags & (TypeFlags.Class | TypeFlags.Interface)) { if (type !== baseType && !hasBaseType(baseType, type)) { if (type.resolvedBaseTypes === emptyArray) { - type.resolvedBaseTypes = [baseType]; + type.resolvedBaseTypes = [baseType]; } else { - type.resolvedBaseTypes.push(baseType); + type.resolvedBaseTypes.push(baseType); } } else { @@ -4093,7 +4093,7 @@ namespace ts { return type; } - function getTypeWithThisArgument(type: ObjectType, thisArgument?: Type) { + function getTypeWithThisArgument(type: Type, thisArgument?: Type) { if (type.flags & TypeFlags.Reference) { return createTypeReference((type).target, concatenate((type).typeArguments, [thisArgument || (type).target.thisType])); @@ -4131,7 +4131,7 @@ namespace ts { } const thisArgument = lastOrUndefined(typeArguments); for (const baseType of baseTypes) { - const instantiatedBaseType = thisArgument ? getTypeWithThisArgument(instantiateType(baseType, mapper), thisArgument) : baseType; + const instantiatedBaseType = thisArgument ? getTypeWithThisArgument(instantiateType(baseType, mapper), thisArgument) : baseType; addInheritedMembers(members, getPropertiesOfObjectType(instantiatedBaseType)); callSignatures = concatenate(callSignatures, getSignaturesOfType(instantiatedBaseType, SignatureKind.Call)); constructSignatures = concatenate(constructSignatures, getSignaturesOfType(instantiatedBaseType, SignatureKind.Construct)); @@ -4139,7 +4139,7 @@ namespace ts { numberIndexInfo = numberIndexInfo || getIndexInfoOfType(instantiatedBaseType, IndexKind.Number); } } - setObjectTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); + setStructuredTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); } function resolveClassOrInterfaceMembers(type: InterfaceType): void { @@ -4286,7 +4286,7 @@ namespace ts { const constructSignatures = getUnionSignatures(type.types, SignatureKind.Construct); const stringIndexInfo = getUnionIndexInfo(type.types, IndexKind.String); const numberIndexInfo = getUnionIndexInfo(type.types, IndexKind.Number); - setObjectTypeMembers(type, emptySymbols, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); + setStructuredTypeMembers(type, emptySymbols, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); } function intersectTypes(type1: Type, type2: Type): Type { @@ -4311,7 +4311,7 @@ namespace ts { stringIndexInfo = intersectIndexInfos(stringIndexInfo, getIndexInfoOfType(t, IndexKind.String)); numberIndexInfo = intersectIndexInfos(numberIndexInfo, getIndexInfoOfType(t, IndexKind.Number)); } - setObjectTypeMembers(type, emptySymbols, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); + setStructuredTypeMembers(type, emptySymbols, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); } function resolveAnonymousTypeMembers(type: AnonymousType) { @@ -4322,7 +4322,7 @@ namespace ts { const constructSignatures = instantiateList(getSignaturesOfType(type.target, SignatureKind.Construct), type.mapper, instantiateSignature); const stringIndexInfo = instantiateIndexInfo(getIndexInfoOfType(type.target, IndexKind.String), type.mapper); const numberIndexInfo = instantiateIndexInfo(getIndexInfoOfType(type.target, IndexKind.Number), type.mapper); - setObjectTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); + setStructuredTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); } else if (symbol.flags & SymbolFlags.TypeLiteral) { const members = symbol.members; @@ -4330,7 +4330,7 @@ namespace ts { const constructSignatures = getSignaturesOfSymbol(members["__new"]); const stringIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.String); const numberIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.Number); - setObjectTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); + setStructuredTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); } else { // Combinations of function, class, enum and module @@ -4352,7 +4352,7 @@ namespace ts { } } const numberIndexInfo = symbol.flags & SymbolFlags.Enum ? enumNumberIndexInfo : undefined; - setObjectTypeMembers(type, members, emptyArray, constructSignatures, undefined, numberIndexInfo); + setStructuredTypeMembers(type, members, emptyArray, constructSignatures, undefined, numberIndexInfo); // We resolve the members before computing the signatures because a signature may use // typeof with a qualified name expression that circularly references the type we are // in the process of resolving (see issue #6072). The temporarily empty signature list @@ -4363,7 +4363,7 @@ namespace ts { } } - function resolveStructuredTypeMembers(type: ObjectType): ResolvedType { + function resolveStructuredTypeMembers(type: StructuredType): ResolvedType { if (!(type).members) { if (type.flags & TypeFlags.Reference) { resolveTypeReferenceMembers(type); @@ -4569,7 +4569,7 @@ namespace ts { function getPropertyOfType(type: Type, name: string): Symbol { type = getApparentType(type); if (type.flags & TypeFlags.ObjectType) { - const resolved = resolveStructuredTypeMembers(type); + const resolved = resolveStructuredTypeMembers(type); const symbol = resolved.members[name]; if (symbol && symbolIsValue(symbol)) { return symbol; @@ -5316,7 +5316,7 @@ namespace ts { /** * Instantiates a global type that is generic with some element type, and returns that instantiation. */ - function createTypeFromGenericGlobalType(genericGlobalType: GenericType, typeArguments: Type[]): Type { + function createTypeFromGenericGlobalType(genericGlobalType: GenericType, typeArguments: Type[]): ObjectType { return genericGlobalType !== emptyGenericType ? createTypeReference(genericGlobalType, typeArguments) : emptyObjectType; } @@ -5328,7 +5328,7 @@ namespace ts { return createTypeFromGenericGlobalType(getGlobalIterableIteratorType(), [elementType]); } - function createArrayType(elementType: Type): Type { + function createArrayType(elementType: Type): ObjectType { return createTypeFromGenericGlobalType(globalArrayType, [elementType]); } @@ -5568,7 +5568,7 @@ namespace ts { let type = unionTypes[id]; if (!type) { const propagatedFlags = getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable); - type = unionTypes[id] = createObjectType(TypeFlags.Union | propagatedFlags); + type = unionTypes[id] = createType(TypeFlags.Union | propagatedFlags); type.types = types; type.aliasSymbol = aliasSymbol; type.aliasTypeArguments = aliasTypeArguments; @@ -5639,7 +5639,7 @@ namespace ts { let type = intersectionTypes[id]; if (!type) { const propagatedFlags = getPropagatingFlagsOfTypes(typeSet, /*excludeKinds*/ TypeFlags.Nullable); - type = intersectionTypes[id] = createObjectType(TypeFlags.Intersection | propagatedFlags); + type = intersectionTypes[id] = createType(TypeFlags.Intersection | propagatedFlags); type.types = typeSet; type.aliasSymbol = aliasSymbol; type.aliasTypeArguments = aliasTypeArguments; @@ -5980,7 +5980,7 @@ namespace ts { function instantiateAnonymousType(type: AnonymousType, mapper: TypeMapper): ObjectType { if (mapper.instantiations) { - const cachedType = mapper.instantiations[type.id]; + const cachedType = mapper.instantiations[type.id]; if (cachedType) { return cachedType; } @@ -6479,8 +6479,8 @@ namespace ts { containingMessageChain?: DiagnosticMessageChain): boolean { let errorInfo: DiagnosticMessageChain; - let sourceStack: ObjectType[]; - let targetStack: ObjectType[]; + let sourceStack: Type[]; + let targetStack: Type[]; let maybeStack: Map[]; let expandingFlags: number; let depth = 0; @@ -6697,7 +6697,7 @@ namespace ts { // type, a property is considered known if it is known in any constituent type. function isKnownProperty(type: Type, name: string): boolean { if (type.flags & TypeFlags.ObjectType) { - const resolved = resolveStructuredTypeMembers(type); + const resolved = resolveStructuredTypeMembers(type); if ((relation === assignableRelation || relation === comparableRelation) && (type === globalObjectType || isEmptyObjectType(resolved)) || resolved.stringIndexInfo || (resolved.numberIndexInfo && isNumericLiteralName(name)) || @@ -17182,7 +17182,7 @@ namespace ts { } } - function checkBaseTypeAccessibility(type: ObjectType, node: ExpressionWithTypeArguments) { + function checkBaseTypeAccessibility(type: Type, node: ExpressionWithTypeArguments) { const signatures = getSignaturesOfType(type, SignatureKind.Construct); if (signatures.length) { const declaration = signatures[0].declaration; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index dd1878f36a7..540d6e8c406 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2699,6 +2699,7 @@ namespace ts { // Object types (TypeFlags.ObjectType) export interface ObjectType extends Type { + _objectTypeBrand: any; isObjectLiteralPatternWithComputedProperties?: boolean; } @@ -2753,6 +2754,8 @@ namespace ts { export interface IntersectionType extends UnionOrIntersectionType { } + export type StructuredType = ObjectType | UnionType | IntersectionType; + /* @internal */ // An instantiated anonymous type has a target and a mapper export interface AnonymousType extends ObjectType { From 3f234f2e7f170b80e9b6eef515b2efa05342ffa3 Mon Sep 17 00:00:00 2001 From: Zhengbo Li Date: Thu, 20 Oct 2016 11:30:05 -0700 Subject: [PATCH 13/49] Move setTimeout to sys (#11746) --- src/compiler/sys.ts | 6 +++++- src/compiler/tsc.ts | 24 ++++++++++++++++-------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 585fb60df32..13bbfc2ab15 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -34,6 +34,8 @@ namespace ts { realpath?(path: string): string; /*@internal*/ getEnvironmentVariable(name: string): string; /*@internal*/ tryEnableSourceMapsForHost?(): void; + setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any; + clearTimeout?(timeoutId: any): void; } export interface FileWatcher { @@ -560,7 +562,9 @@ namespace ts { catch (e) { // Could not enable source maps. } - } + }, + setTimeout, + clearTimeout }; return nodeSystem; } diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index aa277c9b507..10d4a0c1d36 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -250,8 +250,8 @@ namespace ts { let compilerOptions: CompilerOptions; // Compiler options for compilation let compilerHost: CompilerHost; // Compiler host let hostGetSourceFile: typeof compilerHost.getSourceFile; // getSourceFile method from default host - let timerHandleForRecompilation: number; // Handle for 0.25s wait timer to trigger recompilation - let timerHandleForDirectoryChanges: number; // Handle for 0.25s wait timer to trigger directory change handler + let timerHandleForRecompilation: any; // Handle for 0.25s wait timer to trigger recompilation + let timerHandleForDirectoryChanges: any; // Handle for 0.25s wait timer to trigger directory change handler // This map stores and reuses results of fileExists check that happen inside 'createProgram' // This allows to save time in module resolution heavy scenarios when existence of the same file might be checked multiple times. @@ -503,10 +503,14 @@ namespace ts { } function startTimerForHandlingDirectoryChanges() { - if (timerHandleForDirectoryChanges) { - clearTimeout(timerHandleForDirectoryChanges); + if (!sys.setTimeout || !sys.clearTimeout) { + return; } - timerHandleForDirectoryChanges = setTimeout(directoryChangeHandler, 250); + + if (timerHandleForDirectoryChanges) { + sys.clearTimeout(timerHandleForDirectoryChanges); + } + timerHandleForDirectoryChanges = sys.setTimeout(directoryChangeHandler, 250); } function directoryChangeHandler() { @@ -525,10 +529,14 @@ namespace ts { // operations (such as saving all modified files in an editor) a chance to complete before we kick // off a new compilation. function startTimerForRecompilation() { - if (timerHandleForRecompilation) { - clearTimeout(timerHandleForRecompilation); + if (!sys.setTimeout || !sys.clearTimeout) { + return; } - timerHandleForRecompilation = setTimeout(recompile, 250); + + if (timerHandleForRecompilation) { + sys.clearTimeout(timerHandleForRecompilation); + } + timerHandleForRecompilation = sys.setTimeout(recompile, 250); } function recompile() { From fab085986983f18d8823278474a706cadeb8030d Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 15 Sep 2016 09:51:32 -0700 Subject: [PATCH 14/49] Allow destructuring in catch clauses --- src/compiler/binder.ts | 16 ++++- src/compiler/checker.ts | 20 +++--- src/compiler/diagnosticMessages.json | 4 -- src/compiler/transformers/es2015.ts | 21 ++++++ src/compiler/utilities.ts | 14 ++-- .../catchClauseWithBindingPattern1.errors.txt | 10 --- .../catchClauseWithBindingPattern1.js | 11 ---- .../baselines/reference/destructuringCatch.js | 59 +++++++++++++++++ .../reference/destructuringCatch.symbols | 51 +++++++++++++++ .../reference/destructuringCatch.types | 65 +++++++++++++++++++ .../redeclareParameterInCatchBlock.errors.txt | 24 ++++++- .../redeclareParameterInCatchBlock.js | 22 ++++++- .../catchClauseWithBindingPattern1.ts | 4 -- .../redeclareParameterInCatchBlock.ts | 13 +++- .../es6/destructuring/destructuringCatch.ts | 29 +++++++++ 15 files changed, 310 insertions(+), 53 deletions(-) delete mode 100644 tests/baselines/reference/catchClauseWithBindingPattern1.errors.txt delete mode 100644 tests/baselines/reference/catchClauseWithBindingPattern1.js create mode 100644 tests/baselines/reference/destructuringCatch.js create mode 100644 tests/baselines/reference/destructuringCatch.symbols create mode 100644 tests/baselines/reference/destructuringCatch.types delete mode 100644 tests/cases/compiler/catchClauseWithBindingPattern1.ts create mode 100644 tests/cases/conformance/es6/destructuring/destructuringCatch.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index a256b632fac..447a7625d38 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1007,7 +1007,7 @@ namespace ts { currentFlow = finishFlowLabel(preFinallyLabel); bind(node.finallyBlock); // if flow after finally is unreachable - keep it - // otherwise check if flows after try and after catch are unreachable + // otherwise check if flows after try and after catch are unreachable // if yes - convert current flow to unreachable // i.e. // try { return "1" } finally { console.log(1); } @@ -2421,6 +2421,9 @@ namespace ts { case SyntaxKind.HeritageClause: return computeHeritageClause(node, subtreeFlags); + case SyntaxKind.CatchClause: + return computeCatchClause(node, subtreeFlags); + case SyntaxKind.ExpressionWithTypeArguments: return computeExpressionWithTypeArguments(node, subtreeFlags); @@ -2650,6 +2653,17 @@ namespace ts { return transformFlags & ~TransformFlags.NodeExcludes; } + function computeCatchClause(node: CatchClause, subtreeFlags: TransformFlags) { + let transformFlags = subtreeFlags; + + if (node.variableDeclaration && isBindingPattern(node.variableDeclaration.name)) { + transformFlags |= TransformFlags.AssertES2015; + } + + node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; + return transformFlags & ~TransformFlags.NodeExcludes; + } + function computeExpressionWithTypeArguments(node: ExpressionWithTypeArguments, subtreeFlags: TransformFlags) { // An ExpressionWithTypeArguments is ES6 syntax, as it is used in the // extends clause of a class. diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 369d61ce484..67a306b4fce 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3304,7 +3304,7 @@ namespace ts { } // Handle catch clause variables const declaration = symbol.valueDeclaration; - if (declaration.parent.kind === SyntaxKind.CatchClause) { + if (isCatchClauseVariableDeclarationOrBindingElement(declaration)) { return links.type = anyType; } // Handle export default expressions @@ -16906,22 +16906,20 @@ namespace ts { if (catchClause) { // Grammar checking if (catchClause.variableDeclaration) { - if (catchClause.variableDeclaration.name.kind !== SyntaxKind.Identifier) { - grammarErrorOnFirstToken(catchClause.variableDeclaration.name, Diagnostics.Catch_clause_variable_name_must_be_an_identifier); - } - else if (catchClause.variableDeclaration.type) { + if (catchClause.variableDeclaration.type) { grammarErrorOnFirstToken(catchClause.variableDeclaration.type, Diagnostics.Catch_clause_variable_cannot_have_a_type_annotation); } else if (catchClause.variableDeclaration.initializer) { grammarErrorOnFirstToken(catchClause.variableDeclaration.initializer, Diagnostics.Catch_clause_variable_cannot_have_an_initializer); } else { - const identifierName = (catchClause.variableDeclaration.name).text; - const locals = catchClause.block.locals; - 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); + const blockLocals = catchClause.block.locals; + if (blockLocals) { + for (const caughtName in catchClause.locals) { + const blockLocal = blockLocals[caughtName]; + if (blockLocal && (blockLocal.flags & SymbolFlags.BlockScopedVariable) !== 0) { + grammarErrorOnNode(blockLocal.valueDeclaration, Diagnostics.Cannot_redeclare_identifier_0_in_catch_clause, caughtName); + } } } } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 21f04da488d..bb6c62d83e2 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -603,10 +603,6 @@ "category": "Error", "code": 1194 }, - "Catch clause variable name must be an identifier.": { - "category": "Error", - "code": 1195 - }, "Catch clause variable cannot have a type annotation.": { "category": "Error", "code": 1196 diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index 9972f339c16..b48539e8722 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -362,6 +362,9 @@ namespace ts { case SyntaxKind.ObjectLiteralExpression: return visitObjectLiteralExpression(node); + case SyntaxKind.CatchClause: + return visitCatchClause(node); + case SyntaxKind.ShorthandPropertyAssignment: return visitShorthandPropertyAssignment(node); @@ -2622,6 +2625,24 @@ namespace ts { return expression; } + function visitCatchClause(node: CatchClause): CatchClause { + Debug.assert(isBindingPattern(node.variableDeclaration.name)); + + const temp = createTempVariable(undefined); + const newVariableDeclaration = createVariableDeclaration(temp, undefined, undefined, node.variableDeclaration); + + const vars = flattenVariableDestructuring(node.variableDeclaration, temp, visitor); + const list = createVariableDeclarationList(vars, /*location*/node.variableDeclaration, /*flags*/node.variableDeclaration.flags); + const destructure = createVariableStatement(undefined, list); + + return updateCatchClause(node, newVariableDeclaration, addStatementToStartOfBlock(node.block, destructure)); + } + + function addStatementToStartOfBlock(block: Block, statement: Statement): Block { + const transformedStatements = visitNodes(block.statements, visitor, isStatement); + return updateBlock(block, [statement].concat(transformedStatements)); + } + /** * Visits a MethodDeclaration of an ObjectLiteralExpression and transforms it into a * PropertyAssignment. diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 87f6cd5e4aa..3a8d09f113d 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -406,7 +406,12 @@ namespace ts { export function isBlockOrCatchScoped(declaration: Declaration) { return (getCombinedNodeFlags(declaration) & NodeFlags.BlockScoped) !== 0 || - isCatchClauseVariableDeclaration(declaration); + isCatchClauseVariableDeclarationOrBindingElement(declaration); + } + + export function isCatchClauseVariableDeclarationOrBindingElement(declaration: Declaration) { + const node = getRootDeclaration(declaration); + return node.kind === SyntaxKind.VariableDeclaration && node.parent.kind === SyntaxKind.CatchClause; } export function isAmbientModule(node: Node): boolean { @@ -489,13 +494,6 @@ namespace ts { } } - export function isCatchClauseVariableDeclaration(declaration: Declaration) { - return declaration && - declaration.kind === SyntaxKind.VariableDeclaration && - declaration.parent && - declaration.parent.kind === SyntaxKind.CatchClause; - } - // Return display name of an identifier // Computed property names will just be emitted as "[]", where is the source // text of the expression in the computed property. diff --git a/tests/baselines/reference/catchClauseWithBindingPattern1.errors.txt b/tests/baselines/reference/catchClauseWithBindingPattern1.errors.txt deleted file mode 100644 index 0b7482850e5..00000000000 --- a/tests/baselines/reference/catchClauseWithBindingPattern1.errors.txt +++ /dev/null @@ -1,10 +0,0 @@ -tests/cases/compiler/catchClauseWithBindingPattern1.ts(3,8): error TS1195: Catch clause variable name must be an identifier. - - -==== tests/cases/compiler/catchClauseWithBindingPattern1.ts (1 errors) ==== - try { - } - catch ({a}) { - ~ -!!! error TS1195: Catch clause variable name must be an identifier. - } \ No newline at end of file diff --git a/tests/baselines/reference/catchClauseWithBindingPattern1.js b/tests/baselines/reference/catchClauseWithBindingPattern1.js deleted file mode 100644 index bbdc259946c..00000000000 --- a/tests/baselines/reference/catchClauseWithBindingPattern1.js +++ /dev/null @@ -1,11 +0,0 @@ -//// [catchClauseWithBindingPattern1.ts] -try { -} -catch ({a}) { -} - -//// [catchClauseWithBindingPattern1.js] -try { -} -catch (a = (void 0).a) { -} diff --git a/tests/baselines/reference/destructuringCatch.js b/tests/baselines/reference/destructuringCatch.js new file mode 100644 index 00000000000..4593b22f127 --- /dev/null +++ b/tests/baselines/reference/destructuringCatch.js @@ -0,0 +1,59 @@ +//// [destructuringCatch.ts] + +try { + throw [0, 1]; +} +catch ([a, b]) { + a + b; +} + +try { + throw { a: 0, b: 1 }; +} +catch ({a, b}) { + a + b; +} + +try { + throw [{ x: [0], z: 1 }]; +} +catch ([{x: [y], z}]) { + y + z; +} + +// Test of comment ranges. A fix to GH#11755 should update this. +try { +} +catch (/*Test comment ranges*/[/*a*/a]) { + +} + + +//// [destructuringCatch.js] +try { + throw [0, 1]; +} +catch (_a) { + var a = _a[0], b = _a[1]; + a + b; +} +try { + throw { a: 0, b: 1 }; +} +catch (_b) { + var a = _b.a, b = _b.b; + a + b; +} +try { + throw [{ x: [0], z: 1 }]; +} +catch (_c) { + var _d = _c[0], y = _d.x[0], z = _d.z; + y + z; +} +// Test of comment ranges. A fix to GH#11755 should update this. +try { +} +catch (_e) { + var /*a*/ a = _e[0]; +} diff --git a/tests/baselines/reference/destructuringCatch.symbols b/tests/baselines/reference/destructuringCatch.symbols new file mode 100644 index 00000000000..1d0a954b377 --- /dev/null +++ b/tests/baselines/reference/destructuringCatch.symbols @@ -0,0 +1,51 @@ +=== tests/cases/conformance/es6/destructuring/destructuringCatch.ts === + +try { + throw [0, 1]; +} +catch ([a, b]) { +>a : Symbol(a, Decl(destructuringCatch.ts, 4, 8)) +>b : Symbol(b, Decl(destructuringCatch.ts, 4, 10)) + + a + b; +>a : Symbol(a, Decl(destructuringCatch.ts, 4, 8)) +>b : Symbol(b, Decl(destructuringCatch.ts, 4, 10)) +} + +try { + throw { a: 0, b: 1 }; +>a : Symbol(a, Decl(destructuringCatch.ts, 9, 11)) +>b : Symbol(b, Decl(destructuringCatch.ts, 9, 17)) +} +catch ({a, b}) { +>a : Symbol(a, Decl(destructuringCatch.ts, 11, 8)) +>b : Symbol(b, Decl(destructuringCatch.ts, 11, 10)) + + a + b; +>a : Symbol(a, Decl(destructuringCatch.ts, 11, 8)) +>b : Symbol(b, Decl(destructuringCatch.ts, 11, 10)) +} + +try { + throw [{ x: [0], z: 1 }]; +>x : Symbol(x, Decl(destructuringCatch.ts, 16, 12)) +>z : Symbol(z, Decl(destructuringCatch.ts, 16, 20)) +} +catch ([{x: [y], z}]) { +>x : Symbol(x) +>y : Symbol(y, Decl(destructuringCatch.ts, 18, 13)) +>z : Symbol(z, Decl(destructuringCatch.ts, 18, 16)) + + y + z; +>y : Symbol(y, Decl(destructuringCatch.ts, 18, 13)) +>z : Symbol(z, Decl(destructuringCatch.ts, 18, 16)) +} + +// Test of comment ranges. A fix to GH#11755 should update this. +try { +} +catch (/*Test comment ranges*/[/*a*/a]) { +>a : Symbol(a, Decl(destructuringCatch.ts, 25, 31)) + +} + diff --git a/tests/baselines/reference/destructuringCatch.types b/tests/baselines/reference/destructuringCatch.types new file mode 100644 index 00000000000..3f057e64e4e --- /dev/null +++ b/tests/baselines/reference/destructuringCatch.types @@ -0,0 +1,65 @@ +=== tests/cases/conformance/es6/destructuring/destructuringCatch.ts === + +try { + throw [0, 1]; +>[0, 1] : number[] +>0 : 0 +>1 : 1 +} +catch ([a, b]) { +>a : any +>b : any + + a + b; +>a + b : any +>a : any +>b : any +} + +try { + throw { a: 0, b: 1 }; +>{ a: 0, b: 1 } : { a: number; b: number; } +>a : number +>0 : 0 +>b : number +>1 : 1 +} +catch ({a, b}) { +>a : any +>b : any + + a + b; +>a + b : any +>a : any +>b : any +} + +try { + throw [{ x: [0], z: 1 }]; +>[{ x: [0], z: 1 }] : { x: number[]; z: number; }[] +>{ x: [0], z: 1 } : { x: number[]; z: number; } +>x : number[] +>[0] : number[] +>0 : 0 +>z : number +>1 : 1 +} +catch ([{x: [y], z}]) { +>x : any +>y : any +>z : any + + y + z; +>y + z : any +>y : any +>z : any +} + +// Test of comment ranges. A fix to GH#11755 should update this. +try { +} +catch (/*Test comment ranges*/[/*a*/a]) { +>a : any + +} + diff --git a/tests/baselines/reference/redeclareParameterInCatchBlock.errors.txt b/tests/baselines/reference/redeclareParameterInCatchBlock.errors.txt index bd307bb89ae..9fd4c63f02f 100644 --- a/tests/baselines/reference/redeclareParameterInCatchBlock.errors.txt +++ b/tests/baselines/reference/redeclareParameterInCatchBlock.errors.txt @@ -1,8 +1,11 @@ tests/cases/compiler/redeclareParameterInCatchBlock.ts(5,11): error TS2492: Cannot redeclare identifier 'e' in catch clause tests/cases/compiler/redeclareParameterInCatchBlock.ts(11,9): error TS2492: Cannot redeclare identifier 'e' in catch clause +tests/cases/compiler/redeclareParameterInCatchBlock.ts(17,15): error TS2492: Cannot redeclare identifier 'b' in catch clause +tests/cases/compiler/redeclareParameterInCatchBlock.ts(22,15): error TS2451: Cannot redeclare block-scoped variable 'x'. +tests/cases/compiler/redeclareParameterInCatchBlock.ts(22,21): error TS2451: Cannot redeclare block-scoped variable 'x'. -==== tests/cases/compiler/redeclareParameterInCatchBlock.ts (2 errors) ==== +==== tests/cases/compiler/redeclareParameterInCatchBlock.ts (5 errors) ==== try { @@ -22,10 +25,27 @@ tests/cases/compiler/redeclareParameterInCatchBlock.ts(11,9): error TS2492: Cann try { + } catch ([a, b]) { + const [c, b] = [0, 1]; + ~ +!!! error TS2492: Cannot redeclare identifier 'b' in catch clause + } + + try { + + } catch ({ a: x, b: x }) { + ~ +!!! error TS2451: Cannot redeclare block-scoped variable 'x'. + ~ +!!! error TS2451: Cannot redeclare block-scoped variable 'x'. + + } + + try { + } catch(e) { function test() { let e; } } - \ No newline at end of file diff --git a/tests/baselines/reference/redeclareParameterInCatchBlock.js b/tests/baselines/reference/redeclareParameterInCatchBlock.js index 704d56676bf..9fafe0b17be 100644 --- a/tests/baselines/reference/redeclareParameterInCatchBlock.js +++ b/tests/baselines/reference/redeclareParameterInCatchBlock.js @@ -14,12 +14,23 @@ try { try { +} catch ([a, b]) { + const [c, b] = [0, 1]; +} + +try { + +} catch ({ a: x, b: x }) { + +} + +try { + } catch(e) { function test() { let e; } } - //// [redeclareParameterInCatchBlock.js] @@ -35,6 +46,15 @@ catch (e) { } try { } +catch ([a, b]) { + const [c, b] = [0, 1]; +} +try { +} +catch ({ a: x, b: x }) { +} +try { +} catch (e) { function test() { let e; diff --git a/tests/cases/compiler/catchClauseWithBindingPattern1.ts b/tests/cases/compiler/catchClauseWithBindingPattern1.ts deleted file mode 100644 index dbd0f81c576..00000000000 --- a/tests/cases/compiler/catchClauseWithBindingPattern1.ts +++ /dev/null @@ -1,4 +0,0 @@ -try { -} -catch ({a}) { -} \ No newline at end of file diff --git a/tests/cases/compiler/redeclareParameterInCatchBlock.ts b/tests/cases/compiler/redeclareParameterInCatchBlock.ts index 37ca5fc3a9e..7f98d68b29d 100644 --- a/tests/cases/compiler/redeclareParameterInCatchBlock.ts +++ b/tests/cases/compiler/redeclareParameterInCatchBlock.ts @@ -14,9 +14,20 @@ try { try { +} catch ([a, b]) { + const [c, b] = [0, 1]; +} + +try { + +} catch ({ a: x, b: x }) { + +} + +try { + } catch(e) { function test() { let e; } } - diff --git a/tests/cases/conformance/es6/destructuring/destructuringCatch.ts b/tests/cases/conformance/es6/destructuring/destructuringCatch.ts new file mode 100644 index 00000000000..31afd672084 --- /dev/null +++ b/tests/cases/conformance/es6/destructuring/destructuringCatch.ts @@ -0,0 +1,29 @@ +// @noImplicitAny: true + +try { + throw [0, 1]; +} +catch ([a, b]) { + a + b; +} + +try { + throw { a: 0, b: 1 }; +} +catch ({a, b}) { + a + b; +} + +try { + throw [{ x: [0], z: 1 }]; +} +catch ([{x: [y], z}]) { + y + z; +} + +// Test of comment ranges. A fix to GH#11755 should update this. +try { +} +catch (/*Test comment ranges*/[/*a*/a]) { + +} From 8e6467cdb8a4d81ab7c330f6396555831a3b4829 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 18 Oct 2016 12:24:41 -0700 Subject: [PATCH 15/49] Add testcase for incorrect emit of jsx --- .../reference/jsxEmitWithAttributes.js | 81 +++++++++++ .../reference/jsxEmitWithAttributes.symbols | 119 ++++++++++++++++ .../reference/jsxEmitWithAttributes.types | 134 ++++++++++++++++++ tests/cases/compiler/jsxEmitWithAttributes.ts | 53 +++++++ 4 files changed, 387 insertions(+) create mode 100644 tests/baselines/reference/jsxEmitWithAttributes.js create mode 100644 tests/baselines/reference/jsxEmitWithAttributes.symbols create mode 100644 tests/baselines/reference/jsxEmitWithAttributes.types create mode 100644 tests/cases/compiler/jsxEmitWithAttributes.ts diff --git a/tests/baselines/reference/jsxEmitWithAttributes.js b/tests/baselines/reference/jsxEmitWithAttributes.js new file mode 100644 index 00000000000..cd50a4f3411 --- /dev/null +++ b/tests/baselines/reference/jsxEmitWithAttributes.js @@ -0,0 +1,81 @@ +//// [tests/cases/compiler/jsxEmitWithAttributes.ts] //// + +//// [Element.ts] + +declare namespace JSX { + interface Element { + name: string; + isIntrinsic: boolean; + isCustomElement: boolean; + toString(renderId?: number): string; + bindDOM(renderId?: number): number; + resetComponent(): void; + instantiateComponents(renderId?: number): number; + props: any; + } +} +export namespace Element { + export function isElement(el: any): el is JSX.Element { + return el.markAsChildOfRootElement !== undefined; + } + + export function createElement(args: any[]) { + + return { + } + } +} + +export let createElement = Element.createElement; + +function toCamelCase(text: string): string { + return text[0].toLowerCase() + text.substring(1); +} + +//// [test.tsx] +import { Element} from './Element'; + +let c: { + a?: { + b: string + } +}; + +class A { + view() { + return [ + , + + ]; + } +} + +//// [Element.js] +"use strict"; +var Element; +(function (Element) { + function isElement(el) { + return el.markAsChildOfRootElement !== undefined; + } + Element.isElement = isElement; + function createElement(args) { + return {}; + } + Element.createElement = createElement; +})(Element = exports.Element || (exports.Element = {})); +exports.createElement = Element.createElement; +function toCamelCase(text) { + return text[0].toLowerCase() + text.substring(1); +} +//// [test.js] +"use strict"; +const Element_1 = require("./Element"); +let c; +class A { + view() { + return [ + Element_1.Element.createElement("meta", { content: "helloworld" }), + Element.createElement("meta", { content: c.a.b }) + ]; + } +} diff --git a/tests/baselines/reference/jsxEmitWithAttributes.symbols b/tests/baselines/reference/jsxEmitWithAttributes.symbols new file mode 100644 index 00000000000..16cd35d1ca3 --- /dev/null +++ b/tests/baselines/reference/jsxEmitWithAttributes.symbols @@ -0,0 +1,119 @@ +=== tests/cases/compiler/Element.ts === + +declare namespace JSX { +>JSX : Symbol(JSX, Decl(Element.ts, 0, 0)) + + interface Element { +>Element : Symbol(Element, Decl(Element.ts, 1, 23)) + + name: string; +>name : Symbol(Element.name, Decl(Element.ts, 2, 23)) + + isIntrinsic: boolean; +>isIntrinsic : Symbol(Element.isIntrinsic, Decl(Element.ts, 3, 21)) + + isCustomElement: boolean; +>isCustomElement : Symbol(Element.isCustomElement, Decl(Element.ts, 4, 29)) + + toString(renderId?: number): string; +>toString : Symbol(Element.toString, Decl(Element.ts, 5, 33)) +>renderId : Symbol(renderId, Decl(Element.ts, 6, 17)) + + bindDOM(renderId?: number): number; +>bindDOM : Symbol(Element.bindDOM, Decl(Element.ts, 6, 44)) +>renderId : Symbol(renderId, Decl(Element.ts, 7, 16)) + + resetComponent(): void; +>resetComponent : Symbol(Element.resetComponent, Decl(Element.ts, 7, 43)) + + instantiateComponents(renderId?: number): number; +>instantiateComponents : Symbol(Element.instantiateComponents, Decl(Element.ts, 8, 31)) +>renderId : Symbol(renderId, Decl(Element.ts, 9, 30)) + + props: any; +>props : Symbol(Element.props, Decl(Element.ts, 9, 57)) + } +} +export namespace Element { +>Element : Symbol(Element, Decl(Element.ts, 12, 1)) + + export function isElement(el: any): el is JSX.Element { +>isElement : Symbol(isElement, Decl(Element.ts, 13, 26)) +>el : Symbol(el, Decl(Element.ts, 14, 30)) +>el : Symbol(el, Decl(Element.ts, 14, 30)) +>JSX : Symbol(JSX, Decl(Element.ts, 0, 0)) +>Element : Symbol(JSX.Element, Decl(Element.ts, 1, 23)) + + return el.markAsChildOfRootElement !== undefined; +>el : Symbol(el, Decl(Element.ts, 14, 30)) +>undefined : Symbol(undefined) + } + + export function createElement(args: any[]) { +>createElement : Symbol(createElement, Decl(Element.ts, 16, 5)) +>args : Symbol(args, Decl(Element.ts, 18, 34)) + + return { + } + } +} + +export let createElement = Element.createElement; +>createElement : Symbol(createElement, Decl(Element.ts, 25, 10)) +>Element.createElement : Symbol(Element.createElement, Decl(Element.ts, 16, 5)) +>Element : Symbol(Element, Decl(Element.ts, 12, 1)) +>createElement : Symbol(Element.createElement, Decl(Element.ts, 16, 5)) + +function toCamelCase(text: string): string { +>toCamelCase : Symbol(toCamelCase, Decl(Element.ts, 25, 49)) +>text : Symbol(text, Decl(Element.ts, 27, 21)) + + return text[0].toLowerCase() + text.substring(1); +>text[0].toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --)) +>text : Symbol(text, Decl(Element.ts, 27, 21)) +>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --)) +>text.substring : Symbol(String.substring, Decl(lib.es5.d.ts, --, --)) +>text : Symbol(text, Decl(Element.ts, 27, 21)) +>substring : Symbol(String.substring, Decl(lib.es5.d.ts, --, --)) +} + +=== tests/cases/compiler/test.tsx === +import { Element} from './Element'; +>Element : Symbol(Element, Decl(test.tsx, 0, 8)) + +let c: { +>c : Symbol(c, Decl(test.tsx, 2, 3)) + + a?: { +>a : Symbol(a, Decl(test.tsx, 2, 8)) + + b: string +>b : Symbol(b, Decl(test.tsx, 3, 6)) + } +}; + +class A { +>A : Symbol(A, Decl(test.tsx, 6, 2)) + + view() { +>view : Symbol(A.view, Decl(test.tsx, 8, 9)) + + return [ + , +>meta : Symbol(unknown) +>content : Symbol(unknown) +>meta : Symbol(unknown) + + +>meta : Symbol(unknown) +>content : Symbol(unknown) +>c.a!.b : Symbol(b, Decl(test.tsx, 3, 6)) +>c.a : Symbol(a, Decl(test.tsx, 2, 8)) +>c : Symbol(c, Decl(test.tsx, 2, 3)) +>a : Symbol(a, Decl(test.tsx, 2, 8)) +>b : Symbol(b, Decl(test.tsx, 3, 6)) +>meta : Symbol(unknown) + + ]; + } +} diff --git a/tests/baselines/reference/jsxEmitWithAttributes.types b/tests/baselines/reference/jsxEmitWithAttributes.types new file mode 100644 index 00000000000..663ba4b5bfc --- /dev/null +++ b/tests/baselines/reference/jsxEmitWithAttributes.types @@ -0,0 +1,134 @@ +=== tests/cases/compiler/Element.ts === + +declare namespace JSX { +>JSX : any + + interface Element { +>Element : Element + + name: string; +>name : string + + isIntrinsic: boolean; +>isIntrinsic : boolean + + isCustomElement: boolean; +>isCustomElement : boolean + + toString(renderId?: number): string; +>toString : (renderId?: number) => string +>renderId : number + + bindDOM(renderId?: number): number; +>bindDOM : (renderId?: number) => number +>renderId : number + + resetComponent(): void; +>resetComponent : () => void + + instantiateComponents(renderId?: number): number; +>instantiateComponents : (renderId?: number) => number +>renderId : number + + props: any; +>props : any + } +} +export namespace Element { +>Element : typeof Element + + export function isElement(el: any): el is JSX.Element { +>isElement : (el: any) => el is JSX.Element +>el : any +>el : any +>JSX : any +>Element : JSX.Element + + return el.markAsChildOfRootElement !== undefined; +>el.markAsChildOfRootElement !== undefined : boolean +>el.markAsChildOfRootElement : any +>el : any +>markAsChildOfRootElement : any +>undefined : undefined + } + + export function createElement(args: any[]) { +>createElement : (args: any[]) => {} +>args : any[] + + return { +>{ } : {} + } + } +} + +export let createElement = Element.createElement; +>createElement : (args: any[]) => {} +>Element.createElement : (args: any[]) => {} +>Element : typeof Element +>createElement : (args: any[]) => {} + +function toCamelCase(text: string): string { +>toCamelCase : (text: string) => string +>text : string + + return text[0].toLowerCase() + text.substring(1); +>text[0].toLowerCase() + text.substring(1) : string +>text[0].toLowerCase() : string +>text[0].toLowerCase : () => string +>text[0] : string +>text : string +>0 : 0 +>toLowerCase : () => string +>text.substring(1) : string +>text.substring : (start: number, end?: number) => string +>text : string +>substring : (start: number, end?: number) => string +>1 : 1 +} + +=== tests/cases/compiler/test.tsx === +import { Element} from './Element'; +>Element : typeof Element + +let c: { +>c : { a?: { b: string; }; } + + a?: { +>a : { b: string; } + + b: string +>b : string + } +}; + +class A { +>A : A + + view() { +>view : () => any[] + + return [ +>[ , ] : any[] + + , +> : any +>meta : any +>content : any +>meta : any + + +> : any +>meta : any +>content : any +>c.a!.b : string +>c.a! : { b: string; } +>c.a : { b: string; } +>c : { a?: { b: string; }; } +>a : { b: string; } +>b : string +>meta : any + + ]; + } +} diff --git a/tests/cases/compiler/jsxEmitWithAttributes.ts b/tests/cases/compiler/jsxEmitWithAttributes.ts new file mode 100644 index 00000000000..1fb7a9ccf8d --- /dev/null +++ b/tests/cases/compiler/jsxEmitWithAttributes.ts @@ -0,0 +1,53 @@ +//@jsx: react +//@target: es6 +//@module: commonjs +//@reactNamespace: Element + +// @filename: Element.ts +declare namespace JSX { + interface Element { + name: string; + isIntrinsic: boolean; + isCustomElement: boolean; + toString(renderId?: number): string; + bindDOM(renderId?: number): number; + resetComponent(): void; + instantiateComponents(renderId?: number): number; + props: any; + } +} +export namespace Element { + export function isElement(el: any): el is JSX.Element { + return el.markAsChildOfRootElement !== undefined; + } + + export function createElement(args: any[]) { + + return { + } + } +} + +export let createElement = Element.createElement; + +function toCamelCase(text: string): string { + return text[0].toLowerCase() + text.substring(1); +} + +// @filename: test.tsx +import { Element} from './Element'; + +let c: { + a?: { + b: string + } +}; + +class A { + view() { + return [ + , + + ]; + } +} \ No newline at end of file From 202093a73059cf7d7ca07bc99675419e6a27d5ca Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 20 Oct 2016 15:25:26 -0700 Subject: [PATCH 16/49] When creating react namespace identifier, set its parent to jsx opening element in the parse tree This helps in resolving the react identifier correctly and Fixes #11654 --- src/compiler/factory.ts | 4 +++- tests/baselines/reference/jsxEmitWithAttributes.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 4194b7aaf5f..cd55b3099c1 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -1625,7 +1625,9 @@ namespace ts { // flag and setting a parent node. const react = createIdentifier(reactNamespace || "React"); react.flags &= ~NodeFlags.Synthesized; - react.parent = parent; + // Set the parent that is in parse tree + // this makes sure that parent chain is intact for checker to traverse complete scope tree + react.parent = getParseTreeNode(parent); return react; } diff --git a/tests/baselines/reference/jsxEmitWithAttributes.js b/tests/baselines/reference/jsxEmitWithAttributes.js index cd50a4f3411..1acead2801d 100644 --- a/tests/baselines/reference/jsxEmitWithAttributes.js +++ b/tests/baselines/reference/jsxEmitWithAttributes.js @@ -75,7 +75,7 @@ class A { view() { return [ Element_1.Element.createElement("meta", { content: "helloworld" }), - Element.createElement("meta", { content: c.a.b }) + Element_1.Element.createElement("meta", { content: c.a.b }) ]; } } From 10c6ab6703eb8b7a028a0a5f896df4edab9352d7 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 20 Oct 2016 15:28:32 -0700 Subject: [PATCH 17/49] Introduce ObjectFlags in object types --- src/compiler/checker.ts | 180 ++++++++++++++++++---------------- src/compiler/types.ts | 34 ++++--- src/services/services.ts | 3 +- src/services/symbolDisplay.ts | 2 +- 4 files changed, 117 insertions(+), 102 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c260b86e114..32705df1725 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -538,6 +538,10 @@ namespace ts { return nodeLinks[nodeId] || (nodeLinks[nodeId] = { flags: 0 }); } + function getObjectFlags(type: Type): ObjectFlags { + return type.flags & TypeFlags.ObjectType ? (type).objectFlags : 0; + } + function isGlobalSourceFile(node: Node) { return node.kind === SyntaxKind.SourceFile && !isExternalOrCommonJsModule(node); } @@ -1589,8 +1593,9 @@ namespace ts { return type; } - function createObjectType(kind: TypeFlags, symbol?: Symbol): ObjectType { - const type = createType(kind); + function createObjectType(objectFlags: ObjectFlags, symbol?: Symbol): ObjectType { + const type = createType(TypeFlags.ObjectType); + type.objectFlags = objectFlags; type.symbol = symbol; return type; } @@ -1631,7 +1636,7 @@ namespace ts { } function createAnonymousType(symbol: Symbol, members: SymbolTable, callSignatures: Signature[], constructSignatures: Signature[], stringIndexInfo: IndexInfo, numberIndexInfo: IndexInfo): ResolvedType { - return setStructuredTypeMembers(createObjectType(TypeFlags.Anonymous, symbol), + return setStructuredTypeMembers(createObjectType(ObjectFlags.Anonymous, symbol), members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); } @@ -2182,7 +2187,7 @@ namespace ts { } writer.writeKeyword("this"); } - else if (type.flags & TypeFlags.Reference) { + else if (getObjectFlags(type) & ObjectFlags.Reference) { writeTypeReference(type, nextFlags); } else if (type.flags & TypeFlags.EnumLiteral) { @@ -2190,11 +2195,11 @@ namespace ts { writePunctuation(writer, SyntaxKind.DotToken); appendSymbolNameOnly(type.symbol, writer); } - else if (type.flags & (TypeFlags.Class | TypeFlags.Interface | TypeFlags.Enum | TypeFlags.TypeParameter)) { + else if (getObjectFlags(type) & (ObjectFlags.Class | ObjectFlags.Interface) || type.flags & (TypeFlags.Enum | TypeFlags.TypeParameter)) { // The specified symbol flags need to be reinterpreted as type flags buildSymbolDisplay(type.symbol, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, nextFlags); } - else if (!(flags & TypeFormatFlags.InTypeAlias) && ((type.flags & TypeFlags.Anonymous && !(type).target) || type.flags & TypeFlags.UnionOrIntersection) && type.aliasSymbol && + else if (!(flags & TypeFormatFlags.InTypeAlias) && ((getObjectFlags(type) & ObjectFlags.Anonymous && !(type).target) || type.flags & TypeFlags.UnionOrIntersection) && type.aliasSymbol && isSymbolAccessible(type.aliasSymbol, enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false).accessibility === SymbolAccessibility.Accessible) { // We emit inferred type as type-alias at the current localtion if all the following is true // the input type is has alias symbol that is accessible @@ -2208,7 +2213,7 @@ namespace ts { else if (type.flags & TypeFlags.UnionOrIntersection) { writeUnionOrIntersectionType(type, nextFlags); } - else if (type.flags & TypeFlags.Anonymous) { + else if (getObjectFlags(type) & ObjectFlags.Anonymous) { writeAnonymousType(type, nextFlags); } else if (type.flags & TypeFlags.StringOrNumberLiteral) { @@ -2264,7 +2269,7 @@ namespace ts { writePunctuation(writer, SyntaxKind.OpenBracketToken); writePunctuation(writer, SyntaxKind.CloseBracketToken); } - else if (type.target.flags & TypeFlags.Tuple) { + else if (type.target.objectFlags & ObjectFlags.Tuple) { writePunctuation(writer, SyntaxKind.OpenBracketToken); writeTypeList(type.typeArguments.slice(0, getTypeReferenceArity(type)), SyntaxKind.CommaToken); writePunctuation(writer, SyntaxKind.CloseBracketToken); @@ -2866,7 +2871,6 @@ namespace ts { return getSymbolLinks(target).declaredType; } if (propertyName === TypeSystemPropertyName.ResolvedBaseConstructorType) { - Debug.assert(!!((target).flags & TypeFlags.Class)); return (target).resolvedBaseConstructorType; } if (propertyName === TypeSystemPropertyName.ResolvedReturnType) { @@ -3456,7 +3460,7 @@ namespace ts { links.type = anyType; } else { - const type = createObjectType(TypeFlags.Anonymous, symbol); + const type = createObjectType(ObjectFlags.Anonymous, symbol); links.type = strictNullChecks && symbol.flags & SymbolFlags.Optional ? includeFalsyTypes(type, TypeFlags.Undefined) : type; } @@ -3520,7 +3524,7 @@ namespace ts { } function getTargetType(type: Type): Type { - return type.flags & TypeFlags.Reference ? (type).target : type; + return getObjectFlags(type) & ObjectFlags.Reference ? (type).target : type; } function hasBaseType(type: InterfaceType, checkBase: InterfaceType) { @@ -3653,7 +3657,7 @@ namespace ts { function getBaseTypes(type: InterfaceType): ObjectType[] { if (!type.resolvedBaseTypes) { - if (type.flags & TypeFlags.Tuple) { + if (type.objectFlags & ObjectFlags.Tuple) { type.resolvedBaseTypes = [createArrayType(getUnionType(type.typeParameters))]; } else if (type.symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { @@ -3701,7 +3705,7 @@ namespace ts { if (baseType === unknownType) { return; } - if (!(getTargetType(baseType).flags & (TypeFlags.Class | TypeFlags.Interface))) { + if (!(getObjectFlags(getTargetType(baseType)) & (ObjectFlags.Class | ObjectFlags.Interface))) { error(baseTypeNode.expression, Diagnostics.Base_constructor_return_type_0_is_not_a_class_or_interface_type, typeToString(baseType)); return; } @@ -3737,7 +3741,7 @@ namespace ts { for (const node of getInterfaceBaseTypeNodes(declaration)) { const baseType = getTypeFromTypeNode(node); if (baseType !== unknownType) { - if (getTargetType(baseType).flags & (TypeFlags.Class | TypeFlags.Interface)) { + if (getObjectFlags(getTargetType(baseType)) & (ObjectFlags.Class | ObjectFlags.Interface)) { if (type !== baseType && !hasBaseType(baseType, type)) { if (type.resolvedBaseTypes === emptyArray) { type.resolvedBaseTypes = [baseType]; @@ -3787,7 +3791,7 @@ namespace ts { function getDeclaredTypeOfClassOrInterface(symbol: Symbol): InterfaceType { const links = getSymbolLinks(symbol); if (!links.declaredType) { - const kind = symbol.flags & SymbolFlags.Class ? TypeFlags.Class : TypeFlags.Interface; + const kind = symbol.flags & SymbolFlags.Class ? ObjectFlags.Class : ObjectFlags.Interface; const type = links.declaredType = createObjectType(kind, symbol); const outerTypeParameters = getOuterTypeParametersOfClassOrInterface(symbol); const localTypeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); @@ -3796,8 +3800,8 @@ namespace ts { // property types inferred from initializers and method return types inferred from return statements are very hard // to exhaustively analyze). We give interfaces a "this" type if we can't definitely determine that they are free of // "this" references. - if (outerTypeParameters || localTypeParameters || kind === TypeFlags.Class || !isIndependentInterface(symbol)) { - type.flags |= TypeFlags.Reference; + if (outerTypeParameters || localTypeParameters || kind === ObjectFlags.Class || !isIndependentInterface(symbol)) { + type.objectFlags |= ObjectFlags.Reference; type.typeParameters = concatenate(outerTypeParameters, localTypeParameters); type.outerTypeParameters = outerTypeParameters; type.localTypeParameters = localTypeParameters; @@ -4094,7 +4098,7 @@ namespace ts { } function getTypeWithThisArgument(type: Type, thisArgument?: Type) { - if (type.flags & TypeFlags.Reference) { + if (getObjectFlags(type) & ObjectFlags.Reference) { return createTypeReference((type).target, concatenate((type).typeArguments, [thisArgument || (type).target.thisType])); } @@ -4365,14 +4369,16 @@ namespace ts { function resolveStructuredTypeMembers(type: StructuredType): ResolvedType { if (!(type).members) { - if (type.flags & TypeFlags.Reference) { - resolveTypeReferenceMembers(type); - } - else if (type.flags & (TypeFlags.Class | TypeFlags.Interface)) { - resolveClassOrInterfaceMembers(type); - } - else if (type.flags & TypeFlags.Anonymous) { - resolveAnonymousTypeMembers(type); + if (type.flags & TypeFlags.ObjectType) { + if ((type).objectFlags & ObjectFlags.Reference) { + resolveTypeReferenceMembers(type); + } + else if ((type).objectFlags & (ObjectFlags.Class | ObjectFlags.Interface)) { + resolveClassOrInterfaceMembers(type); + } + else if ((type).objectFlags & ObjectFlags.Anonymous) { + resolveAnonymousTypeMembers(type); + } } else if (type.flags & TypeFlags.Union) { resolveUnionTypeMembers(type); @@ -4927,7 +4933,7 @@ namespace ts { function getRestTypeOfSignature(signature: Signature): Type { if (signature.hasRestParameter) { const type = getTypeOfSymbol(lastOrUndefined(signature.parameters)); - if (type.flags & TypeFlags.Reference && (type).target === globalArrayType) { + if (getObjectFlags(type) & ObjectFlags.Reference && (type).target === globalArrayType) { return (type).typeArguments[0]; } } @@ -4953,7 +4959,7 @@ namespace ts { // will result in a different declaration kind. if (!signature.isolatedSignatureType) { const isConstructor = signature.declaration.kind === SyntaxKind.Constructor || signature.declaration.kind === SyntaxKind.ConstructSignature; - const type = createObjectType(TypeFlags.Anonymous); + const type = createObjectType(ObjectFlags.Anonymous); type.members = emptySymbols; type.properties = emptyArray; type.callSignatures = !isConstructor ? [signature] : emptyArray; @@ -5081,9 +5087,8 @@ namespace ts { const id = getTypeListId(typeArguments); let type = target.instantiations[id]; if (!type) { - const propagatedFlags = typeArguments ? getPropagatingFlagsOfTypes(typeArguments, /*excludeKinds*/ 0) : 0; - const flags = TypeFlags.Reference | propagatedFlags; - type = target.instantiations[id] = createObjectType(flags, target.symbol); + type = target.instantiations[id] = createObjectType(ObjectFlags.Reference, target.symbol); + type.flags |= typeArguments ? getPropagatingFlagsOfTypes(typeArguments, /*excludeKinds*/ 0) : 0; type.target = target; type.typeArguments = typeArguments; } @@ -5091,7 +5096,9 @@ namespace ts { } function cloneTypeReference(source: TypeReference): TypeReference { - const type = createObjectType(source.flags, source.symbol); + const type = createType(source.flags); + type.symbol = source.symbol; + type.objectFlags = source.objectFlags; type.target = source.target; type.typeArguments = source.typeArguments; return type; @@ -5357,7 +5364,7 @@ namespace ts { property.type = typeParameter; properties.push(property); } - const type = createObjectType(TypeFlags.Tuple | TypeFlags.Reference); + const type = createObjectType(ObjectFlags.Tuple | ObjectFlags.Reference); type.typeParameters = typeParameters; type.outerTypeParameters = undefined; type.localTypeParameters = typeParameters; @@ -5446,7 +5453,8 @@ namespace ts { const len = typeSet.length; const index = len && type.id > typeSet[len - 1].id ? ~len : binarySearchTypes(typeSet, type); if (index < 0) { - if (!(flags & TypeFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && containsIdenticalType(typeSet, type))) { + if (!(flags & TypeFlags.ObjectType && (type).objectFlags & ObjectFlags.Anonymous && + type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && containsIdenticalType(typeSet, type))) { typeSet.splice(~index, 0, type); } } @@ -5659,7 +5667,7 @@ namespace ts { const links = getNodeLinks(node); if (!links.resolvedType) { // Deferred resolution of members is handled by resolveObjectTypeMembers - const type = createObjectType(TypeFlags.Anonymous, node.symbol); + const type = createObjectType(ObjectFlags.Anonymous, node.symbol); type.aliasSymbol = aliasSymbol; type.aliasTypeArguments = aliasTypeArguments; links.resolvedType = type; @@ -5989,7 +5997,7 @@ namespace ts { mapper.instantiations = []; } // Mark the anonymous type as instantiated such that our infinite instantiation detection logic can recognize it - const result = createObjectType(TypeFlags.Anonymous | TypeFlags.Instantiated, type.symbol); + const result = createObjectType(ObjectFlags.Anonymous | ObjectFlags.Instantiated, type.symbol); result.target = type; result.mapper = mapper; result.aliasSymbol = type.aliasSymbol; @@ -6052,20 +6060,22 @@ namespace ts { if (type.flags & TypeFlags.TypeParameter) { return mapper(type); } - if (type.flags & TypeFlags.Anonymous) { - // If the anonymous type originates in a declaration of a function, method, class, or - // interface, in an object type literal, or in an object literal expression, we may need - // to instantiate the type because it might reference a type parameter. We skip instantiation - // if none of the type parameters that are in scope in the type's declaration are mapped by - // the given mapper, however we can only do that analysis if the type isn't itself an - // instantiation. - return type.symbol && - type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && - (type.flags & TypeFlags.Instantiated || isSymbolInScopeOfMappedTypeParameter(type.symbol, mapper)) ? - instantiateAnonymousType(type, mapper) : type; - } - if (type.flags & TypeFlags.Reference) { - return createTypeReference((type).target, instantiateList((type).typeArguments, mapper, instantiateType)); + if (type.flags & TypeFlags.ObjectType) { + if ((type).objectFlags & ObjectFlags.Anonymous) { + // If the anonymous type originates in a declaration of a function, method, class, or + // interface, in an object type literal, or in an object literal expression, we may need + // to instantiate the type because it might reference a type parameter. We skip instantiation + // if none of the type parameters that are in scope in the type's declaration are mapped by + // the given mapper, however we can only do that analysis if the type isn't itself an + // instantiation. + return type.symbol && + type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && + ((type).objectFlags & ObjectFlags.Instantiated || isSymbolInScopeOfMappedTypeParameter(type.symbol, mapper)) ? + instantiateAnonymousType(type, mapper) : type; + } + if ((type).objectFlags & ObjectFlags.Reference) { + return createTypeReference((type).target, instantiateList((type).typeArguments, mapper, instantiateType)); + } } if (type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Primitive)) { return getUnionType(instantiateList((type).types, mapper, instantiateType), /*subtypeReduction*/ false, type.aliasSymbol, mapper.targetTypes); @@ -6140,7 +6150,7 @@ namespace ts { if (type.flags & TypeFlags.ObjectType) { const resolved = resolveStructuredTypeMembers(type); if (resolved.constructSignatures.length) { - const result = createObjectType(TypeFlags.Anonymous, type.symbol); + const result = createObjectType(ObjectFlags.Anonymous, type.symbol); result.members = resolved.members; result.properties = resolved.properties; result.callSignatures = emptyArray; @@ -6556,7 +6566,7 @@ namespace ts { if (isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined)) return Ternary.True; - if (source.flags & TypeFlags.ObjectLiteral && source.flags & TypeFlags.FreshLiteral) { + if (getObjectFlags(source) & ObjectFlags.ObjectLiteral && source.flags & TypeFlags.FreshLiteral) { if (hasExcessProperties(source, target, reportErrors)) { if (reportErrors) { reportRelationError(headMessage, source, target); @@ -6635,7 +6645,7 @@ namespace ts { } } else { - if (source.flags & TypeFlags.Reference && target.flags & TypeFlags.Reference && (source).target === (target).target) { + if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (source).target === (target).target) { // We have type references to same target type, see if relationship holds for all type arguments if (result = typeArgumentsRelatedTo(source, target, reportErrors)) { return result; @@ -6672,7 +6682,7 @@ namespace ts { function isIdenticalTo(source: Type, target: Type): Ternary { let result: Ternary; if (source.flags & TypeFlags.ObjectType && target.flags & TypeFlags.ObjectType) { - if (source.flags & TypeFlags.Reference && target.flags & TypeFlags.Reference && (source).target === (target).target) { + if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (source).target === (target).target) { // We have type references to same target type, see if all type arguments are identical if (result = typeArgumentsRelatedTo(source, target, /*reportErrors*/ false)) { return result; @@ -6919,7 +6929,7 @@ namespace ts { } let result = Ternary.True; const properties = getPropertiesOfObjectType(target); - const requireOptionalProperties = relation === subtypeRelation && !(source.flags & TypeFlags.ObjectLiteral); + const requireOptionalProperties = relation === subtypeRelation && !(getObjectFlags(source) & ObjectFlags.ObjectLiteral); for (const targetProp of properties) { const sourceProp = getPropertyOfType(source, targetProp.name); @@ -7200,7 +7210,7 @@ namespace ts { // Return true if the given type is the constructor type for an abstract class function isAbstractConstructorType(type: Type) { - if (type.flags & TypeFlags.Anonymous) { + if (getObjectFlags(type) & ObjectFlags.Anonymous) { const symbol = type.symbol; if (symbol && symbol.flags & SymbolFlags.Class) { const declaration = getClassLikeDeclarationOfSymbol(symbol); @@ -7219,12 +7229,12 @@ namespace ts { // some level beyond that. function isDeeplyNestedGeneric(type: Type, stack: Type[], depth: number): boolean { // We track type references (created by createTypeReference) and instantiated types (created by instantiateType) - if (type.flags & (TypeFlags.Reference | TypeFlags.Instantiated) && depth >= 5) { + if (getObjectFlags(type) & (ObjectFlags.Reference | ObjectFlags.Instantiated) && depth >= 5) { const symbol = type.symbol; let count = 0; for (let i = 0; i < depth; i++) { const t = stack[i]; - if (t.flags & (TypeFlags.Reference | TypeFlags.Instantiated) && t.symbol === symbol) { + if (getObjectFlags(t) & (ObjectFlags.Reference | ObjectFlags.Instantiated) && t.symbol === symbol) { count++; if (count >= 5) return true; } @@ -7427,13 +7437,13 @@ namespace ts { } function isArrayType(type: Type): boolean { - return type.flags & TypeFlags.Reference && (type).target === globalArrayType; + return getObjectFlags(type) & ObjectFlags.Reference && (type).target === globalArrayType; } function isArrayLikeType(type: Type): boolean { // A type is array-like if it is a reference to the global Array or global ReadonlyArray type, // or if it is not the undefined or null type and if it is assignable to ReadonlyArray - return type.flags & TypeFlags.Reference && ((type).target === globalArrayType || (type).target === globalReadonlyArrayType) || + return getObjectFlags(type) & ObjectFlags.Reference && ((type).target === globalArrayType || (type).target === globalReadonlyArrayType) || !(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyReadonlyArrayType); } @@ -7474,7 +7484,7 @@ namespace ts { * Prefer using isTupleLikeType() unless the use of `elementTypes` is required. */ function isTupleType(type: Type): boolean { - return !!(type.flags & TypeFlags.Reference && (type).target.flags & TypeFlags.Tuple); + return !!(getObjectFlags(type) & ObjectFlags.Reference && (type).target.objectFlags & ObjectFlags.Tuple); } function getFalsyFlagsOfTypes(types: Type[]): TypeFlags { @@ -7558,7 +7568,7 @@ namespace ts { * Leave signatures alone since they are not subject to the check. */ function getRegularTypeOfObjectLiteral(type: Type): Type { - if (!(type.flags & TypeFlags.ObjectLiteral && type.flags & TypeFlags.FreshLiteral)) { + if (!(getObjectFlags(type) & ObjectFlags.ObjectLiteral && type.flags & TypeFlags.FreshLiteral)) { return type; } const regularType = (type).regularType; @@ -7575,6 +7585,7 @@ namespace ts { resolved.stringIndexInfo, resolved.numberIndexInfo); regularNew.flags = resolved.flags & ~TypeFlags.FreshLiteral; + regularNew.objectFlags |= ObjectFlags.ObjectLiteral; (type).regularType = regularNew; return regularNew; } @@ -7600,7 +7611,7 @@ namespace ts { if (type.flags & TypeFlags.Nullable) { return anyType; } - if (type.flags & TypeFlags.ObjectLiteral) { + if (getObjectFlags(type) & ObjectFlags.ObjectLiteral) { return getWidenedTypeOfObjectLiteral(type); } if (type.flags & TypeFlags.Union) { @@ -7640,7 +7651,7 @@ namespace ts { } } } - if (type.flags & TypeFlags.ObjectLiteral) { + if (getObjectFlags(type) & ObjectFlags.ObjectLiteral) { for (const p of getPropertiesOfObjectType(type)) { const t = getTypeOfSymbol(p); if (t.flags & TypeFlags.ContainsWideningType) { @@ -7743,8 +7754,8 @@ namespace ts { // results for union and intersection types for performance reasons. function couldContainTypeParameters(type: Type): boolean { return !!(type.flags & TypeFlags.TypeParameter || - type.flags & TypeFlags.Reference && forEach((type).typeArguments, couldContainTypeParameters) || - type.flags & TypeFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.Class) || + getObjectFlags(type) & ObjectFlags.Reference && forEach((type).typeArguments, couldContainTypeParameters) || + getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.Class) || type.flags & TypeFlags.UnionOrIntersection && couldUnionOrIntersectionContainTypeParameters(type)); } @@ -7852,7 +7863,7 @@ namespace ts { } } } - else if (source.flags & TypeFlags.Reference && target.flags & TypeFlags.Reference && (source).target === (target).target) { + else if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (source).target === (target).target) { // If source and target are references to the same generic type, infer from type arguments const sourceTypes = (source).typeArguments || emptyArray; const targetTypes = (target).typeArguments || emptyArray; @@ -8548,7 +8559,7 @@ namespace ts { // array types are ultimately converted into manifest array types (using getFinalArrayType) // and never escape the getFlowTypeOfReference function. function createEvolvingArrayType(elementType: Type): AnonymousType { - const result = createObjectType(TypeFlags.Anonymous); + const result = createObjectType(ObjectFlags.Anonymous); result.elementType = elementType; return result; } @@ -8566,7 +8577,7 @@ namespace ts { } function isEvolvingArrayType(type: Type) { - return !!(type.flags & TypeFlags.Anonymous && (type).elementType); + return !!(getObjectFlags(type) & ObjectFlags.Anonymous && (type).elementType); } function createFinalArrayType(elementType: Type) { @@ -9101,10 +9112,10 @@ namespace ts { if (!targetType) { // Target type is type of construct signature let constructSignatures: Signature[]; - if (rightType.flags & TypeFlags.Interface) { + if (getObjectFlags(rightType) & ObjectFlags.Interface) { constructSignatures = resolveDeclaredMembers(rightType).declaredConstructSignatures; } - else if (rightType.flags & TypeFlags.Anonymous) { + else if (getObjectFlags(rightType) & ObjectFlags.Anonymous) { constructSignatures = getSignaturesOfType(rightType, SignatureKind.Construct); } if (constructSignatures && constructSignatures.length) { @@ -10654,7 +10665,8 @@ namespace ts { const numberIndexInfo = hasComputedNumberProperty ? getObjectLiteralIndexInfo(node, propertiesArray, IndexKind.Number) : undefined; const result = createAnonymousType(node.symbol, propertiesTable, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo); const freshObjectLiteralFlag = compilerOptions.suppressExcessPropertyErrors ? 0 : TypeFlags.FreshLiteral; - result.flags |= TypeFlags.ObjectLiteral | TypeFlags.ContainsObjectLiteral | freshObjectLiteralFlag | (typeFlags & TypeFlags.PropagatingFlags); + result.flags |= TypeFlags.ContainsObjectLiteral | freshObjectLiteralFlag | (typeFlags & TypeFlags.PropagatingFlags); + result.objectFlags |= ObjectFlags.ObjectLiteral; if (patternWithComputedProperties) { result.isObjectLiteralPatternWithComputedProperties = true; } @@ -11239,7 +11251,7 @@ namespace ts { } // TODO: why is the first part of this check here? - if (!(getTargetType(type).flags & (TypeFlags.Class | TypeFlags.Interface) && hasBaseType(type, enclosingClass))) { + if (!(getObjectFlags(getTargetType(type)) & (ObjectFlags.Class | ObjectFlags.Interface) && hasBaseType(type, enclosingClass))) { error(errorNode, Diagnostics.Property_0_is_protected_and_only_accessible_through_an_instance_of_class_1, symbolToString(prop), typeToString(enclosingClass)); return false; } @@ -13528,7 +13540,7 @@ namespace ts { } function isConstEnumObjectType(type: Type): boolean { - return type.flags & (TypeFlags.ObjectType | TypeFlags.Anonymous) && type.symbol && isConstEnumSymbol(type.symbol); + return getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol && isConstEnumSymbol(type.symbol); } function isConstEnumSymbol(symbol: Symbol): boolean { @@ -15293,7 +15305,7 @@ namespace ts { return undefined; } - if (promise.flags & TypeFlags.Reference) { + if (getObjectFlags(promise) & ObjectFlags.Reference) { if ((promise).target === tryGetGlobalPromiseType() || (promise).target === getGlobalPromiseLikeType()) { return (promise).typeArguments[0]; @@ -16562,7 +16574,7 @@ namespace ts { if (!typeAsIterable.iterableElementType) { // As an optimization, if the type is instantiated directly using the globalIterableType (Iterable), // then just grab its type argument. - if ((type.flags & TypeFlags.Reference) && (type).target === getGlobalIterableType()) { + if ((getObjectFlags(type) & ObjectFlags.Reference) && (type).target === getGlobalIterableType()) { typeAsIterable.iterableElementType = (type).typeArguments[0]; } else { @@ -16608,7 +16620,7 @@ namespace ts { if (!typeAsIterator.iteratorElementType) { // As an optimization, if the type is instantiated directly using the globalIteratorType (Iterator), // then just grab its type argument. - if ((type.flags & TypeFlags.Reference) && (type).target === getGlobalIteratorType()) { + if ((getObjectFlags(type) & ObjectFlags.Reference) && (type).target === getGlobalIteratorType()) { typeAsIterator.iteratorElementType = (type).typeArguments[0]; } else { @@ -16652,7 +16664,7 @@ namespace ts { // As an optimization, if the type is instantiated directly using the globalIterableIteratorType (IterableIterator), // then just grab its type argument. - if ((type.flags & TypeFlags.Reference) && (type).target === getGlobalIterableIteratorType()) { + if ((getObjectFlags(type) & ObjectFlags.Reference) && (type).target === getGlobalIterableIteratorType()) { return (type).typeArguments[0]; } @@ -16949,7 +16961,7 @@ namespace ts { checkIndexConstraintForProperty(prop, propType, type, declaredNumberIndexer, numberIndexType, IndexKind.Number); }); - if (type.flags & TypeFlags.Class && isClassLike(type.symbol.valueDeclaration)) { + if (getObjectFlags(type) & ObjectFlags.Class && isClassLike(type.symbol.valueDeclaration)) { const classDeclaration = type.symbol.valueDeclaration; for (const member of classDeclaration.members) { // Only process instance properties with computed names here. @@ -16968,7 +16980,7 @@ namespace ts { if (stringIndexType && numberIndexType) { errorNode = declaredNumberIndexer || declaredStringIndexer; // condition 'errorNode === undefined' may appear if types does not declare nor string neither number indexer - if (!errorNode && (type.flags & TypeFlags.Interface)) { + if (!errorNode && (getObjectFlags(type) & ObjectFlags.Interface)) { const someBaseTypeHasBothIndexers = forEach(getBaseTypes(type), base => getIndexTypeOfType(base, IndexKind.String) && getIndexTypeOfType(base, IndexKind.Number)); errorNode = someBaseTypeHasBothIndexers ? undefined : type.symbol.declarations[0]; } @@ -17005,7 +17017,7 @@ namespace ts { else if (indexDeclaration) { errorNode = indexDeclaration; } - else if (containingType.flags & TypeFlags.Interface) { + else if (getObjectFlags(containingType) & ObjectFlags.Interface) { // for interfaces property and indexer might be inherited from different bases // check if any base class already has both property and indexer. // check should be performed only if 'type' is the first type that brings property\indexer together @@ -17164,8 +17176,8 @@ namespace ts { if (produceDiagnostics) { const t = getTypeFromTypeNode(typeRefNode); if (t !== unknownType) { - const declaredType = (t.flags & TypeFlags.Reference) ? (t).target : t; - if (declaredType.flags & (TypeFlags.Class | TypeFlags.Interface)) { + const declaredType = (getObjectFlags(t) & ObjectFlags.Reference) ? (t).target : t; + if (getObjectFlags(declaredType) & (ObjectFlags.Class | ObjectFlags.Interface)) { checkTypeAssignableTo(typeWithThis, getTypeWithThisArgument(t, type.thisType), node.name || node, Diagnostics.Class_0_incorrectly_implements_interface_1); } else { @@ -19671,7 +19683,7 @@ namespace ts { const thenPropertySymbol = createSymbol(SymbolFlags.Transient | SymbolFlags.Property, "then"); getSymbolLinks(thenPropertySymbol).type = globalFunctionType; - const thenableType = createObjectType(TypeFlags.Anonymous); + const thenableType = createObjectType(ObjectFlags.Anonymous); thenableType.properties = [thenPropertySymbol]; thenableType.members = createSymbolTable(thenableType.properties); thenableType.callSignatures = []; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 540d6e8c406..0a298a78d02 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2612,24 +2612,17 @@ namespace ts { Null = 1 << 12, Never = 1 << 13, // Never type TypeParameter = 1 << 14, // Type parameter - Class = 1 << 15, // Class - Interface = 1 << 16, // Interface - Reference = 1 << 17, // Generic type reference - Tuple = 1 << 18, // Synthesized generic tuple type - Union = 1 << 19, // Union (T | U) - Intersection = 1 << 20, // Intersection (T & U) - Anonymous = 1 << 21, // Anonymous - Instantiated = 1 << 22, // Instantiated anonymous type + ObjectType = 1 << 15, // Object type + Union = 1 << 16, // Union (T | U) + Intersection = 1 << 17, // Intersection (T & U) /* @internal */ - ObjectLiteral = 1 << 23, // Originates in an object literal + FreshLiteral = 1 << 18, // Fresh literal type /* @internal */ - FreshLiteral = 1 << 24, // Fresh literal type + ContainsWideningType = 1 << 19, // Type is or contains undefined or null widening type /* @internal */ - ContainsWideningType = 1 << 25, // Type is or contains undefined or null widening type + ContainsObjectLiteral = 1 << 20, // Type is or contains object literal type /* @internal */ - ContainsObjectLiteral = 1 << 26, // Type is or contains object literal type - /* @internal */ - ContainsAnyFunctionType = 1 << 27, // Type is or contains object literal type + ContainsAnyFunctionType = 1 << 21, // Type is or contains object literal type /* @internal */ Nullable = Undefined | Null, @@ -2646,7 +2639,6 @@ namespace ts { NumberLike = Number | NumberLiteral | Enum | EnumLiteral, BooleanLike = Boolean | BooleanLiteral, EnumLike = Enum | EnumLiteral, - ObjectType = Class | Interface | Reference | Tuple | Anonymous, UnionOrIntersection = Union | Intersection, StructuredType = ObjectType | Union | Intersection, StructuredOrTypeParameter = StructuredType | TypeParameter, @@ -2663,6 +2655,16 @@ namespace ts { export type DestructuringPattern = BindingPattern | ObjectLiteralExpression | ArrayLiteralExpression; + export const enum ObjectFlags { + Class = 1 << 0, // Class + Interface = 1 << 1, // Interface + Reference = 1 << 2, // Generic type reference + Tuple = 1 << 3, // Synthesized generic tuple type + Anonymous = 1 << 4, // Anonymous + Instantiated = 1 << 5, // Instantiated anonymous type + ObjectLiteral = 1 << 6, // Originates in an object literal + } + // Properties common to all types export interface Type { flags: TypeFlags; // Flags @@ -2699,7 +2701,7 @@ namespace ts { // Object types (TypeFlags.ObjectType) export interface ObjectType extends Type { - _objectTypeBrand: any; + objectFlags: ObjectFlags; isObjectLiteralPatternWithComputedProperties?: boolean; } diff --git a/src/services/services.ts b/src/services/services.ts index 9ed73318d8e..a562aa44bfd 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -347,6 +347,7 @@ namespace ts { class TypeObject implements Type { checker: TypeChecker; flags: TypeFlags; + objectFlags?: ObjectFlags; id: number; symbol: Symbol; constructor(checker: TypeChecker, flags: TypeFlags) { @@ -381,7 +382,7 @@ namespace ts { return this.checker.getIndexTypeOfType(this, IndexKind.Number); } getBaseTypes(): ObjectType[] { - return this.flags & (TypeFlags.Class | TypeFlags.Interface) + return this.flags & TypeFlags.ObjectType && this.objectFlags & (ObjectFlags.Class | ObjectFlags.Interface) ? this.checker.getBaseTypes(this) : undefined; } diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts index c9141278b10..37753547768 100644 --- a/src/services/symbolDisplay.ts +++ b/src/services/symbolDisplay.ts @@ -173,7 +173,7 @@ namespace ts.SymbolDisplay { displayParts.push(keywordPart(SyntaxKind.NewKeyword)); displayParts.push(spacePart()); } - if (!(type.flags & TypeFlags.Anonymous) && type.symbol) { + if (!(type.flags & TypeFlags.ObjectType && (type).objectFlags & ObjectFlags.Anonymous) && type.symbol) { addRange(displayParts, symbolToDisplayParts(typeChecker, type.symbol, enclosingDeclaration, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOrArguments)); } addSignatureDisplayParts(signature, allSignatures, TypeFormatFlags.WriteArrowStyleSignature); From cfe37ce054f735786e6a01de678014bbfc880ebf Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Thu, 20 Oct 2016 15:36:44 -0700 Subject: [PATCH 18/49] Jenkins / .net CI support --- jenkins.sh | 13 +++++++++++++ netci.groovy | 22 ++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100755 jenkins.sh create mode 100644 netci.groovy diff --git a/jenkins.sh b/jenkins.sh new file mode 100755 index 00000000000..377a44b7bf7 --- /dev/null +++ b/jenkins.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +# Set up NVM +export NVM_DIR="/home/dotnet-bot/.nvm" +[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" + +nvm install $1 + +npm uninstall typescript +npm uninstall tslint +npm install +npm update +npm test diff --git a/netci.groovy b/netci.groovy new file mode 100644 index 00000000000..9f2a96cdeef --- /dev/null +++ b/netci.groovy @@ -0,0 +1,22 @@ +// Import the utility functionality. +import jobs.generation.Utilities; + +// Defines a the new of the repo, used elsewhere in the file +def project = GithubProject +def branch = GithubBranchName + +def nodeVersions = ['stable', '4'] + +nodeVersions.each { nodeVer -> + + def newJobName = "typescript_node.${nodeVer}" + def newJob = job(Utilities.getFullJobName(project, newJobName, true)) { + steps { + shell("./jenkins.sh ${nodeVer}") + } + } + + Utilities.standardJobSetup(newJob, project, true, "*/${branch}") + Utilities.setMachineAffinity(newJob, 'Ubuntu', '20161020') + Utilities.addGithubPRTriggerForBranch(newJob, branch, "TypeScript Test Run ${newJobName}") +} From b37313c90d6d2039e4df5d93aa8007e1fd7e16b9 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 20 Oct 2016 15:42:24 -0700 Subject: [PATCH 19/49] Introduce EvolvingArrayType and associated ObjectFlag.EvolvingArray --- src/compiler/checker.ts | 20 ++++++++++---------- src/compiler/types.ts | 8 ++++++-- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 32705df1725..843a21cbae3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -115,7 +115,7 @@ namespace ts { const intersectionTypes = createMap(); const stringLiteralTypes = createMap(); const numericLiteralTypes = createMap(); - const evolvingArrayTypes: AnonymousType[] = []; + const evolvingArrayTypes: EvolvingArrayType[] = []; const unknownSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "unknown"); const resolvingSymbol = createSymbol(SymbolFlags.Transient, "__resolving__"); @@ -8558,26 +8558,26 @@ namespace ts { // 'x.push(value)' or 'x[n] = value' operation along the control flow graph. Evolving // array types are ultimately converted into manifest array types (using getFinalArrayType) // and never escape the getFlowTypeOfReference function. - function createEvolvingArrayType(elementType: Type): AnonymousType { - const result = createObjectType(ObjectFlags.Anonymous); + function createEvolvingArrayType(elementType: Type): EvolvingArrayType { + const result = createObjectType(ObjectFlags.EvolvingArray); result.elementType = elementType; return result; } - function getEvolvingArrayType(elementType: Type): AnonymousType { + function getEvolvingArrayType(elementType: Type): EvolvingArrayType { return evolvingArrayTypes[elementType.id] || (evolvingArrayTypes[elementType.id] = createEvolvingArrayType(elementType)); } // When adding evolving array element types we do not perform subtype reduction. Instead, // we defer subtype reduction until the evolving array type is finalized into a manifest // array type. - function addEvolvingArrayElementType(evolvingArrayType: AnonymousType, node: Expression): AnonymousType { + function addEvolvingArrayElementType(evolvingArrayType: EvolvingArrayType, node: Expression): EvolvingArrayType { const elementType = getBaseTypeOfLiteralType(checkExpression(node)); return isTypeSubsetOf(elementType, evolvingArrayType.elementType) ? evolvingArrayType : getEvolvingArrayType(getUnionType([evolvingArrayType.elementType, elementType])); } function isEvolvingArrayType(type: Type) { - return !!(getObjectFlags(type) & ObjectFlags.Anonymous && (type).elementType); + return !!(getObjectFlags(type) & ObjectFlags.EvolvingArray); } function createFinalArrayType(elementType: Type) { @@ -8589,16 +8589,16 @@ namespace ts { } // We perform subtype reduction upon obtaining the final array type from an evolving array type. - function getFinalArrayType(evolvingArrayType: AnonymousType): Type { + function getFinalArrayType(evolvingArrayType: EvolvingArrayType): Type { return evolvingArrayType.finalArrayType || (evolvingArrayType.finalArrayType = createFinalArrayType(evolvingArrayType.elementType)); } function finalizeEvolvingArrayType(type: Type): Type { - return isEvolvingArrayType(type) ? getFinalArrayType(type) : type; + return isEvolvingArrayType(type) ? getFinalArrayType(type) : type; } function getElementTypeOfEvolvingArrayType(type: Type) { - return isEvolvingArrayType(type) ? (type).elementType : neverType; + return isEvolvingArrayType(type) ? (type).elementType : neverType; } function isEvolvingArrayTypeList(types: Type[]) { @@ -8770,7 +8770,7 @@ namespace ts { const flowType = getTypeAtFlowNode(flow.antecedent); const type = getTypeFromFlowType(flowType); if (isEvolvingArrayType(type)) { - let evolvedType = type; + let evolvedType = type; if (node.kind === SyntaxKind.CallExpression) { for (const arg of (node).arguments) { evolvedType = addEvolvingArrayElementType(evolvedType, arg); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 0a298a78d02..551f8b8697e 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2663,6 +2663,7 @@ namespace ts { Anonymous = 1 << 4, // Anonymous Instantiated = 1 << 5, // Instantiated anonymous type ObjectLiteral = 1 << 6, // Originates in an object literal + EvolvingArray = 1 << 7, // Evolving array type } // Properties common to all types @@ -2763,8 +2764,11 @@ namespace ts { export interface AnonymousType extends ObjectType { target?: AnonymousType; // Instantiation target mapper?: TypeMapper; // Instantiation mapper - elementType?: Type; // Element expressions of evolving array type - finalArrayType?: Type; // Final array type of evolving array type + } + + export interface EvolvingArrayType extends ObjectType { + elementType: Type; // Element expressions of evolving array type + finalArrayType?: Type; // Final array type of evolving array type } /* @internal */ From 81383e172f715ec005d0110d4bbf672685f92644 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 20 Oct 2016 15:56:21 -0700 Subject: [PATCH 20/49] add test --- .../comparisonOperatorWithNumericLiteral.ts | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithNumericLiteral.ts diff --git a/tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithNumericLiteral.ts b/tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithNumericLiteral.ts new file mode 100644 index 00000000000..457a324e971 --- /dev/null +++ b/tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithNumericLiteral.ts @@ -0,0 +1,42 @@ +type BrandedNum = number & { __numberBrand: any }; +var x : BrandedNum; + +// operator > +x > 0; +x > 0; +x > 0; + +// operator < +x < 0; +x < 0; +x < 0; + +// operator >= +x >= 0; +x >= 0; +x >= 0; + +// operator <= +x <= 0; +x <= 0; +x <= 0; + +// operator == +x == 0; +x == 0; +x == 0; + +// operator != +x != 0; +x != 0; +x != 0; + +// operator === +x === 0; +x === 0; +x === 0; + +// operator !== +x !== 0; +x !== 0; +x !== 0; From 1a41ebf542525847b5f041b0db5b544dc0d93697 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 20 Oct 2016 16:27:02 -0700 Subject: [PATCH 21/49] update baselines --- ...risonOperatorWithNumericLiteral.errors.txt | 58 ++++++++++++++ .../comparisonOperatorWithNumericLiteral.js | 79 +++++++++++++++++++ .../comparisonOperatorWithNumericLiteral.ts | 42 ++++++++++ 3 files changed, 179 insertions(+) create mode 100644 tests/baselines/reference/comparisonOperatorWithNumericLiteral.errors.txt create mode 100644 tests/baselines/reference/comparisonOperatorWithNumericLiteral.js create mode 100644 tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithNumericLiteral.ts diff --git a/tests/baselines/reference/comparisonOperatorWithNumericLiteral.errors.txt b/tests/baselines/reference/comparisonOperatorWithNumericLiteral.errors.txt new file mode 100644 index 00000000000..d863ca29090 --- /dev/null +++ b/tests/baselines/reference/comparisonOperatorWithNumericLiteral.errors.txt @@ -0,0 +1,58 @@ +tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithNumericLiteral.ts(5,1): error TS2365: Operator '>' cannot be applied to types 'BrandedNum' and '0'. +tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithNumericLiteral.ts(10,1): error TS2365: Operator '<' cannot be applied to types 'BrandedNum' and '0'. +tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithNumericLiteral.ts(15,1): error TS2365: Operator '>=' cannot be applied to types 'BrandedNum' and '0'. +tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithNumericLiteral.ts(20,1): error TS2365: Operator '<=' cannot be applied to types 'BrandedNum' and '0'. + + +==== tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithNumericLiteral.ts (4 errors) ==== + type BrandedNum = number & { __numberBrand: any }; + var x : BrandedNum; + + // operator > + x > 0; + ~~~~~ +!!! error TS2365: Operator '>' cannot be applied to types 'BrandedNum' and '0'. + x > 0; + x > 0; + + // operator < + x < 0; + ~~~~~ +!!! error TS2365: Operator '<' cannot be applied to types 'BrandedNum' and '0'. + x < 0; + x < 0; + + // operator >= + x >= 0; + ~~~~~~ +!!! error TS2365: Operator '>=' cannot be applied to types 'BrandedNum' and '0'. + x >= 0; + x >= 0; + + // operator <= + x <= 0; + ~~~~~~ +!!! error TS2365: Operator '<=' cannot be applied to types 'BrandedNum' and '0'. + x <= 0; + x <= 0; + + // operator == + x == 0; + x == 0; + x == 0; + + // operator != + x != 0; + x != 0; + x != 0; + + // operator === + x === 0; + x === 0; + x === 0; + + // operator !== + x !== 0; + x !== 0; + x !== 0; + \ No newline at end of file diff --git a/tests/baselines/reference/comparisonOperatorWithNumericLiteral.js b/tests/baselines/reference/comparisonOperatorWithNumericLiteral.js new file mode 100644 index 00000000000..6f28423d166 --- /dev/null +++ b/tests/baselines/reference/comparisonOperatorWithNumericLiteral.js @@ -0,0 +1,79 @@ +//// [comparisonOperatorWithNumericLiteral.ts] +type BrandedNum = number & { __numberBrand: any }; +var x : BrandedNum; + +// operator > +x > 0; +x > 0; +x > 0; + +// operator < +x < 0; +x < 0; +x < 0; + +// operator >= +x >= 0; +x >= 0; +x >= 0; + +// operator <= +x <= 0; +x <= 0; +x <= 0; + +// operator == +x == 0; +x == 0; +x == 0; + +// operator != +x != 0; +x != 0; +x != 0; + +// operator === +x === 0; +x === 0; +x === 0; + +// operator !== +x !== 0; +x !== 0; +x !== 0; + + +//// [comparisonOperatorWithNumericLiteral.js] +var x; +// operator > +x > 0; +x > 0; +x > 0; +// operator < +x < 0; +x < 0; +x < 0; +// operator >= +x >= 0; +x >= 0; +x >= 0; +// operator <= +x <= 0; +x <= 0; +x <= 0; +// operator == +x == 0; +x == 0; +x == 0; +// operator != +x != 0; +x != 0; +x != 0; +// operator === +x === 0; +x === 0; +x === 0; +// operator !== +x !== 0; +x !== 0; +x !== 0; diff --git a/tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithNumericLiteral.ts b/tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithNumericLiteral.ts new file mode 100644 index 00000000000..457a324e971 --- /dev/null +++ b/tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithNumericLiteral.ts @@ -0,0 +1,42 @@ +type BrandedNum = number & { __numberBrand: any }; +var x : BrandedNum; + +// operator > +x > 0; +x > 0; +x > 0; + +// operator < +x < 0; +x < 0; +x < 0; + +// operator >= +x >= 0; +x >= 0; +x >= 0; + +// operator <= +x <= 0; +x <= 0; +x <= 0; + +// operator == +x == 0; +x == 0; +x == 0; + +// operator != +x != 0; +x != 0; +x != 0; + +// operator === +x === 0; +x === 0; +x === 0; + +// operator !== +x !== 0; +x !== 0; +x !== 0; From 58d6156c69e08e8479e3102d90cccc34a0e9d832 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 20 Oct 2016 17:00:07 -0700 Subject: [PATCH 22/49] Move ObjectLiteralPatternWithComputedProperties to ObjectFlags --- src/compiler/checker.ts | 10 ++++------ src/compiler/types.ts | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 843a21cbae3..ae99919fabe 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3222,7 +3222,7 @@ namespace ts { result.pattern = pattern; } if (hasComputedProperties) { - result.isObjectLiteralPatternWithComputedProperties = true; + result.objectFlags |= ObjectFlags.ObjectLiteralPatternWithComputedProperties; } return result; } @@ -6734,8 +6734,7 @@ namespace ts { } function hasExcessProperties(source: FreshObjectLiteralType, target: Type, reportErrors: boolean): boolean { - if (maybeTypeOfKind(target, TypeFlags.ObjectType) && - (!(target.flags & TypeFlags.ObjectType) || !(target as ObjectType).isObjectLiteralPatternWithComputedProperties)) { + if (maybeTypeOfKind(target, TypeFlags.ObjectType) && !(getObjectFlags(target) & ObjectFlags.ObjectLiteralPatternWithComputedProperties)) { for (const prop of getPropertiesOfObjectType(source)) { if (!isKnownProperty(target, prop.name)) { if (reportErrors) { @@ -10599,8 +10598,7 @@ namespace ts { patternWithComputedProperties = true; } } - else if (contextualTypeHasPattern && - !(contextualType.flags & TypeFlags.ObjectType && (contextualType as ObjectType).isObjectLiteralPatternWithComputedProperties)) { + else if (contextualTypeHasPattern && !(getObjectFlags(contextualType) & ObjectFlags.ObjectLiteralPatternWithComputedProperties)) { // If object literal is contextually typed by the implied type of a binding pattern, and if the // binding pattern specifies a default value for the property, make the property optional. const impliedProp = getPropertyOfType(contextualType, member.name); @@ -10668,7 +10666,7 @@ namespace ts { result.flags |= TypeFlags.ContainsObjectLiteral | freshObjectLiteralFlag | (typeFlags & TypeFlags.PropagatingFlags); result.objectFlags |= ObjectFlags.ObjectLiteral; if (patternWithComputedProperties) { - result.isObjectLiteralPatternWithComputedProperties = true; + result.objectFlags |= ObjectFlags.ObjectLiteralPatternWithComputedProperties; } if (inDestructuringPattern) { result.pattern = node; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 551f8b8697e..9d6108f27fb 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2664,6 +2664,7 @@ namespace ts { Instantiated = 1 << 5, // Instantiated anonymous type ObjectLiteral = 1 << 6, // Originates in an object literal EvolvingArray = 1 << 7, // Evolving array type + ObjectLiteralPatternWithComputedProperties = 1 << 8, // Object literal pattern with computed properties } // Properties common to all types @@ -2703,7 +2704,6 @@ namespace ts { // Object types (TypeFlags.ObjectType) export interface ObjectType extends Type { objectFlags: ObjectFlags; - isObjectLiteralPatternWithComputedProperties?: boolean; } // Class and interface types (TypeFlags.Class and TypeFlags.Interface) From f05ecec313d8ea00f0023223c01cf92c38d5657c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 20 Oct 2016 17:28:53 -0700 Subject: [PATCH 23/49] Refactoring a bit more --- src/compiler/checker.ts | 28 ++++++++++++---------------- src/compiler/types.ts | 25 +++++++++++++------------ 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ae99919fabe..49d453c7761 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2195,7 +2195,7 @@ namespace ts { writePunctuation(writer, SyntaxKind.DotToken); appendSymbolNameOnly(type.symbol, writer); } - else if (getObjectFlags(type) & (ObjectFlags.Class | ObjectFlags.Interface) || type.flags & (TypeFlags.Enum | TypeFlags.TypeParameter)) { + else if (getObjectFlags(type) & ObjectFlags.ClassOrInterface || type.flags & (TypeFlags.Enum | TypeFlags.TypeParameter)) { // The specified symbol flags need to be reinterpreted as type flags buildSymbolDisplay(type.symbol, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, nextFlags); } @@ -3705,7 +3705,7 @@ namespace ts { if (baseType === unknownType) { return; } - if (!(getObjectFlags(getTargetType(baseType)) & (ObjectFlags.Class | ObjectFlags.Interface))) { + if (!(getObjectFlags(getTargetType(baseType)) & ObjectFlags.ClassOrInterface)) { error(baseTypeNode.expression, Diagnostics.Base_constructor_return_type_0_is_not_a_class_or_interface_type, typeToString(baseType)); return; } @@ -3741,7 +3741,7 @@ namespace ts { for (const node of getInterfaceBaseTypeNodes(declaration)) { const baseType = getTypeFromTypeNode(node); if (baseType !== unknownType) { - if (getObjectFlags(getTargetType(baseType)) & (ObjectFlags.Class | ObjectFlags.Interface)) { + if (getObjectFlags(getTargetType(baseType)) & ObjectFlags.ClassOrInterface) { if (type !== baseType && !hasBaseType(baseType, type)) { if (type.resolvedBaseTypes === emptyArray) { type.resolvedBaseTypes = [baseType]; @@ -4373,7 +4373,7 @@ namespace ts { if ((type).objectFlags & ObjectFlags.Reference) { resolveTypeReferenceMembers(type); } - else if ((type).objectFlags & (ObjectFlags.Class | ObjectFlags.Interface)) { + else if ((type).objectFlags & ObjectFlags.ClassOrInterface) { resolveClassOrInterfaceMembers(type); } else if ((type).objectFlags & ObjectFlags.Anonymous) { @@ -8575,10 +8575,6 @@ namespace ts { return isTypeSubsetOf(elementType, evolvingArrayType.elementType) ? evolvingArrayType : getEvolvingArrayType(getUnionType([evolvingArrayType.elementType, elementType])); } - function isEvolvingArrayType(type: Type) { - return !!(getObjectFlags(type) & ObjectFlags.EvolvingArray); - } - function createFinalArrayType(elementType: Type) { return elementType.flags & TypeFlags.Never ? autoArrayType : @@ -8593,18 +8589,18 @@ namespace ts { } function finalizeEvolvingArrayType(type: Type): Type { - return isEvolvingArrayType(type) ? getFinalArrayType(type) : type; + return getObjectFlags(type) & ObjectFlags.EvolvingArray ? getFinalArrayType(type) : type; } function getElementTypeOfEvolvingArrayType(type: Type) { - return isEvolvingArrayType(type) ? (type).elementType : neverType; + return getObjectFlags(type) & ObjectFlags.EvolvingArray ? (type).elementType : neverType; } function isEvolvingArrayTypeList(types: Type[]) { let hasEvolvingArrayType = false; for (const t of types) { if (!(t.flags & TypeFlags.Never)) { - if (!isEvolvingArrayType(t)) { + if (!(getObjectFlags(t) & ObjectFlags.EvolvingArray)) { return false; } hasEvolvingArrayType = true; @@ -8655,7 +8651,7 @@ namespace ts { // we give type 'any[]' to 'x' instead of using the type determined by control flow analysis such that operations // on empty arrays are possible without implicit any errors and new element types can be inferred without // type mismatch errors. - const resultType = isEvolvingArrayType(evolvedType) && isEvolvingArrayOperationTarget(reference) ? anyArrayType : finalizeEvolvingArrayType(evolvedType); + const resultType = getObjectFlags(evolvedType) & ObjectFlags.EvolvingArray && isEvolvingArrayOperationTarget(reference) ? anyArrayType : finalizeEvolvingArrayType(evolvedType); if (reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) { return declaredType; } @@ -8768,7 +8764,7 @@ namespace ts { if (isMatchingReference(reference, getReferenceCandidate(expr))) { const flowType = getTypeAtFlowNode(flow.antecedent); const type = getTypeFromFlowType(flowType); - if (isEvolvingArrayType(type)) { + if (getObjectFlags(type) & ObjectFlags.EvolvingArray) { let evolvedType = type; if (node.kind === SyntaxKind.CallExpression) { for (const arg of (node).arguments) { @@ -11249,7 +11245,7 @@ namespace ts { } // TODO: why is the first part of this check here? - if (!(getObjectFlags(getTargetType(type)) & (ObjectFlags.Class | ObjectFlags.Interface) && hasBaseType(type, enclosingClass))) { + if (!(getObjectFlags(getTargetType(type)) & ObjectFlags.ClassOrInterface && hasBaseType(type, enclosingClass))) { error(errorNode, Diagnostics.Property_0_is_protected_and_only_accessible_through_an_instance_of_class_1, symbolToString(prop), typeToString(enclosingClass)); return false; } @@ -17174,8 +17170,8 @@ namespace ts { if (produceDiagnostics) { const t = getTypeFromTypeNode(typeRefNode); if (t !== unknownType) { - const declaredType = (getObjectFlags(t) & ObjectFlags.Reference) ? (t).target : t; - if (getObjectFlags(declaredType) & (ObjectFlags.Class | ObjectFlags.Interface)) { + const declaredType = getObjectFlags(t) & ObjectFlags.Reference ? (t).target : t; + if (getObjectFlags(declaredType) & ObjectFlags.ClassOrInterface) { checkTypeAssignableTo(typeWithThis, getTypeWithThisArgument(t, type.thisType), node.name || node, Diagnostics.Class_0_incorrectly_implements_interface_1); } else { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 9d6108f27fb..ed4d55c584b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2655,18 +2655,6 @@ namespace ts { export type DestructuringPattern = BindingPattern | ObjectLiteralExpression | ArrayLiteralExpression; - export const enum ObjectFlags { - Class = 1 << 0, // Class - Interface = 1 << 1, // Interface - Reference = 1 << 2, // Generic type reference - Tuple = 1 << 3, // Synthesized generic tuple type - Anonymous = 1 << 4, // Anonymous - Instantiated = 1 << 5, // Instantiated anonymous type - ObjectLiteral = 1 << 6, // Originates in an object literal - EvolvingArray = 1 << 7, // Evolving array type - ObjectLiteralPatternWithComputedProperties = 1 << 8, // Object literal pattern with computed properties - } - // Properties common to all types export interface Type { flags: TypeFlags; // Flags @@ -2701,6 +2689,19 @@ namespace ts { baseType: EnumType & UnionType; // Base enum type } + export const enum ObjectFlags { + Class = 1 << 0, // Class + Interface = 1 << 1, // Interface + Reference = 1 << 2, // Generic type reference + Tuple = 1 << 3, // Synthesized generic tuple type + Anonymous = 1 << 4, // Anonymous + Instantiated = 1 << 5, // Instantiated anonymous type + ObjectLiteral = 1 << 6, // Originates in an object literal + EvolvingArray = 1 << 7, // Evolving array type + ObjectLiteralPatternWithComputedProperties = 1 << 8, // Object literal pattern with computed properties + ClassOrInterface = Class | Interface + } + // Object types (TypeFlags.ObjectType) export interface ObjectType extends Type { objectFlags: ObjectFlags; From a477d1f7bbdea99ebf022e0b99f558d4c6add24f Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Thu, 20 Oct 2016 21:15:47 -0700 Subject: [PATCH 24/49] Merge pull request #11764 from Microsoft/vladima/11744 watch configuration files if they exist even if they cannot be parsed --- src/harness/unittests/typingsInstaller.ts | 69 +++++++++++++++---- .../typingsInstaller/typingsInstaller.ts | 6 +- src/services/jsTyping.ts | 4 +- 3 files changed, 63 insertions(+), 16 deletions(-) diff --git a/src/harness/unittests/typingsInstaller.ts b/src/harness/unittests/typingsInstaller.ts index 323d8c0ed9e..4a0021e6ad4 100644 --- a/src/harness/unittests/typingsInstaller.ts +++ b/src/harness/unittests/typingsInstaller.ts @@ -569,7 +569,7 @@ namespace ts.projectSystem { } executeRequest(requestKind: TI.RequestKind, _requestId: number, args: string[], _cwd: string, cb: TI.RequestCompletedAction): void { if (requestKind === TI.NpmInstallRequest) { - let typingFiles: (FileOrFolder & { typings: string}) [] = []; + let typingFiles: (FileOrFolder & { typings: string })[] = []; if (args.indexOf("@types/commander") >= 0) { typingFiles = [commander, jquery, lodash, cordova]; } @@ -591,7 +591,7 @@ namespace ts.projectSystem { projectFileName: projectFileName1, options: { allowJS: true, moduleResolution: ModuleResolutionKind.NodeJs }, rootFiles: [toExternalFile(lodashJs.path), toExternalFile(commanderJs.path), toExternalFile(file3.path)], - typingOptions: { include: ["jquery", "cordova" ] } + typingOptions: { include: ["jquery", "cordova"] } }); installer.checkPendingCommands([TI.NpmViewRequest, TI.NpmViewRequest, TI.NpmViewRequest]); @@ -626,7 +626,7 @@ namespace ts.projectSystem { installer.executePendingCommands(); checkProjectActualFiles(p1, [lodashJs.path, commanderJs.path, file3.path, commander.path, jquery.path, lodash.path, cordova.path]); - checkProjectActualFiles(p2, [file3.path, grunt.path, gulp.path ]); + checkProjectActualFiles(p2, [file3.path, grunt.path, gulp.path]); }); it("configured projects discover from node_modules", () => { @@ -687,10 +687,10 @@ namespace ts.projectSystem { const bowerJson = { path: "/bower.json", content: JSON.stringify({ - "dependencies": { - "jquery": "^3.1.0" - } - }) + "dependencies": { + "jquery": "^3.1.0" + } + }) }; const jqueryDTS = { path: "/tmp/node_modules/@types/jquery/index.d.ts", @@ -720,26 +720,69 @@ namespace ts.projectSystem { checkNumberOfProjects(projectService, { configuredProjects: 1 }); checkProjectActualFiles(p, [app.path, jqueryDTS.path]); }); + + it("Malformed package.json should be watched", () => { + const f = { + path: "/a/b/app.js", + content: "var x = require('commander')" + }; + const brokenPackageJson = { + path: "/a/b/package.json", + content: `{ "dependencies": { "co } }` + }; + const fixedPackageJson = { + path: brokenPackageJson.path, + content: `{ "dependencies": { "commander": "0.0.2" } }` + }; + const cachePath = "/a/cache/"; + const commander = { + path: cachePath + "node_modules/@types/commander/index.d.ts", + content: "export let x: number" + }; + const host = createServerHost([f, brokenPackageJson]); + const installer = new (class extends Installer { + constructor() { + super(host, { globalTypingsCacheLocation: cachePath }); + } + executeRequest(requestKind: TI.RequestKind, _requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) { + const installedTypings = ["@types/commander"]; + const typingFiles = [commander]; + executeCommand(this, host, installedTypings, typingFiles, requestKind, cb); + } + })(); + const service = createProjectService(host, { typingsInstaller: installer }); + service.openClientFile(f.path); + + installer.checkPendingCommands([]); + + host.reloadFS([f, fixedPackageJson]); + host.triggerFileWatcherCallback(fixedPackageJson.path, /*removed*/ false); + // expected one view and one install request + installer.installAll([TI.NpmViewRequest], [TI.NpmInstallRequest]); + + service.checkNumberOfProjects({ inferredProjects: 1 }); + checkProjectActualFiles(service.inferredProjects[0], [f.path, commander.path]); + }); }); describe("Validate package name:", () => { - it ("name cannot be too long", () => { + it("name cannot be too long", () => { let packageName = "a"; for (let i = 0; i < 8; i++) { packageName += packageName; } assert.equal(TI.validatePackageName(packageName), TI.PackageNameValidationResult.NameTooLong); }); - it ("name cannot start with dot", () => { + it("name cannot start with dot", () => { assert.equal(TI.validatePackageName(".foo"), TI.PackageNameValidationResult.NameStartsWithDot); }); - it ("name cannot start with underscore", () => { + it("name cannot start with underscore", () => { assert.equal(TI.validatePackageName("_foo"), TI.PackageNameValidationResult.NameStartsWithUnderscore); }); - it ("scoped packages not supported", () => { + it("scoped packages not supported", () => { assert.equal(TI.validatePackageName("@scope/bar"), TI.PackageNameValidationResult.ScopedPackagesNotSupported); }); - it ("non URI safe characters are not supported", () => { + it("non URI safe characters are not supported", () => { assert.equal(TI.validatePackageName(" scope "), TI.PackageNameValidationResult.NameContainsNonURISafeCharacters); assert.equal(TI.validatePackageName("; say ‘Hello from TypeScript!’ #"), TI.PackageNameValidationResult.NameContainsNonURISafeCharacters); assert.equal(TI.validatePackageName("a/b/c"), TI.PackageNameValidationResult.NameContainsNonURISafeCharacters); @@ -747,7 +790,7 @@ namespace ts.projectSystem { }); describe("Invalid package names", () => { - it ("should not be installed", () => { + it("should not be installed", () => { const f1 = { path: "/a/b/app.js", content: "let x = 1" diff --git a/src/server/typingsInstaller/typingsInstaller.ts b/src/server/typingsInstaller/typingsInstaller.ts index 2851da3a64d..21f85c241a6 100644 --- a/src/server/typingsInstaller/typingsInstaller.ts +++ b/src/server/typingsInstaller/typingsInstaller.ts @@ -381,8 +381,10 @@ namespace ts.server.typingsInstaller { if (this.log.isEnabled()) { this.log.writeLine(`Got FS notification for ${f}, handler is already invoked '${isInvoked}'`); } - this.sendResponse({ projectName: projectName, kind: "invalidate" }); - isInvoked = true; + if (!isInvoked) { + this.sendResponse({ projectName: projectName, kind: "invalidate" }); + isInvoked = true; + } }); watchers.push(w); } diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index 96654ec6702..561e0110cda 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -135,10 +135,12 @@ namespace ts.JsTyping { * Get the typing info from common package manager json files like package.json or bower.json */ function getTypingNamesFromJson(jsonPath: string, filesToWatch: string[]) { + if (host.fileExists(jsonPath)) { + filesToWatch.push(jsonPath); + } const result = readConfigFile(jsonPath, (path: string) => host.readFile(path)); if (result.config) { const jsonConfig: PackageJson = result.config; - filesToWatch.push(jsonPath); if (jsonConfig.dependencies) { mergeTypings(getOwnKeys(jsonConfig.dependencies)); } From a645b6e4ddd12db0e2298a6b8c8c0d57000be45e Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Fri, 21 Oct 2016 14:37:23 -0700 Subject: [PATCH 25/49] Allow unused locals in for in or for of that start with _ Fixes #11734 --- src/compiler/checker.ts | 20 ++++++++++++-- ...sedLocalsStartingWithUnderscore.errors.txt | 18 +++++++++++++ .../unusedLocalsStartingWithUnderscore.js | 27 +++++++++++++++++++ .../unusedLocalsStartingWithUnderscore.ts | 13 +++++++++ 4 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/unusedLocalsStartingWithUnderscore.errors.txt create mode 100644 tests/baselines/reference/unusedLocalsStartingWithUnderscore.js create mode 100644 tests/cases/compiler/unusedLocalsStartingWithUnderscore.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 369d61ce484..4fde6a321a4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -15836,15 +15836,31 @@ namespace ts { } } else if (compilerOptions.noUnusedLocals) { - forEach(local.declarations, d => error(d.name || d, Diagnostics._0_is_declared_but_never_used, local.name)); + forEach(local.declarations, d => errorUnusedLocal(d.name || d, local.name)); } } } } } + function errorUnusedLocal(node: Node, name: string) { + if (isIdentifierThatStartsWithUnderScore(node)) { + const declaration = getRootDeclaration(node.parent); + if (declaration.kind === SyntaxKind.VariableDeclaration && + (declaration.parent.parent.kind === SyntaxKind.ForInStatement || + declaration.parent.parent.kind === SyntaxKind.ForOfStatement)) { + return; + } + } + error(node, Diagnostics._0_is_declared_but_never_used, name); + } + function parameterNameStartsWithUnderscore(parameter: ParameterDeclaration) { - return parameter.name && parameter.name.kind === SyntaxKind.Identifier && (parameter.name).text.charCodeAt(0) === CharacterCodes._; + return parameter.name && isIdentifierThatStartsWithUnderScore(parameter.name); + } + + function isIdentifierThatStartsWithUnderScore(node: Node) { + return node.kind === SyntaxKind.Identifier && (node).text.charCodeAt(0) === CharacterCodes._; } function checkUnusedClassMembers(node: ClassDeclaration | ClassExpression): void { diff --git a/tests/baselines/reference/unusedLocalsStartingWithUnderscore.errors.txt b/tests/baselines/reference/unusedLocalsStartingWithUnderscore.errors.txt new file mode 100644 index 00000000000..ae72b6fcdd3 --- /dev/null +++ b/tests/baselines/reference/unusedLocalsStartingWithUnderscore.errors.txt @@ -0,0 +1,18 @@ +tests/cases/compiler/unusedLocalsStartingWithUnderscore.ts(7,9): error TS6133: '_' is declared but never used. + + +==== tests/cases/compiler/unusedLocalsStartingWithUnderscore.ts (1 errors) ==== + + for (const _ of []) { } + + for (const _ in []) { } + + namespace M { + let _; + ~ +!!! error TS6133: '_' is declared but never used. + for (const _ of []) { } + + for (const _ in []) { } + } + \ No newline at end of file diff --git a/tests/baselines/reference/unusedLocalsStartingWithUnderscore.js b/tests/baselines/reference/unusedLocalsStartingWithUnderscore.js new file mode 100644 index 00000000000..cca16a97824 --- /dev/null +++ b/tests/baselines/reference/unusedLocalsStartingWithUnderscore.js @@ -0,0 +1,27 @@ +//// [unusedLocalsStartingWithUnderscore.ts] + +for (const _ of []) { } + +for (const _ in []) { } + +namespace M { + let _; + for (const _ of []) { } + + for (const _ in []) { } +} + + +//// [unusedLocalsStartingWithUnderscore.js] +for (var _i = 0, _a = []; _i < _a.length; _i++) { + var _ = _a[_i]; +} +for (var _ in []) { } +var M; +(function (M) { + var _; + for (var _i = 0, _a = []; _i < _a.length; _i++) { + var _1 = _a[_i]; + } + for (var _2 in []) { } +})(M || (M = {})); diff --git a/tests/cases/compiler/unusedLocalsStartingWithUnderscore.ts b/tests/cases/compiler/unusedLocalsStartingWithUnderscore.ts new file mode 100644 index 00000000000..4e6930a6282 --- /dev/null +++ b/tests/cases/compiler/unusedLocalsStartingWithUnderscore.ts @@ -0,0 +1,13 @@ +//@noUnusedLocals:true + +for (const _ of []) { } + +for (const _ in []) { } + +namespace M { + let _; + for (const _ of []) { } + + for (const _ in []) { } +} + \ No newline at end of file From acd574066bef47df1ad8f14d6e29d194b1041eb7 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Fri, 21 Oct 2016 16:15:19 -0700 Subject: [PATCH 26/49] Add test for #11166 --- .../nounusedTypeParameterConstraint.errors.txt | 12 ++++++++++++ .../reference/nounusedTypeParameterConstraint.js | 14 ++++++++++++++ .../compiler/nounusedTypeParameterConstraint.ts | 8 ++++++++ 3 files changed, 34 insertions(+) create mode 100644 tests/baselines/reference/nounusedTypeParameterConstraint.errors.txt create mode 100644 tests/baselines/reference/nounusedTypeParameterConstraint.js create mode 100644 tests/cases/compiler/nounusedTypeParameterConstraint.ts diff --git a/tests/baselines/reference/nounusedTypeParameterConstraint.errors.txt b/tests/baselines/reference/nounusedTypeParameterConstraint.errors.txt new file mode 100644 index 00000000000..a5797b44d92 --- /dev/null +++ b/tests/baselines/reference/nounusedTypeParameterConstraint.errors.txt @@ -0,0 +1,12 @@ +tests/cases/compiler/test.ts(1,10): error TS6133: 'IEventSourcedEntity' is declared but never used. + + +==== tests/cases/compiler/bar.ts (0 errors) ==== + + export interface IEventSourcedEntity { } + +==== tests/cases/compiler/test.ts (1 errors) ==== + import { IEventSourcedEntity } from "./bar"; + ~~~~~~~~~~~~~~~~~~~ +!!! error TS6133: 'IEventSourcedEntity' is declared but never used. + export type DomainEntityConstructor = { new(): TEntity; }; \ No newline at end of file diff --git a/tests/baselines/reference/nounusedTypeParameterConstraint.js b/tests/baselines/reference/nounusedTypeParameterConstraint.js new file mode 100644 index 00000000000..aa403187f7a --- /dev/null +++ b/tests/baselines/reference/nounusedTypeParameterConstraint.js @@ -0,0 +1,14 @@ +//// [tests/cases/compiler/nounusedTypeParameterConstraint.ts] //// + +//// [bar.ts] + +export interface IEventSourcedEntity { } + +//// [test.ts] +import { IEventSourcedEntity } from "./bar"; +export type DomainEntityConstructor = { new(): TEntity; }; + +//// [bar.js] +"use strict"; +//// [test.js] +"use strict"; diff --git a/tests/cases/compiler/nounusedTypeParameterConstraint.ts b/tests/cases/compiler/nounusedTypeParameterConstraint.ts new file mode 100644 index 00000000000..d2c3a1677ee --- /dev/null +++ b/tests/cases/compiler/nounusedTypeParameterConstraint.ts @@ -0,0 +1,8 @@ +//@noUnusedLocals:true + +//@filename: bar.ts +export interface IEventSourcedEntity { } + +//@filename: test.ts +import { IEventSourcedEntity } from "./bar"; +export type DomainEntityConstructor = { new(): TEntity; }; \ No newline at end of file From 45ba67d36f8ea2f59e746d60db1cd9c868eb9331 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Fri, 21 Oct 2016 16:15:19 -0700 Subject: [PATCH 27/49] Remove unused locals/parameters from webTestServer.ts --- tests/webTestServer.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/webTestServer.ts b/tests/webTestServer.ts index 97027dfdd49..3d23ef3e961 100644 --- a/tests/webTestServer.ts +++ b/tests/webTestServer.ts @@ -128,7 +128,7 @@ function dir(dirPath: string, spec?: string, options?: any) { // fs.rmdirSync won't delete directories with files in it function deleteFolderRecursive(dirPath: string) { if (fs.existsSync(dirPath)) { - fs.readdirSync(dirPath).forEach((file, index) => { + fs.readdirSync(dirPath).forEach((file) => { const curPath = path.join(path, file); if (fs.statSync(curPath).isDirectory()) { // recurse deleteFolderRecursive(curPath); @@ -141,7 +141,7 @@ function deleteFolderRecursive(dirPath: string) { } }; -function writeFile(path: string, data: any, opts: { recursive: boolean }) { +function writeFile(path: string, data: any) { ensureDirectoriesExist(getDirectoryPath(path)); fs.writeFileSync(path, data); } @@ -208,7 +208,7 @@ enum RequestType { Unknown } -function getRequestOperation(req: http.ServerRequest, filename: string) { +function getRequestOperation(req: http.ServerRequest) { if (req.method === "GET" && req.url.indexOf("?") === -1) { if (req.url.indexOf(".") !== -1) return RequestType.GetFile; else return RequestType.GetDir; @@ -258,7 +258,7 @@ function handleRequestOperation(req: http.ServerRequest, res: http.ServerRespons break; case RequestType.WriteFile: processPost(req, res, (data) => { - writeFile(reqPath, data, { recursive: true }); + writeFile(reqPath, data); }); send(ResponseCode.Success, res, undefined); break; @@ -306,7 +306,7 @@ http.createServer((req: http.ServerRequest, res: http.ServerResponse) => { log(`${req.method} ${req.url}`); const uri = url.parse(req.url).pathname; const reqPath = path.join(process.cwd(), uri); - const operation = getRequestOperation(req, reqPath); + const operation = getRequestOperation(req); handleRequestOperation(req, res, operation, reqPath); }).listen(port); From 7facab08cbd529605ccb406a1b062fd0aeaf7672 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 21 Oct 2016 16:32:28 -0700 Subject: [PATCH 28/49] Rename TypeFlags.ObjectType to TypeFlags.Object --- src/compiler/checker.ts | 56 +++++++++++++++++------------------ src/compiler/types.ts | 6 ++-- src/services/services.ts | 2 +- src/services/symbolDisplay.ts | 2 +- 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 49d453c7761..2f2bc4baef7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -539,7 +539,7 @@ namespace ts { } function getObjectFlags(type: Type): ObjectFlags { - return type.flags & TypeFlags.ObjectType ? (type).objectFlags : 0; + return type.flags & TypeFlags.Object ? (type).objectFlags : 0; } function isGlobalSourceFile(node: Node) { @@ -1594,7 +1594,7 @@ namespace ts { } function createObjectType(objectFlags: ObjectFlags, symbol?: Symbol): ObjectType { - const type = createType(TypeFlags.ObjectType); + const type = createType(TypeFlags.Object); type.objectFlags = objectFlags; type.symbol = symbol; return type; @@ -3600,7 +3600,7 @@ namespace ts { } function isConstructorType(type: Type): boolean { - return type.flags & TypeFlags.ObjectType && getSignaturesOfType(type, SignatureKind.Construct).length > 0; + return type.flags & TypeFlags.Object && getSignaturesOfType(type, SignatureKind.Construct).length > 0; } function getBaseTypeNodeOfClass(type: InterfaceType): ExpressionWithTypeArguments { @@ -3637,7 +3637,7 @@ namespace ts { return unknownType; } const baseConstructorType = checkExpression(baseTypeNode.expression); - if (baseConstructorType.flags & TypeFlags.ObjectType) { + if (baseConstructorType.flags & TypeFlags.Object) { // Resolving the members of a class requires us to resolve the base class of that class. // We force resolution here such that we catch circularities now. resolveStructuredTypeMembers(baseConstructorType); @@ -3678,7 +3678,7 @@ namespace ts { function resolveBaseTypesOfClass(type: InterfaceType): void { type.resolvedBaseTypes = type.resolvedBaseTypes || emptyArray; const baseConstructorType = getBaseConstructorTypeOfClass(type); - if (!(baseConstructorType.flags & TypeFlags.ObjectType)) { + if (!(baseConstructorType.flags & TypeFlags.Object)) { return; } const baseTypeNode = getBaseTypeNodeOfClass(type); @@ -4350,7 +4350,7 @@ namespace ts { constructSignatures = getDefaultConstructSignatures(classType); } const baseConstructorType = getBaseConstructorTypeOfClass(classType); - if (baseConstructorType.flags & TypeFlags.ObjectType) { + if (baseConstructorType.flags & TypeFlags.Object) { members = createSymbolTable(getNamedMembers(members)); addInheritedMembers(members, getPropertiesOfObjectType(baseConstructorType)); } @@ -4369,7 +4369,7 @@ namespace ts { function resolveStructuredTypeMembers(type: StructuredType): ResolvedType { if (!(type).members) { - if (type.flags & TypeFlags.ObjectType) { + if (type.flags & TypeFlags.Object) { if ((type).objectFlags & ObjectFlags.Reference) { resolveTypeReferenceMembers(type); } @@ -4392,7 +4392,7 @@ namespace ts { /** Return properties of an object type or an empty array for other types */ function getPropertiesOfObjectType(type: Type): Symbol[] { - if (type.flags & TypeFlags.ObjectType) { + if (type.flags & TypeFlags.Object) { return resolveStructuredTypeMembers(type).properties; } return emptyArray; @@ -4401,7 +4401,7 @@ namespace ts { /** If the given type is an object type and that type has a property by the given name, * return the symbol for that property. Otherwise return undefined. */ function getPropertyOfObjectType(type: Type, name: string): Symbol { - if (type.flags & TypeFlags.ObjectType) { + if (type.flags & TypeFlags.Object) { const resolved = resolveStructuredTypeMembers(type); const symbol = resolved.members[name]; if (symbol && symbolIsValue(symbol)) { @@ -4574,7 +4574,7 @@ namespace ts { */ function getPropertyOfType(type: Type, name: string): Symbol { type = getApparentType(type); - if (type.flags & TypeFlags.ObjectType) { + if (type.flags & TypeFlags.Object) { const resolved = resolveStructuredTypeMembers(type); const symbol = resolved.members[name]; if (symbol && symbolIsValue(symbol)) { @@ -5273,7 +5273,7 @@ namespace ts { return arity ? emptyGenericType : emptyObjectType; } const type = getDeclaredTypeOfSymbol(symbol); - if (!(type.flags & TypeFlags.ObjectType)) { + if (!(type.flags & TypeFlags.Object)) { error(getTypeDeclaration(symbol), Diagnostics.Global_type_0_must_be_a_class_or_interface_type, symbol.name); return arity ? emptyGenericType : emptyObjectType; } @@ -5453,7 +5453,7 @@ namespace ts { const len = typeSet.length; const index = len && type.id > typeSet[len - 1].id ? ~len : binarySearchTypes(typeSet, type); if (index < 0) { - if (!(flags & TypeFlags.ObjectType && (type).objectFlags & ObjectFlags.Anonymous && + if (!(flags & TypeFlags.Object && (type).objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && containsIdenticalType(typeSet, type))) { typeSet.splice(~index, 0, type); } @@ -6060,7 +6060,7 @@ namespace ts { if (type.flags & TypeFlags.TypeParameter) { return mapper(type); } - if (type.flags & TypeFlags.ObjectType) { + if (type.flags & TypeFlags.Object) { if ((type).objectFlags & ObjectFlags.Anonymous) { // If the anonymous type originates in a declaration of a function, method, class, or // interface, in an object type literal, or in an object literal expression, we may need @@ -6147,7 +6147,7 @@ namespace ts { } function getTypeWithoutSignatures(type: Type): Type { - if (type.flags & TypeFlags.ObjectType) { + if (type.flags & TypeFlags.Object) { const resolved = resolveStructuredTypeMembers(type); if (resolved.constructSignatures.length) { const result = createObjectType(ObjectFlags.Anonymous, type.symbol); @@ -6457,7 +6457,7 @@ namespace ts { if (source === target || relation !== identityRelation && isSimpleTypeRelatedTo(source, target, relation)) { return true; } - if (source.flags & TypeFlags.ObjectType && target.flags & TypeFlags.ObjectType) { + if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) { const id = relation !== identityRelation || source.id < target.id ? source.id + "," + target.id : target.id + "," + source.id; const related = relation[id]; if (related !== undefined) { @@ -6657,7 +6657,7 @@ namespace ts { // In a check of the form X = A & B, we will have previously checked if A relates to X or B relates // to X. Failing both of those we want to check if the aggregation of A and B's members structurally // relates to X. Thus, we include intersection types on the source side here. - if (apparentSource.flags & (TypeFlags.ObjectType | TypeFlags.Intersection) && target.flags & TypeFlags.ObjectType) { + if (apparentSource.flags & (TypeFlags.Object | TypeFlags.Intersection) && target.flags & TypeFlags.Object) { // Report structural errors only if we haven't reported any errors yet const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo && !(source.flags & TypeFlags.Primitive); if (result = objectTypeRelatedTo(apparentSource, source, target, reportStructuralErrors)) { @@ -6668,10 +6668,10 @@ namespace ts { } if (reportErrors) { - if (source.flags & TypeFlags.ObjectType && target.flags & TypeFlags.Primitive) { + if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Primitive) { tryElaborateErrorsForPrimitivesAndObjects(source, target); } - else if (source.symbol && source.flags & TypeFlags.ObjectType && globalObjectType === source) { + else if (source.symbol && source.flags & TypeFlags.Object && globalObjectType === source) { reportError(Diagnostics.The_Object_type_is_assignable_to_very_few_other_types_Did_you_mean_to_use_the_any_type_instead); } reportRelationError(headMessage, source, target); @@ -6681,7 +6681,7 @@ namespace ts { function isIdenticalTo(source: Type, target: Type): Ternary { let result: Ternary; - if (source.flags & TypeFlags.ObjectType && target.flags & TypeFlags.ObjectType) { + if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) { if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (source).target === (target).target) { // We have type references to same target type, see if all type arguments are identical if (result = typeArgumentsRelatedTo(source, target, /*reportErrors*/ false)) { @@ -6706,7 +6706,7 @@ namespace ts { // index signatures, or if the property is actually declared in the object type. In a union or intersection // type, a property is considered known if it is known in any constituent type. function isKnownProperty(type: Type, name: string): boolean { - if (type.flags & TypeFlags.ObjectType) { + if (type.flags & TypeFlags.Object) { const resolved = resolveStructuredTypeMembers(type); if ((relation === assignableRelation || relation === comparableRelation) && (type === globalObjectType || isEmptyObjectType(resolved)) || resolved.stringIndexInfo || @@ -6734,7 +6734,7 @@ namespace ts { } function hasExcessProperties(source: FreshObjectLiteralType, target: Type, reportErrors: boolean): boolean { - if (maybeTypeOfKind(target, TypeFlags.ObjectType) && !(getObjectFlags(target) & ObjectFlags.ObjectLiteralPatternWithComputedProperties)) { + if (maybeTypeOfKind(target, TypeFlags.Object) && !(getObjectFlags(target) & ObjectFlags.ObjectLiteralPatternWithComputedProperties)) { for (const prop of getPropertiesOfObjectType(source)) { if (!isKnownProperty(target, prop.name)) { if (reportErrors) { @@ -7007,7 +7007,7 @@ namespace ts { } function propertiesIdenticalTo(source: Type, target: Type): Ternary { - if (!(source.flags & TypeFlags.ObjectType && target.flags & TypeFlags.ObjectType)) { + if (!(source.flags & TypeFlags.Object && target.flags & TypeFlags.Object)) { return Ternary.False; } const sourceProperties = getPropertiesOfObjectType(source); @@ -7903,7 +7903,7 @@ namespace ts { } else { source = getApparentType(source); - if (source.flags & TypeFlags.ObjectType) { + if (source.flags & TypeFlags.Object) { if (isInProcess(source, target)) { return; } @@ -8295,7 +8295,7 @@ namespace ts { type === falseType ? TypeFacts.FalseStrictFacts : TypeFacts.TrueStrictFacts : type === falseType ? TypeFacts.FalseFacts : TypeFacts.TrueFacts; } - if (flags & TypeFlags.ObjectType) { + if (flags & TypeFlags.Object) { return isFunctionObjectType(type) ? strictNullChecks ? TypeFacts.FunctionStrictFacts : TypeFacts.FunctionFacts : strictNullChecks ? TypeFacts.ObjectStrictFacts : TypeFacts.ObjectFacts; @@ -11732,7 +11732,7 @@ namespace ts { // If type has a single call signature and no other members, return that signature. Otherwise, return undefined. function getSingleCallSignature(type: Type): Signature { - if (type.flags & TypeFlags.ObjectType) { + if (type.flags & TypeFlags.Object) { const resolved = resolveStructuredTypeMembers(type); if (resolved.callSignatures.length === 1 && resolved.constructSignatures.length === 0 && resolved.properties.length === 0 && !resolved.stringIndexInfo && !resolved.numberIndexInfo) { @@ -13571,7 +13571,7 @@ namespace ts { if (!isTypeAnyOrAllConstituentTypesHaveKind(leftType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol)) { error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol); } - if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.ObjectType | TypeFlags.TypeParameter)) { + if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.Object | TypeFlags.TypeParameter)) { error(right, Diagnostics.The_right_hand_side_of_an_in_expression_must_be_of_type_any_an_object_type_or_a_type_parameter); } return booleanType; @@ -16478,7 +16478,7 @@ namespace ts { const rightType = checkNonNullExpression(node.expression); // unknownType is returned i.e. if node.expression is identifier whose name cannot be resolved // in this case error about missing name is already reported - do not report extra one - if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.ObjectType | TypeFlags.TypeParameter)) { + if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.Object | TypeFlags.TypeParameter)) { error(node.expression, Diagnostics.The_right_hand_side_of_a_for_in_statement_must_be_of_type_any_an_object_type_or_a_type_parameter); } @@ -19245,7 +19245,7 @@ namespace ts { } function isFunctionType(type: Type): boolean { - return type.flags & TypeFlags.ObjectType && getSignaturesOfType(type, SignatureKind.Call).length > 0; + return type.flags & TypeFlags.Object && getSignaturesOfType(type, SignatureKind.Call).length > 0; } function getTypeReferenceSerializationKind(typeName: EntityName, location?: Node): TypeReferenceSerializationKind { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index ed4d55c584b..f9ec3e9f268 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2612,7 +2612,7 @@ namespace ts { Null = 1 << 12, Never = 1 << 13, // Never type TypeParameter = 1 << 14, // Type parameter - ObjectType = 1 << 15, // Object type + Object = 1 << 15, // Object type Union = 1 << 16, // Union (T | U) Intersection = 1 << 17, // Intersection (T & U) /* @internal */ @@ -2640,13 +2640,13 @@ namespace ts { BooleanLike = Boolean | BooleanLiteral, EnumLike = Enum | EnumLiteral, UnionOrIntersection = Union | Intersection, - StructuredType = ObjectType | Union | Intersection, + StructuredType = Object | Union | Intersection, StructuredOrTypeParameter = StructuredType | TypeParameter, // 'Narrowable' types are types where narrowing actually narrows. // This *should* be every type other than null, undefined, void, and never Narrowable = Any | StructuredType | TypeParameter | StringLike | NumberLike | BooleanLike | ESSymbol, - NotUnionOrUnit = Any | ESSymbol | ObjectType, + NotUnionOrUnit = Any | ESSymbol | Object, /* @internal */ RequiresWidening = ContainsWideningType | ContainsObjectLiteral, /* @internal */ diff --git a/src/services/services.ts b/src/services/services.ts index a562aa44bfd..64c9f04370f 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -382,7 +382,7 @@ namespace ts { return this.checker.getIndexTypeOfType(this, IndexKind.Number); } getBaseTypes(): ObjectType[] { - return this.flags & TypeFlags.ObjectType && this.objectFlags & (ObjectFlags.Class | ObjectFlags.Interface) + return this.flags & TypeFlags.Object && this.objectFlags & (ObjectFlags.Class | ObjectFlags.Interface) ? this.checker.getBaseTypes(this) : undefined; } diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts index 37753547768..516b5d7fbc5 100644 --- a/src/services/symbolDisplay.ts +++ b/src/services/symbolDisplay.ts @@ -173,7 +173,7 @@ namespace ts.SymbolDisplay { displayParts.push(keywordPart(SyntaxKind.NewKeyword)); displayParts.push(spacePart()); } - if (!(type.flags & TypeFlags.ObjectType && (type).objectFlags & ObjectFlags.Anonymous) && type.symbol) { + if (!(type.flags & TypeFlags.Object && (type).objectFlags & ObjectFlags.Anonymous) && type.symbol) { addRange(displayParts, symbolToDisplayParts(typeChecker, type.symbol, enclosingDeclaration, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOrArguments)); } addSignatureDisplayParts(signature, allSignatures, TypeFormatFlags.WriteArrowStyleSignature); From f8c3a550caaba17f40a80bba7809cefbfe883f5f Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Fri, 21 Oct 2016 16:40:11 -0700 Subject: [PATCH 29/49] Check type parameters of the type alias declaration Fixes #11166 --- src/compiler/checker.ts | 1 + .../nounusedTypeParameterConstraint.errors.txt | 12 ------------ .../nounusedTypeParameterConstraint.symbols | 15 +++++++++++++++ .../nounusedTypeParameterConstraint.types | 15 +++++++++++++++ .../reference/typeAliasDeclarationEmit.errors.txt | 10 ++++++++++ 5 files changed, 41 insertions(+), 12 deletions(-) delete mode 100644 tests/baselines/reference/nounusedTypeParameterConstraint.errors.txt create mode 100644 tests/baselines/reference/nounusedTypeParameterConstraint.symbols create mode 100644 tests/baselines/reference/nounusedTypeParameterConstraint.types create mode 100644 tests/baselines/reference/typeAliasDeclarationEmit.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 369d61ce484..5d66221df2b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17418,6 +17418,7 @@ namespace ts { checkGrammarDecorators(node) || checkGrammarModifiers(node); checkTypeNameIsReserved(node.name, Diagnostics.Type_alias_name_cannot_be_0); + checkTypeParameters(node.typeParameters); checkSourceElement(node.type); } diff --git a/tests/baselines/reference/nounusedTypeParameterConstraint.errors.txt b/tests/baselines/reference/nounusedTypeParameterConstraint.errors.txt deleted file mode 100644 index a5797b44d92..00000000000 --- a/tests/baselines/reference/nounusedTypeParameterConstraint.errors.txt +++ /dev/null @@ -1,12 +0,0 @@ -tests/cases/compiler/test.ts(1,10): error TS6133: 'IEventSourcedEntity' is declared but never used. - - -==== tests/cases/compiler/bar.ts (0 errors) ==== - - export interface IEventSourcedEntity { } - -==== tests/cases/compiler/test.ts (1 errors) ==== - import { IEventSourcedEntity } from "./bar"; - ~~~~~~~~~~~~~~~~~~~ -!!! error TS6133: 'IEventSourcedEntity' is declared but never used. - export type DomainEntityConstructor = { new(): TEntity; }; \ No newline at end of file diff --git a/tests/baselines/reference/nounusedTypeParameterConstraint.symbols b/tests/baselines/reference/nounusedTypeParameterConstraint.symbols new file mode 100644 index 00000000000..40364460f50 --- /dev/null +++ b/tests/baselines/reference/nounusedTypeParameterConstraint.symbols @@ -0,0 +1,15 @@ +=== tests/cases/compiler/bar.ts === + +export interface IEventSourcedEntity { } +>IEventSourcedEntity : Symbol(IEventSourcedEntity, Decl(bar.ts, 0, 0)) + +=== tests/cases/compiler/test.ts === +import { IEventSourcedEntity } from "./bar"; +>IEventSourcedEntity : Symbol(IEventSourcedEntity, Decl(test.ts, 0, 8)) + +export type DomainEntityConstructor = { new(): TEntity; }; +>DomainEntityConstructor : Symbol(DomainEntityConstructor, Decl(test.ts, 0, 44)) +>TEntity : Symbol(TEntity, Decl(test.ts, 1, 36)) +>IEventSourcedEntity : Symbol(IEventSourcedEntity, Decl(test.ts, 0, 8)) +>TEntity : Symbol(TEntity, Decl(test.ts, 1, 36)) + diff --git a/tests/baselines/reference/nounusedTypeParameterConstraint.types b/tests/baselines/reference/nounusedTypeParameterConstraint.types new file mode 100644 index 00000000000..8f2de483d26 --- /dev/null +++ b/tests/baselines/reference/nounusedTypeParameterConstraint.types @@ -0,0 +1,15 @@ +=== tests/cases/compiler/bar.ts === + +export interface IEventSourcedEntity { } +>IEventSourcedEntity : IEventSourcedEntity + +=== tests/cases/compiler/test.ts === +import { IEventSourcedEntity } from "./bar"; +>IEventSourcedEntity : any + +export type DomainEntityConstructor = { new(): TEntity; }; +>DomainEntityConstructor : new () => TEntity +>TEntity : TEntity +>IEventSourcedEntity : IEventSourcedEntity +>TEntity : TEntity + diff --git a/tests/baselines/reference/typeAliasDeclarationEmit.errors.txt b/tests/baselines/reference/typeAliasDeclarationEmit.errors.txt new file mode 100644 index 00000000000..2fd71844dc4 --- /dev/null +++ b/tests/baselines/reference/typeAliasDeclarationEmit.errors.txt @@ -0,0 +1,10 @@ +tests/cases/compiler/typeAliasDeclarationEmit.ts(4,37): error TS2314: Generic type 'callback' requires 1 type argument(s). + + +==== tests/cases/compiler/typeAliasDeclarationEmit.ts (1 errors) ==== + + export type callback = () => T; + + export type CallbackArray = () => T; + ~~~~~~~~ +!!! error TS2314: Generic type 'callback' requires 1 type argument(s). \ No newline at end of file From 95670c62519d1b0e105cbcb1e93924aa696386fb Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Mon, 24 Oct 2016 10:14:41 -0700 Subject: [PATCH 30/49] Perform a useful comparison. Fixes #11805 --- src/compiler/program.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index b619f005ded..a9c64f3ecbe 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -474,7 +474,7 @@ namespace ts { (oldOptions.baseUrl !== options.baseUrl) || (oldOptions.maxNodeModuleJsDepth !== options.maxNodeModuleJsDepth) || !arrayIsEqualTo(oldOptions.lib, options.lib) || - !arrayIsEqualTo(oldOptions.typeRoots, oldOptions.typeRoots) || + !arrayIsEqualTo(oldOptions.typeRoots, options.typeRoots) || !arrayIsEqualTo(oldOptions.rootDirs, options.rootDirs) || !equalOwnProperties(oldOptions.paths, options.paths)) { return false; From 287b54518d62c25dc12c4cdc17a599d1c7c4772b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=28=C2=B4=E3=83=BB=CF=89=E3=83=BB=EF=BD=80=29?= Date: Tue, 25 Oct 2016 01:45:07 +0800 Subject: [PATCH 31/49] Fix #10967, allow boolean flags to have explicit value (#11798) * Fix #10967, allow boolean flag to have explicit value * add commandLineParsing test for boolean flags --- src/compiler/commandLineParser.ts | 8 ++++- src/harness/unittests/commandLineParsing.ts | 35 ++++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index d6c7dad0e8e..c4e079c6a13 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -599,7 +599,13 @@ namespace ts { i++; break; case "boolean": - options[opt.name] = true; + // boolean flag has optional value true, false, others + let optValue = args[i]; + options[opt.name] = optValue !== "false"; + // consume next argument as boolean flag value + if (optValue === "false" || optValue === "true") { + i++; + } break; case "string": options[opt.name] = args[i] || ""; diff --git a/src/harness/unittests/commandLineParsing.ts b/src/harness/unittests/commandLineParsing.ts index 67bc1e9b795..c15490738fa 100644 --- a/src/harness/unittests/commandLineParsing.ts +++ b/src/harness/unittests/commandLineParsing.ts @@ -1,4 +1,4 @@ -/// +/// /// namespace ts { @@ -338,5 +338,38 @@ namespace ts { } }); }); + + it("Parse explicit boolean flag value", () => { + assertParseResult(["--strictNullChecks", "false", "0.ts"], + { + errors: [], + fileNames: ["0.ts"], + options: { + strictNullChecks: false, + } + }); + }); + + it("Parse non boolean argument after boolean flag", () => { + assertParseResult(["--noImplicitAny", "t", "0.ts"], + { + errors: [], + fileNames: ["t", "0.ts"], + options: { + noImplicitAny: true, + } + }); + }); + + it("Parse implicit boolean flag value", () => { + assertParseResult(["--strictNullChecks"], + { + errors: [], + fileNames: [], + options: { + strictNullChecks: true, + } + }); + }); }); } From 1e32b6742ecb2dd172de6fc0950991a05649509d Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Mon, 24 Oct 2016 10:58:51 -0700 Subject: [PATCH 32/49] Add test case for destructured unused parameter --- .../unusedDestructuringParameters.js | 15 ++++++++++++ .../unusedDestructuringParameters.symbols | 16 +++++++++++++ .../unusedDestructuringParameters.types | 24 +++++++++++++++++++ .../compiler/unusedDestructuringParameters.ts | 5 ++++ 4 files changed, 60 insertions(+) create mode 100644 tests/baselines/reference/unusedDestructuringParameters.js create mode 100644 tests/baselines/reference/unusedDestructuringParameters.symbols create mode 100644 tests/baselines/reference/unusedDestructuringParameters.types create mode 100644 tests/cases/compiler/unusedDestructuringParameters.ts diff --git a/tests/baselines/reference/unusedDestructuringParameters.js b/tests/baselines/reference/unusedDestructuringParameters.js new file mode 100644 index 00000000000..9d47f6f9f1b --- /dev/null +++ b/tests/baselines/reference/unusedDestructuringParameters.js @@ -0,0 +1,15 @@ +//// [unusedDestructuringParameters.ts] +const f = ([a]) => { }; +f([1]); +const f2 = ({a}) => { }; +f2({ a: 10 }); + +//// [unusedDestructuringParameters.js] +var f = function (_a) { + var a = _a[0]; +}; +f([1]); +var f2 = function (_a) { + var a = _a.a; +}; +f2({ a: 10 }); diff --git a/tests/baselines/reference/unusedDestructuringParameters.symbols b/tests/baselines/reference/unusedDestructuringParameters.symbols new file mode 100644 index 00000000000..d40d54f3fce --- /dev/null +++ b/tests/baselines/reference/unusedDestructuringParameters.symbols @@ -0,0 +1,16 @@ +=== tests/cases/compiler/unusedDestructuringParameters.ts === +const f = ([a]) => { }; +>f : Symbol(f, Decl(unusedDestructuringParameters.ts, 0, 5)) +>a : Symbol(a, Decl(unusedDestructuringParameters.ts, 0, 12)) + +f([1]); +>f : Symbol(f, Decl(unusedDestructuringParameters.ts, 0, 5)) + +const f2 = ({a}) => { }; +>f2 : Symbol(f2, Decl(unusedDestructuringParameters.ts, 2, 5)) +>a : Symbol(a, Decl(unusedDestructuringParameters.ts, 2, 13)) + +f2({ a: 10 }); +>f2 : Symbol(f2, Decl(unusedDestructuringParameters.ts, 2, 5)) +>a : Symbol(a, Decl(unusedDestructuringParameters.ts, 3, 4)) + diff --git a/tests/baselines/reference/unusedDestructuringParameters.types b/tests/baselines/reference/unusedDestructuringParameters.types new file mode 100644 index 00000000000..ebbe1efbc30 --- /dev/null +++ b/tests/baselines/reference/unusedDestructuringParameters.types @@ -0,0 +1,24 @@ +=== tests/cases/compiler/unusedDestructuringParameters.ts === +const f = ([a]) => { }; +>f : ([a]: [any]) => void +>([a]) => { } : ([a]: [any]) => void +>a : any + +f([1]); +>f([1]) : void +>f : ([a]: [any]) => void +>[1] : [number] +>1 : 1 + +const f2 = ({a}) => { }; +>f2 : ({a}: { a: any; }) => void +>({a}) => { } : ({a}: { a: any; }) => void +>a : any + +f2({ a: 10 }); +>f2({ a: 10 }) : void +>f2 : ({a}: { a: any; }) => void +>{ a: 10 } : { a: number; } +>a : number +>10 : 10 + diff --git a/tests/cases/compiler/unusedDestructuringParameters.ts b/tests/cases/compiler/unusedDestructuringParameters.ts new file mode 100644 index 00000000000..748c9ea83fe --- /dev/null +++ b/tests/cases/compiler/unusedDestructuringParameters.ts @@ -0,0 +1,5 @@ +//@noUnusedParameters: true +const f = ([a]) => { }; +f([1]); +const f2 = ({a}) => { }; +f2({ a: 10 }); \ No newline at end of file From 4dc60282639aa22b87f6446b625fe360e525c3ed Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Mon, 24 Oct 2016 11:00:32 -0700 Subject: [PATCH 33/49] Fix #11650 add an error message for no source files parsing a tsconfig.json (#11743) * Fix #11650 add an error message for no source files parsing a tsconfig.json * Use the file name in error message * Use constants * Review comments: change message text --- src/compiler/commandLineParser.ts | 16 ++++- src/compiler/diagnosticMessages.json | 10 +++ src/harness/unittests/matchFiles.ts | 80 +++++++++++++++++------- src/harness/unittests/tsconfigParsing.ts | 67 ++++++++++++++++++++ 4 files changed, 148 insertions(+), 25 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index c4e079c6a13..686af79a6cc 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -912,6 +912,9 @@ namespace ts { if (hasProperty(json, "files")) { if (isArray(json["files"])) { fileNames = json["files"]; + if (fileNames.length === 0) { + errors.push(createCompilerDiagnostic(Diagnostics.The_files_list_in_config_file_0_is_empty, configFileName || "tsconfig.json")); + } } else { errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "files", "Array")); @@ -954,7 +957,18 @@ namespace ts { includeSpecs = ["**/*"]; } - return matchFileNames(fileNames, includeSpecs, excludeSpecs, basePath, options, host, errors); + const result = matchFileNames(fileNames, includeSpecs, excludeSpecs, basePath, options, host, errors); + + if (result.fileNames.length === 0 && !hasProperty(json, "files") && resolutionStack.length === 0) { + errors.push( + createCompilerDiagnostic( + Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, + configFileName || "tsconfig.json", + JSON.stringify(includeSpecs || []), + JSON.stringify(excludeSpecs || []))); + } + + return result; } } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 21f04da488d..0c92f0acd7f 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3077,6 +3077,7 @@ "category": "Error", "code": 17010 }, + "Circularity detected while resolving configuration: {0}": { "category": "Error", "code": 18000 @@ -3085,6 +3086,15 @@ "category": "Error", "code": 18001 }, + "The 'files' list in config file '{0}' is empty.": { + "category": "Error", + "code": 18002 + }, + "No inputs were found in config file '{0}'. Specified 'include' paths were '{1}' and 'exclude' paths were '{2}'.": { + "category": "Error", + "code": 18003 + }, + "Add missing 'super()' call.": { "category": "Message", "code": 90001 diff --git a/src/harness/unittests/matchFiles.ts b/src/harness/unittests/matchFiles.ts index 6b499e56989..79e9668d073 100644 --- a/src/harness/unittests/matchFiles.ts +++ b/src/harness/unittests/matchFiles.ts @@ -3,6 +3,7 @@ namespace ts { const caseInsensitiveBasePath = "c:/dev/"; + const caseInsensitiveTsconfigPath = "c:/dev/tsconfig.json"; const caseInsensitiveHost = new Utils.MockParseConfigHost(caseInsensitiveBasePath, /*useCaseSensitiveFileNames*/ false, [ "c:/dev/a.ts", "c:/dev/a.d.ts", @@ -88,6 +89,8 @@ namespace ts { "c:/dev/g.min.js/.g/g.ts" ]); + const defaultExcludes = ["node_modules", "bower_components", "jspm_packages"]; + describe("matchFiles", () => { describe("with literal file list", () => { it("without exclusions", () => { @@ -189,11 +192,14 @@ namespace ts { }; const expected: ts.ParsedCommandLine = { options: {}, - errors: [], + errors: [ + ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, + caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(defaultExcludes)) + ], fileNames: [], wildcardDirectories: {}, }; - const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath); + const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath); assert.deepEqual(actual.fileNames, expected.fileNames); assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories); assert.deepEqual(actual.errors, expected.errors); @@ -207,11 +213,14 @@ namespace ts { }; const expected: ts.ParsedCommandLine = { options: {}, - errors: [], + errors: [ + ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, + caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(defaultExcludes)) + ], fileNames: [], wildcardDirectories: {}, }; - const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath); + const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath); assert.deepEqual(actual.fileNames, expected.fileNames); assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories); assert.deepEqual(actual.errors, expected.errors); @@ -551,13 +560,16 @@ namespace ts { }; const expected: ts.ParsedCommandLine = { options: {}, - errors: [], + errors: [ + ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, + caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(defaultExcludes)) + ], fileNames: [], wildcardDirectories: { "c:/dev": ts.WatchDirectoryFlags.Recursive }, }; - const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath); + const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath); assert.deepEqual(actual.fileNames, expected.fileNames); assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories); assert.deepEqual(actual.errors, expected.errors); @@ -619,7 +631,7 @@ namespace ts { it("with common package folders and no exclusions", () => { const json = { include: [ - "**/a.ts" + "**/a.ts" ] }; const expected: ts.ParsedCommandLine = { @@ -701,13 +713,16 @@ namespace ts { options: { allowJs: false }, - errors: [], + errors: [ + ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, + caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(defaultExcludes)) + ], fileNames: [], wildcardDirectories: { "c:/dev/js": ts.WatchDirectoryFlags.None } }; - const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath); + const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath); assert.deepEqual(actual.fileNames, expected.fileNames); assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories); assert.deepEqual(actual.errors, expected.errors); @@ -828,11 +843,14 @@ namespace ts { }; const expected: ts.ParsedCommandLine = { options: {}, - errors: [], + errors: [ + ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, + caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(json.exclude))] + , fileNames: [], wildcardDirectories: {} }; - const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath); + const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath); assert.deepEqual(actual.fileNames, expected.fileNames); assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories); assert.deepEqual(actual.errors, expected.errors); @@ -1030,12 +1048,14 @@ namespace ts { const expected: ts.ParsedCommandLine = { options: {}, errors: [ - ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**") + ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**"), + ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, + caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(defaultExcludes)) ], fileNames: [], wildcardDirectories: {} }; - const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath); + const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath); assert.deepEqual(actual.fileNames, expected.fileNames); assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories); assert.deepEqual(actual.errors, expected.errors); @@ -1051,11 +1071,14 @@ namespace ts { }; const expected: ts.ParsedCommandLine = { options: {}, - errors: [], + errors: [ + ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, + caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(json.exclude)) + ], fileNames: [], wildcardDirectories: {} }; - const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath); + const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath); assert.deepEqual(actual.fileNames, expected.fileNames); assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories); assert.deepEqual(actual.errors, expected.errors); @@ -1071,12 +1094,14 @@ namespace ts { const expected: ts.ParsedCommandLine = { options: {}, errors: [ - ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_multiple_recursive_directory_wildcards_Asterisk_Asterisk_Colon_0, "**/x/**/*") + ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_multiple_recursive_directory_wildcards_Asterisk_Asterisk_Colon_0, "**/x/**/*"), + ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, + caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(defaultExcludes)) ], fileNames: [], wildcardDirectories: {} }; - const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath); + const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath); assert.deepEqual(actual.fileNames, expected.fileNames); assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories); assert.deepEqual(actual.errors, expected.errors); @@ -1122,12 +1147,14 @@ namespace ts { const expected: ts.ParsedCommandLine = { options: {}, errors: [ - ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/../*") + ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/../*"), + ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, + caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(defaultExcludes)) ], fileNames: [], wildcardDirectories: {} }; - const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath); + const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath); assert.deepEqual(actual.fileNames, expected.fileNames); assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories); assert.deepEqual(actual.errors, expected.errors); @@ -1142,12 +1169,14 @@ namespace ts { const expected: ts.ParsedCommandLine = { options: {}, errors: [ - ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/y/../*") + ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/y/../*"), + ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, + caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(defaultExcludes)) ], fileNames: [], wildcardDirectories: {} }; - const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath); + const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath); assert.deepEqual(actual.fileNames, expected.fileNames); assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories); assert.deepEqual(actual.errors, expected.errors); @@ -1195,7 +1224,7 @@ namespace ts { const expected: ts.ParsedCommandLine = { options: {}, errors: [ - ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/y/..") + ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/y/..") ], fileNames: [ "c:/dev/a.ts", @@ -1320,11 +1349,14 @@ namespace ts { }; const expected: ts.ParsedCommandLine = { options: {}, - errors: [], + errors: [ + ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, + caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(json.exclude)) + ], fileNames: [], wildcardDirectories: {} }; - const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveDottedFoldersHost, caseInsensitiveBasePath); + const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveDottedFoldersHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath); assert.deepEqual(actual.fileNames, expected.fileNames); assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories); assert.deepEqual(actual.errors, expected.errors); diff --git a/src/harness/unittests/tsconfigParsing.ts b/src/harness/unittests/tsconfigParsing.ts index f38a05b9cfa..7b980cdd2d8 100644 --- a/src/harness/unittests/tsconfigParsing.ts +++ b/src/harness/unittests/tsconfigParsing.ts @@ -28,6 +28,14 @@ namespace ts { assert.isTrue(arrayIsEqualTo(parsed.fileNames.sort(), expectedFileList.sort())); } + function assertParseFileDiagnostics(jsonText: string, configFileName: string, basePath: string, allFileList: string[], expectedDiagnosticCode: number) { + const json = JSON.parse(jsonText); + const host: ParseConfigHost = new Utils.MockParseConfigHost(basePath, true, allFileList); + const parsed = ts.parseJsonConfigFileContent(json, host, basePath, /*existingOptions*/ undefined, configFileName); + assert.isTrue(parsed.errors.length >= 0); + assert.isTrue(parsed.errors.filter(e => e.code === expectedDiagnosticCode).length > 0, `Expected error code ${expectedDiagnosticCode} to be in ${JSON.stringify(parsed.errors)}`); + } + it("returns empty config for file with only whitespaces", () => { assertParseResult("", { config : {} }); assertParseResult(" ", { config : {} }); @@ -202,5 +210,64 @@ namespace ts { assert.isTrue(diagnostics.length === 2); assert.equal(JSON.stringify(configJsonObject), JSON.stringify(expectedResult)); }); + + it("generates errors for empty files list", () => { + const content = `{ + "files": [] + }`; + assertParseFileDiagnostics(content, + "/apath/tsconfig.json", + "tests/cases/unittests", + ["/apath/a.ts"], + Diagnostics.The_files_list_in_config_file_0_is_empty.code); + }); + + it("generates errors for directory with no .ts files", () => { + const content = `{ + }`; + assertParseFileDiagnostics(content, + "/apath/tsconfig.json", + "tests/cases/unittests", + ["/apath/a.js"], + Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code); + }); + + it("generates errors for empty directory", () => { + const content = `{ + "compilerOptions": { + "allowJs": true + } + }`; + assertParseFileDiagnostics(content, + "/apath/tsconfig.json", + "tests/cases/unittests", + [], + Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code); + }); + + it("generates errors for empty include", () => { + const content = `{ + "include": [] + }`; + assertParseFileDiagnostics(content, + "/apath/tsconfig.json", + "tests/cases/unittests", + ["/apath/a.ts"], + Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code); + }); + + it("generates errors for includes with outDir", () => { + const content = `{ + "compilerOptions": { + "outDir": "./" + }, + "include": ["**/*"] + }`; + assertParseFileDiagnostics(content, + "/apath/tsconfig.json", + "tests/cases/unittests", + ["/apath/a.ts"], + Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code); + }); }); } From c1c670f8f6d78f5ed6b02efeeef07e27ba619c5d Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Mon, 24 Oct 2016 11:12:15 -0700 Subject: [PATCH 34/49] Report error on unused destructured parameters Fixes #11795 --- src/compiler/checker.ts | 10 ++++---- .../unusedDestructuringParameters.errors.txt | 15 ++++++++++++ .../unusedDestructuringParameters.js | 8 ++++++- .../unusedDestructuringParameters.symbols | 16 ------------- .../unusedDestructuringParameters.types | 24 ------------------- .../unusedParametersWithUnderscore.errors.txt | 14 +---------- .../compiler/unusedDestructuringParameters.ts | 4 +++- 7 files changed, 31 insertions(+), 60 deletions(-) create mode 100644 tests/baselines/reference/unusedDestructuringParameters.errors.txt delete mode 100644 tests/baselines/reference/unusedDestructuringParameters.symbols delete mode 100644 tests/baselines/reference/unusedDestructuringParameters.types diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5b85a932c05..de807600066 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -15832,12 +15832,12 @@ namespace ts { for (const key in node.locals) { const local = node.locals[key]; if (!local.isReferenced) { - if (local.valueDeclaration && local.valueDeclaration.kind === SyntaxKind.Parameter) { - const parameter = local.valueDeclaration; + if (local.valueDeclaration && getRootDeclaration(local.valueDeclaration).kind === SyntaxKind.Parameter) { + const parameter = getRootDeclaration(local.valueDeclaration); if (compilerOptions.noUnusedParameters && !isParameterPropertyDeclaration(parameter) && !parameterIsThisKeyword(parameter) && - !parameterNameStartsWithUnderscore(parameter)) { + !parameterNameStartsWithUnderscore(local.valueDeclaration.name)) { error(local.valueDeclaration.name, Diagnostics._0_is_declared_but_never_used, local.name); } } @@ -15861,8 +15861,8 @@ namespace ts { error(node, Diagnostics._0_is_declared_but_never_used, name); } - function parameterNameStartsWithUnderscore(parameter: ParameterDeclaration) { - return parameter.name && isIdentifierThatStartsWithUnderScore(parameter.name); + function parameterNameStartsWithUnderscore(parameterName: DeclarationName) { + return parameterName && isIdentifierThatStartsWithUnderScore(parameterName); } function isIdentifierThatStartsWithUnderScore(node: Node) { diff --git a/tests/baselines/reference/unusedDestructuringParameters.errors.txt b/tests/baselines/reference/unusedDestructuringParameters.errors.txt new file mode 100644 index 00000000000..b6814df6b7a --- /dev/null +++ b/tests/baselines/reference/unusedDestructuringParameters.errors.txt @@ -0,0 +1,15 @@ +tests/cases/compiler/unusedDestructuringParameters.ts(1,13): error TS6133: 'a' is declared but never used. +tests/cases/compiler/unusedDestructuringParameters.ts(3,14): error TS6133: 'a' is declared but never used. + + +==== tests/cases/compiler/unusedDestructuringParameters.ts (2 errors) ==== + const f = ([a]) => { }; + ~ +!!! error TS6133: 'a' is declared but never used. + f([1]); + const f2 = ({a}) => { }; + ~ +!!! error TS6133: 'a' is declared but never used. + f2({ a: 10 }); + const f3 = ([_]) => { }; + f3([10]); \ No newline at end of file diff --git a/tests/baselines/reference/unusedDestructuringParameters.js b/tests/baselines/reference/unusedDestructuringParameters.js index 9d47f6f9f1b..ca0ab80b86e 100644 --- a/tests/baselines/reference/unusedDestructuringParameters.js +++ b/tests/baselines/reference/unusedDestructuringParameters.js @@ -2,7 +2,9 @@ const f = ([a]) => { }; f([1]); const f2 = ({a}) => { }; -f2({ a: 10 }); +f2({ a: 10 }); +const f3 = ([_]) => { }; +f3([10]); //// [unusedDestructuringParameters.js] var f = function (_a) { @@ -13,3 +15,7 @@ var f2 = function (_a) { var a = _a.a; }; f2({ a: 10 }); +var f3 = function (_a) { + var _ = _a[0]; +}; +f3([10]); diff --git a/tests/baselines/reference/unusedDestructuringParameters.symbols b/tests/baselines/reference/unusedDestructuringParameters.symbols deleted file mode 100644 index d40d54f3fce..00000000000 --- a/tests/baselines/reference/unusedDestructuringParameters.symbols +++ /dev/null @@ -1,16 +0,0 @@ -=== tests/cases/compiler/unusedDestructuringParameters.ts === -const f = ([a]) => { }; ->f : Symbol(f, Decl(unusedDestructuringParameters.ts, 0, 5)) ->a : Symbol(a, Decl(unusedDestructuringParameters.ts, 0, 12)) - -f([1]); ->f : Symbol(f, Decl(unusedDestructuringParameters.ts, 0, 5)) - -const f2 = ({a}) => { }; ->f2 : Symbol(f2, Decl(unusedDestructuringParameters.ts, 2, 5)) ->a : Symbol(a, Decl(unusedDestructuringParameters.ts, 2, 13)) - -f2({ a: 10 }); ->f2 : Symbol(f2, Decl(unusedDestructuringParameters.ts, 2, 5)) ->a : Symbol(a, Decl(unusedDestructuringParameters.ts, 3, 4)) - diff --git a/tests/baselines/reference/unusedDestructuringParameters.types b/tests/baselines/reference/unusedDestructuringParameters.types deleted file mode 100644 index ebbe1efbc30..00000000000 --- a/tests/baselines/reference/unusedDestructuringParameters.types +++ /dev/null @@ -1,24 +0,0 @@ -=== tests/cases/compiler/unusedDestructuringParameters.ts === -const f = ([a]) => { }; ->f : ([a]: [any]) => void ->([a]) => { } : ([a]: [any]) => void ->a : any - -f([1]); ->f([1]) : void ->f : ([a]: [any]) => void ->[1] : [number] ->1 : 1 - -const f2 = ({a}) => { }; ->f2 : ({a}: { a: any; }) => void ->({a}) => { } : ({a}: { a: any; }) => void ->a : any - -f2({ a: 10 }); ->f2({ a: 10 }) : void ->f2 : ({a}: { a: any; }) => void ->{ a: 10 } : { a: number; } ->a : number ->10 : 10 - diff --git a/tests/baselines/reference/unusedParametersWithUnderscore.errors.txt b/tests/baselines/reference/unusedParametersWithUnderscore.errors.txt index adfe7bb9bc1..5e9ffa993b0 100644 --- a/tests/baselines/reference/unusedParametersWithUnderscore.errors.txt +++ b/tests/baselines/reference/unusedParametersWithUnderscore.errors.txt @@ -2,15 +2,11 @@ tests/cases/compiler/unusedParametersWithUnderscore.ts(2,12): error TS6133: 'a' tests/cases/compiler/unusedParametersWithUnderscore.ts(2,19): error TS6133: 'c' is declared but never used. tests/cases/compiler/unusedParametersWithUnderscore.ts(2,27): error TS6133: 'd' is declared but never used. tests/cases/compiler/unusedParametersWithUnderscore.ts(2,29): error TS6133: 'e___' is declared but never used. -tests/cases/compiler/unusedParametersWithUnderscore.ts(6,14): error TS6133: '_a' is declared but never used. -tests/cases/compiler/unusedParametersWithUnderscore.ts(6,18): error TS6133: '___b' is declared but never used. -tests/cases/compiler/unusedParametersWithUnderscore.ts(9,14): error TS6133: '_a' is declared but never used. -tests/cases/compiler/unusedParametersWithUnderscore.ts(9,19): error TS6133: '___b' is declared but never used. tests/cases/compiler/unusedParametersWithUnderscore.ts(12,16): error TS6133: 'arg' is declared but never used. tests/cases/compiler/unusedParametersWithUnderscore.ts(18,13): error TS6133: 'arg' is declared but never used. -==== tests/cases/compiler/unusedParametersWithUnderscore.ts (10 errors) ==== +==== tests/cases/compiler/unusedParametersWithUnderscore.ts (6 errors) ==== function f(a, _b, c, ___, d,e___, _f) { ~ @@ -25,17 +21,9 @@ tests/cases/compiler/unusedParametersWithUnderscore.ts(18,13): error TS6133: 'ar function f2({_a, __b}) { - ~~ -!!! error TS6133: '_a' is declared but never used. - ~~~ -!!! error TS6133: '___b' is declared but never used. } function f3([_a, ,__b]) { - ~~ -!!! error TS6133: '_a' is declared but never used. - ~~~ -!!! error TS6133: '___b' is declared but never used. } function f4(...arg) { diff --git a/tests/cases/compiler/unusedDestructuringParameters.ts b/tests/cases/compiler/unusedDestructuringParameters.ts index 748c9ea83fe..7c042351049 100644 --- a/tests/cases/compiler/unusedDestructuringParameters.ts +++ b/tests/cases/compiler/unusedDestructuringParameters.ts @@ -2,4 +2,6 @@ const f = ([a]) => { }; f([1]); const f2 = ({a}) => { }; -f2({ a: 10 }); \ No newline at end of file +f2({ a: 10 }); +const f3 = ([_]) => { }; +f3([10]); \ No newline at end of file From b7ea3e5bddd73d2827d6355c1d688267f64bba17 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Mon, 24 Oct 2016 11:19:41 -0700 Subject: [PATCH 35/49] treat ambient non-aliased 'require' as commonjs 'require' --- src/compiler/checker.ts | 33 ++++++++++++++++--- .../reference/ambientRequireFunction.js | 21 ++++++++++++ .../reference/ambientRequireFunction.symbols | 27 +++++++++++++++ .../reference/ambientRequireFunction.types | 30 +++++++++++++++++ .../reference/localRequireFunction.js | 15 +++++++++ .../reference/localRequireFunction.symbols | 18 ++++++++++ .../reference/localRequireFunction.types | 24 ++++++++++++++ .../cases/compiler/ambientRequireFunction.ts | 17 ++++++++++ tests/cases/compiler/localRequireFunction.ts | 11 +++++++ tests/webTestServer.ts | 10 +++--- 10 files changed, 197 insertions(+), 9 deletions(-) create mode 100644 tests/baselines/reference/ambientRequireFunction.js create mode 100644 tests/baselines/reference/ambientRequireFunction.symbols create mode 100644 tests/baselines/reference/ambientRequireFunction.types create mode 100644 tests/baselines/reference/localRequireFunction.js create mode 100644 tests/baselines/reference/localRequireFunction.symbols create mode 100644 tests/baselines/reference/localRequireFunction.types create mode 100644 tests/cases/compiler/ambientRequireFunction.ts create mode 100644 tests/cases/compiler/localRequireFunction.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 369d61ce484..5aba1b46006 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12819,16 +12819,41 @@ namespace ts { } // In JavaScript files, calls to any identifier 'require' are treated as external module imports - if (isInJavaScriptFile(node) && - isRequireCall(node, /*checkArgumentIsStringLiteral*/true) && - // Make sure require is not a local function - !resolveName(node.expression, (node.expression).text, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined)) { + if (isInJavaScriptFile(node) && isCommonJsRequire(node)) { return resolveExternalModuleTypeByLiteral(node.arguments[0]); } return getReturnTypeOfSignature(signature); } + function isCommonJsRequire(node: Node) { + if (!isRequireCall(node, /*checkArgumentIsStringLiteral*/true)) { + return false; + } + // Make sure require is not a local function + const resolvedRequire = resolveName(node.expression, (node.expression).text, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined); + if (!resolvedRequire) { + // project does not contain symbol named 'require' - assume commonjs require + return true; + } + // project includes symbol named 'require' - make sure that it it ambient and local non-alias + if (resolvedRequire.flags & SymbolFlags.Alias) { + return false; + } + + const targetDeclarationKind = resolvedRequire.flags & SymbolFlags.Function + ? SyntaxKind.FunctionDeclaration + : resolvedRequire.flags & SymbolFlags.Variable + ? SyntaxKind.VariableDeclaration + : SyntaxKind.Unknown; + if (targetDeclarationKind !== SyntaxKind.Unknown) { + const decl = getDeclarationOfKind(resolvedRequire, targetDeclarationKind); + // function/variable declaration should be ambient + return isInAmbientContext(decl); + } + return false; + } + function checkTaggedTemplateExpression(node: TaggedTemplateExpression): Type { return getReturnTypeOfSignature(getResolvedSignature(node)); } diff --git a/tests/baselines/reference/ambientRequireFunction.js b/tests/baselines/reference/ambientRequireFunction.js new file mode 100644 index 00000000000..cd00b287c82 --- /dev/null +++ b/tests/baselines/reference/ambientRequireFunction.js @@ -0,0 +1,21 @@ +//// [tests/cases/compiler/ambientRequireFunction.ts] //// + +//// [node.d.ts] + + +declare function require(moduleName: string): any; + +declare module "fs" { + export function readFileSync(s: string): string; +} + +//// [app.js] +/// + +const fs = require("fs"); +const text = fs.readFileSync("/a/b/c"); + +//// [app.js] +/// +var fs = require("fs"); +var text = fs.readFileSync("/a/b/c"); diff --git a/tests/baselines/reference/ambientRequireFunction.symbols b/tests/baselines/reference/ambientRequireFunction.symbols new file mode 100644 index 00000000000..6afb47e37fb --- /dev/null +++ b/tests/baselines/reference/ambientRequireFunction.symbols @@ -0,0 +1,27 @@ +=== tests/cases/compiler/app.js === +/// + +const fs = require("fs"); +>fs : Symbol(fs, Decl(app.js, 2, 5)) +>require : Symbol(require, Decl(node.d.ts, 0, 0)) +>"fs" : Symbol("fs", Decl(node.d.ts, 2, 50)) + +const text = fs.readFileSync("/a/b/c"); +>text : Symbol(text, Decl(app.js, 3, 5)) +>fs.readFileSync : Symbol(readFileSync, Decl(node.d.ts, 4, 21)) +>fs : Symbol(fs, Decl(app.js, 2, 5)) +>readFileSync : Symbol(readFileSync, Decl(node.d.ts, 4, 21)) + +=== tests/cases/compiler/node.d.ts === + + +declare function require(moduleName: string): any; +>require : Symbol(require, Decl(node.d.ts, 0, 0)) +>moduleName : Symbol(moduleName, Decl(node.d.ts, 2, 25)) + +declare module "fs" { + export function readFileSync(s: string): string; +>readFileSync : Symbol(readFileSync, Decl(node.d.ts, 4, 21)) +>s : Symbol(s, Decl(node.d.ts, 5, 33)) +} + diff --git a/tests/baselines/reference/ambientRequireFunction.types b/tests/baselines/reference/ambientRequireFunction.types new file mode 100644 index 00000000000..7b01a59268f --- /dev/null +++ b/tests/baselines/reference/ambientRequireFunction.types @@ -0,0 +1,30 @@ +=== tests/cases/compiler/app.js === +/// + +const fs = require("fs"); +>fs : typeof "fs" +>require("fs") : typeof "fs" +>require : (moduleName: string) => any +>"fs" : "fs" + +const text = fs.readFileSync("/a/b/c"); +>text : string +>fs.readFileSync("/a/b/c") : string +>fs.readFileSync : (s: string) => string +>fs : typeof "fs" +>readFileSync : (s: string) => string +>"/a/b/c" : "/a/b/c" + +=== tests/cases/compiler/node.d.ts === + + +declare function require(moduleName: string): any; +>require : (moduleName: string) => any +>moduleName : string + +declare module "fs" { + export function readFileSync(s: string): string; +>readFileSync : (s: string) => string +>s : string +} + diff --git a/tests/baselines/reference/localRequireFunction.js b/tests/baselines/reference/localRequireFunction.js new file mode 100644 index 00000000000..1ae20821a07 --- /dev/null +++ b/tests/baselines/reference/localRequireFunction.js @@ -0,0 +1,15 @@ +//// [app.js] + +function require(a) { + return a; +} + +const fs = require("fs"); +const text = fs.readFileSync("/a/b/c"); + +//// [app.js] +function require(a) { + return a; +} +var fs = require("fs"); +var text = fs.readFileSync("/a/b/c"); diff --git a/tests/baselines/reference/localRequireFunction.symbols b/tests/baselines/reference/localRequireFunction.symbols new file mode 100644 index 00000000000..b977328f0ef --- /dev/null +++ b/tests/baselines/reference/localRequireFunction.symbols @@ -0,0 +1,18 @@ +=== tests/cases/compiler/app.js === + +function require(a) { +>require : Symbol(require, Decl(app.js, 0, 0)) +>a : Symbol(a, Decl(app.js, 1, 17)) + + return a; +>a : Symbol(a, Decl(app.js, 1, 17)) +} + +const fs = require("fs"); +>fs : Symbol(fs, Decl(app.js, 5, 5)) +>require : Symbol(require, Decl(app.js, 0, 0)) + +const text = fs.readFileSync("/a/b/c"); +>text : Symbol(text, Decl(app.js, 6, 5)) +>fs : Symbol(fs, Decl(app.js, 5, 5)) + diff --git a/tests/baselines/reference/localRequireFunction.types b/tests/baselines/reference/localRequireFunction.types new file mode 100644 index 00000000000..4b5c7578cd9 --- /dev/null +++ b/tests/baselines/reference/localRequireFunction.types @@ -0,0 +1,24 @@ +=== tests/cases/compiler/app.js === + +function require(a) { +>require : (a: any) => any +>a : any + + return a; +>a : any +} + +const fs = require("fs"); +>fs : any +>require("fs") : any +>require : (a: any) => any +>"fs" : "fs" + +const text = fs.readFileSync("/a/b/c"); +>text : any +>fs.readFileSync("/a/b/c") : any +>fs.readFileSync : any +>fs : any +>readFileSync : any +>"/a/b/c" : "/a/b/c" + diff --git a/tests/cases/compiler/ambientRequireFunction.ts b/tests/cases/compiler/ambientRequireFunction.ts new file mode 100644 index 00000000000..ce9019e00bc --- /dev/null +++ b/tests/cases/compiler/ambientRequireFunction.ts @@ -0,0 +1,17 @@ +// @module: commonjs +// @allowJs: true +// @outDir: ./out/ + +// @filename: node.d.ts + +declare function require(moduleName: string): any; + +declare module "fs" { + export function readFileSync(s: string): string; +} + +// @filename: app.js +/// + +const fs = require("fs"); +const text = fs.readFileSync("/a/b/c"); \ No newline at end of file diff --git a/tests/cases/compiler/localRequireFunction.ts b/tests/cases/compiler/localRequireFunction.ts new file mode 100644 index 00000000000..c8f3c2452ff --- /dev/null +++ b/tests/cases/compiler/localRequireFunction.ts @@ -0,0 +1,11 @@ +// @module: commonjs +// @allowJs: true +// @outDir: ./out/ + +// @filename: app.js +function require(a) { + return a; +} + +const fs = require("fs"); +const text = fs.readFileSync("/a/b/c"); \ No newline at end of file diff --git a/tests/webTestServer.ts b/tests/webTestServer.ts index 97027dfdd49..3d23ef3e961 100644 --- a/tests/webTestServer.ts +++ b/tests/webTestServer.ts @@ -128,7 +128,7 @@ function dir(dirPath: string, spec?: string, options?: any) { // fs.rmdirSync won't delete directories with files in it function deleteFolderRecursive(dirPath: string) { if (fs.existsSync(dirPath)) { - fs.readdirSync(dirPath).forEach((file, index) => { + fs.readdirSync(dirPath).forEach((file) => { const curPath = path.join(path, file); if (fs.statSync(curPath).isDirectory()) { // recurse deleteFolderRecursive(curPath); @@ -141,7 +141,7 @@ function deleteFolderRecursive(dirPath: string) { } }; -function writeFile(path: string, data: any, opts: { recursive: boolean }) { +function writeFile(path: string, data: any) { ensureDirectoriesExist(getDirectoryPath(path)); fs.writeFileSync(path, data); } @@ -208,7 +208,7 @@ enum RequestType { Unknown } -function getRequestOperation(req: http.ServerRequest, filename: string) { +function getRequestOperation(req: http.ServerRequest) { if (req.method === "GET" && req.url.indexOf("?") === -1) { if (req.url.indexOf(".") !== -1) return RequestType.GetFile; else return RequestType.GetDir; @@ -258,7 +258,7 @@ function handleRequestOperation(req: http.ServerRequest, res: http.ServerRespons break; case RequestType.WriteFile: processPost(req, res, (data) => { - writeFile(reqPath, data, { recursive: true }); + writeFile(reqPath, data); }); send(ResponseCode.Success, res, undefined); break; @@ -306,7 +306,7 @@ http.createServer((req: http.ServerRequest, res: http.ServerResponse) => { log(`${req.method} ${req.url}`); const uri = url.parse(req.url).pathname; const reqPath = path.join(process.cwd(), uri); - const operation = getRequestOperation(req, reqPath); + const operation = getRequestOperation(req); handleRequestOperation(req, res, operation, reqPath); }).listen(port); From 7b684214fa5a88b4c885614fa5a8dd0de1d5a94a Mon Sep 17 00:00:00 2001 From: Alexander Rusakov Date: Mon, 24 Oct 2016 23:18:46 +0300 Subject: [PATCH 36/49] Fix #11545 ('export as namespace foo' occurs EOF without semicolon) (#11797) --- src/compiler/parser.ts | 2 +- tests/baselines/reference/exportAsNamespace.d.symbols | 9 +++++++++ tests/baselines/reference/exportAsNamespace.d.types | 9 +++++++++ tests/cases/compiler/exportAsNamespace.d.ts | 4 ++++ 4 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/exportAsNamespace.d.symbols create mode 100644 tests/baselines/reference/exportAsNamespace.d.types create mode 100644 tests/cases/compiler/exportAsNamespace.d.ts diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 8cdbf956d42..7048677e8a4 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -5472,7 +5472,7 @@ namespace ts { exportDeclaration.name = parseIdentifier(); - parseExpected(SyntaxKind.SemicolonToken); + parseSemicolon(); return finishNode(exportDeclaration); } diff --git a/tests/baselines/reference/exportAsNamespace.d.symbols b/tests/baselines/reference/exportAsNamespace.d.symbols new file mode 100644 index 00000000000..ca65cb2574e --- /dev/null +++ b/tests/baselines/reference/exportAsNamespace.d.symbols @@ -0,0 +1,9 @@ +=== tests/cases/compiler/exportAsNamespace.d.ts === +// issue: https://github.com/Microsoft/TypeScript/issues/11545 + +export var X; +>X : Symbol(X, Decl(exportAsNamespace.d.ts, 2, 10)) + +export as namespace N +>N : Symbol(N, Decl(exportAsNamespace.d.ts, 2, 13)) + diff --git a/tests/baselines/reference/exportAsNamespace.d.types b/tests/baselines/reference/exportAsNamespace.d.types new file mode 100644 index 00000000000..706857bf8ed --- /dev/null +++ b/tests/baselines/reference/exportAsNamespace.d.types @@ -0,0 +1,9 @@ +=== tests/cases/compiler/exportAsNamespace.d.ts === +// issue: https://github.com/Microsoft/TypeScript/issues/11545 + +export var X; +>X : any + +export as namespace N +>N : typeof N + diff --git a/tests/cases/compiler/exportAsNamespace.d.ts b/tests/cases/compiler/exportAsNamespace.d.ts new file mode 100644 index 00000000000..755d4fdb2a9 --- /dev/null +++ b/tests/cases/compiler/exportAsNamespace.d.ts @@ -0,0 +1,4 @@ +// issue: https://github.com/Microsoft/TypeScript/issues/11545 + +export var X; +export as namespace N \ No newline at end of file From cb804df1510e346748638a1ae8e8d11519f55b7a Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Mon, 24 Oct 2016 17:53:03 -0700 Subject: [PATCH 37/49] remove whitespace --- src/services/navigationBar.ts | 21 +++++++++++- .../navigationBarItemsFunctionProperties.ts | 33 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 tests/cases/fourslash/navigationBarItemsFunctionProperties.ts diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index 7b4fb8cdba6..7ebefe7bdbd 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -377,6 +377,7 @@ namespace ts.NavigationBar { } function getItemName(node: Node): string { + if (node.kind === SyntaxKind.ModuleDeclaration) { return getModuleName(node); } @@ -403,7 +404,10 @@ namespace ts.NavigationBar { if (getModifierFlags(node) & ModifierFlags.Default) { return "default"; } - return getFunctionOrClassName(node); + // We ma get a string with newlines or other whitespace in the case of an object dereference + // (eg: "app\n.onactivated"), so we should remove the whitespace for readabiltiy in the + // navigation bar. + return getFunctionOrClassName(node).replace(whiteSpaceRegex, ""); case SyntaxKind.Constructor: return "constructor"; case SyntaxKind.ConstructSignature: @@ -620,4 +624,19 @@ namespace ts.NavigationBar { function isFunctionOrClassExpression(node: Node): boolean { return node.kind === SyntaxKind.FunctionExpression || node.kind === SyntaxKind.ArrowFunction || node.kind === SyntaxKind.ClassExpression; } + + /** + * Matches all whitespace characters in a string. Eg: + * + * "app. + * + * onactivated" + * + * matches because of the newline, whereas + * + * "app.onactivated" + * + * does not match. + */ + const whiteSpaceRegex = /(\s|\t|\n|\r)+/g; } diff --git a/tests/cases/fourslash/navigationBarItemsFunctionProperties.ts b/tests/cases/fourslash/navigationBarItemsFunctionProperties.ts new file mode 100644 index 00000000000..75c20736b41 --- /dev/null +++ b/tests/cases/fourslash/navigationBarItemsFunctionProperties.ts @@ -0,0 +1,33 @@ +/// + +//// (function(){ +//// var A; +//// A/*1*/ +//// .a = function() { }; +//// })(); + +function navExact(name: string, kind: string) { + return; +} + +verify.navigationTree( +{ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "", + "kind": "function", + "childItems": [ + { + "text": "A", + "kind": "var" + }, + { + "text": "A.a", + "kind": "function" + } + ] + } + ] +}); \ No newline at end of file From f98369b71118289e89edf80425c25f2c95cd8307 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Mon, 24 Oct 2016 18:10:05 -0700 Subject: [PATCH 38/49] Fix the AV resulting from presence of undefined in the jsDocPropertyTags Fixes #11703 --- src/compiler/parser.ts | 14 +++++++++----- .../fourslash/incorrectJsDocObjectLiteralType.ts | 8 ++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 tests/cases/fourslash/incorrectJsDocObjectLiteralType.ts diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 7048677e8a4..22bc8763008 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -6639,12 +6639,16 @@ namespace ts { return true; case "prop": case "property": - if (!parentTag.jsDocPropertyTags) { - parentTag.jsDocPropertyTags = >[]; - } const propertyTag = parsePropertyTag(atToken, tagName); - parentTag.jsDocPropertyTags.push(propertyTag); - return true; + if (propertyTag) { + if (!parentTag.jsDocPropertyTags) { + parentTag.jsDocPropertyTags = >[]; + } + parentTag.jsDocPropertyTags.push(propertyTag); + return true; + } + // Error parsing property tag + return false; } return false; } diff --git a/tests/cases/fourslash/incorrectJsDocObjectLiteralType.ts b/tests/cases/fourslash/incorrectJsDocObjectLiteralType.ts new file mode 100644 index 00000000000..ddf65ec0e8a --- /dev/null +++ b/tests/cases/fourslash/incorrectJsDocObjectLiteralType.ts @@ -0,0 +1,8 @@ +/// + +//// /**/ + +goTo.marker(); +verify.navigationItemsListCount(0, "foo", "exact"); +edit.insert("/**\n * @typedef {Object} foo\n * @property {any} [obj]\n */\nexport default function foo() {\n}"); +verify.navigationItemsListContains("foo", "function", "foo", "exact"); From 82e8e4323695ff303b98687712fc0fc3392f280e Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Tue, 25 Oct 2016 10:38:41 -0700 Subject: [PATCH 39/49] only remove whitespace for expr assignment --- src/services/navigationBar.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index 7ebefe7bdbd..f274810a323 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -404,10 +404,10 @@ namespace ts.NavigationBar { if (getModifierFlags(node) & ModifierFlags.Default) { return "default"; } - // We ma get a string with newlines or other whitespace in the case of an object dereference + // We may get a string with newlines or other whitespace in the case of an object dereference // (eg: "app\n.onactivated"), so we should remove the whitespace for readabiltiy in the // navigation bar. - return getFunctionOrClassName(node).replace(whiteSpaceRegex, ""); + return getFunctionOrClassName(node); case SyntaxKind.Constructor: return "constructor"; case SyntaxKind.ConstructSignature: @@ -606,11 +606,11 @@ namespace ts.NavigationBar { // See if it is of the form " = function(){...}". If so, use the text from the left-hand side. else if (node.parent.kind === SyntaxKind.BinaryExpression && (node.parent as BinaryExpression).operatorToken.kind === SyntaxKind.EqualsToken) { - return nodeText((node.parent as BinaryExpression).left); + return nodeText((node.parent as BinaryExpression).left).replace(whiteSpaceRegex, ""); } // See if it is a property assignment, and if so use the property name else if (node.parent.kind === SyntaxKind.PropertyAssignment && (node.parent as PropertyAssignment).name) { - return nodeText((node.parent as PropertyAssignment).name); + return nodeText((node.parent as PropertyAssignment).name).replace(whiteSpaceRegex, "#"); } // Default exports are named "default" else if (getModifierFlags(node) & ModifierFlags.Default) { From 476b6e02c873675137094f946538207ce69a626d Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Tue, 25 Oct 2016 10:59:17 -0700 Subject: [PATCH 40/49] remove check on property assignment --- src/services/navigationBar.ts | 2 +- tests/cases/fourslash/indentationWithBaseIndent.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index f274810a323..3cdf7c38195 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -610,7 +610,7 @@ namespace ts.NavigationBar { } // See if it is a property assignment, and if so use the property name else if (node.parent.kind === SyntaxKind.PropertyAssignment && (node.parent as PropertyAssignment).name) { - return nodeText((node.parent as PropertyAssignment).name).replace(whiteSpaceRegex, "#"); + return nodeText((node.parent as PropertyAssignment).name); } // Default exports are named "default" else if (getModifierFlags(node) & ModifierFlags.Default) { diff --git a/tests/cases/fourslash/indentationWithBaseIndent.ts b/tests/cases/fourslash/indentationWithBaseIndent.ts index 53ee01c9a3c..112ce225d01 100644 --- a/tests/cases/fourslash/indentationWithBaseIndent.ts +++ b/tests/cases/fourslash/indentationWithBaseIndent.ts @@ -205,7 +205,6 @@ //// function unterminatedListIndentation(a, ////{| "indent": 14 , "baseIndentSize": 10 |} -debugger; test.markers().forEach(marker => { verify.indentationAtPositionIs(marker.fileName, marker.position, marker.data.indent, ts.IndentStyle.Smart, marker.data.baseIndentSize); }); From 77df58bdb630c85f1c928576c40d6b49dac8e777 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Tue, 25 Oct 2016 11:03:54 -0700 Subject: [PATCH 41/49] simplify regex --- src/services/navigationBar.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index 3cdf7c38195..10ed52858c7 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -638,5 +638,5 @@ namespace ts.NavigationBar { * * does not match. */ - const whiteSpaceRegex = /(\s|\t|\n|\r)+/g; + const whiteSpaceRegex = /\s+/g; } From 59cfa2eafe48346a1da4dd4673938f303b0132c4 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Tue, 25 Oct 2016 11:06:04 -0700 Subject: [PATCH 42/49] remove whitespace --- src/services/navigationBar.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index 10ed52858c7..51ce9b6001d 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -377,7 +377,6 @@ namespace ts.NavigationBar { } function getItemName(node: Node): string { - if (node.kind === SyntaxKind.ModuleDeclaration) { return getModuleName(node); } From 66c1178134855b6554613e949c9cd447dfdcc854 Mon Sep 17 00:00:00 2001 From: Asad Saeeduddin Date: Tue, 25 Oct 2016 14:45:32 -0400 Subject: [PATCH 43/49] Use symbol fully-qualified name instead of node text in error message (#11761) * Add test * Add baselines * Use fqn of symbol instead of node text --- src/compiler/checker.ts | 2 +- .../extendPrivateConstructorClass.errors.txt | 15 ++++++++++++ .../extendPrivateConstructorClass.js | 24 +++++++++++++++++++ .../compiler/extendPrivateConstructorClass.ts | 8 +++++++ 4 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/extendPrivateConstructorClass.errors.txt create mode 100644 tests/baselines/reference/extendPrivateConstructorClass.js create mode 100644 tests/cases/compiler/extendPrivateConstructorClass.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 902353aded6..3c98513423a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17234,7 +17234,7 @@ namespace ts { if (declaration && getModifierFlags(declaration) & ModifierFlags.Private) { const typeClassDeclaration = getClassLikeDeclarationOfSymbol(type.symbol); if (!isNodeWithinClass(node, typeClassDeclaration)) { - error(node, Diagnostics.Cannot_extend_a_class_0_Class_constructor_is_marked_as_private, (node.expression).text); + error(node, Diagnostics.Cannot_extend_a_class_0_Class_constructor_is_marked_as_private, getFullyQualifiedName(type.symbol)); } } } diff --git a/tests/baselines/reference/extendPrivateConstructorClass.errors.txt b/tests/baselines/reference/extendPrivateConstructorClass.errors.txt new file mode 100644 index 00000000000..02e9c6a1c88 --- /dev/null +++ b/tests/baselines/reference/extendPrivateConstructorClass.errors.txt @@ -0,0 +1,15 @@ +tests/cases/compiler/extendPrivateConstructorClass.ts(7,17): error TS2675: Cannot extend a class 'abc.XYZ'. Class constructor is marked as private. + + +==== tests/cases/compiler/extendPrivateConstructorClass.ts (1 errors) ==== + declare namespace abc { + class XYZ { + private constructor(); + } + } + + class C extends abc.XYZ { + ~~~~~~~ +!!! error TS2675: Cannot extend a class 'abc.XYZ'. Class constructor is marked as private. + } + \ No newline at end of file diff --git a/tests/baselines/reference/extendPrivateConstructorClass.js b/tests/baselines/reference/extendPrivateConstructorClass.js new file mode 100644 index 00000000000..8893e9735ff --- /dev/null +++ b/tests/baselines/reference/extendPrivateConstructorClass.js @@ -0,0 +1,24 @@ +//// [extendPrivateConstructorClass.ts] +declare namespace abc { + class XYZ { + private constructor(); + } +} + +class C extends abc.XYZ { +} + + +//// [extendPrivateConstructorClass.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 C = (function (_super) { + __extends(C, _super); + function C() { + return _super.apply(this, arguments) || this; + } + return C; +}(abc.XYZ)); diff --git a/tests/cases/compiler/extendPrivateConstructorClass.ts b/tests/cases/compiler/extendPrivateConstructorClass.ts new file mode 100644 index 00000000000..90629657f76 --- /dev/null +++ b/tests/cases/compiler/extendPrivateConstructorClass.ts @@ -0,0 +1,8 @@ +declare namespace abc { + class XYZ { + private constructor(); + } +} + +class C extends abc.XYZ { +} From f48728af7488a7c38f7fd7dd7e5f0c6c1840c124 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Tue, 25 Oct 2016 13:18:57 -0700 Subject: [PATCH 44/49] report typings installer process id to parent process --- src/server/server.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/server/server.ts b/src/server/server.ts index 6ddd267f56e..fe3743c057d 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -40,6 +40,7 @@ namespace ts.server { send(message: any, sendHandle?: any): void; on(message: "message", f: (m: any) => void): void; kill(): void; + pid: number; } interface NodeSocket { @@ -179,6 +180,7 @@ namespace ts.server { class NodeTypingsInstaller implements ITypingsInstaller { private installer: NodeChildProcess; + private installerPidReported = false; private socket: NodeSocket; private projectService: ProjectService; @@ -190,10 +192,25 @@ namespace ts.server { if (eventPort) { const s = net.connect({ port: eventPort }, () => { this.socket = s; + this.reportInstallerProcessId(); }); } } + private reportInstallerProcessId() { + if (this.installerPidReported) { + return; + } + if (this.socket && this.installer) { + this.sendEvent(0, "typingsInstallerPid", { pid: this.installer.pid }); + this.installerPidReported = true; + } + } + + private sendEvent(seq: number, event: string, body: any): void { + this.socket.write(formatMessage({ seq, type: "event", event, body }, this.logger, Buffer.byteLength, this.newLine), "utf8"); + } + attach(projectService: ProjectService) { this.projectService = projectService; if (this.logger.hasLevel(LogLevel.requestTime)) { @@ -222,6 +239,8 @@ namespace ts.server { this.installer = childProcess.fork(combinePaths(__dirname, "typingsInstaller.js"), args, { execArgv }); this.installer.on("message", m => this.handleMessage(m)); + this.reportInstallerProcessId(); + process.on("exit", () => { this.installer.kill(); }); @@ -245,7 +264,7 @@ namespace ts.server { } this.projectService.updateTypingsForProject(response); if (response.kind == "set" && this.socket) { - this.socket.write(formatMessage({ seq: 0, type: "event", message: response }, this.logger, Buffer.byteLength, this.newLine), "utf8"); + this.sendEvent(0, "setTypings", response); } } } From d0170d1ac835891145a811c27b39038b1f4b7295 Mon Sep 17 00:00:00 2001 From: Zhengbo Li Date: Tue, 25 Oct 2016 13:32:48 -0700 Subject: [PATCH 45/49] Support dotted name in @typedef tag (#11695) * Support dotted name in @typedef tag * Use a new node flag to get rid of the extra logics * replace jsdoc namespace node flag with optional property * Fix linting error --- src/compiler/binder.ts | 26 ++++++++++++++++- src/compiler/parser.ts | 29 ++++++++++++++++++- src/compiler/scanner.ts | 3 ++ src/compiler/types.ts | 11 +++++-- src/compiler/utilities.ts | 2 +- ...sCorrectly.typedefTagWithChildrenTags.json | 6 ++++ .../server/jsdocTypedefTagNamespace.ts | 27 +++++++++++++++++ 7 files changed, 99 insertions(+), 5 deletions(-) create mode 100644 tests/cases/fourslash/server/jsdocTypedefTagNamespace.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 447a7625d38..cc447a55d49 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -54,6 +54,11 @@ namespace ts { const body = (node).body; return body ? getModuleInstanceState(body) : ModuleInstanceState.Instantiated; } + // Only jsdoc typedef definition can exist in jsdoc namespace, and it should + // be considered the same as type alias + else if (node.kind === SyntaxKind.Identifier && (node).isInJSDocNamespace) { + return ModuleInstanceState.NonInstantiated; + } else { return ModuleInstanceState.Instantiated; } @@ -429,7 +434,11 @@ namespace ts { // during global merging in the checker. Why? The only case when ambient module is permitted inside another module is module augmentation // and this case is specially handled. Module augmentations should only be merged with original module definition // and should never be merged directly with other augmentation, and the latter case would be possible if automatic merge is allowed. - if (!isAmbientModule(node) && (hasExportModifier || container.flags & NodeFlags.ExportContext)) { + const isJSDocTypedefInJSDocNamespace = node.kind === SyntaxKind.JSDocTypedefTag && + node.name && + node.name.kind === SyntaxKind.Identifier && + (node.name).isInJSDocNamespace; + if ((!isAmbientModule(node) && (hasExportModifier || container.flags & NodeFlags.ExportContext)) || isJSDocTypedefInJSDocNamespace) { const exportKind = (symbolFlags & SymbolFlags.Value ? SymbolFlags.ExportValue : 0) | (symbolFlags & SymbolFlags.Type ? SymbolFlags.ExportType : 0) | @@ -1828,6 +1837,17 @@ namespace ts { switch (node.kind) { /* Strict mode checks */ case SyntaxKind.Identifier: + // for typedef type names with namespaces, bind the new jsdoc type symbol here + // because it requires all containing namespaces to be in effect, namely the + // current "blockScopeContainer" needs to be set to its immediate namespace parent. + if ((node).isInJSDocNamespace) { + let parentNode = node.parent; + while (parentNode && parentNode.kind !== SyntaxKind.JSDocTypedefTag) { + parentNode = parentNode.parent; + } + bindBlockScopedDeclaration(parentNode, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes); + break; + } case SyntaxKind.ThisKeyword: if (currentFlow && (isExpression(node) || parent.kind === SyntaxKind.ShorthandPropertyAssignment)) { node.flowNode = currentFlow; @@ -1951,6 +1971,10 @@ namespace ts { case SyntaxKind.InterfaceDeclaration: return bindBlockScopedDeclaration(node, SymbolFlags.Interface, SymbolFlags.InterfaceExcludes); case SyntaxKind.JSDocTypedefTag: + if (!(node).fullName || (node).fullName.kind === SyntaxKind.Identifier) { + return bindBlockScopedDeclaration(node, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes); + } + break; case SyntaxKind.TypeAliasDeclaration: return bindBlockScopedDeclaration(node, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes); case SyntaxKind.EnumDeclaration: diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 22bc8763008..a5b02f98fd3 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -411,6 +411,7 @@ namespace ts { return visitNodes(cbNodes, (node).typeParameters); case SyntaxKind.JSDocTypedefTag: return visitNode(cbNode, (node).typeExpression) || + visitNode(cbNode, (node).fullName) || visitNode(cbNode, (node).name) || visitNode(cbNode, (node).jsDocTypeLiteral); case SyntaxKind.JSDocTypeLiteral: @@ -6552,7 +6553,14 @@ namespace ts { const typedefTag = createNode(SyntaxKind.JSDocTypedefTag, atToken.pos); typedefTag.atToken = atToken; typedefTag.tagName = tagName; - typedefTag.name = parseJSDocIdentifierName(); + typedefTag.fullName = parseJSDocTypeNameWithNamespace(/*flags*/ 0); + if (typedefTag.fullName) { + let rightNode = typedefTag.fullName; + while (rightNode.kind !== SyntaxKind.Identifier) { + rightNode = rightNode.body; + } + typedefTag.name = rightNode; + } typedefTag.typeExpression = typeExpression; skipWhitespace(); @@ -6615,8 +6623,27 @@ namespace ts { scanner.setTextPos(resumePos); return finishNode(jsDocTypeLiteral); } + + function parseJSDocTypeNameWithNamespace(flags: NodeFlags) { + const pos = scanner.getTokenPos(); + const typeNameOrNamespaceName = parseJSDocIdentifierName(); + + if (typeNameOrNamespaceName && parseOptional(SyntaxKind.DotToken)) { + const jsDocNamespaceNode = createNode(SyntaxKind.ModuleDeclaration, pos); + jsDocNamespaceNode.flags |= flags; + jsDocNamespaceNode.name = typeNameOrNamespaceName; + jsDocNamespaceNode.body = parseJSDocTypeNameWithNamespace(NodeFlags.NestedNamespace); + return jsDocNamespaceNode; + } + + if (typeNameOrNamespaceName && flags & NodeFlags.NestedNamespace) { + typeNameOrNamespaceName.isInJSDocNamespace = true; + } + return typeNameOrNamespaceName; + } } + function tryParseChildTag(parentTag: JSDocTypeLiteral): boolean { Debug.assert(token() === SyntaxKind.AtToken); const atToken = createNode(SyntaxKind.AtToken, scanner.getStartPos()); diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index ab76c7ffa19..1c0baa457f4 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -1799,6 +1799,9 @@ namespace ts { case CharacterCodes.comma: pos++; return token = SyntaxKind.CommaToken; + case CharacterCodes.dot: + pos++; + return token = SyntaxKind.DotToken; } if (isIdentifierStart(ch, ScriptTarget.Latest)) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f9ec3e9f268..0dc588ed96f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -421,7 +421,7 @@ namespace ts { ThisNodeHasError = 1 << 19, // If the parser encountered an error when parsing the code that created this node JavaScriptFile = 1 << 20, // If node was parsed in a JavaScript ThisNodeOrAnySubNodesHasError = 1 << 21, // If this node or any of its children had an error - HasAggregatedChildData = 1 << 22, // If we've computed data from children and cached it in this node + HasAggregatedChildData = 1 << 22, // If we've computed data from children and cached it in this node BlockScoped = Let | Const, @@ -545,6 +545,7 @@ namespace ts { originalKeywordKind?: SyntaxKind; // Original syntaxKind which get set so that we can report an error later /*@internal*/ autoGenerateKind?: GeneratedIdentifierKind; // Specifies whether to auto-generate the text for an identifier. /*@internal*/ autoGenerateId?: number; // Ensures unique generated identifiers get unique names, but clones get the same name. + isInJSDocNamespace?: boolean; // if the node is a member in a JSDoc namespace } // Transient identifier node (marked by id === -1) @@ -1634,7 +1635,7 @@ namespace ts { export interface ModuleDeclaration extends DeclarationStatement { kind: SyntaxKind.ModuleDeclaration; name: Identifier | LiteralExpression; - body?: ModuleBlock | NamespaceDeclaration; + body?: ModuleBlock | NamespaceDeclaration | JSDocNamespaceDeclaration | Identifier; } export interface NamespaceDeclaration extends ModuleDeclaration { @@ -1642,6 +1643,11 @@ namespace ts { body: ModuleBlock | NamespaceDeclaration; } + export interface JSDocNamespaceDeclaration extends ModuleDeclaration { + name: Identifier; + body: JSDocNamespaceDeclaration | Identifier; + } + export interface ModuleBlock extends Node, Statement { kind: SyntaxKind.ModuleBlock; statements: NodeArray; @@ -1871,6 +1877,7 @@ namespace ts { export interface JSDocTypedefTag extends JSDocTag, Declaration { kind: SyntaxKind.JSDocTypedefTag; + fullName?: JSDocNamespaceDeclaration | Identifier; name?: Identifier; typeExpression?: JSDocTypeExpression; jsDocTypeLiteral?: JSDocTypeLiteral; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 3a8d09f113d..4144a870454 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3036,7 +3036,7 @@ namespace ts { } } - if (node.flags & NodeFlags.NestedNamespace) { + if (node.flags & NodeFlags.NestedNamespace || (node.kind === SyntaxKind.Identifier && (node).isInJSDocNamespace)) { flags |= ModifierFlags.Export; } diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.typedefTagWithChildrenTags.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.typedefTagWithChildrenTags.json index 30ca205587e..7ef64f5a971 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.typedefTagWithChildrenTags.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.typedefTagWithChildrenTags.json @@ -18,6 +18,12 @@ "end": 16, "text": "typedef" }, + "fullName": { + "kind": "Identifier", + "pos": 17, + "end": 23, + "text": "People" + }, "name": { "kind": "Identifier", "pos": 17, diff --git a/tests/cases/fourslash/server/jsdocTypedefTagNamespace.ts b/tests/cases/fourslash/server/jsdocTypedefTagNamespace.ts new file mode 100644 index 00000000000..1d7a0bf318a --- /dev/null +++ b/tests/cases/fourslash/server/jsdocTypedefTagNamespace.ts @@ -0,0 +1,27 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: jsdocCompletion_typedef.js + +//// /** +//// * @typedef {string | number} T.NumberLike +//// * @typedef {{age: number}} T.People +//// * @typedef {string | number} T.O.Q.NumberLike +//// * @type {T.NumberLike} +//// */ +//// var x; x./*1*/; +//// /** @type {T.O.Q.NumberLike} */ +//// var x1; x1./*2*/; +//// /** @type {T.People} */ +//// var x1; x1./*3*/; + +goTo.marker("1"); +verify.memberListContains('charAt'); +verify.memberListContains('toExponential'); + +goTo.marker("2"); +verify.memberListContains('age'); + +goTo.marker("3"); +verify.memberListContains('charAt'); +verify.memberListContains('toExponential'); \ No newline at end of file From 7890f632502821305307fcd84dfdb9d03f2a0da3 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Tue, 25 Oct 2016 15:24:21 -0700 Subject: [PATCH 46/49] use unresolved imports as a source of used typings (#11828) --- src/compiler/core.ts | 48 ++++- src/compiler/program.ts | 16 +- src/compiler/types.ts | 1 + src/compiler/utilities.ts | 19 -- .../unittests/tsserverProjectSystem.ts | 4 +- src/harness/unittests/typingsInstaller.ts | 168 +++++++++++++++++- src/server/editorServices.ts | 6 +- src/server/lsHost.ts | 49 ++++- src/server/project.ts | 106 ++++++++++- src/server/server.ts | 20 ++- src/server/types.d.ts | 16 +- src/server/typingsCache.ts | 48 ++--- .../typingsInstaller/typingsInstaller.ts | 12 +- src/server/utilities.ts | 14 +- src/services/jsTyping.ts | 23 ++- src/services/shims.ts | 3 +- 16 files changed, 457 insertions(+), 96 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 395ddb40574..d8f8cff42a2 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -456,6 +456,44 @@ namespace ts { return result; } + export function arrayIsEqualTo(array1: ReadonlyArray, array2: ReadonlyArray, equaler?: (a: T, b: T) => boolean): boolean { + if (!array1 || !array2) { + return array1 === array2; + } + + if (array1.length !== array2.length) { + return false; + } + + for (let i = 0; i < array1.length; i++) { + const equals = equaler ? equaler(array1[i], array2[i]) : array1[i] === array2[i]; + if (!equals) { + return false; + } + } + + return true; + } + + export function changesAffectModuleResolution(oldOptions: CompilerOptions, newOptions: CompilerOptions): boolean { + return !oldOptions || + (oldOptions.module !== newOptions.module) || + (oldOptions.moduleResolution !== newOptions.moduleResolution) || + (oldOptions.noResolve !== newOptions.noResolve) || + (oldOptions.target !== newOptions.target) || + (oldOptions.noLib !== newOptions.noLib) || + (oldOptions.jsx !== newOptions.jsx) || + (oldOptions.allowJs !== newOptions.allowJs) || + (oldOptions.rootDir !== newOptions.rootDir) || + (oldOptions.configFilePath !== newOptions.configFilePath) || + (oldOptions.baseUrl !== newOptions.baseUrl) || + (oldOptions.maxNodeModuleJsDepth !== newOptions.maxNodeModuleJsDepth) || + !arrayIsEqualTo(oldOptions.lib, newOptions.lib) || + !arrayIsEqualTo(oldOptions.typeRoots, newOptions.typeRoots) || + !arrayIsEqualTo(oldOptions.rootDirs, newOptions.rootDirs) || + !equalOwnProperties(oldOptions.paths, newOptions.paths); + } + /** * Compacts an array, removing any falsey elements. */ @@ -821,7 +859,7 @@ namespace ts { return result; } - export function extend(first: T1 , second: T2): T1 & T2 { + export function extend(first: T1, second: T2): T1 & T2 { const result: T1 & T2 = {}; for (const id in second) if (hasOwnProperty.call(second, id)) { (result as any)[id] = (second as any)[id]; @@ -974,8 +1012,8 @@ namespace ts { Debug.assert(length >= 0, "length must be non-negative, is " + length); if (file) { - Debug.assert(start <= file.text.length, `start must be within the bounds of the file. ${ start } > ${ file.text.length }`); - Debug.assert(end <= file.text.length, `end must be the bounds of the file. ${ end } > ${ file.text.length }`); + Debug.assert(start <= file.text.length, `start must be within the bounds of the file. ${start} > ${file.text.length}`); + Debug.assert(end <= file.text.length, `end must be the bounds of the file. ${end} > ${file.text.length}`); } let text = getLocaleSpecificMessage(message); @@ -1525,7 +1563,7 @@ namespace ts { return undefined; } - const replaceWildcardCharacter = usage === "files" ? replaceWildCardCharacterFiles : replaceWildCardCharacterOther; + const replaceWildcardCharacter = usage === "files" ? replaceWildCardCharacterFiles : replaceWildCardCharacterOther; const singleAsteriskRegexFragment = usage === "files" ? singleAsteriskRegexFragmentFiles : singleAsteriskRegexFragmentOther; /** @@ -1767,7 +1805,7 @@ namespace 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); + const allSupportedExtensions = supportedTypeScriptExtensions.concat(supportedJavascriptExtensions); export function getSupportedExtensions(options?: CompilerOptions): string[] { return options && options.allowJs ? allSupportedExtensions : supportedTypeScriptExtensions; diff --git a/src/compiler/program.ts b/src/compiler/program.ts index a9c64f3ecbe..25e85d13dc9 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -462,21 +462,7 @@ namespace ts { // check properties that can affect structure of the program or module resolution strategy // if any of these properties has changed - structure cannot be reused const oldOptions = oldProgram.getCompilerOptions(); - if ((oldOptions.module !== options.module) || - (oldOptions.moduleResolution !== options.moduleResolution) || - (oldOptions.noResolve !== options.noResolve) || - (oldOptions.target !== options.target) || - (oldOptions.noLib !== options.noLib) || - (oldOptions.jsx !== options.jsx) || - (oldOptions.allowJs !== options.allowJs) || - (oldOptions.rootDir !== options.rootDir) || - (oldOptions.configFilePath !== options.configFilePath) || - (oldOptions.baseUrl !== options.baseUrl) || - (oldOptions.maxNodeModuleJsDepth !== options.maxNodeModuleJsDepth) || - !arrayIsEqualTo(oldOptions.lib, options.lib) || - !arrayIsEqualTo(oldOptions.typeRoots, options.typeRoots) || - !arrayIsEqualTo(oldOptions.rootDirs, options.rootDirs) || - !equalOwnProperties(oldOptions.paths, options.paths)) { + if (changesAffectModuleResolution(oldOptions, options)) { return false; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 0dc588ed96f..e6c13235c9b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3041,6 +3041,7 @@ namespace ts { packageNameToTypingLocation: Map; // The map of package names to their cached typing locations typingOptions: TypingOptions; // Used to customize the typing inference process compilerOptions: CompilerOptions; // Used as a source for typing inference + unresolvedImports: ReadonlyArray; // List of unresolved module ids from imports } export enum ModuleKind { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 4144a870454..69d42f2ed26 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -83,25 +83,6 @@ namespace ts { return node.end - node.pos; } - export function arrayIsEqualTo(array1: ReadonlyArray, array2: ReadonlyArray, equaler?: (a: T, b: T) => boolean): boolean { - if (!array1 || !array2) { - return array1 === array2; - } - - if (array1.length !== array2.length) { - return false; - } - - for (let i = 0; i < array1.length; i++) { - const equals = equaler ? equaler(array1[i], array2[i]) : array1[i] === array2[i]; - if (!equals) { - return false; - } - } - - return true; - } - export function hasResolvedModule(sourceFile: SourceFile, moduleNameText: string): boolean { return !!(sourceFile && sourceFile.resolvedModules && sourceFile.resolvedModules[moduleNameText]); } diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 2abfd66b983..7f12036a777 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -94,8 +94,8 @@ namespace ts.projectSystem { this.projectService.updateTypingsForProject(response); } - enqueueInstallTypingsRequest(project: server.Project, typingOptions: TypingOptions) { - const request = server.createInstallTypingsRequest(project, typingOptions, this.globalTypingsCacheLocation); + enqueueInstallTypingsRequest(project: server.Project, typingOptions: TypingOptions, unresolvedImports: server.SortedReadonlyArray) { + const request = server.createInstallTypingsRequest(project, typingOptions, unresolvedImports, this.globalTypingsCacheLocation); this.install(request); } diff --git a/src/harness/unittests/typingsInstaller.ts b/src/harness/unittests/typingsInstaller.ts index 4a0021e6ad4..356992d5717 100644 --- a/src/harness/unittests/typingsInstaller.ts +++ b/src/harness/unittests/typingsInstaller.ts @@ -217,9 +217,9 @@ namespace ts.projectSystem { constructor() { super(host); } - enqueueInstallTypingsRequest(project: server.Project, typingOptions: TypingOptions) { + enqueueInstallTypingsRequest(project: server.Project, typingOptions: TypingOptions, unresolvedImports: server.SortedReadonlyArray) { enqueueIsCalled = true; - super.enqueueInstallTypingsRequest(project, typingOptions); + super.enqueueInstallTypingsRequest(project, typingOptions, unresolvedImports); } executeRequest(requestKind: TI.RequestKind, _requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void { const installedTypings = ["@types/jquery"]; @@ -319,9 +319,9 @@ namespace ts.projectSystem { constructor() { super(host); } - enqueueInstallTypingsRequest(project: server.Project, typingOptions: TypingOptions) { + enqueueInstallTypingsRequest(project: server.Project, typingOptions: TypingOptions, unresolvedImports: server.SortedReadonlyArray) { enqueueIsCalled = true; - super.enqueueInstallTypingsRequest(project, typingOptions); + super.enqueueInstallTypingsRequest(project, typingOptions, unresolvedImports); } executeRequest(requestKind: TI.RequestKind, _requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void { const installedTypings: string[] = []; @@ -724,7 +724,7 @@ namespace ts.projectSystem { it("Malformed package.json should be watched", () => { const f = { path: "/a/b/app.js", - content: "var x = require('commander')" + content: "var x = 1" }; const brokenPackageJson = { path: "/a/b/package.json", @@ -763,6 +763,133 @@ namespace ts.projectSystem { service.checkNumberOfProjects({ inferredProjects: 1 }); checkProjectActualFiles(service.inferredProjects[0], [f.path, commander.path]); }); + + it("should install typings for unresolved imports", () => { + const file = { + path: "/a/b/app.js", + content: ` + import * as fs from "fs"; + import * as commander from "commander";` + }; + const cachePath = "/a/cache"; + const node = { + path: cachePath + "/node_modules/@types/node/index.d.ts", + content: "export let x: number" + }; + const commander = { + path: cachePath + "/node_modules/@types/commander/index.d.ts", + content: "export let y: string" + }; + const host = createServerHost([file]); + const installer = new (class extends Installer { + constructor() { + super(host, { globalTypingsCacheLocation: cachePath }); + } + executeRequest(requestKind: TI.RequestKind, _requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) { + const installedTypings = ["@types/node", "@types/commander"]; + const typingFiles = [node, commander]; + executeCommand(this, host, installedTypings, typingFiles, requestKind, cb); + } + })(); + const service = createProjectService(host, { typingsInstaller: installer }); + service.openClientFile(file.path); + + service.checkNumberOfProjects({ inferredProjects: 1 }); + checkProjectActualFiles(service.inferredProjects[0], [file.path]); + + installer.installAll([TI.NpmViewRequest, TI.NpmViewRequest], [TI.NpmInstallRequest]); + + assert.isTrue(host.fileExists(node.path), "typings for 'node' should be created"); + assert.isTrue(host.fileExists(commander.path), "typings for 'commander' should be created"); + + checkProjectActualFiles(service.inferredProjects[0], [file.path, node.path, commander.path]); + }); + + it("should pick typing names from non-relative unresolved imports", () => { + const f1 = { + path: "/a/b/app.js", + content: ` + import * as a from "foo/a/a"; + import * as b from "foo/a/b"; + import * as c from "foo/a/c"; + import * as d from "@bar/router/"; + import * as e from "@bar/common/shared"; + import * as e from "@bar/common/apps"; + import * as f from "./lib" + ` + }; + + const host = createServerHost([f1]); + const installer = new (class extends Installer { + constructor() { + super(host, { globalTypingsCacheLocation: "/tmp" }); + } + executeRequest(requestKind: TI.RequestKind, _requestId: number, args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) { + if (requestKind === TI.NpmViewRequest) { + // args should have only non-scoped packages - scoped packages are not yet supported + assert.deepEqual(args, ["foo"]); + } + executeCommand(this, host, ["foo"], [], requestKind, cb); + } + })(); + const projectService = createProjectService(host, { typingsInstaller: installer }); + projectService.openClientFile(f1.path); + projectService.checkNumberOfProjects({ inferredProjects: 1 }); + + const proj = projectService.inferredProjects[0]; + proj.updateGraph(); + + assert.deepEqual( + proj.getCachedUnresolvedImportsPerFile_TestOnly().get(f1.path), + ["foo", "foo", "foo", "@bar/router", "@bar/common", "@bar/common"] + ); + + installer.installAll([TI.NpmViewRequest], [TI.NpmInstallRequest]); + }); + + it("cached unresolved typings are not recomputed if program structure did not change", () => { + const host = createServerHost([]); + const session = createSession(host); + const f = { + path: "/a/app.js", + content: ` + import * as fs from "fs"; + import * as cmd from "commander + ` + }; + session.executeCommand({ + seq: 1, + type: "request", + command: "open", + arguments: { + file: f.path, + fileContent: f.content + } + }); + const projectService = session.getProjectService(); + checkNumberOfProjects(projectService, { inferredProjects: 1 }); + const proj = projectService.inferredProjects[0]; + const version1 = proj.getCachedUnresolvedImportsPerFile_TestOnly().getVersion(); + + // make a change that should not affect the structure of the program + session.executeCommand({ + seq: 2, + type: "request", + command: "change", + arguments: { + file: f.path, + insertString: "\nlet x = 1;", + line: 2, + offset: 0, + endLine: 2, + endOffset: 0 + } + }); + host.checkTimeoutQueueLength(1); + host.runQueuedTimeoutCallbacks(); + const version2 = proj.getCachedUnresolvedImportsPerFile_TestOnly().getVersion(); + assert.equal(version1, version2, "set of unresolved imports should not change"); + }); }); describe("Validate package name:", () => { @@ -820,4 +947,35 @@ namespace ts.projectSystem { assert.isTrue(messages.indexOf("Package name '; say ‘Hello from TypeScript!’ #' contains non URI safe characters") > 0, "should find package with invalid name"); }); }); + + describe("discover typings", () => { + it("should return node for core modules", () => { + const f = { + path: "/a/b/app.js", + content: "" + }; + const host = createServerHost([f]); + const cache = createMap(); + for (const name of JsTyping.nodeCoreModuleList) { + const result = JsTyping.discoverTypings(host, [f.path], getDirectoryPath(f.path), /*safeListPath*/ undefined, cache, { enableAutoDiscovery: true }, [name, "somename"]); + assert.deepEqual(result.newTypingNames.sort(), ["node", "somename"]); + } + }); + + it("should use cached locaitons", () => { + const f = { + path: "/a/b/app.js", + content: "" + }; + const node = { + path: "/a/b/node.d.ts", + content: "" + }; + const host = createServerHost([f, node]); + const cache = createMap({ "node": node.path }); + const result = JsTyping.discoverTypings(host, [f.path], getDirectoryPath(f.path), /*safeListPath*/ undefined, cache, { enableAutoDiscovery: true }, ["fs", "bar"]); + assert.deepEqual(result.cachedTypingPaths, [node.path]); + assert.deepEqual(result.newTypingNames, ["bar"]); + }); + }); } \ No newline at end of file diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 6556aa01460..6565940c22d 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -287,13 +287,13 @@ namespace ts.server { } switch (response.kind) { case "set": - this.typingsCache.updateTypingsForProject(response.projectName, response.compilerOptions, response.typingOptions, response.typings); - project.updateGraph(); + this.typingsCache.updateTypingsForProject(response.projectName, response.compilerOptions, response.typingOptions, response.unresolvedImports, response.typings); break; case "invalidate": - this.typingsCache.invalidateCachedTypingsForProject(project); + this.typingsCache.deleteTypingsForProject(response.projectName); break; } + project.updateGraph(); } setCompilerOptionsForInferredProjects(projectCompilerOptions: protocol.ExternalProjectCompilerOptions): void { diff --git a/src/server/lsHost.ts b/src/server/lsHost.ts index b36f73a19c5..8b882197820 100644 --- a/src/server/lsHost.ts +++ b/src/server/lsHost.ts @@ -9,6 +9,8 @@ namespace ts.server { private readonly resolvedTypeReferenceDirectives: ts.FileMap>; private readonly getCanonicalFileName: (fileName: string) => string; + private filesWithChangedSetOfUnresolvedImports: Path[]; + private readonly resolveModuleName: typeof resolveModuleName; readonly trace: (s: string) => void; @@ -52,12 +54,23 @@ namespace ts.server { }; } + public startRecordingFilesWithChangedResolutions() { + this.filesWithChangedSetOfUnresolvedImports = []; + } + + public finishRecordingFilesWithChangedResolutions() { + const collected = this.filesWithChangedSetOfUnresolvedImports; + this.filesWithChangedSetOfUnresolvedImports = undefined; + return collected; + } + private resolveNamesWithLocalCache( names: string[], containingFile: string, cache: ts.FileMap>, loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost) => T, - getResult: (s: T) => R): R[] { + getResult: (s: T) => R, + logChanges: boolean): R[] { const path = toPath(containingFile, this.host.getCurrentDirectory(), this.getCanonicalFileName); const currentResolutionsInFile = cache.get(path); @@ -79,6 +92,11 @@ namespace ts.server { else { newResolutions[name] = resolution = loader(name, containingFile, compilerOptions, this); } + if (logChanges && this.filesWithChangedSetOfUnresolvedImports && !resolutionIsEqualTo(existingResolution, resolution)) { + this.filesWithChangedSetOfUnresolvedImports.push(path); + // reset log changes to avoid recording the same file multiple times + logChanges = false; + } } ts.Debug.assert(resolution !== undefined); @@ -90,6 +108,24 @@ namespace ts.server { cache.set(path, newResolutions); return resolvedModules; + function resolutionIsEqualTo(oldResolution: T, newResolution: T): boolean { + if (oldResolution === newResolution) { + return true; + } + if (!oldResolution || !newResolution) { + return false; + } + const oldResult = getResult(oldResolution); + const newResult = getResult(newResolution); + if (oldResult === newResult) { + return true; + } + if (!oldResult || !newResult) { + return false; + } + return oldResult.resolvedFileName === newResult.resolvedFileName; + } + function moduleResolutionIsValid(resolution: T): boolean { if (!resolution) { return false; @@ -126,11 +162,11 @@ namespace ts.server { } resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[] { - return this.resolveNamesWithLocalCache(typeDirectiveNames, containingFile, this.resolvedTypeReferenceDirectives, resolveTypeReferenceDirective, m => m.resolvedTypeReferenceDirective); + return this.resolveNamesWithLocalCache(typeDirectiveNames, containingFile, this.resolvedTypeReferenceDirectives, resolveTypeReferenceDirective, m => m.resolvedTypeReferenceDirective, /*logChanges*/ false); } resolveModuleNames(moduleNames: string[], containingFile: string): ResolvedModule[] { - return this.resolveNamesWithLocalCache(moduleNames, containingFile, this.resolvedModuleNames, this.resolveModuleName, m => m.resolvedModule); + return this.resolveNamesWithLocalCache(moduleNames, containingFile, this.resolvedModuleNames, this.resolveModuleName, m => m.resolvedModule, /*logChanges*/ true); } getDefaultLibFileName() { @@ -197,10 +233,11 @@ namespace ts.server { } setCompilationSettings(opt: ts.CompilerOptions) { + if (changesAffectModuleResolution(this.compilationSettings, opt)) { + this.resolvedModuleNames.clear(); + this.resolvedTypeReferenceDirectives.clear(); + } this.compilationSettings = opt; - // conservatively assume that changing compiler options might affect module resolution strategy - this.resolvedModuleNames.clear(); - this.resolvedTypeReferenceDirectives.clear(); } } } \ No newline at end of file diff --git a/src/server/project.ts b/src/server/project.ts index dd8902e83d1..5f4f8d40490 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -62,12 +62,43 @@ namespace ts.server { projectErrors: Diagnostic[]; } + export class UnresolvedImportsMap { + readonly perFileMap = createFileMap>(); + private version = 0; + + public clear() { + this.perFileMap.clear(); + this.version = 0; + } + + public getVersion() { + return this.version; + } + + public remove(path: Path) { + this.perFileMap.remove(path); + this.version++; + } + + public get(path: Path) { + return this.perFileMap.get(path); + } + + public set(path: Path, value: ReadonlyArray) { + this.perFileMap.set(path, value); + this.version++; + } + } + export abstract class Project { private rootFiles: ScriptInfo[] = []; private rootFilesMap: FileMap = createFileMap(); private lsHost: ServerLanguageServiceHost; private program: ts.Program; + private cachedUnresolvedImportsPerFile = new UnresolvedImportsMap(); + private lastCachedUnresolvedImportsList: SortedReadonlyArray; + private languageService: LanguageService; builder: Builder; /** @@ -91,7 +122,7 @@ namespace ts.server { */ private projectStateVersion = 0; - private typingFiles: TypingsArray; + private typingFiles: SortedReadonlyArray; protected projectErrors: Diagnostic[]; @@ -107,6 +138,10 @@ namespace ts.server { return hasOneOrMoreJsAndNoTsFiles(this); } + public getCachedUnresolvedImportsPerFile_TestOnly() { + return this.cachedUnresolvedImportsPerFile; + } + constructor( readonly projectKind: ProjectKind, readonly projectService: ProjectService, @@ -326,6 +361,7 @@ namespace ts.server { removeFile(info: ScriptInfo, detachFromProject = true) { this.removeRootFileIfNecessary(info); this.lsHost.notifyFileRemoved(info); + this.cachedUnresolvedImportsPerFile.remove(info.path); if (detachFromProject) { info.detachFromProject(this); @@ -338,6 +374,38 @@ namespace ts.server { this.projectStateVersion++; } + private extractUnresolvedImportsFromSourceFile(file: SourceFile, result: string[]) { + const cached = this.cachedUnresolvedImportsPerFile.get(file.path); + if (cached) { + // found cached result - use it and return + for (const f of cached) { + result.push(f); + } + return; + } + let unresolvedImports: string[]; + if (file.resolvedModules) { + for (const name in file.resolvedModules) { + // pick unresolved non-relative names + if (!file.resolvedModules[name] && !isExternalModuleNameRelative(name)) { + // for non-scoped names extract part up-to the first slash + // for scoped names - extract up to the second slash + let trimmed = name.trim(); + let i = trimmed.indexOf("/"); + if (i !== -1 && trimmed.charCodeAt(0) === CharacterCodes.at) { + i = trimmed.indexOf("/", i + 1); + } + if (i !== -1) { + trimmed = trimmed.substr(0, i); + } + (unresolvedImports || (unresolvedImports = [])).push(trimmed); + result.push(trimmed); + } + } + } + this.cachedUnresolvedImportsPerFile.set(file.path, unresolvedImports || emptyArray); + } + /** * Updates set of files that contribute to this project * @returns: true if set of files in the project stays the same and false - otherwise. @@ -346,8 +414,35 @@ namespace ts.server { if (!this.languageServiceEnabled) { return true; } + + this.lsHost.startRecordingFilesWithChangedResolutions(); + let hasChanges = this.updateGraphWorker(); - const cachedTypings = this.projectService.typingsCache.getTypingsForProject(this, hasChanges); + + const changedFiles: ReadonlyArray = this.lsHost.finishRecordingFilesWithChangedResolutions() || emptyArray; + + for (const file of changedFiles) { + // delete cached information for changed files + this.cachedUnresolvedImportsPerFile.remove(file); + } + + // 1. no changes in structure, no changes in unresolved imports - do nothing + // 2. no changes in structure, unresolved imports were changed - collect unresolved imports for all files + // (can reuse cached imports for files that were not changed) + // 3. new files were added/removed, but compilation settings stays the same - collect unresolved imports for all new/modified files + // (can reuse cached imports for files that were not changed) + // 4. compilation settings were changed in the way that might affect module resolution - drop all caches and collect all data from the scratch + let unresolvedImports: SortedReadonlyArray; + if (hasChanges || changedFiles.length) { + const result: string[] = []; + for (const sourceFile of this.program.getSourceFiles()) { + this.extractUnresolvedImportsFromSourceFile(sourceFile, result); + } + this.lastCachedUnresolvedImportsList = toSortedReadonlyArray(result); + } + unresolvedImports = this.lastCachedUnresolvedImportsList; + + const cachedTypings = this.projectService.typingsCache.getTypingsForProject(this, unresolvedImports, hasChanges); if (this.setTypings(cachedTypings)) { hasChanges = this.updateGraphWorker() || hasChanges; } @@ -357,7 +452,7 @@ namespace ts.server { return !hasChanges; } - private setTypings(typings: TypingsArray): boolean { + private setTypings(typings: SortedReadonlyArray): boolean { if (arrayIsEqualTo(this.typingFiles, typings)) { return false; } @@ -430,6 +525,11 @@ namespace ts.server { compilerOptions.allowJs = true; } compilerOptions.allowNonTsExtensions = true; + if (changesAffectModuleResolution(this.compilerOptions, compilerOptions)) { + // reset cached unresolved imports if changes in compiler options affected module resolution + this.cachedUnresolvedImportsPerFile.clear(); + this.lastCachedUnresolvedImportsList = undefined; + } this.compilerOptions = compilerOptions; this.lsHost.setCompilationSettings(compilerOptions); diff --git a/src/server/server.ts b/src/server/server.ts index 6ddd267f56e..0ce647e3752 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -181,12 +181,15 @@ namespace ts.server { private installer: NodeChildProcess; private socket: NodeSocket; private projectService: ProjectService; + private throttledOperations: ThrottledOperations; constructor( private readonly logger: server.Logger, + host: ServerHost, eventPort: number, readonly globalTypingsCacheLocation: string, private newLine: string) { + this.throttledOperations = new ThrottledOperations(host); if (eventPort) { const s = net.connect({ port: eventPort }, () => { this.socket = s; @@ -231,12 +234,19 @@ namespace ts.server { this.installer.send({ projectName: p.getProjectName(), kind: "closeProject" }); } - enqueueInstallTypingsRequest(project: Project, typingOptions: TypingOptions): void { - const request = createInstallTypingsRequest(project, typingOptions); + enqueueInstallTypingsRequest(project: Project, typingOptions: TypingOptions, unresolvedImports: SortedReadonlyArray): void { + const request = createInstallTypingsRequest(project, typingOptions, unresolvedImports); if (this.logger.hasLevel(LogLevel.verbose)) { - this.logger.info(`Sending request: ${JSON.stringify(request)}`); + if (this.logger.hasLevel(LogLevel.verbose)) { + this.logger.info(`Scheduling throttled operation: ${JSON.stringify(request)}`); + } } - this.installer.send(request); + this.throttledOperations.schedule(project.getProjectName(), /*ms*/ 250, () => { + if (this.logger.hasLevel(LogLevel.verbose)) { + this.logger.info(`Sending request: ${JSON.stringify(request)}`); + } + this.installer.send(request); + }); } private handleMessage(response: SetTypings | InvalidateCachedTypings) { @@ -266,7 +276,7 @@ namespace ts.server { useSingleInferredProject, disableAutomaticTypingAcquisition ? nullTypingsInstaller - : new NodeTypingsInstaller(logger, installerEventPort, globalTypingsCacheLocation, host.newLine), + : new NodeTypingsInstaller(logger, host, installerEventPort, globalTypingsCacheLocation, host.newLine), Buffer.byteLength, process.hrtime, logger, diff --git a/src/server/types.d.ts b/src/server/types.d.ts index 14af59d5cd3..ec2befe8fa9 100644 --- a/src/server/types.d.ts +++ b/src/server/types.d.ts @@ -18,6 +18,10 @@ declare namespace ts.server { trace?(s: string): void; } + export interface SortedReadonlyArray extends ReadonlyArray { + " __sortedReadonlyArrayBrand": any; + } + export interface TypingInstallerRequest { readonly projectName: string; readonly kind: "discover" | "closeProject"; @@ -26,8 +30,9 @@ declare namespace ts.server { export interface DiscoverTypings extends TypingInstallerRequest { readonly fileNames: string[]; readonly projectRootPath: ts.Path; - readonly typingOptions: ts.TypingOptions; readonly compilerOptions: ts.CompilerOptions; + readonly typingOptions: ts.TypingOptions; + readonly unresolvedImports: SortedReadonlyArray; readonly cachePath?: string; readonly kind: "discover"; } @@ -36,20 +41,23 @@ declare namespace ts.server { readonly kind: "closeProject"; } + export type SetRequest = "set"; + export type InvalidateRequest = "invalidate"; export interface TypingInstallerResponse { readonly projectName: string; - readonly kind: "set" | "invalidate"; + readonly kind: SetRequest | InvalidateRequest; } export interface SetTypings extends TypingInstallerResponse { readonly typingOptions: ts.TypingOptions; readonly compilerOptions: ts.CompilerOptions; readonly typings: string[]; - readonly kind: "set"; + readonly unresolvedImports: SortedReadonlyArray; + readonly kind: SetRequest; } export interface InvalidateCachedTypings extends TypingInstallerResponse { - readonly kind: "invalidate"; + readonly kind: InvalidateRequest; } export interface InstallTypingHost extends JsTyping.TypingResolutionHost { diff --git a/src/server/typingsCache.ts b/src/server/typingsCache.ts index 04c321b46f9..9013622e24a 100644 --- a/src/server/typingsCache.ts +++ b/src/server/typingsCache.ts @@ -2,7 +2,7 @@ namespace ts.server { export interface ITypingsInstaller { - enqueueInstallTypingsRequest(p: Project, typingOptions: TypingOptions): void; + enqueueInstallTypingsRequest(p: Project, typingOptions: TypingOptions, unresolvedImports: SortedReadonlyArray): void; attach(projectService: ProjectService): void; onProjectClosed(p: Project): void; readonly globalTypingsCacheLocation: string; @@ -18,7 +18,9 @@ namespace ts.server { class TypingsCacheEntry { readonly typingOptions: TypingOptions; readonly compilerOptions: CompilerOptions; - readonly typings: TypingsArray; + readonly typings: SortedReadonlyArray; + readonly unresolvedImports: SortedReadonlyArray; + /* mainly useful for debugging */ poisoned: boolean; } @@ -61,13 +63,11 @@ namespace ts.server { return opt1.allowJs != opt2.allowJs; } - export interface TypingsArray extends ReadonlyArray { - " __typingsArrayBrand": any; - } - - function toTypingsArray(arr: string[]): TypingsArray { - arr.sort(); - return arr; + function unresolvedImportsChanged(imports1: SortedReadonlyArray, imports2: SortedReadonlyArray): boolean { + if (imports1 === imports2) { + return false; + } + return !arrayIsEqualTo(imports1, imports2); } export class TypingsCache { @@ -76,7 +76,7 @@ namespace ts.server { constructor(private readonly installer: ITypingsInstaller) { } - getTypingsForProject(project: Project, forceRefresh: boolean): TypingsArray { + getTypingsForProject(project: Project, unresolvedImports: SortedReadonlyArray, forceRefresh: boolean): SortedReadonlyArray { const typingOptions = project.getTypingOptions(); if (!typingOptions || !typingOptions.enableAutoDiscovery) { @@ -84,39 +84,41 @@ namespace ts.server { } const entry = this.perProjectCache[project.getProjectName()]; - const result: TypingsArray = entry ? entry.typings : emptyArray; - if (forceRefresh || !entry || typingOptionsChanged(typingOptions, entry.typingOptions) || compilerOptionsChanged(project.getCompilerOptions(), entry.compilerOptions)) { + const result: SortedReadonlyArray = entry ? entry.typings : emptyArray; + if (forceRefresh || + !entry || + typingOptionsChanged(typingOptions, entry.typingOptions) || + compilerOptionsChanged(project.getCompilerOptions(), entry.compilerOptions) || + unresolvedImportsChanged(unresolvedImports, entry.unresolvedImports)) { // Note: entry is now poisoned since it does not really contain typings for a given combination of compiler options\typings options. // instead it acts as a placeholder to prevent issuing multiple requests this.perProjectCache[project.getProjectName()] = { compilerOptions: project.getCompilerOptions(), typingOptions, typings: result, + unresolvedImports, poisoned: true }; // something has been changed, issue a request to update typings - this.installer.enqueueInstallTypingsRequest(project, typingOptions); + this.installer.enqueueInstallTypingsRequest(project, typingOptions, unresolvedImports); } return result; } - invalidateCachedTypingsForProject(project: Project) { - const typingOptions = project.getTypingOptions(); - if (!typingOptions.enableAutoDiscovery) { - return; - } - this.installer.enqueueInstallTypingsRequest(project, typingOptions); - } - - updateTypingsForProject(projectName: string, compilerOptions: CompilerOptions, typingOptions: TypingOptions, newTypings: string[]) { + updateTypingsForProject(projectName: string, compilerOptions: CompilerOptions, typingOptions: TypingOptions, unresolvedImports: SortedReadonlyArray, newTypings: string[]) { this.perProjectCache[projectName] = { compilerOptions, typingOptions, - typings: toTypingsArray(newTypings), + typings: toSortedReadonlyArray(newTypings), + unresolvedImports, poisoned: false }; } + deleteTypingsForProject(projectName: string) { + delete this.perProjectCache[projectName]; + } + onProjectClosed(project: Project) { delete this.perProjectCache[project.getProjectName()]; this.installer.onProjectClosed(project); diff --git a/src/server/typingsInstaller/typingsInstaller.ts b/src/server/typingsInstaller/typingsInstaller.ts index 21f85c241a6..df043fc26ae 100644 --- a/src/server/typingsInstaller/typingsInstaller.ts +++ b/src/server/typingsInstaller/typingsInstaller.ts @@ -26,6 +26,7 @@ namespace ts.server.typingsInstaller { export enum PackageNameValidationResult { Ok, ScopedPackagesNotSupported, + EmptyName, NameTooLong, NameStartsWithDot, NameStartsWithUnderscore, @@ -38,7 +39,9 @@ namespace ts.server.typingsInstaller { * Validates package name using rules defined at https://docs.npmjs.com/files/package.json */ export function validatePackageName(packageName: string): PackageNameValidationResult { - Debug.assert(!!packageName, "Package name is not specified"); + if (!packageName) { + return PackageNameValidationResult.EmptyName; + } if (packageName.length > MaxPackageNameLength) { return PackageNameValidationResult.NameTooLong; } @@ -145,7 +148,8 @@ namespace ts.server.typingsInstaller { req.projectRootPath, this.safeListPath, this.packageNameToTypingLocation, - req.typingOptions); + req.typingOptions, + req.unresolvedImports); if (this.log.isEnabled()) { this.log.writeLine(`Finished typings discovery: ${JSON.stringify(discoverTypingsResult)}`); @@ -238,6 +242,9 @@ namespace ts.server.typingsInstaller { this.missingTypingsSet[typing] = true; if (this.log.isEnabled()) { switch (validationResult) { + case PackageNameValidationResult.EmptyName: + this.log.writeLine(`Package name '${typing}' cannot be empty`); + break; case PackageNameValidationResult.NameTooLong: this.log.writeLine(`Package name '${typing}' should be less than ${MaxPackageNameLength} characters`); break; @@ -397,6 +404,7 @@ namespace ts.server.typingsInstaller { typingOptions: request.typingOptions, compilerOptions: request.compilerOptions, typings, + unresolvedImports: request.unresolvedImports, kind: "set" }; } diff --git a/src/server/utilities.ts b/src/server/utilities.ts index 5bd3423e570..8806b759e3f 100644 --- a/src/server/utilities.ts +++ b/src/server/utilities.ts @@ -45,12 +45,13 @@ namespace ts.server { } } - export function createInstallTypingsRequest(project: Project, typingOptions: TypingOptions, cachePath?: string): DiscoverTypings { + export function createInstallTypingsRequest(project: Project, typingOptions: TypingOptions, unresolvedImports: SortedReadonlyArray, cachePath?: string): DiscoverTypings { return { projectName: project.getProjectName(), fileNames: project.getFileNames(), compilerOptions: project.getCompilerOptions(), typingOptions, + unresolvedImports, projectRootPath: getProjectRootPath(project), cachePath, kind: "discover" @@ -209,11 +210,15 @@ namespace ts.server { export interface ServerLanguageServiceHost { setCompilationSettings(options: CompilerOptions): void; notifyFileRemoved(info: ScriptInfo): void; + startRecordingFilesWithChangedResolutions(): void; + finishRecordingFilesWithChangedResolutions(): Path[]; } export const nullLanguageServiceHost: ServerLanguageServiceHost = { setCompilationSettings: () => undefined, - notifyFileRemoved: () => undefined + notifyFileRemoved: () => undefined, + startRecordingFilesWithChangedResolutions: () => undefined, + finishRecordingFilesWithChangedResolutions: () => undefined }; export interface ProjectOptions { @@ -240,6 +245,11 @@ namespace ts.server { return `/dev/null/inferredProject${counter}*`; } + export function toSortedReadonlyArray(arr: string[]): SortedReadonlyArray { + arr.sort(); + return arr; + } + export class ThrottledOperations { private pendingTimeouts: Map = createMap(); constructor(private readonly host: ServerHost) { diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index 561e0110cda..0f635c15174 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -31,6 +31,17 @@ namespace ts.JsTyping { const EmptySafeList: Map = createMap(); + /* @internal */ + export const nodeCoreModuleList: ReadonlyArray = [ + "buffer", "querystring", "events", "http", "cluster", + "zlib", "os", "https", "punycode", "repl", "readline", + "vm", "child_process", "url", "dns", "net", + "dgram", "fs", "path", "string_decoder", "tls", + "crypto", "stream", "util", "assert", "tty", "domain", + "constants", "process", "v8", "timers", "console"]; + + const nodeCoreModules = arrayToMap(nodeCoreModuleList, x => x); + /** * @param host is the object providing I/O related operations. * @param fileNames are the file names that belong to the same project @@ -46,7 +57,8 @@ namespace ts.JsTyping { projectRootPath: Path, safeListPath: Path, packageNameToTypingLocation: Map, - typingOptions: TypingOptions): + typingOptions: TypingOptions, + unresolvedImports: ReadonlyArray): { cachedTypingPaths: string[], newTypingNames: string[], filesToWatch: string[] } { // A typing name to typing file path mapping @@ -92,6 +104,15 @@ namespace ts.JsTyping { } getTypingNamesFromSourceFileNames(fileNames); + // add typings for unresolved imports + if (unresolvedImports) { + for (const moduleId of unresolvedImports) { + const typingName = moduleId in nodeCoreModules ? "node" : moduleId; + if (!(typingName in inferredTypings)) { + inferredTypings[typingName] = undefined; + } + } + } // Add the cached typing locations for inferred typings that are already installed for (const name in packageNameToTypingLocation) { if (name in inferredTypings && !inferredTypings[name]) { diff --git a/src/services/shims.ts b/src/services/shims.ts index bd4c48838da..16a65928448 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -1168,7 +1168,8 @@ namespace ts { toPath(info.projectRootPath, info.projectRootPath, getCanonicalFileName), toPath(info.safeListPath, info.safeListPath, getCanonicalFileName), info.packageNameToTypingLocation, - info.typingOptions); + info.typingOptions, + info.unresolvedImports); }); } } From 19591547db59cb64eca631da2993b1c21e08f334 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Tue, 25 Oct 2016 15:48:13 -0700 Subject: [PATCH 47/49] Run tests on branch release-2.1 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c16ae086448..2751337c708 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ matrix: branches: only: - master - - release-2.0 + - release-2.1 install: - npm uninstall typescript From f6b82d5e9ff4ee5a9e2188536b9d6dcf57d3c893 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Tue, 25 Oct 2016 16:36:02 -0700 Subject: [PATCH 48/49] add a fallback logic for older versions of node that don't support 'homedir' (#11845) * add a fallback logic for older versions of node that don't support 'homedir' * try os.homedir first --- src/server/server.ts | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/server/server.ts b/src/server/server.ts index 4bab628f9a5..5057a13dbd1 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -14,21 +14,33 @@ namespace ts.server { } = require("child_process"); const os: { - homedir(): string + homedir?(): string; + tmpdir(): string; } = require("os"); - function getGlobalTypingsCacheLocation() { let basePath: string; switch (process.platform) { case "win32": - basePath = process.env.LOCALAPPDATA || process.env.APPDATA || os.homedir(); + basePath = process.env.LOCALAPPDATA || + process.env.APPDATA || + (os.homedir && os.homedir()) || + process.env.USERPROFILE || + (process.env.HOMEDRIVE && process.env.HOMEPATH && normalizeSlashes(process.env.HOMEDRIVE + process.env.HOMEPATH)) || + os.tmpdir(); break; case "linux": - basePath = os.homedir(); + basePath = (os.homedir && os.homedir()) || + process.env.HOME || + ((process.env.LOGNAME || process.env.USER) && `/home/${process.env.LOGNAME || process.env.USER}`) || + os.tmpdir(); break; case "darwin": - basePath = combinePaths(os.homedir(), "Library/Application Support/"); + const homeDir = (os.homedir && os.homedir()) || + process.env.HOME || + ((process.env.LOGNAME || process.env.USER) && `/Users/${process.env.LOGNAME || process.env.USER}`) || + os.tmpdir(); + basePath = combinePaths(homeDir, "Library/Application Support/"); break; } From 8ad68adac956eafa865b78c9c45cae1958c963cd Mon Sep 17 00:00:00 2001 From: Alexander Rusakov Date: Wed, 26 Oct 2016 20:01:23 +0300 Subject: [PATCH 49/49] Fix #10108 (Completion suggestion for object literal with getter) (#11808) * Fix #10108 (Completion suggestion for object literal with getter) * completions for setter --- src/services/completions.ts | 4 +++- tests/cases/fourslash/server/completions03.ts | 20 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 tests/cases/fourslash/server/completions03.ts diff --git a/src/services/completions.ts b/src/services/completions.ts index ba5d1503075..b710aa8cd78 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -1590,7 +1590,9 @@ namespace ts.Completions { if (m.kind !== SyntaxKind.PropertyAssignment && m.kind !== SyntaxKind.ShorthandPropertyAssignment && m.kind !== SyntaxKind.BindingElement && - m.kind !== SyntaxKind.MethodDeclaration) { + m.kind !== SyntaxKind.MethodDeclaration && + m.kind !== SyntaxKind.GetAccessor && + m.kind !== SyntaxKind.SetAccessor) { continue; } diff --git a/tests/cases/fourslash/server/completions03.ts b/tests/cases/fourslash/server/completions03.ts new file mode 100644 index 00000000000..ef5f1651951 --- /dev/null +++ b/tests/cases/fourslash/server/completions03.ts @@ -0,0 +1,20 @@ +/// + +// issue: https://github.com/Microsoft/TypeScript/issues/10108 + +//// interface Foo { +//// one: any; +//// two: any; +//// three: any; +//// } +//// +//// let x: Foo = { +//// get one() { return "" }, +//// set two(t) {}, +//// /**/ +//// } + +goTo.marker(""); +verify.completionListContains("three"); +verify.not.completionListContains("one"); +verify.not.completionListContains("two");