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

Merged Declarations for Classes and Interfaces #3333

Merged
merged 37 commits into from
Jul 2, 2015
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
813d227
classInterface: changed excludes flags
May 27, 2015
18abf47
Revert "classInterface: changed excludes flags"
Jun 1, 2015
05500f4
Added merged declarations for ambient class/interfaces
Jun 1, 2015
6b8d033
Merge branch 'master' into mergedDeclarationClassInterface
Jun 1, 2015
6c98c67
added conformance tests
Jun 1, 2015
b864df2
New Baselines for class-interface merging
Jun 1, 2015
ed46bc3
Fixed intendation typo
Jun 1, 2015
d25b910
fixed indentation
Jun 1, 2015
1f74b13
fixed style, added comment
Jun 2, 2015
2d77cbd
cleaner hasNonAmbientClass
Jun 2, 2015
edc4611
removed comma
Jun 2, 2015
a50ab3e
Remove checking in declareSymbol
Jun 2, 2015
90e1955
merge compatiblity now performed in checker.ts
Jun 2, 2015
c629c3f
deleted redundant tests
Jun 2, 2015
e4bc29e
Updated tests
Jun 2, 2015
b293da4
updated baselines
Jun 2, 2015
936aea8
fixed merge conflict
Jun 2, 2015
0917582
removed extra newlines
Jun 2, 2015
fa06f3e
fixed merge conflict.
Jun 2, 2015
fa9b6fc
fixed loops, merged baseline
Jun 3, 2015
9e1ab92
merged with master
Jun 3, 2015
f3278e2
fixed a grammatical issue
Jun 3, 2015
2b899f1
simplified check
Jun 3, 2015
29c9286
Updated error message
Jun 3, 2015
d000e01
updated baselines to reflect new error message
Jun 3, 2015
143890b
New test
Jun 3, 2015
323ce24
new baselines got mergeClassInterfaceAndModule
Jun 3, 2015
365ea3d
Check for ambient context instead of ambient flag
Jun 3, 2015
015c2c1
Merge branch 'master' into mergedDeclarationClassInterface
Jun 3, 2015
5ef426c
new baselines
Jun 3, 2015
3a3479d
New Test and Baseline
Jun 4, 2015
19b0c51
merged master
Jun 17, 2015
91e3a5c
updated baselines
Jun 18, 2015
851c7e4
fixed comment, spacing
Jun 18, 2015
4878cce
merged with master
Jul 2, 2015
c06e5eb
Update test
Jul 2, 2015
3af3177
update baselines
Jul 2, 2015
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
18 changes: 14 additions & 4 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,14 @@ namespace ts {
return node.name ? declarationNameToString(node.name) : getDeclarationName(node);
}

/**
* Declares a Symbol for the node and adds it to symbols. Reports errors for conflicting identifier names.
* @param symbolTable - The symbol table which node will be added to.
* @param parent - If node is in a class, parent denotes the parent declaration.
Copy link
Member

Choose a reason for hiding this comment

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

@JsonFreeman @CyrusNajmabadi does parent only refer to classes?

Copy link
Member

Choose a reason for hiding this comment

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

And if so, just say "denotes that class"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

Copy link
Contributor

Choose a reason for hiding this comment

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

No, it's for any member relationship. So it can be in a class, interface, object type literal, enum or module (if it's a module the member has to be exported to have a parent).

* @param node - The declaration to be added to the symbol table
* @param includes - The SymbolFlags that node has in addition to its declaration type (eg: export, ambient, etc.)
* @param excludes - The flags which node cannot be declared alongside in a symbol table. Used to report forbidden declarations.
*/
function declareSymbol(symbolTable: SymbolTable, parent: Symbol, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags): Symbol {
Debug.assert(!hasDynamicName(node));

Expand All @@ -181,10 +189,11 @@ namespace ts {

let symbol: Symbol;
if (name !== undefined) {

// Check and see if the symbol table already has a symbol with this name. If not,
// create a new symbol with this name and add it to the table. Note that we don't
// give the new symbol any flags *yet*. This ensures that it will not conflict
// witht he 'excludes' flags we pass in.
// with the 'excludes' flags we pass in.
//
// If we do get an existing symbol, see if it conflicts with the new symbol we're
// creating. For example, a 'var' symbol and a 'class' symbol will conflict within
Expand All @@ -202,11 +211,11 @@ namespace ts {
symbol = hasProperty(symbolTable, name)
? symbolTable[name]
: (symbolTable[name] = createSymbol(SymbolFlags.None, name));

Copy link
Member

Choose a reason for hiding this comment

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

Remove the leading space if you get the chance.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed.

if (name && (includes & SymbolFlags.Classifiable)) {
classifiableNames[name] = name;
}

}
if (symbol.flags & excludes) {
if (node.name) {
node.name.parent = node;
Expand Down Expand Up @@ -314,6 +323,7 @@ namespace ts {

addToContainerChain(container);
}

else if (containerFlags & ContainerFlags.IsBlockScopedContainer) {
blockScopeContainer = node;
blockScopeContainer.locals = undefined;
Expand Down
16 changes: 16 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10641,6 +10641,11 @@ namespace ts {
});
}

// Interfaces cannot be merged with non-ambient classes.
if (symbol.flags & SymbolFlags.Interface && !isInAmbientContext(node)) {
error(node, Diagnostics.Only_an_ambient_class_can_be_merged_with_an_interface)
}

forEach(node.members, checkSourceElement);
if (produceDiagnostics) {
checkIndexConstraints(type);
Expand Down Expand Up @@ -10820,13 +10825,24 @@ namespace ts {
checkIndexConstraints(type);
}
}

// Interfaces cannot merge with non-ambient classes.
if (symbol && symbol.declarations) {
for (let declaration of symbol.declarations) {
if (declaration.kind === SyntaxKind.ClassDeclaration && !isInAmbientContext(declaration)) {
error(node, Diagnostics.Only_an_ambient_class_can_be_merged_with_an_interface);
break;
}
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Same here, you can just check symbol.flags & SymbolFlags.Class.

Copy link
Contributor

Choose a reason for hiding this comment

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

Again, need to check if the class is in an ambient context

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This doesn't work for

interface I {}
declare class I {}
module I {}

Since the module occupies the value space, the blanket check looking at the symbol will trigger an error, but the merge should be able to go through.

}
forEach(getInterfaceBaseTypeNodes(node), heritageElement => {
if (!isSupportedExpressionWithTypeArguments(heritageElement)) {
error(heritageElement.expression, Diagnostics.An_interface_can_only_extend_an_identifier_Slashqualified_name_with_optional_type_arguments);
}
checkTypeReferenceNode(heritageElement);
});

forEach(node.members, checkSourceElement);

if (produceDiagnostics) {
Expand Down
1 change: 1 addition & 0 deletions src/compiler/diagnosticInformationMap.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@ namespace ts {
No_base_constructor_has_the_specified_number_of_type_arguments: { code: 2508, category: DiagnosticCategory.Error, key: "No base constructor has the specified number of type arguments." },
Base_constructor_return_type_0_is_not_a_class_or_interface_type: { code: 2509, category: DiagnosticCategory.Error, key: "Base constructor return type '{0}' is not a class or interface type." },
Base_constructors_must_all_have_the_same_return_type: { code: 2510, category: DiagnosticCategory.Error, key: "Base constructors must all have the same return type." },
Only_an_ambient_class_can_be_merged_with_an_interface: { code: 2511, category: DiagnosticCategory.Error, key: "Only an ambient class can be merged with an interface." },
Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." },
Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: { code: 4002, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using private name '{1}'." },
Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1: { code: 4004, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported interface has or is using private name '{1}'." },
Expand Down
5 changes: 4 additions & 1 deletion src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1545,7 +1545,10 @@
"category": "Error",
"code": 2510
},

"Only an ambient class can be merged with an interface.": {
"category": "Error",
"code": 2511
},
"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
"code": 4000
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1503,8 +1503,8 @@ namespace ts {
PropertyExcludes = Value,
EnumMemberExcludes = Value,
FunctionExcludes = Value & ~(Function | ValueModule),
ClassExcludes = (Value | Type) & ~ValueModule,
InterfaceExcludes = Type & ~Interface,
ClassExcludes = (Value | Type) & ~(ValueModule | Interface), // 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
ValueModuleExcludes = Value & ~(Function | Class | RegularEnum | ValueModule),
Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/augmentedTypesClass2.errors.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
tests/cases/compiler/augmentedTypesClass2.ts(4,7): error TS2300: Duplicate identifier 'c11'.
tests/cases/compiler/augmentedTypesClass2.ts(10,11): error TS2300: Duplicate identifier 'c11'.
tests/cases/compiler/augmentedTypesClass2.ts(4,7): error TS2506: Only an ambient class can be merged with an interface.
tests/cases/compiler/augmentedTypesClass2.ts(10,11): error TS2506: Only an ambient class can be merged with an interface.
tests/cases/compiler/augmentedTypesClass2.ts(16,7): error TS2300: Duplicate identifier 'c33'.
tests/cases/compiler/augmentedTypesClass2.ts(21,6): error TS2300: Duplicate identifier 'c33'.

Expand All @@ -10,15 +10,15 @@ tests/cases/compiler/augmentedTypesClass2.ts(21,6): error TS2300: Duplicate iden
// class then interface
class c11 { // error
~~~
!!! error TS2300: Duplicate identifier 'c11'.
!!! error TS2506: Only an ambient class can be merged with an interface.
foo() {
return 1;
}
}

interface c11 { // error
~~~
!!! error TS2300: Duplicate identifier 'c11'.
!!! error TS2506: Only an ambient class can be merged with an interface.
bar(): void;
}

Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/augmentedTypesInterface.errors.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
tests/cases/compiler/augmentedTypesInterface.ts(12,11): error TS2300: Duplicate identifier 'i2'.
tests/cases/compiler/augmentedTypesInterface.ts(16,7): error TS2300: Duplicate identifier 'i2'.
tests/cases/compiler/augmentedTypesInterface.ts(12,11): error TS2506: Only an ambient class can be merged with an interface.
tests/cases/compiler/augmentedTypesInterface.ts(16,7): error TS2506: Only an ambient class can be merged with an interface.
tests/cases/compiler/augmentedTypesInterface.ts(23,11): error TS2300: Duplicate identifier 'i3'.
tests/cases/compiler/augmentedTypesInterface.ts(26,6): error TS2300: Duplicate identifier 'i3'.

Expand All @@ -18,13 +18,13 @@ tests/cases/compiler/augmentedTypesInterface.ts(26,6): error TS2300: Duplicate i
// interface then class
interface i2 { // error
~~
!!! error TS2300: Duplicate identifier 'i2'.
!!! error TS2506: Only an ambient class can be merged with an interface.
foo(): void;
}

class i2 { // error
~~
!!! error TS2300: Duplicate identifier 'i2'.
!!! error TS2506: Only an ambient class can be merged with an interface.
bar() {
return 1;
}
Expand Down
11 changes: 0 additions & 11 deletions tests/baselines/reference/class1.errors.txt

This file was deleted.

10 changes: 0 additions & 10 deletions tests/baselines/reference/class1.js

This file was deleted.

11 changes: 0 additions & 11 deletions tests/baselines/reference/classAndInterface1.errors.txt

This file was deleted.

10 changes: 0 additions & 10 deletions tests/baselines/reference/classAndInterface1.js

This file was deleted.

39 changes: 39 additions & 0 deletions tests/baselines/reference/classAndInterfaceMerge.d.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
=== tests/cases/conformance/classes/classDeclarations/classAndInterfaceMerge.d.ts ===

interface C { }
>C : Symbol(C, Decl(classAndInterfaceMerge.d.ts, 0, 0), Decl(classAndInterfaceMerge.d.ts, 1, 15), Decl(classAndInterfaceMerge.d.ts, 3, 19), Decl(classAndInterfaceMerge.d.ts, 5, 15))

declare class C { }
>C : Symbol(C, Decl(classAndInterfaceMerge.d.ts, 0, 0), Decl(classAndInterfaceMerge.d.ts, 1, 15), Decl(classAndInterfaceMerge.d.ts, 3, 19), Decl(classAndInterfaceMerge.d.ts, 5, 15))

interface C { }
>C : Symbol(C, Decl(classAndInterfaceMerge.d.ts, 0, 0), Decl(classAndInterfaceMerge.d.ts, 1, 15), Decl(classAndInterfaceMerge.d.ts, 3, 19), Decl(classAndInterfaceMerge.d.ts, 5, 15))

interface C { }
>C : Symbol(C, Decl(classAndInterfaceMerge.d.ts, 0, 0), Decl(classAndInterfaceMerge.d.ts, 1, 15), Decl(classAndInterfaceMerge.d.ts, 3, 19), Decl(classAndInterfaceMerge.d.ts, 5, 15))

declare module M {
>M : Symbol(M, Decl(classAndInterfaceMerge.d.ts, 7, 15), Decl(classAndInterfaceMerge.d.ts, 20, 1))

interface C1 { }
>C1 : Symbol(C1, Decl(classAndInterfaceMerge.d.ts, 9, 18), Decl(classAndInterfaceMerge.d.ts, 11, 20), Decl(classAndInterfaceMerge.d.ts, 13, 16), Decl(classAndInterfaceMerge.d.ts, 15, 20))

class C1 { }
>C1 : Symbol(C1, Decl(classAndInterfaceMerge.d.ts, 9, 18), Decl(classAndInterfaceMerge.d.ts, 11, 20), Decl(classAndInterfaceMerge.d.ts, 13, 16), Decl(classAndInterfaceMerge.d.ts, 15, 20))

interface C1 { }
>C1 : Symbol(C1, Decl(classAndInterfaceMerge.d.ts, 9, 18), Decl(classAndInterfaceMerge.d.ts, 11, 20), Decl(classAndInterfaceMerge.d.ts, 13, 16), Decl(classAndInterfaceMerge.d.ts, 15, 20))

interface C1 { }
>C1 : Symbol(C1, Decl(classAndInterfaceMerge.d.ts, 9, 18), Decl(classAndInterfaceMerge.d.ts, 11, 20), Decl(classAndInterfaceMerge.d.ts, 13, 16), Decl(classAndInterfaceMerge.d.ts, 15, 20))

export class C2 { }
>C2 : Symbol(C2, Decl(classAndInterfaceMerge.d.ts, 17, 20), Decl(classAndInterfaceMerge.d.ts, 22, 18))
}

declare module M {
>M : Symbol(M, Decl(classAndInterfaceMerge.d.ts, 7, 15), Decl(classAndInterfaceMerge.d.ts, 20, 1))

export interface C2 { }
>C2 : Symbol(C2, Decl(classAndInterfaceMerge.d.ts, 17, 20), Decl(classAndInterfaceMerge.d.ts, 22, 18))
}
39 changes: 39 additions & 0 deletions tests/baselines/reference/classAndInterfaceMerge.d.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
=== tests/cases/conformance/classes/classDeclarations/classAndInterfaceMerge.d.ts ===

interface C { }
>C : C

declare class C { }
>C : C

interface C { }
>C : C

interface C { }
>C : C

declare module M {
>M : typeof M

interface C1 { }
>C1 : C1

class C1 { }
>C1 : C1

interface C1 { }
>C1 : C1

interface C1 { }
>C1 : C1

export class C2 { }
>C2 : C2
}

declare module M {
>M : typeof M

export interface C2 { }
>C2 : C2
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
tests/cases/conformance/classes/classDeclarations/classAndInterfaceMergeConflictingMembers.ts(2,12): error TS2300: Duplicate identifier 'x'.
tests/cases/conformance/classes/classDeclarations/classAndInterfaceMergeConflictingMembers.ts(6,5): error TS2300: Duplicate identifier 'x'.
tests/cases/conformance/classes/classDeclarations/classAndInterfaceMergeConflictingMembers.ts(10,15): error TS2300: Duplicate identifier 'x'.
tests/cases/conformance/classes/classDeclarations/classAndInterfaceMergeConflictingMembers.ts(14,5): error TS2300: Duplicate identifier 'x'.
tests/cases/conformance/classes/classDeclarations/classAndInterfaceMergeConflictingMembers.ts(18,13): error TS2300: Duplicate identifier 'x'.
tests/cases/conformance/classes/classDeclarations/classAndInterfaceMergeConflictingMembers.ts(22,5): error TS2300: Duplicate identifier 'x'.


==== tests/cases/conformance/classes/classDeclarations/classAndInterfaceMergeConflictingMembers.ts (6 errors) ====
declare class C1 {
public x : number;
~
!!! error TS2300: Duplicate identifier 'x'.
}

interface C1 {
x : number;
~
!!! error TS2300: Duplicate identifier 'x'.
}

declare class C2 {
protected x : number;
~
!!! error TS2300: Duplicate identifier 'x'.
}

interface C2 {
x : number;
~
!!! error TS2300: Duplicate identifier 'x'.
}

declare class C3 {
private x : number;
~
!!! error TS2300: Duplicate identifier 'x'.
}

interface C3 {
x : number;
~
!!! error TS2300: Duplicate identifier 'x'.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//// [classAndInterfaceMergeConflictingMembers.ts]
declare class C1 {
public x : number;
}

interface C1 {
x : number;
}

declare class C2 {
protected x : number;
}

interface C2 {
x : number;
}

declare class C3 {
private x : number;
}

interface C3 {
x : number;
}

//// [classAndInterfaceMergeConflictingMembers.js]
Loading