Skip to content

Commit

Permalink
Merge pull request #6631 from Microsoft/reservePromiseInTopLevelModule
Browse files Browse the repository at this point in the history
Reserve promise in top level module
  • Loading branch information
rbuckton committed Jan 27, 2016
2 parents f23c35f + e4c0c00 commit a6af98e
Show file tree
Hide file tree
Showing 50 changed files with 272 additions and 235 deletions.
90 changes: 82 additions & 8 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2465,10 +2465,21 @@ namespace ts {

function getDeclarationContainer(node: Node): Node {
node = getRootDeclaration(node);
while (node) {
switch (node.kind) {
case SyntaxKind.VariableDeclaration:
case SyntaxKind.VariableDeclarationList:
case SyntaxKind.ImportSpecifier:
case SyntaxKind.NamedImports:
case SyntaxKind.NamespaceImport:
case SyntaxKind.ImportClause:
node = node.parent;
break;

// Parent chain:
// VaribleDeclaration -> VariableDeclarationList -> VariableStatement -> 'Declaration Container'
return node.kind === SyntaxKind.VariableDeclaration ? node.parent.parent.parent : node.parent;
default:
return node.parent;
}
}
}

function getTypeOfPrototypeProperty(prototype: Symbol): Type {
Expand Down Expand Up @@ -2610,7 +2621,7 @@ namespace ts {
}
}
else if (declaration.kind === SyntaxKind.Parameter) {
// If it's a parameter, see if the parent has a jsdoc comment with an @param
// If it's a parameter, see if the parent has a jsdoc comment with an @param
// annotation.
const paramTag = getCorrespondingJSDocParameterTag(<ParameterDeclaration>declaration);
if (paramTag && paramTag.typeExpression) {
Expand All @@ -2625,7 +2636,7 @@ namespace ts {
function getTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration): Type {
if (declaration.parserContextFlags & ParserContextFlags.JavaScriptFile) {
// If this is a variable in a JavaScript file, then use the JSDoc type (if it has
// one as its type), otherwise fallback to the below standard TS codepaths to
// one as its type), otherwise fallback to the below standard TS codepaths to
// try to figure it out.
const type = getTypeForVariableLikeDeclarationFromJSDocComment(declaration);
if (type && type !== unknownType) {
Expand Down Expand Up @@ -4069,7 +4080,7 @@ namespace ts {
const isJSConstructSignature = isJSDocConstructSignature(declaration);
let returnType: Type = undefined;

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

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

/*
*TypeScript Specification 1.0 (6.3) - July 2014
* An explicitly typed function whose return type isn't the Void type,
* An explicitly typed function whose return type isn't the Void type,
* the Any type, or a union type containing the Void or Any type as a constituent
* must have at least one return statement somewhere in its body.
* An exception to this rule is if the function implementation consists of a single 'throw' statement.
Expand Down Expand Up @@ -11644,6 +11655,9 @@ namespace ts {
checkTypeAssignableTo(iterableIteratorInstantiation, returnType, node.type);
}
}
else if (isAsyncFunctionLike(node)) {
checkAsyncFunctionReturnType(<FunctionLikeDeclaration>node);
}
}
}

Expand Down Expand Up @@ -12513,6 +12527,36 @@ namespace ts {
}
}

/**
* Checks that the return type provided is an instantiation of the global Promise<T> type
* and returns the awaited type of the return type.
*
* @param returnType The return type of a FunctionLikeDeclaration
* @param location The node on which to report the error.
*/
function checkCorrectPromiseType(returnType: Type, location: Node) {
if (returnType === unknownType) {
// The return type already had some other error, so we ignore and return
// the unknown type.
return unknownType;
}

const globalPromiseType = getGlobalPromiseType();
if (globalPromiseType === emptyGenericType
|| globalPromiseType === getTargetType(returnType)) {
// Either we couldn't resolve the global promise type, which would have already
// reported an error, or we could resolve it and the return type is a valid type
// reference to the global type. In either case, we return the awaited type for
// the return type.
return checkAwaitedType(returnType, location, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type);
}

// The promise type was not a valid type reference to the global promise type, so we
// report an error and return the unknown type.
error(location, Diagnostics.The_return_type_of_an_async_function_or_method_must_be_the_global_Promise_T_type);
return unknownType;
}

/**
* Checks the return type of an async function to ensure it is a compatible
* Promise implementation.
Expand All @@ -12527,6 +12571,11 @@ namespace ts {
* callable `then` signature.
*/
function checkAsyncFunctionReturnType(node: FunctionLikeDeclaration): Type {
if (languageVersion >= ScriptTarget.ES6) {
const returnType = getTypeFromTypeNode(node.type);
return checkCorrectPromiseType(returnType, node.type);
}

const globalPromiseConstructorLikeType = getGlobalPromiseConstructorLikeType();
if (globalPromiseConstructorLikeType === emptyObjectType) {
// If we couldn't resolve the global PromiseConstructorLike type we cannot verify
Expand Down Expand Up @@ -12742,6 +12791,7 @@ namespace ts {
checkCollisionWithCapturedSuperVariable(node, node.name);
checkCollisionWithCapturedThisVariable(node, node.name);
checkCollisionWithRequireExportsInGeneratedCode(node, node.name);
checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name);
}
}

Expand Down Expand Up @@ -12926,6 +12976,25 @@ namespace ts {
}
}

function checkCollisionWithGlobalPromiseInGeneratedCode(node: Node, name: Identifier): void {
if (!needCollisionCheckForIdentifier(node, name, "Promise")) {
return;
}

// Uninstantiated modules shouldnt do this check
if (node.kind === SyntaxKind.ModuleDeclaration && getModuleInstanceState(node) !== ModuleInstanceState.Instantiated) {
return;
}

// In case of variable declaration, node.parent is variable statement so look at the variable statement's parent
const parent = getDeclarationContainer(node);
if (parent.kind === SyntaxKind.SourceFile && isExternalOrCommonJsModule(<SourceFile>parent) && parent.flags & NodeFlags.HasAsyncFunctions) {
// If the declaration happens to be in external module, report error that Promise is a reserved identifier.
error(name, Diagnostics.Duplicate_identifier_0_Compiler_reserves_name_1_in_top_level_scope_of_a_module_containing_async_functions,
declarationNameToString(name), declarationNameToString(name));
}
}

function checkVarDeclaredNamesNotShadowed(node: VariableDeclaration | BindingElement) {
// - ScriptBody : StatementList
// It is a Syntax Error if any element of the LexicallyDeclaredNames of StatementList
Expand Down Expand Up @@ -13104,6 +13173,7 @@ namespace ts {
checkCollisionWithCapturedSuperVariable(node, <Identifier>node.name);
checkCollisionWithCapturedThisVariable(node, <Identifier>node.name);
checkCollisionWithRequireExportsInGeneratedCode(node, <Identifier>node.name);
checkCollisionWithGlobalPromiseInGeneratedCode(node, <Identifier>node.name);
}
}

Expand Down Expand Up @@ -13870,6 +13940,7 @@ namespace ts {
checkTypeNameIsReserved(node.name, Diagnostics.Class_name_cannot_be_0);
checkCollisionWithCapturedThisVariable(node, node.name);
checkCollisionWithRequireExportsInGeneratedCode(node, node.name);
checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name);
}
checkTypeParameters(node.typeParameters);
checkExportsOnMergedDeclarations(node);
Expand Down Expand Up @@ -14376,6 +14447,7 @@ namespace ts {
checkTypeNameIsReserved(node.name, Diagnostics.Enum_name_cannot_be_0);
checkCollisionWithCapturedThisVariable(node, node.name);
checkCollisionWithRequireExportsInGeneratedCode(node, node.name);
checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name);
checkExportsOnMergedDeclarations(node);

computeEnumMemberValues(node);
Expand Down Expand Up @@ -14480,6 +14552,7 @@ namespace ts {

checkCollisionWithCapturedThisVariable(node, node.name);
checkCollisionWithRequireExportsInGeneratedCode(node, node.name);
checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name);
checkExportsOnMergedDeclarations(node);
const symbol = getSymbolOfNode(node);

Expand Down Expand Up @@ -14672,6 +14745,7 @@ namespace ts {
function checkImportBinding(node: ImportEqualsDeclaration | ImportClause | NamespaceImport | ImportSpecifier) {
checkCollisionWithCapturedThisVariable(node, node.name);
checkCollisionWithRequireExportsInGeneratedCode(node, node.name);
checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name);
checkAliasSymbol(node);
}

Expand Down
10 changes: 9 additions & 1 deletion src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,10 @@
"category": "Error",
"code": 1063
},
"The return type of an async function or method must be the global Promise<T> type.": {
"category": "Error",
"code": 1064
},
"In ambient enum declarations member initializer must be constant expression.": {
"category": "Error",
"code": 1066
Expand Down Expand Up @@ -802,7 +806,7 @@
"A decorator can only decorate a method implementation, not an overload.": {
"category": "Error",
"code": 1249
},
},
"'with' statements are not allowed in an async function block.": {
"category": "Error",
"code": 1300
Expand Down Expand Up @@ -1695,6 +1699,10 @@
"category": "Error",
"code": 2528
},
"Duplicate identifier '{0}'. Compiler reserves name '{1}' in top level scope of a module containing async functions.": {
"category": "Error",
"code": 2529
},
"JSX element attributes type '{0}' may not be a union type.": {
"category": "Error",
"code": 2600
Expand Down
8 changes: 4 additions & 4 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {

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

if (promiseConstructor) {
emitEntityNameAsExpression(promiseConstructor, /*useFallback*/ false);
if (languageVersion >= ScriptTarget.ES6 || !promiseConstructor) {
write("void 0");
}
else {
write("Promise");
emitEntityNameAsExpression(promiseConstructor, /*useFallback*/ false);
}

// Emit the call to __awaiter.
Expand Down
10 changes: 0 additions & 10 deletions tests/baselines/reference/asyncAliasReturnType_es6.errors.txt

This file was deleted.

2 changes: 1 addition & 1 deletion tests/baselines/reference/asyncAliasReturnType_es6.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ async function f(): PromiseAlias<void> {

//// [asyncAliasReturnType_es6.js]
function f() {
return __awaiter(this, void 0, PromiseAlias, function* () {
return __awaiter(this, void 0, void 0, function* () {
});
}
11 changes: 11 additions & 0 deletions tests/baselines/reference/asyncAliasReturnType_es6.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
=== tests/cases/conformance/async/es6/asyncAliasReturnType_es6.ts ===
type PromiseAlias<T> = Promise<T>;
>PromiseAlias : Symbol(PromiseAlias, Decl(asyncAliasReturnType_es6.ts, 0, 0))
>T : Symbol(T, Decl(asyncAliasReturnType_es6.ts, 0, 18))
>Promise : Symbol(Promise, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>T : Symbol(T, Decl(asyncAliasReturnType_es6.ts, 0, 18))

async function f(): PromiseAlias<void> {
>f : Symbol(f, Decl(asyncAliasReturnType_es6.ts, 0, 34))
>PromiseAlias : Symbol(PromiseAlias, Decl(asyncAliasReturnType_es6.ts, 0, 0))
}
11 changes: 11 additions & 0 deletions tests/baselines/reference/asyncAliasReturnType_es6.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
=== tests/cases/conformance/async/es6/asyncAliasReturnType_es6.ts ===
type PromiseAlias<T> = Promise<T>;
>PromiseAlias : Promise<T>
>T : T
>Promise : Promise<T>
>T : T

async function f(): PromiseAlias<void> {
>f : () => Promise<void>
>PromiseAlias : Promise<T>
}
2 changes: 1 addition & 1 deletion tests/baselines/reference/asyncArrowFunction1_es6.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ var foo = async (): Promise<void> => {
};

//// [asyncArrowFunction1_es6.js]
var foo = () => __awaiter(this, void 0, Promise, function* () {
var foo = () => __awaiter(this, void 0, void 0, function* () {
});
2 changes: 1 addition & 1 deletion tests/baselines/reference/asyncArrowFunction6_es6.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ var foo = async (a = await): Promise<void> => {
}

//// [asyncArrowFunction6_es6.js]
var foo = (a = yield ) => __awaiter(this, void 0, Promise, function* () {
var foo = (a = yield ) => __awaiter(this, void 0, void 0, function* () {
});
4 changes: 2 additions & 2 deletions tests/baselines/reference/asyncArrowFunction7_es6.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ var bar = async (): Promise<void> => {
}

//// [asyncArrowFunction7_es6.js]
var bar = () => __awaiter(this, void 0, Promise, function* () {
var bar = () => __awaiter(this, void 0, void 0, function* () {
// 'await' here is an identifier, and not an await expression.
var foo = (a = yield ) => __awaiter(this, void 0, Promise, function* () {
var foo = (a = yield ) => __awaiter(this, void 0, void 0, function* () {
});
});
2 changes: 1 addition & 1 deletion tests/baselines/reference/asyncArrowFunction8_es6.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ var foo = async (): Promise<void> => {
}

//// [asyncArrowFunction8_es6.js]
var foo = () => __awaiter(this, void 0, Promise, function* () {
var foo = () => __awaiter(this, void 0, void 0, function* () {
var v = { [yield ]: foo };
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ class C {
class C {
method() {
function other() { }
var fn = () => __awaiter(this, arguments, Promise, function* () { return yield other.apply(this, arguments); });
var fn = () => __awaiter(this, arguments, void 0, function* () { return yield other.apply(this, arguments); });
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ class C {
//// [asyncArrowFunctionCapturesThis_es6.js]
class C {
method() {
var fn = () => __awaiter(this, void 0, Promise, function* () { return yield this; });
var fn = () => __awaiter(this, void 0, void 0, function* () { return yield this; });
}
}
Loading

0 comments on commit a6af98e

Please sign in to comment.