From ff43d464fc74e2617de0f8675961193a2f53a34f Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 8 Oct 2015 16:36:15 -0700 Subject: [PATCH 1/6] Add test case --- .../ambientClassDeclarationWithExtends.js | 13 ++++++++++++ ...ambientClassDeclarationWithExtends.symbols | 19 ++++++++++++++++++ .../ambientClassDeclarationWithExtends.types | 20 +++++++++++++++++++ .../ambientClassDeclarationWithExtends.ts | 8 ++++++++ 4 files changed, 60 insertions(+) diff --git a/tests/baselines/reference/ambientClassDeclarationWithExtends.js b/tests/baselines/reference/ambientClassDeclarationWithExtends.js index 5fd4d5b4b28..5a31486bcf0 100644 --- a/tests/baselines/reference/ambientClassDeclarationWithExtends.js +++ b/tests/baselines/reference/ambientClassDeclarationWithExtends.js @@ -1,6 +1,19 @@ //// [ambientClassDeclarationWithExtends.ts] declare class A { } declare class B extends A { } + +declare class C { + public foo; +} +namespace D { var x; } +declare class D extends C { } + +var d: C = new D(); //// [ambientClassDeclarationWithExtends.js] +var D; +(function (D) { + var x; +})(D || (D = {})); +var d = new D(); diff --git a/tests/baselines/reference/ambientClassDeclarationWithExtends.symbols b/tests/baselines/reference/ambientClassDeclarationWithExtends.symbols index 71d1e1a66d8..ba678910a9c 100644 --- a/tests/baselines/reference/ambientClassDeclarationWithExtends.symbols +++ b/tests/baselines/reference/ambientClassDeclarationWithExtends.symbols @@ -6,3 +6,22 @@ declare class B extends A { } >B : Symbol(B, Decl(ambientClassDeclarationWithExtends.ts, 0, 19)) >A : Symbol(A, Decl(ambientClassDeclarationWithExtends.ts, 0, 0)) +declare class C { +>C : Symbol(C, Decl(ambientClassDeclarationWithExtends.ts, 1, 29)) + + public foo; +>foo : Symbol(foo, Decl(ambientClassDeclarationWithExtends.ts, 3, 17)) +} +namespace D { var x; } +>D : Symbol(D, Decl(ambientClassDeclarationWithExtends.ts, 5, 1), Decl(ambientClassDeclarationWithExtends.ts, 6, 22)) +>x : Symbol(x, Decl(ambientClassDeclarationWithExtends.ts, 6, 17)) + +declare class D extends C { } +>D : Symbol(D, Decl(ambientClassDeclarationWithExtends.ts, 5, 1), Decl(ambientClassDeclarationWithExtends.ts, 6, 22)) +>C : Symbol(C, Decl(ambientClassDeclarationWithExtends.ts, 1, 29)) + +var d: C = new D(); +>d : Symbol(d, Decl(ambientClassDeclarationWithExtends.ts, 9, 3)) +>C : Symbol(C, Decl(ambientClassDeclarationWithExtends.ts, 1, 29)) +>D : Symbol(D, Decl(ambientClassDeclarationWithExtends.ts, 5, 1), Decl(ambientClassDeclarationWithExtends.ts, 6, 22)) + diff --git a/tests/baselines/reference/ambientClassDeclarationWithExtends.types b/tests/baselines/reference/ambientClassDeclarationWithExtends.types index 7609856882b..528496e753d 100644 --- a/tests/baselines/reference/ambientClassDeclarationWithExtends.types +++ b/tests/baselines/reference/ambientClassDeclarationWithExtends.types @@ -6,3 +6,23 @@ declare class B extends A { } >B : B >A : A +declare class C { +>C : C + + public foo; +>foo : any +} +namespace D { var x; } +>D : typeof D +>x : any + +declare class D extends C { } +>D : D +>C : C + +var d: C = new D(); +>d : C +>C : C +>new D() : D +>D : typeof D + diff --git a/tests/cases/compiler/ambientClassDeclarationWithExtends.ts b/tests/cases/compiler/ambientClassDeclarationWithExtends.ts index 0c5d8d156bf..554a436c5ef 100644 --- a/tests/cases/compiler/ambientClassDeclarationWithExtends.ts +++ b/tests/cases/compiler/ambientClassDeclarationWithExtends.ts @@ -1,2 +1,10 @@ declare class A { } declare class B extends A { } + +declare class C { + public foo; +} +namespace D { var x; } +declare class D extends C { } + +var d: C = new D(); From 08c78fbe76d87d0e15ca67ac106583e9687ff9cb Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 8 Oct 2015 16:39:53 -0700 Subject: [PATCH 2/6] Check for ClassDeclaration in getBaseTypeNodeOfClass Previously, it just used valueDeclaration. Now, it searches the declarations. --- src/compiler/checker.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 551f428ca9a..86754ff93d0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2766,7 +2766,10 @@ namespace ts { } function getBaseTypeNodeOfClass(type: InterfaceType): ExpressionWithTypeArguments { - return getClassExtendsHeritageClauseElement(type.symbol.valueDeclaration); + let classDeclaration = getDeclarationOfKind(type.symbol, SyntaxKind.ClassDeclaration); + if (classDeclaration) { + return getClassExtendsHeritageClauseElement(classDeclaration as ClassLikeDeclaration); + } } function getConstructorsForTypeArguments(type: ObjectType, typeArgumentNodes: TypeNode[]): Signature[] { From 440d01f0bde14d13cc2ca054439caaadcb5009ac Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Fri, 9 Oct 2015 10:12:17 -0700 Subject: [PATCH 3/6] Fall back to valueDeclaration Fall back to `valueDeclaration` in getBaseTypeNodeOfClass when no ClassDeclaration exists. --- src/compiler/checker.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 86754ff93d0..6c233a57ef4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2766,10 +2766,9 @@ namespace ts { } function getBaseTypeNodeOfClass(type: InterfaceType): ExpressionWithTypeArguments { - let classDeclaration = getDeclarationOfKind(type.symbol, SyntaxKind.ClassDeclaration); - if (classDeclaration) { - return getClassExtendsHeritageClauseElement(classDeclaration as ClassLikeDeclaration); - } + let classDeclaration = + getDeclarationOfKind(type.symbol, SyntaxKind.ClassDeclaration) || type.symbol.valueDeclaration; + return getClassExtendsHeritageClauseElement(classDeclaration as ClassLikeDeclaration); } function getConstructorsForTypeArguments(type: ObjectType, typeArgumentNodes: TypeNode[]): Signature[] { From 9e8031cfc3f889caacd79d98951b125265ab38ff Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Fri, 9 Oct 2015 14:19:49 -0700 Subject: [PATCH 4/6] Non-namespace merges override `valueDeclaration` Instead of searching `declarations` for a class declaration, make the binder and checker merge `valueDeclaration` such that non-namespace merges always have their `valueDeclaration` win. --- src/compiler/binder.ts | 4 +- src/compiler/checker.ts | 10 ++-- .../ambientClassDeclarationWithExtends.js | 25 +++++++++- ...ambientClassDeclarationWithExtends.symbols | 49 ++++++++++++++----- .../ambientClassDeclarationWithExtends.types | 26 +++++++++- .../ambientClassDeclarationWithExtends.ts | 13 +++++ 6 files changed, 106 insertions(+), 21 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 65768332360..813a7e2cd43 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -130,7 +130,9 @@ namespace ts { symbol.members = {}; } - if (symbolFlags & SymbolFlags.Value && !symbol.valueDeclaration) { + if (symbolFlags & SymbolFlags.Value && + (!symbol.valueDeclaration || symbol.valueDeclaration.kind === SyntaxKind.ModuleDeclaration)) { + // other kinds of value declarations take precedence over modules symbol.valueDeclaration = node; } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6c233a57ef4..d22266d951f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -293,7 +293,11 @@ namespace ts { target.constEnumOnlyModule = false; } target.flags |= source.flags; - if (!target.valueDeclaration && source.valueDeclaration) target.valueDeclaration = source.valueDeclaration; + if (source.valueDeclaration && + (!target.valueDeclaration || target.valueDeclaration.kind === SyntaxKind.ModuleDeclaration)) { + // other kinds of value declarations take precedence over modules + target.valueDeclaration = source.valueDeclaration; + } forEach(source.declarations, node => { target.declarations.push(node); }); @@ -2766,9 +2770,7 @@ namespace ts { } function getBaseTypeNodeOfClass(type: InterfaceType): ExpressionWithTypeArguments { - let classDeclaration = - getDeclarationOfKind(type.symbol, SyntaxKind.ClassDeclaration) || type.symbol.valueDeclaration; - return getClassExtendsHeritageClauseElement(classDeclaration as ClassLikeDeclaration); + return getClassExtendsHeritageClauseElement(type.symbol.valueDeclaration); } function getConstructorsForTypeArguments(type: ObjectType, typeArgumentNodes: TypeNode[]): Signature[] { diff --git a/tests/baselines/reference/ambientClassDeclarationWithExtends.js b/tests/baselines/reference/ambientClassDeclarationWithExtends.js index 5a31486bcf0..940efb8899f 100644 --- a/tests/baselines/reference/ambientClassDeclarationWithExtends.js +++ b/tests/baselines/reference/ambientClassDeclarationWithExtends.js @@ -1,4 +1,6 @@ -//// [ambientClassDeclarationWithExtends.ts] +//// [tests/cases/compiler/ambientClassDeclarationWithExtends.ts] //// + +//// [ambientClassDeclarationExtends_singleFile.ts] declare class A { } declare class B extends A { } @@ -10,10 +12,29 @@ declare class D extends C { } var d: C = new D(); +//// [ambientClassDeclarationExtends_file1.ts] + +declare class E { + public bar; +} +namespace F { var y; } -//// [ambientClassDeclarationWithExtends.js] +//// [ambientClassDeclarationExtends_file2.ts] + +declare class F extends E { } +var f: E = new F(); + + +//// [ambientClassDeclarationExtends_singleFile.js] var D; (function (D) { var x; })(D || (D = {})); var d = new D(); +//// [ambientClassDeclarationExtends_file1.js] +var F; +(function (F) { + var y; +})(F || (F = {})); +//// [ambientClassDeclarationExtends_file2.js] +var f = new F(); diff --git a/tests/baselines/reference/ambientClassDeclarationWithExtends.symbols b/tests/baselines/reference/ambientClassDeclarationWithExtends.symbols index ba678910a9c..023ae6bf582 100644 --- a/tests/baselines/reference/ambientClassDeclarationWithExtends.symbols +++ b/tests/baselines/reference/ambientClassDeclarationWithExtends.symbols @@ -1,27 +1,50 @@ -=== tests/cases/compiler/ambientClassDeclarationWithExtends.ts === +=== tests/cases/compiler/ambientClassDeclarationExtends_singleFile.ts === declare class A { } ->A : Symbol(A, Decl(ambientClassDeclarationWithExtends.ts, 0, 0)) +>A : Symbol(A, Decl(ambientClassDeclarationExtends_singleFile.ts, 0, 0)) declare class B extends A { } ->B : Symbol(B, Decl(ambientClassDeclarationWithExtends.ts, 0, 19)) ->A : Symbol(A, Decl(ambientClassDeclarationWithExtends.ts, 0, 0)) +>B : Symbol(B, Decl(ambientClassDeclarationExtends_singleFile.ts, 0, 19)) +>A : Symbol(A, Decl(ambientClassDeclarationExtends_singleFile.ts, 0, 0)) declare class C { ->C : Symbol(C, Decl(ambientClassDeclarationWithExtends.ts, 1, 29)) +>C : Symbol(C, Decl(ambientClassDeclarationExtends_singleFile.ts, 1, 29)) public foo; ->foo : Symbol(foo, Decl(ambientClassDeclarationWithExtends.ts, 3, 17)) +>foo : Symbol(foo, Decl(ambientClassDeclarationExtends_singleFile.ts, 3, 17)) } namespace D { var x; } ->D : Symbol(D, Decl(ambientClassDeclarationWithExtends.ts, 5, 1), Decl(ambientClassDeclarationWithExtends.ts, 6, 22)) ->x : Symbol(x, Decl(ambientClassDeclarationWithExtends.ts, 6, 17)) +>D : Symbol(D, Decl(ambientClassDeclarationExtends_singleFile.ts, 5, 1), Decl(ambientClassDeclarationExtends_singleFile.ts, 6, 22)) +>x : Symbol(x, Decl(ambientClassDeclarationExtends_singleFile.ts, 6, 17)) declare class D extends C { } ->D : Symbol(D, Decl(ambientClassDeclarationWithExtends.ts, 5, 1), Decl(ambientClassDeclarationWithExtends.ts, 6, 22)) ->C : Symbol(C, Decl(ambientClassDeclarationWithExtends.ts, 1, 29)) +>D : Symbol(D, Decl(ambientClassDeclarationExtends_singleFile.ts, 5, 1), Decl(ambientClassDeclarationExtends_singleFile.ts, 6, 22)) +>C : Symbol(C, Decl(ambientClassDeclarationExtends_singleFile.ts, 1, 29)) var d: C = new D(); ->d : Symbol(d, Decl(ambientClassDeclarationWithExtends.ts, 9, 3)) ->C : Symbol(C, Decl(ambientClassDeclarationWithExtends.ts, 1, 29)) ->D : Symbol(D, Decl(ambientClassDeclarationWithExtends.ts, 5, 1), Decl(ambientClassDeclarationWithExtends.ts, 6, 22)) +>d : Symbol(d, Decl(ambientClassDeclarationExtends_singleFile.ts, 9, 3)) +>C : Symbol(C, Decl(ambientClassDeclarationExtends_singleFile.ts, 1, 29)) +>D : Symbol(D, Decl(ambientClassDeclarationExtends_singleFile.ts, 5, 1), Decl(ambientClassDeclarationExtends_singleFile.ts, 6, 22)) + +=== tests/cases/compiler/ambientClassDeclarationExtends_file1.ts === + +declare class E { +>E : Symbol(E, Decl(ambientClassDeclarationExtends_file1.ts, 0, 0)) + + public bar; +>bar : Symbol(bar, Decl(ambientClassDeclarationExtends_file1.ts, 1, 17)) +} +namespace F { var y; } +>F : Symbol(F, Decl(ambientClassDeclarationExtends_file1.ts, 3, 1), Decl(ambientClassDeclarationExtends_file2.ts, 0, 0)) +>y : Symbol(y, Decl(ambientClassDeclarationExtends_file1.ts, 4, 17)) + +=== tests/cases/compiler/ambientClassDeclarationExtends_file2.ts === + +declare class F extends E { } +>F : Symbol(F, Decl(ambientClassDeclarationExtends_file1.ts, 3, 1), Decl(ambientClassDeclarationExtends_file2.ts, 0, 0)) +>E : Symbol(E, Decl(ambientClassDeclarationExtends_file1.ts, 0, 0)) + +var f: E = new F(); +>f : Symbol(f, Decl(ambientClassDeclarationExtends_file2.ts, 2, 3)) +>E : Symbol(E, Decl(ambientClassDeclarationExtends_file1.ts, 0, 0)) +>F : Symbol(F, Decl(ambientClassDeclarationExtends_file1.ts, 3, 1), Decl(ambientClassDeclarationExtends_file2.ts, 0, 0)) diff --git a/tests/baselines/reference/ambientClassDeclarationWithExtends.types b/tests/baselines/reference/ambientClassDeclarationWithExtends.types index 528496e753d..c7f22eeb65f 100644 --- a/tests/baselines/reference/ambientClassDeclarationWithExtends.types +++ b/tests/baselines/reference/ambientClassDeclarationWithExtends.types @@ -1,4 +1,4 @@ -=== tests/cases/compiler/ambientClassDeclarationWithExtends.ts === +=== tests/cases/compiler/ambientClassDeclarationExtends_singleFile.ts === declare class A { } >A : A @@ -26,3 +26,27 @@ var d: C = new D(); >new D() : D >D : typeof D +=== tests/cases/compiler/ambientClassDeclarationExtends_file1.ts === + +declare class E { +>E : E + + public bar; +>bar : any +} +namespace F { var y; } +>F : typeof F +>y : any + +=== tests/cases/compiler/ambientClassDeclarationExtends_file2.ts === + +declare class F extends E { } +>F : F +>E : E + +var f: E = new F(); +>f : E +>E : E +>new F() : F +>F : typeof F + diff --git a/tests/cases/compiler/ambientClassDeclarationWithExtends.ts b/tests/cases/compiler/ambientClassDeclarationWithExtends.ts index 554a436c5ef..ca26f1b2a44 100644 --- a/tests/cases/compiler/ambientClassDeclarationWithExtends.ts +++ b/tests/cases/compiler/ambientClassDeclarationWithExtends.ts @@ -1,3 +1,4 @@ +// @Filename: ambientClassDeclarationExtends_singleFile.ts declare class A { } declare class B extends A { } @@ -8,3 +9,15 @@ namespace D { var x; } declare class D extends C { } var d: C = new D(); + +// @Filename: ambientClassDeclarationExtends_file1.ts + +declare class E { + public bar; +} +namespace F { var y; } + +// @Filename: ambientClassDeclarationExtends_file2.ts + +declare class F extends E { } +var f: E = new F(); From ec2eac53bf15f1729cbcc0c41b9928f53dd2e057 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Sun, 11 Oct 2015 15:33:17 -0700 Subject: [PATCH 5/6] Improved non-namespace overriding Per @ahejlsberg's suggestion, only overwrite a namespace `valueDeclaration` if the new declaration is not a namespace itself. This means that if there are multiple namespace declarations, and nothing else, `valueDeclaration` will be the first namespace declaration, not the last. --- src/compiler/binder.ts | 3 ++- src/compiler/checker.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 813a7e2cd43..57d610c931e 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -131,7 +131,8 @@ namespace ts { } if (symbolFlags & SymbolFlags.Value && - (!symbol.valueDeclaration || symbol.valueDeclaration.kind === SyntaxKind.ModuleDeclaration)) { + (!symbol.valueDeclaration || + (symbol.valueDeclaration.kind === SyntaxKind.ModuleDeclaration && node.kind !== SyntaxKind.ModuleDeclaration))) { // other kinds of value declarations take precedence over modules symbol.valueDeclaration = node; } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d22266d951f..6b3ddf9da32 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -294,7 +294,8 @@ namespace ts { } target.flags |= source.flags; if (source.valueDeclaration && - (!target.valueDeclaration || target.valueDeclaration.kind === SyntaxKind.ModuleDeclaration)) { + (!target.valueDeclaration || + (target.valueDeclaration.kind === SyntaxKind.ModuleDeclaration && source.valueDeclaration.kind !== SyntaxKind.ModuleDeclaration))) { // other kinds of value declarations take precedence over modules target.valueDeclaration = source.valueDeclaration; } From 57f7d7f9dfb05c9e5a87c208e3ab3224be400a73 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 4 Nov 2015 16:44:21 -0800 Subject: [PATCH 6/6] Reword predicate to be more readable --- src/compiler/binder.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 57d610c931e..b33f0b95fa3 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -130,12 +130,14 @@ namespace ts { symbol.members = {}; } - if (symbolFlags & SymbolFlags.Value && - (!symbol.valueDeclaration || - (symbol.valueDeclaration.kind === SyntaxKind.ModuleDeclaration && node.kind !== SyntaxKind.ModuleDeclaration))) { - // other kinds of value declarations take precedence over modules - symbol.valueDeclaration = node; - } + if (symbolFlags & SymbolFlags.Value) { + const valueDeclaration = symbol.valueDeclaration; + if (!valueDeclaration || + (valueDeclaration.kind !== node.kind && valueDeclaration.kind === SyntaxKind.ModuleDeclaration)) { + // other kinds of value declarations take precedence over modules + symbol.valueDeclaration = node; + } + } } // Should not be called on a declaration with a computed property name,