Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow functions and ambient classes to merge #32584

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26276,8 +26276,9 @@ namespace ts {

let duplicateFunctionDeclaration = false;
let multipleConstructorImplementation = false;
let hasNonAmbientClass = false;
for (const current of declarations) {
const node = <SignatureDeclaration>current;
const node = <SignatureDeclaration | ClassDeclaration | ClassExpression>current;
const inAmbientContext = node.flags & NodeFlags.Ambient;
const inAmbientContextOrInterface = node.parent.kind === SyntaxKind.InterfaceDeclaration || node.parent.kind === SyntaxKind.TypeLiteral || inAmbientContext;
if (inAmbientContextOrInterface) {
Expand All @@ -26291,6 +26292,10 @@ namespace ts {
previousDeclaration = undefined;
}

if ((node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression) && !inAmbientContext) {
hasNonAmbientClass = true;
}

if (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature || node.kind === SyntaxKind.Constructor) {
const currentNodeFlags = getEffectiveDeclarationFlags(node, flagsToCheck);
someNodeFlags |= currentNodeFlags;
Expand Down Expand Up @@ -26339,6 +26344,16 @@ namespace ts {
});
}

if (hasNonAmbientClass && !isConstructor && symbol.flags & SymbolFlags.Function) {
// A non-ambient class cannot be an implementation for a non-constructor function/class merge
// TODO: The below just replicates our older error from when classes and functions were
weswigham marked this conversation as resolved.
Show resolved Hide resolved
// entirely unable to merge - a more helpful message like "Class declaration cannot implement overload list"
// might be warranted. :shrug:
forEach(declarations, declaration => {
addDuplicateDeclarationError(getNameOfDeclaration(declaration) || declaration, Diagnostics.Duplicate_identifier_0, symbolName(symbol), filter(declarations, d => d !== declaration));
});
}

// Abstract methods can't have an implementation -- in particular, they don't need one.
if (lastSeenNonAmbientDeclaration && !lastSeenNonAmbientDeclaration.body &&
!hasModifier(lastSeenNonAmbientDeclaration, ModifierFlags.Abstract) && !lastSeenNonAmbientDeclaration.questionToken) {
Expand Down Expand Up @@ -31650,7 +31665,7 @@ namespace ts {
if (!symbol || !(symbol.flags & SymbolFlags.Function)) {
return false;
}
return !!forEachEntry(getExportsOfSymbol(symbol), p => p.flags & SymbolFlags.Value && isPropertyAccessExpression(p.valueDeclaration));
return !!forEachEntry(getExportsOfSymbol(symbol), p => p.flags & SymbolFlags.Value && p.valueDeclaration && isPropertyAccessExpression(p.valueDeclaration));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so an ambient class sets SymbolFlags.Value too? I assume that's why we have to check p.valueDeclaration now, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ambient classes do, in fact, count as values, since they do, in fact, resolve in an expression. A declaration being ambient just means it's missing a body - doesn't change its meaning in any other way, really.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(The value without a value declaration thing shouldn't happen anymore - it's what caused me to do the "Assignment no longer implies Type or Value" in the other PR for @enum I merged last week. The reason I had to add this was because I had a namespace with no value declaration due to an assignment-caused namespace in a test, IIRC.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it sounds like you can revert if it you want, or keep it for future safety. Either way is fine with me.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather keep it - I always feel assuming valueDeclaration exists is sketchy anyway, and there's minimal overhead to checking it here.

}

function getPropertiesOfContainerFunction(node: Declaration): Symbol[] {
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3678,8 +3678,8 @@ namespace ts {
ParameterExcludes = Value,
PropertyExcludes = None,
EnumMemberExcludes = Value | Type,
FunctionExcludes = Value & ~(Function | ValueModule),
ClassExcludes = (Value | Type) & ~(ValueModule | Interface), // class-interface mergability done in checker.ts
FunctionExcludes = Value & ~(Function | ValueModule | Class),
ClassExcludes = (Value | Type) & ~(ValueModule | Interface | Function), // class-interface mergability done in checker.ts
InterfaceExcludes = Type & ~(Interface | Class),
RegularEnumExcludes = (Value | Type) & ~(RegularEnum | ValueModule), // regular enums merge only with regular enums and modules
ConstEnumExcludes = (Value | Type) & ~ConstEnum, // const enums merge only with const enums
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
=== tests/cases/compiler/ambientClassOverloadForFunction.ts ===
declare class foo{};
>foo : Symbol(foo, Decl(ambientClassOverloadForFunction.ts, 0, 0))
>foo : Symbol(foo, Decl(ambientClassOverloadForFunction.ts, 0, 20), Decl(ambientClassOverloadForFunction.ts, 0, 0))

function foo() { return null; }
>foo : Symbol(foo, Decl(ambientClassOverloadForFunction.ts, 0, 20))
>foo : Symbol(foo, Decl(ambientClassOverloadForFunction.ts, 0, 20), Decl(ambientClassOverloadForFunction.ts, 0, 0))

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ declare class foo{};
>foo : foo

function foo() { return null; }
>foo : () => any
>foo : typeof foo
>null : null

4 changes: 2 additions & 2 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2137,8 +2137,8 @@ declare namespace ts {
ParameterExcludes = 111551,
PropertyExcludes = 0,
EnumMemberExcludes = 900095,
FunctionExcludes = 111023,
ClassExcludes = 899519,
FunctionExcludes = 110991,
ClassExcludes = 899503,
InterfaceExcludes = 788872,
RegularEnumExcludes = 899327,
ConstEnumExcludes = 899967,
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2137,8 +2137,8 @@ declare namespace ts {
ParameterExcludes = 111551,
PropertyExcludes = 0,
EnumMemberExcludes = 900095,
FunctionExcludes = 111023,
ClassExcludes = 899519,
FunctionExcludes = 110991,
ClassExcludes = 899503,
InterfaceExcludes = 788872,
RegularEnumExcludes = 899327,
ConstEnumExcludes = 899967,
Expand Down
10 changes: 9 additions & 1 deletion tests/baselines/reference/augmentedTypesClass2a.errors.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
tests/cases/compiler/augmentedTypesClass2a.ts(2,7): error TS2300: Duplicate identifier 'c2'.
tests/cases/compiler/augmentedTypesClass2a.ts(2,7): error TS2300: Duplicate identifier 'c2'.
tests/cases/compiler/augmentedTypesClass2a.ts(3,10): error TS2300: Duplicate identifier 'c2'.
tests/cases/compiler/augmentedTypesClass2a.ts(3,10): error TS2300: Duplicate identifier 'c2'.
tests/cases/compiler/augmentedTypesClass2a.ts(4,5): error TS2300: Duplicate identifier 'c2'.


==== tests/cases/compiler/augmentedTypesClass2a.ts (3 errors) ====
==== tests/cases/compiler/augmentedTypesClass2a.ts (5 errors) ====
//// class then function
class c2 { public foo() { } } // error
~~
!!! error TS2300: Duplicate identifier 'c2'.
sandersn marked this conversation as resolved.
Show resolved Hide resolved
!!! related TS6203 tests/cases/compiler/augmentedTypesClass2a.ts:3:10: 'c2' was also declared here.
~~
!!! error TS2300: Duplicate identifier 'c2'.
function c2() { } // error
~~
!!! error TS2300: Duplicate identifier 'c2'.
!!! related TS6203 tests/cases/compiler/augmentedTypesClass2a.ts:2:7: 'c2' was also declared here.
~~
!!! error TS2300: Duplicate identifier 'c2'.
var c2 = () => { }
~~
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/augmentedTypesClass2a.symbols
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
=== tests/cases/compiler/augmentedTypesClass2a.ts ===
//// class then function
class c2 { public foo() { } } // error
>c2 : Symbol(c2, Decl(augmentedTypesClass2a.ts, 0, 0))
>c2 : Symbol(c2, Decl(augmentedTypesClass2a.ts, 1, 29), Decl(augmentedTypesClass2a.ts, 0, 0))
>foo : Symbol(c2.foo, Decl(augmentedTypesClass2a.ts, 1, 10))

function c2() { } // error
>c2 : Symbol(c2, Decl(augmentedTypesClass2a.ts, 1, 29))
>c2 : Symbol(c2, Decl(augmentedTypesClass2a.ts, 1, 29), Decl(augmentedTypesClass2a.ts, 0, 0))

var c2 = () => { }
>c2 : Symbol(c2, Decl(augmentedTypesClass2a.ts, 3, 3))
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/augmentedTypesClass2a.types
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class c2 { public foo() { } } // error
>foo : () => void

function c2() { } // error
>c2 : () => void
>c2 : typeof c2

var c2 = () => { }
>c2 : () => void
Expand Down
4 changes: 4 additions & 0 deletions tests/baselines/reference/augmentedTypesFunction.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,20 @@ tests/cases/compiler/augmentedTypesFunction.ts(21,6): error TS2567: Enum declara
function y3() { } // error
~~
!!! error TS2300: Duplicate identifier 'y3'.
!!! related TS6203 tests/cases/compiler/augmentedTypesFunction.ts:14:7: 'y3' was also declared here.
class y3 { } // error
~~
!!! error TS2300: Duplicate identifier 'y3'.
!!! related TS6203 tests/cases/compiler/augmentedTypesFunction.ts:13:10: 'y3' was also declared here.

function y3a() { } // error
~~~
!!! error TS2300: Duplicate identifier 'y3a'.
!!! related TS6203 tests/cases/compiler/augmentedTypesFunction.ts:17:7: 'y3a' was also declared here.
class y3a { public foo() { } } // error
~~~
!!! error TS2300: Duplicate identifier 'y3a'.
!!! related TS6203 tests/cases/compiler/augmentedTypesFunction.ts:16:10: 'y3a' was also declared here.

// function then enum
function y4() { } // error
Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/augmentedTypesFunction.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,16 @@ var y2a = () => { } // error

// function then class
function y3() { } // error
>y3 : Symbol(y3, Decl(augmentedTypesFunction.ts, 9, 19))
>y3 : Symbol(y3, Decl(augmentedTypesFunction.ts, 9, 19), Decl(augmentedTypesFunction.ts, 12, 17))

class y3 { } // error
>y3 : Symbol(y3, Decl(augmentedTypesFunction.ts, 12, 17))
>y3 : Symbol(y3, Decl(augmentedTypesFunction.ts, 9, 19), Decl(augmentedTypesFunction.ts, 12, 17))

function y3a() { } // error
>y3a : Symbol(y3a, Decl(augmentedTypesFunction.ts, 13, 12))
>y3a : Symbol(y3a, Decl(augmentedTypesFunction.ts, 13, 12), Decl(augmentedTypesFunction.ts, 15, 18))

class y3a { public foo() { } } // error
>y3a : Symbol(y3a, Decl(augmentedTypesFunction.ts, 15, 18))
>y3a : Symbol(y3a, Decl(augmentedTypesFunction.ts, 13, 12), Decl(augmentedTypesFunction.ts, 15, 18))
>foo : Symbol(y3a.foo, Decl(augmentedTypesFunction.ts, 16, 11))

// function then enum
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/augmentedTypesFunction.types
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ var y2a = () => { } // error

// function then class
function y3() { } // error
>y3 : () => void
>y3 : typeof y3

class y3 { } // error
>y3 : y3

function y3a() { } // error
>y3a : () => void
>y3a : typeof y3a

class y3a { public foo() { } } // error
>y3a : y3a
Expand Down
16 changes: 6 additions & 10 deletions tests/baselines/reference/callOnInstance.errors.txt
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
tests/cases/compiler/callOnInstance.ts(1,18): error TS2300: Duplicate identifier 'D'.
tests/cases/compiler/callOnInstance.ts(3,15): error TS2300: Duplicate identifier 'D'.
tests/cases/compiler/callOnInstance.ts(7,25): error TS2554: Expected 0 arguments, but got 1.
tests/cases/compiler/callOnInstance.ts(7,18): error TS2349: This expression is not callable.
Type 'D' has no call signatures.
tests/cases/compiler/callOnInstance.ts(10,1): error TS2349: This expression is not callable.
Type 'C' has no call signatures.


==== tests/cases/compiler/callOnInstance.ts (4 errors) ====
==== tests/cases/compiler/callOnInstance.ts (2 errors) ====
declare function D(): string; // error
~
!!! error TS2300: Duplicate identifier 'D'.

declare class D { constructor (value: number); } // error
~
!!! error TS2300: Duplicate identifier 'D'.

var s1: string = D(); // OK

var s2: string = (new D(1))();
~
!!! error TS2554: Expected 0 arguments, but got 1.
~~~~~~~~~~
!!! error TS2349: This expression is not callable.
!!! error TS2349: Type 'D' has no call signatures.

declare class C { constructor(value: number); }
(new C(1))(); // Error for calling an instance
Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/callOnInstance.symbols
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
=== tests/cases/compiler/callOnInstance.ts ===
declare function D(): string; // error
>D : Symbol(D, Decl(callOnInstance.ts, 0, 0))
>D : Symbol(D, Decl(callOnInstance.ts, 0, 0), Decl(callOnInstance.ts, 0, 29))

declare class D { constructor (value: number); } // error
>D : Symbol(D, Decl(callOnInstance.ts, 0, 29))
>D : Symbol(D, Decl(callOnInstance.ts, 0, 0), Decl(callOnInstance.ts, 0, 29))
>value : Symbol(value, Decl(callOnInstance.ts, 2, 31))

var s1: string = D(); // OK
>s1 : Symbol(s1, Decl(callOnInstance.ts, 4, 3))
>D : Symbol(D, Decl(callOnInstance.ts, 0, 0))
>D : Symbol(D, Decl(callOnInstance.ts, 0, 0), Decl(callOnInstance.ts, 0, 29))

var s2: string = (new D(1))();
>s2 : Symbol(s2, Decl(callOnInstance.ts, 6, 3))
>D : Symbol(D, Decl(callOnInstance.ts, 0, 0))
>D : Symbol(D, Decl(callOnInstance.ts, 0, 0), Decl(callOnInstance.ts, 0, 29))

declare class C { constructor(value: number); }
>C : Symbol(C, Decl(callOnInstance.ts, 6, 30))
Expand Down
10 changes: 5 additions & 5 deletions tests/baselines/reference/callOnInstance.types
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
=== tests/cases/compiler/callOnInstance.ts ===
declare function D(): string; // error
>D : () => string
>D : typeof D

declare class D { constructor (value: number); } // error
>D : D
Expand All @@ -9,14 +9,14 @@ declare class D { constructor (value: number); } // error
var s1: string = D(); // OK
>s1 : string
>D() : string
>D : () => string
>D : typeof D

var s2: string = (new D(1))();
>s2 : string
>(new D(1))() : any
>(new D(1)) : any
>new D(1) : any
>D : () => string
>(new D(1)) : D
>new D(1) : D
>D : typeof D
>1 : 1

declare class C { constructor(value: number); }
Expand Down
7 changes: 3 additions & 4 deletions tests/baselines/reference/callOverloads1.errors.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
tests/cases/compiler/callOverloads1.ts(1,7): error TS2300: Duplicate identifier 'Foo'.
tests/cases/compiler/callOverloads1.ts(9,10): error TS2300: Duplicate identifier 'Foo'.
tests/cases/compiler/callOverloads1.ts(9,10): error TS2391: Function implementation is missing or not immediately following the declaration.
tests/cases/compiler/callOverloads1.ts(13,18): error TS2554: Expected 0 arguments, but got 1.


==== tests/cases/compiler/callOverloads1.ts (4 errors) ====
==== tests/cases/compiler/callOverloads1.ts (3 errors) ====
class Foo { // error
~~~
!!! error TS2300: Duplicate identifier 'Foo'.
!!! related TS6203 tests/cases/compiler/callOverloads1.ts:9:10: 'Foo' was also declared here.
bar1() { /*WScript.Echo("bar1");*/ }

constructor(x: any) {
Expand All @@ -18,14 +18,13 @@ tests/cases/compiler/callOverloads1.ts(13,18): error TS2554: Expected 0 argument
function Foo(); // error
~~~
!!! error TS2300: Duplicate identifier 'Foo'.
!!! related TS6203 tests/cases/compiler/callOverloads1.ts:1:7: 'Foo' was also declared here.
~~~
!!! error TS2391: Function implementation is missing or not immediately following the declaration.
function F1(s:string);
function F1(a:any) { return a;}

var f1 = new Foo("hey");
~~~~~
!!! error TS2554: Expected 0 arguments, but got 1.


f1.bar1();
Expand Down
10 changes: 6 additions & 4 deletions tests/baselines/reference/callOverloads1.symbols
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
=== tests/cases/compiler/callOverloads1.ts ===
class Foo { // error
>Foo : Symbol(Foo, Decl(callOverloads1.ts, 0, 0))
>Foo : Symbol(Foo, Decl(callOverloads1.ts, 6, 1), Decl(callOverloads1.ts, 0, 0))

bar1() { /*WScript.Echo("bar1");*/ }
>bar1 : Symbol(Foo.bar1, Decl(callOverloads1.ts, 0, 11))
Expand All @@ -13,7 +13,7 @@ class Foo { // error
}

function Foo(); // error
>Foo : Symbol(Foo, Decl(callOverloads1.ts, 6, 1))
>Foo : Symbol(Foo, Decl(callOverloads1.ts, 6, 1), Decl(callOverloads1.ts, 0, 0))

function F1(s:string);
>F1 : Symbol(F1, Decl(callOverloads1.ts, 8, 15), Decl(callOverloads1.ts, 9, 22))
Expand All @@ -26,12 +26,14 @@ function F1(a:any) { return a;}

var f1 = new Foo("hey");
>f1 : Symbol(f1, Decl(callOverloads1.ts, 12, 3))
>Foo : Symbol(Foo, Decl(callOverloads1.ts, 6, 1))
>Foo : Symbol(Foo, Decl(callOverloads1.ts, 6, 1), Decl(callOverloads1.ts, 0, 0))


f1.bar1();
>f1.bar1 : Symbol(Foo.bar1, Decl(callOverloads1.ts, 0, 11))
>f1 : Symbol(f1, Decl(callOverloads1.ts, 12, 3))
>bar1 : Symbol(Foo.bar1, Decl(callOverloads1.ts, 0, 11))

Foo();
>Foo : Symbol(Foo, Decl(callOverloads1.ts, 6, 1))
>Foo : Symbol(Foo, Decl(callOverloads1.ts, 6, 1), Decl(callOverloads1.ts, 0, 0))

Loading