Skip to content

Commit a6af98e

Browse files
committed
Merge pull request #6631 from Microsoft/reservePromiseInTopLevelModule
Reserve promise in top level module
2 parents f23c35f + e4c0c00 commit a6af98e

File tree

50 files changed

+272
-235
lines changed

Some content is hidden

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

50 files changed

+272
-235
lines changed

src/compiler/checker.ts

+82-8
Original file line numberDiff line numberDiff line change
@@ -2465,10 +2465,21 @@ namespace ts {
24652465

24662466
function getDeclarationContainer(node: Node): Node {
24672467
node = getRootDeclaration(node);
2468+
while (node) {
2469+
switch (node.kind) {
2470+
case SyntaxKind.VariableDeclaration:
2471+
case SyntaxKind.VariableDeclarationList:
2472+
case SyntaxKind.ImportSpecifier:
2473+
case SyntaxKind.NamedImports:
2474+
case SyntaxKind.NamespaceImport:
2475+
case SyntaxKind.ImportClause:
2476+
node = node.parent;
2477+
break;
24682478

2469-
// Parent chain:
2470-
// VaribleDeclaration -> VariableDeclarationList -> VariableStatement -> 'Declaration Container'
2471-
return node.kind === SyntaxKind.VariableDeclaration ? node.parent.parent.parent : node.parent;
2479+
default:
2480+
return node.parent;
2481+
}
2482+
}
24722483
}
24732484

24742485
function getTypeOfPrototypeProperty(prototype: Symbol): Type {
@@ -2610,7 +2621,7 @@ namespace ts {
26102621
}
26112622
}
26122623
else if (declaration.kind === SyntaxKind.Parameter) {
2613-
// If it's a parameter, see if the parent has a jsdoc comment with an @param
2624+
// If it's a parameter, see if the parent has a jsdoc comment with an @param
26142625
// annotation.
26152626
const paramTag = getCorrespondingJSDocParameterTag(<ParameterDeclaration>declaration);
26162627
if (paramTag && paramTag.typeExpression) {
@@ -2625,7 +2636,7 @@ namespace ts {
26252636
function getTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration): Type {
26262637
if (declaration.parserContextFlags & ParserContextFlags.JavaScriptFile) {
26272638
// If this is a variable in a JavaScript file, then use the JSDoc type (if it has
2628-
// one as its type), otherwise fallback to the below standard TS codepaths to
2639+
// one as its type), otherwise fallback to the below standard TS codepaths to
26292640
// try to figure it out.
26302641
const type = getTypeForVariableLikeDeclarationFromJSDocComment(declaration);
26312642
if (type && type !== unknownType) {
@@ -4069,7 +4080,7 @@ namespace ts {
40694080
const isJSConstructSignature = isJSDocConstructSignature(declaration);
40704081
let returnType: Type = undefined;
40714082

4072-
// If this is a JSDoc construct signature, then skip the first parameter in the
4083+
// If this is a JSDoc construct signature, then skip the first parameter in the
40734084
// parameter list. The first parameter represents the return type of the construct
40744085
// signature.
40754086
for (let i = isJSConstructSignature ? 1 : 0, n = declaration.parameters.length; i < n; i++) {
@@ -4478,7 +4489,7 @@ namespace ts {
44784489
}
44794490

44804491
if (symbol.flags & SymbolFlags.Value && node.kind === SyntaxKind.JSDocTypeReference) {
4481-
// A JSDocTypeReference may have resolved to a value (as opposed to a type). In
4492+
// A JSDocTypeReference may have resolved to a value (as opposed to a type). In
44824493
// that case, the type of this reference is just the type of the value we resolved
44834494
// to.
44844495
return getTypeOfSymbol(symbol);
@@ -10488,7 +10499,7 @@ namespace ts {
1048810499

1048910500
/*
1049010501
*TypeScript Specification 1.0 (6.3) - July 2014
10491-
* An explicitly typed function whose return type isn't the Void type,
10502+
* An explicitly typed function whose return type isn't the Void type,
1049210503
* the Any type, or a union type containing the Void or Any type as a constituent
1049310504
* must have at least one return statement somewhere in its body.
1049410505
* An exception to this rule is if the function implementation consists of a single 'throw' statement.
@@ -11644,6 +11655,9 @@ namespace ts {
1164411655
checkTypeAssignableTo(iterableIteratorInstantiation, returnType, node.type);
1164511656
}
1164611657
}
11658+
else if (isAsyncFunctionLike(node)) {
11659+
checkAsyncFunctionReturnType(<FunctionLikeDeclaration>node);
11660+
}
1164711661
}
1164811662
}
1164911663

@@ -12513,6 +12527,36 @@ namespace ts {
1251312527
}
1251412528
}
1251512529

12530+
/**
12531+
* Checks that the return type provided is an instantiation of the global Promise<T> type
12532+
* and returns the awaited type of the return type.
12533+
*
12534+
* @param returnType The return type of a FunctionLikeDeclaration
12535+
* @param location The node on which to report the error.
12536+
*/
12537+
function checkCorrectPromiseType(returnType: Type, location: Node) {
12538+
if (returnType === unknownType) {
12539+
// The return type already had some other error, so we ignore and return
12540+
// the unknown type.
12541+
return unknownType;
12542+
}
12543+
12544+
const globalPromiseType = getGlobalPromiseType();
12545+
if (globalPromiseType === emptyGenericType
12546+
|| globalPromiseType === getTargetType(returnType)) {
12547+
// Either we couldn't resolve the global promise type, which would have already
12548+
// reported an error, or we could resolve it and the return type is a valid type
12549+
// reference to the global type. In either case, we return the awaited type for
12550+
// the return type.
12551+
return checkAwaitedType(returnType, location, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type);
12552+
}
12553+
12554+
// The promise type was not a valid type reference to the global promise type, so we
12555+
// report an error and return the unknown type.
12556+
error(location, Diagnostics.The_return_type_of_an_async_function_or_method_must_be_the_global_Promise_T_type);
12557+
return unknownType;
12558+
}
12559+
1251612560
/**
1251712561
* Checks the return type of an async function to ensure it is a compatible
1251812562
* Promise implementation.
@@ -12527,6 +12571,11 @@ namespace ts {
1252712571
* callable `then` signature.
1252812572
*/
1252912573
function checkAsyncFunctionReturnType(node: FunctionLikeDeclaration): Type {
12574+
if (languageVersion >= ScriptTarget.ES6) {
12575+
const returnType = getTypeFromTypeNode(node.type);
12576+
return checkCorrectPromiseType(returnType, node.type);
12577+
}
12578+
1253012579
const globalPromiseConstructorLikeType = getGlobalPromiseConstructorLikeType();
1253112580
if (globalPromiseConstructorLikeType === emptyObjectType) {
1253212581
// If we couldn't resolve the global PromiseConstructorLike type we cannot verify
@@ -12742,6 +12791,7 @@ namespace ts {
1274212791
checkCollisionWithCapturedSuperVariable(node, node.name);
1274312792
checkCollisionWithCapturedThisVariable(node, node.name);
1274412793
checkCollisionWithRequireExportsInGeneratedCode(node, node.name);
12794+
checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name);
1274512795
}
1274612796
}
1274712797

@@ -12926,6 +12976,25 @@ namespace ts {
1292612976
}
1292712977
}
1292812978

12979+
function checkCollisionWithGlobalPromiseInGeneratedCode(node: Node, name: Identifier): void {
12980+
if (!needCollisionCheckForIdentifier(node, name, "Promise")) {
12981+
return;
12982+
}
12983+
12984+
// Uninstantiated modules shouldnt do this check
12985+
if (node.kind === SyntaxKind.ModuleDeclaration && getModuleInstanceState(node) !== ModuleInstanceState.Instantiated) {
12986+
return;
12987+
}
12988+
12989+
// In case of variable declaration, node.parent is variable statement so look at the variable statement's parent
12990+
const parent = getDeclarationContainer(node);
12991+
if (parent.kind === SyntaxKind.SourceFile && isExternalOrCommonJsModule(<SourceFile>parent) && parent.flags & NodeFlags.HasAsyncFunctions) {
12992+
// If the declaration happens to be in external module, report error that Promise is a reserved identifier.
12993+
error(name, Diagnostics.Duplicate_identifier_0_Compiler_reserves_name_1_in_top_level_scope_of_a_module_containing_async_functions,
12994+
declarationNameToString(name), declarationNameToString(name));
12995+
}
12996+
}
12997+
1292912998
function checkVarDeclaredNamesNotShadowed(node: VariableDeclaration | BindingElement) {
1293012999
// - ScriptBody : StatementList
1293113000
// It is a Syntax Error if any element of the LexicallyDeclaredNames of StatementList
@@ -13104,6 +13173,7 @@ namespace ts {
1310413173
checkCollisionWithCapturedSuperVariable(node, <Identifier>node.name);
1310513174
checkCollisionWithCapturedThisVariable(node, <Identifier>node.name);
1310613175
checkCollisionWithRequireExportsInGeneratedCode(node, <Identifier>node.name);
13176+
checkCollisionWithGlobalPromiseInGeneratedCode(node, <Identifier>node.name);
1310713177
}
1310813178
}
1310913179

@@ -13870,6 +13940,7 @@ namespace ts {
1387013940
checkTypeNameIsReserved(node.name, Diagnostics.Class_name_cannot_be_0);
1387113941
checkCollisionWithCapturedThisVariable(node, node.name);
1387213942
checkCollisionWithRequireExportsInGeneratedCode(node, node.name);
13943+
checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name);
1387313944
}
1387413945
checkTypeParameters(node.typeParameters);
1387513946
checkExportsOnMergedDeclarations(node);
@@ -14376,6 +14447,7 @@ namespace ts {
1437614447
checkTypeNameIsReserved(node.name, Diagnostics.Enum_name_cannot_be_0);
1437714448
checkCollisionWithCapturedThisVariable(node, node.name);
1437814449
checkCollisionWithRequireExportsInGeneratedCode(node, node.name);
14450+
checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name);
1437914451
checkExportsOnMergedDeclarations(node);
1438014452

1438114453
computeEnumMemberValues(node);
@@ -14480,6 +14552,7 @@ namespace ts {
1448014552

1448114553
checkCollisionWithCapturedThisVariable(node, node.name);
1448214554
checkCollisionWithRequireExportsInGeneratedCode(node, node.name);
14555+
checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name);
1448314556
checkExportsOnMergedDeclarations(node);
1448414557
const symbol = getSymbolOfNode(node);
1448514558

@@ -14672,6 +14745,7 @@ namespace ts {
1467214745
function checkImportBinding(node: ImportEqualsDeclaration | ImportClause | NamespaceImport | ImportSpecifier) {
1467314746
checkCollisionWithCapturedThisVariable(node, node.name);
1467414747
checkCollisionWithRequireExportsInGeneratedCode(node, node.name);
14748+
checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name);
1467514749
checkAliasSymbol(node);
1467614750
}
1467714751

src/compiler/diagnosticMessages.json

+9-1
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,10 @@
199199
"category": "Error",
200200
"code": 1063
201201
},
202+
"The return type of an async function or method must be the global Promise<T> type.": {
203+
"category": "Error",
204+
"code": 1064
205+
},
202206
"In ambient enum declarations member initializer must be constant expression.": {
203207
"category": "Error",
204208
"code": 1066
@@ -802,7 +806,7 @@
802806
"A decorator can only decorate a method implementation, not an overload.": {
803807
"category": "Error",
804808
"code": 1249
805-
},
809+
},
806810
"'with' statements are not allowed in an async function block.": {
807811
"category": "Error",
808812
"code": 1300
@@ -1695,6 +1699,10 @@
16951699
"category": "Error",
16961700
"code": 2528
16971701
},
1702+
"Duplicate identifier '{0}'. Compiler reserves name '{1}' in top level scope of a module containing async functions.": {
1703+
"category": "Error",
1704+
"code": 2529
1705+
},
16981706
"JSX element attributes type '{0}' may not be a union type.": {
16991707
"category": "Error",
17001708
"code": 2600

src/compiler/emitter.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
320320

321321
const awaiterHelper = `
322322
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
323-
return new P(function (resolve, reject) {
323+
return new (P || (P = Promise))(function (resolve, reject) {
324324
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
325325
function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } }
326326
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
@@ -4561,11 +4561,11 @@ const _super = (function (geti, seti) {
45614561
write(", void 0, ");
45624562
}
45634563

4564-
if (promiseConstructor) {
4565-
emitEntityNameAsExpression(promiseConstructor, /*useFallback*/ false);
4564+
if (languageVersion >= ScriptTarget.ES6 || !promiseConstructor) {
4565+
write("void 0");
45664566
}
45674567
else {
4568-
write("Promise");
4568+
emitEntityNameAsExpression(promiseConstructor, /*useFallback*/ false);
45694569
}
45704570

45714571
// Emit the call to __awaiter.

tests/baselines/reference/asyncAliasReturnType_es6.errors.txt

-10
This file was deleted.

tests/baselines/reference/asyncAliasReturnType_es6.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ async function f(): PromiseAlias<void> {
66

77
//// [asyncAliasReturnType_es6.js]
88
function f() {
9-
return __awaiter(this, void 0, PromiseAlias, function* () {
9+
return __awaiter(this, void 0, void 0, function* () {
1010
});
1111
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
=== tests/cases/conformance/async/es6/asyncAliasReturnType_es6.ts ===
2+
type PromiseAlias<T> = Promise<T>;
3+
>PromiseAlias : Symbol(PromiseAlias, Decl(asyncAliasReturnType_es6.ts, 0, 0))
4+
>T : Symbol(T, Decl(asyncAliasReturnType_es6.ts, 0, 18))
5+
>Promise : Symbol(Promise, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
6+
>T : Symbol(T, Decl(asyncAliasReturnType_es6.ts, 0, 18))
7+
8+
async function f(): PromiseAlias<void> {
9+
>f : Symbol(f, Decl(asyncAliasReturnType_es6.ts, 0, 34))
10+
>PromiseAlias : Symbol(PromiseAlias, Decl(asyncAliasReturnType_es6.ts, 0, 0))
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
=== tests/cases/conformance/async/es6/asyncAliasReturnType_es6.ts ===
2+
type PromiseAlias<T> = Promise<T>;
3+
>PromiseAlias : Promise<T>
4+
>T : T
5+
>Promise : Promise<T>
6+
>T : T
7+
8+
async function f(): PromiseAlias<void> {
9+
>f : () => Promise<void>
10+
>PromiseAlias : Promise<T>
11+
}

tests/baselines/reference/asyncArrowFunction1_es6.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ var foo = async (): Promise<void> => {
44
};
55

66
//// [asyncArrowFunction1_es6.js]
7-
var foo = () => __awaiter(this, void 0, Promise, function* () {
7+
var foo = () => __awaiter(this, void 0, void 0, function* () {
88
});

tests/baselines/reference/asyncArrowFunction6_es6.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ var foo = async (a = await): Promise<void> => {
44
}
55

66
//// [asyncArrowFunction6_es6.js]
7-
var foo = (a = yield ) => __awaiter(this, void 0, Promise, function* () {
7+
var foo = (a = yield ) => __awaiter(this, void 0, void 0, function* () {
88
});

tests/baselines/reference/asyncArrowFunction7_es6.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ var bar = async (): Promise<void> => {
77
}
88

99
//// [asyncArrowFunction7_es6.js]
10-
var bar = () => __awaiter(this, void 0, Promise, function* () {
10+
var bar = () => __awaiter(this, void 0, void 0, function* () {
1111
// 'await' here is an identifier, and not an await expression.
12-
var foo = (a = yield ) => __awaiter(this, void 0, Promise, function* () {
12+
var foo = (a = yield ) => __awaiter(this, void 0, void 0, function* () {
1313
});
1414
});

tests/baselines/reference/asyncArrowFunction8_es6.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ var foo = async (): Promise<void> => {
55
}
66

77
//// [asyncArrowFunction8_es6.js]
8-
var foo = () => __awaiter(this, void 0, Promise, function* () {
8+
var foo = () => __awaiter(this, void 0, void 0, function* () {
99
var v = { [yield ]: foo };
1010
});

tests/baselines/reference/asyncArrowFunctionCapturesArguments_es6.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ class C {
1111
class C {
1212
method() {
1313
function other() { }
14-
var fn = () => __awaiter(this, arguments, Promise, function* () { return yield other.apply(this, arguments); });
14+
var fn = () => __awaiter(this, arguments, void 0, function* () { return yield other.apply(this, arguments); });
1515
}
1616
}

tests/baselines/reference/asyncArrowFunctionCapturesThis_es6.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ class C {
99
//// [asyncArrowFunctionCapturesThis_es6.js]
1010
class C {
1111
method() {
12-
var fn = () => __awaiter(this, void 0, Promise, function* () { return yield this; });
12+
var fn = () => __awaiter(this, void 0, void 0, function* () { return yield this; });
1313
}
1414
}

0 commit comments

Comments
 (0)