Skip to content

Commit bb9498f

Browse files
committed
Merge remote-tracking branch 'origin/master' into pathMappingModuleResolution
2 parents 0130c23 + 4735c00 commit bb9498f

57 files changed

Lines changed: 1033 additions & 497 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/compiler/checker.ts

Lines changed: 112 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ namespace ts {
5151
const emitResolver = createResolver();
5252

5353
const undefinedSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "undefined");
54+
undefinedSymbol.declarations = [];
5455
const argumentsSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "arguments");
5556

5657
const checker: TypeChecker = {
@@ -234,6 +235,10 @@ namespace ts {
234235
ResolvedReturnType
235236
}
236237

238+
const builtinGlobals: SymbolTable = {
239+
[undefinedSymbol.name]: undefinedSymbol
240+
};
241+
237242
initializeTypeChecker();
238243

239244
return checker;
@@ -360,6 +365,24 @@ namespace ts {
360365
}
361366
}
362367

368+
function addToSymbolTable(target: SymbolTable, source: SymbolTable, message: DiagnosticMessage) {
369+
for (const id in source) {
370+
if (hasProperty(source, id)) {
371+
if (hasProperty(target, id)) {
372+
// Error on redeclarations
373+
forEach(target[id].declarations, addDeclarationDiagnostic(id, message));
374+
}
375+
else {
376+
target[id] = source[id];
377+
}
378+
}
379+
}
380+
381+
function addDeclarationDiagnostic(id: string, message: DiagnosticMessage) {
382+
return (declaration: Declaration) => diagnostics.add(createDiagnosticForNode(declaration, message, id));
383+
}
384+
}
385+
363386
function getSymbolLinks(symbol: Symbol): SymbolLinks {
364387
if (symbol.flags & SymbolFlags.Transient) return <TransientSymbol>symbol;
365388
const id = getSymbolId(symbol);
@@ -1103,39 +1126,81 @@ namespace ts {
11031126
return links.resolvedExports || (links.resolvedExports = getExportsForModule(moduleSymbol));
11041127
}
11051128

1106-
function extendExportSymbols(target: SymbolTable, source: SymbolTable) {
1129+
interface ExportCollisionTracker {
1130+
specifierText: string;
1131+
exportsWithDuplicate: ExportDeclaration[];
1132+
}
1133+
1134+
/**
1135+
* Extends one symbol table with another while collecting information on name collisions for error message generation into the `lookupTable` argument
1136+
* Not passing `lookupTable` and `exportNode` disables this collection, and just extends the tables
1137+
*/
1138+
function extendExportSymbols(target: SymbolTable, source: SymbolTable, lookupTable?: Map<ExportCollisionTracker>, exportNode?: ExportDeclaration) {
11071139
for (const id in source) {
11081140
if (id !== "default" && !hasProperty(target, id)) {
11091141
target[id] = source[id];
1142+
if (lookupTable && exportNode) {
1143+
lookupTable[id] = {
1144+
specifierText: getTextOfNode(exportNode.moduleSpecifier)
1145+
} as ExportCollisionTracker;
1146+
}
1147+
}
1148+
else if (lookupTable && exportNode && id !== "default" && hasProperty(target, id) && resolveSymbol(target[id]) !== resolveSymbol(source[id])) {
1149+
if (!lookupTable[id].exportsWithDuplicate) {
1150+
lookupTable[id].exportsWithDuplicate = [exportNode];
1151+
}
1152+
else {
1153+
lookupTable[id].exportsWithDuplicate.push(exportNode);
1154+
}
11101155
}
11111156
}
11121157
}
11131158

11141159
function getExportsForModule(moduleSymbol: Symbol): SymbolTable {
1115-
let result: SymbolTable;
11161160
const visitedSymbols: Symbol[] = [];
1117-
visit(moduleSymbol);
1118-
return result || moduleSymbol.exports;
1161+
return visit(moduleSymbol) || moduleSymbol.exports;
11191162

11201163
// The ES6 spec permits export * declarations in a module to circularly reference the module itself. For example,
11211164
// module 'a' can 'export * from "b"' and 'b' can 'export * from "a"' without error.
1122-
function visit(symbol: Symbol) {
1123-
if (symbol && symbol.flags & SymbolFlags.HasExports && !contains(visitedSymbols, symbol)) {
1124-
visitedSymbols.push(symbol);
1125-
if (symbol !== moduleSymbol) {
1126-
if (!result) {
1127-
result = cloneSymbolTable(moduleSymbol.exports);
1165+
function visit(symbol: Symbol): SymbolTable {
1166+
if (!(symbol && symbol.flags & SymbolFlags.HasExports && !contains(visitedSymbols, symbol))) {
1167+
return;
1168+
}
1169+
visitedSymbols.push(symbol);
1170+
const symbols = cloneSymbolTable(symbol.exports);
1171+
// All export * declarations are collected in an __export symbol by the binder
1172+
const exportStars = symbol.exports["__export"];
1173+
if (exportStars) {
1174+
const nestedSymbols: SymbolTable = {};
1175+
const lookupTable: Map<ExportCollisionTracker> = {};
1176+
for (const node of exportStars.declarations) {
1177+
const resolvedModule = resolveExternalModuleName(node, (node as ExportDeclaration).moduleSpecifier);
1178+
const exportedSymbols = visit(resolvedModule);
1179+
extendExportSymbols(
1180+
nestedSymbols,
1181+
exportedSymbols,
1182+
lookupTable,
1183+
node as ExportDeclaration
1184+
);
1185+
}
1186+
for (const id in lookupTable) {
1187+
const { exportsWithDuplicate } = lookupTable[id];
1188+
// It's not an error if the file with multiple `export *`s with duplicate names exports a member with that name itself
1189+
if (id === "export=" || !(exportsWithDuplicate && exportsWithDuplicate.length) || hasProperty(symbols, id)) {
1190+
continue;
11281191
}
1129-
extendExportSymbols(result, symbol.exports);
1130-
}
1131-
// All export * declarations are collected in an __export symbol by the binder
1132-
const exportStars = symbol.exports["__export"];
1133-
if (exportStars) {
1134-
for (const node of exportStars.declarations) {
1135-
visit(resolveExternalModuleName(node, (<ExportDeclaration>node).moduleSpecifier));
1192+
for (const node of exportsWithDuplicate) {
1193+
diagnostics.add(createDiagnosticForNode(
1194+
node,
1195+
Diagnostics.Module_0_has_already_exported_a_member_named_1_Consider_explicitly_re_exporting_to_resolve_the_ambiguity,
1196+
lookupTable[id].specifierText,
1197+
id
1198+
));
11361199
}
11371200
}
1201+
extendExportSymbols(symbols, nestedSymbols);
11381202
}
1203+
return symbols;
11391204
}
11401205
}
11411206

@@ -2948,10 +3013,6 @@ namespace ts {
29483013
return type.resolvedBaseConstructorType;
29493014
}
29503015

2951-
function hasClassBaseType(type: InterfaceType): boolean {
2952-
return !!forEach(getBaseTypes(type), t => !!(t.symbol.flags & SymbolFlags.Class));
2953-
}
2954-
29553016
function getBaseTypes(type: InterfaceType): ObjectType[] {
29563017
const isClass = type.symbol.flags & SymbolFlags.Class;
29573018
const isInterface = type.symbol.flags & SymbolFlags.Interface;
@@ -3385,11 +3446,11 @@ namespace ts {
33853446
}
33863447

33873448
function getDefaultConstructSignatures(classType: InterfaceType): Signature[] {
3388-
if (!hasClassBaseType(classType)) {
3389-
return [createSignature(undefined, classType.localTypeParameters, emptyArray, classType, undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false)];
3390-
}
33913449
const baseConstructorType = getBaseConstructorTypeOfClass(classType);
33923450
const baseSignatures = getSignaturesOfType(baseConstructorType, SignatureKind.Construct);
3451+
if (baseSignatures.length === 0) {
3452+
return [createSignature(undefined, classType.localTypeParameters, emptyArray, classType, undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false)];
3453+
}
33933454
const baseTypeNode = getBaseTypeNodeOfClass(classType);
33943455
const typeArguments = map(baseTypeNode.typeArguments, getTypeFromTypeNode);
33953456
const typeArgCount = typeArguments ? typeArguments.length : 0;
@@ -3607,16 +3668,16 @@ namespace ts {
36073668
return <ResolvedType>type;
36083669
}
36093670

3610-
// Return properties of an object type or an empty array for other types
3671+
/** Return properties of an object type or an empty array for other types */
36113672
function getPropertiesOfObjectType(type: Type): Symbol[] {
36123673
if (type.flags & TypeFlags.ObjectType) {
36133674
return resolveStructuredTypeMembers(<ObjectType>type).properties;
36143675
}
36153676
return emptyArray;
36163677
}
36173678

3618-
// If the given type is an object type and that type has a property by the given name,
3619-
// return the symbol for that property.Otherwise return undefined.
3679+
/** If the given type is an object type and that type has a property by the given name,
3680+
* return the symbol for that property. Otherwise return undefined. */
36203681
function getPropertyOfObjectType(type: Type, name: string): Symbol {
36213682
if (type.flags & TypeFlags.ObjectType) {
36223683
const resolved = resolveStructuredTypeMembers(<ObjectType>type);
@@ -14132,8 +14193,29 @@ namespace ts {
1413214193
const declaration = getDeclarationOfAliasSymbol(exportEqualsSymbol) || exportEqualsSymbol.valueDeclaration;
1413314194
error(declaration, Diagnostics.An_export_assignment_cannot_be_used_in_a_module_with_other_exported_elements);
1413414195
}
14196+
// Checks for export * conflicts
14197+
const exports = getExportsOfModule(moduleSymbol);
14198+
for (const id in exports) {
14199+
if (id === "__export") {
14200+
continue;
14201+
}
14202+
const { declarations, flags } = exports[id];
14203+
// ECMA262: 15.2.1.1 It is a Syntax Error if the ExportedNames of ModuleItemList contains any duplicate entries. (TS Exceptions: namespaces, function overloads, enums, and interfaces)
14204+
if (!(flags & (SymbolFlags.Namespace | SymbolFlags.Interface | SymbolFlags.Enum)) && declarations.length > 1) {
14205+
const exportedDeclarations: Declaration[] = filter(declarations, isNotOverload);
14206+
if (exportedDeclarations.length > 1) {
14207+
for (const declaration of exportedDeclarations) {
14208+
diagnostics.add(createDiagnosticForNode(declaration, Diagnostics.Cannot_redeclare_exported_variable_0, id));
14209+
}
14210+
}
14211+
}
14212+
}
1413514213
links.exportsChecked = true;
1413614214
}
14215+
14216+
function isNotOverload(declaration: Declaration): boolean {
14217+
return declaration.kind !== SyntaxKind.FunctionDeclaration || !!(declaration as FunctionDeclaration).body;
14218+
}
1413714219
}
1413814220

1413914221
function checkTypePredicate(node: TypePredicateNode) {
@@ -15268,10 +15350,12 @@ namespace ts {
1526815350
}
1526915351
});
1527015352

15353+
// Setup global builtins
15354+
addToSymbolTable(globals, builtinGlobals, Diagnostics.Declaration_name_conflicts_with_built_in_global_identifier_0);
15355+
1527115356
getSymbolLinks(undefinedSymbol).type = undefinedType;
1527215357
getSymbolLinks(argumentsSymbol).type = getGlobalType("IArguments");
1527315358
getSymbolLinks(unknownSymbol).type = unknownType;
15274-
globals[undefinedSymbol.name] = undefinedSymbol;
1527515359

1527615360
// Initialize special types
1527715361
globalArrayType = <GenericType>getGlobalType("Array", /*arity*/ 1);

src/compiler/diagnosticMessages.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,10 @@
844844
"category": "Error",
845845
"code": 2307
846846
},
847+
"Module {0} has already exported a member named '{1}'. Consider explicitly re-exporting to resolve the ambiguity.": {
848+
"category": "Error",
849+
"code": 2308
850+
},
847851
"An export assignment cannot be used in a module with other exported elements.": {
848852
"category": "Error",
849853
"code": 2309
@@ -900,6 +904,10 @@
900904
"category": "Error",
901905
"code": 2322
902906
},
907+
"Cannot redeclare exported variable '{0}'.": {
908+
"category": "Error",
909+
"code": 2323
910+
},
903911
"Property '{0}' is missing in type '{1}'.": {
904912
"category": "Error",
905913
"code": 2324
@@ -1180,6 +1188,10 @@
11801188
"category": "Error",
11811189
"code": 2396
11821190
},
1191+
"Declaration name conflicts with built-in global identifier '{0}'.": {
1192+
"category": "Error",
1193+
"code": 2397
1194+
},
11831195
"Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.": {
11841196
"category": "Error",
11851197
"code": 2399

src/compiler/emitter.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5795,9 +5795,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
57955795

57965796
if (!shouldHoistDeclarationInSystemJsModule(node)) {
57975797
// do not emit var if variable was already hoisted
5798-
if (!(node.flags & NodeFlags.Export) || isES6ExportedDeclaration(node)) {
5798+
5799+
const isES6ExportedEnum = isES6ExportedDeclaration(node);
5800+
if (!(node.flags & NodeFlags.Export) || (isES6ExportedEnum && isFirstDeclarationOfKind(node, node.symbol && node.symbol.declarations, SyntaxKind.EnumDeclaration))) {
57995801
emitStart(node);
5800-
if (isES6ExportedDeclaration(node)) {
5802+
if (isES6ExportedEnum) {
58015803
write("export ");
58025804
}
58035805
write("var ");
@@ -5894,6 +5896,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
58945896
return languageVersion === ScriptTarget.ES6 && !!(resolver.getNodeCheckFlags(node) & NodeCheckFlags.LexicalModuleMergesWithClass);
58955897
}
58965898

5899+
function isFirstDeclarationOfKind(node: Declaration, declarations: Declaration[], kind: SyntaxKind) {
5900+
return !forEach(declarations, declaration => declaration.kind === kind && declaration.pos < node.pos);
5901+
}
5902+
58975903
function emitModuleDeclaration(node: ModuleDeclaration) {
58985904
// Emit only if this module is non-ambient.
58995905
const shouldEmit = shouldEmitModuleDeclaration(node);
@@ -5905,15 +5911,18 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
59055911
const emitVarForModule = !hoistedInDeclarationScope && !isModuleMergedWithES6Class(node);
59065912

59075913
if (emitVarForModule) {
5908-
emitStart(node);
5909-
if (isES6ExportedDeclaration(node)) {
5910-
write("export ");
5914+
const isES6ExportedNamespace = isES6ExportedDeclaration(node);
5915+
if (!isES6ExportedNamespace || isFirstDeclarationOfKind(node, node.symbol && node.symbol.declarations, SyntaxKind.ModuleDeclaration)) {
5916+
emitStart(node);
5917+
if (isES6ExportedNamespace) {
5918+
write("export ");
5919+
}
5920+
write("var ");
5921+
emit(node.name);
5922+
write(";");
5923+
emitEnd(node);
5924+
writeLine();
59115925
}
5912-
write("var ");
5913-
emit(node.name);
5914-
write(";");
5915-
emitEnd(node);
5916-
writeLine();
59175926
}
59185927

59195928
emitStart(node);
@@ -7814,6 +7823,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
78147823
const shebang = getShebang(currentText);
78157824
if (shebang) {
78167825
write(shebang);
7826+
writeLine();
78177827
}
78187828
}
78197829
}

src/compiler/parser.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5310,16 +5310,17 @@ namespace ts {
53105310
}
53115311

53125312
function parseModuleSpecifier(): Expression {
5313-
// We allow arbitrary expressions here, even though the grammar only allows string
5314-
// literals. We check to ensure that it is only a string literal later in the grammar
5315-
// walker.
5316-
const result = parseExpression();
5317-
// Ensure the string being required is in our 'identifier' table. This will ensure
5318-
// that features like 'find refs' will look inside this file when search for its name.
5319-
if (result.kind === SyntaxKind.StringLiteral) {
5313+
if (token === SyntaxKind.StringLiteral) {
5314+
const result = parseLiteralNode();
53205315
internIdentifier((<LiteralExpression>result).text);
5316+
return result;
5317+
}
5318+
else {
5319+
// We allow arbitrary expressions here, even though the grammar only allows string
5320+
// literals. We check to ensure that it is only a string literal later in the grammar
5321+
// check pass.
5322+
return parseExpression();
53215323
}
5322-
return result;
53235324
}
53245325

53255326
function parseNamespaceImport(): NamespaceImport {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//// [enumExportMergingES6.ts]
2+
export enum Animals {
3+
Cat = 1
4+
}
5+
export enum Animals {
6+
Dog = 2
7+
}
8+
export enum Animals {
9+
CatDog = Cat | Dog
10+
}
11+
12+
13+
//// [enumExportMergingES6.js]
14+
export var Animals;
15+
(function (Animals) {
16+
Animals[Animals["Cat"] = 1] = "Cat";
17+
})(Animals || (Animals = {}));
18+
(function (Animals) {
19+
Animals[Animals["Dog"] = 2] = "Dog";
20+
})(Animals || (Animals = {}));
21+
(function (Animals) {
22+
Animals[Animals["CatDog"] = 3] = "CatDog";
23+
})(Animals || (Animals = {}));
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
=== tests/cases/conformance/enums/enumExportMergingES6.ts ===
2+
export enum Animals {
3+
>Animals : Symbol(Animals, Decl(enumExportMergingES6.ts, 0, 0), Decl(enumExportMergingES6.ts, 2, 1), Decl(enumExportMergingES6.ts, 5, 1))
4+
5+
Cat = 1
6+
>Cat : Symbol(Animals.Cat, Decl(enumExportMergingES6.ts, 0, 21))
7+
}
8+
export enum Animals {
9+
>Animals : Symbol(Animals, Decl(enumExportMergingES6.ts, 0, 0), Decl(enumExportMergingES6.ts, 2, 1), Decl(enumExportMergingES6.ts, 5, 1))
10+
11+
Dog = 2
12+
>Dog : Symbol(Animals.Dog, Decl(enumExportMergingES6.ts, 3, 21))
13+
}
14+
export enum Animals {
15+
>Animals : Symbol(Animals, Decl(enumExportMergingES6.ts, 0, 0), Decl(enumExportMergingES6.ts, 2, 1), Decl(enumExportMergingES6.ts, 5, 1))
16+
17+
CatDog = Cat | Dog
18+
>CatDog : Symbol(Animals.CatDog, Decl(enumExportMergingES6.ts, 6, 21))
19+
>Cat : Symbol(Animals.Cat, Decl(enumExportMergingES6.ts, 0, 21))
20+
>Dog : Symbol(Animals.Dog, Decl(enumExportMergingES6.ts, 3, 21))
21+
}
22+

0 commit comments

Comments
 (0)