From 8421f735322e88f67b4b17feb2720e54c61639ea Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 5 Oct 2016 17:20:42 -0700 Subject: [PATCH 1/3] Report missing global diagnostics --- src/compiler/checker.ts | 172 +++++++++++++++++++++++++++++++++------- src/compiler/core.ts | 33 +++++++- 2 files changed, 173 insertions(+), 32 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ef428f26de9..9840df7d805 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5787,6 +5787,90 @@ namespace ts { } } + function getStaticTypeFromTypeNode(node: TypeNode) { + switch (node.kind) { + case SyntaxKind.AnyKeyword: + case SyntaxKind.JSDocAllType: + case SyntaxKind.JSDocUnknownType: + return anyType; + case SyntaxKind.StringKeyword: + return stringType; + case SyntaxKind.NumberKeyword: + return numberType; + case SyntaxKind.BooleanKeyword: + return booleanType; + case SyntaxKind.SymbolKeyword: + return esSymbolType; + case SyntaxKind.VoidKeyword: + return voidType; + case SyntaxKind.UndefinedKeyword: + return undefinedType; + case SyntaxKind.NullKeyword: + return nullType; + case SyntaxKind.NeverKeyword: + return neverType; + case SyntaxKind.JSDocNullKeyword: + return nullType; + case SyntaxKind.JSDocUndefinedKeyword: + return undefinedType; + case SyntaxKind.JSDocNeverKeyword: + return neverType; + case SyntaxKind.ThisType: + case SyntaxKind.ThisKeyword: + return getTypeFromThisTypeNode(node); + case SyntaxKind.LiteralType: + return getTypeFromLiteralTypeNode(node); + case SyntaxKind.JSDocLiteralType: + return getTypeFromLiteralTypeNode((node).literal); + case SyntaxKind.TypeReference: + case SyntaxKind.JSDocTypeReference: + return getTypeFromTypeReference(node); + case SyntaxKind.TypePredicate: + return booleanType; + case SyntaxKind.ExpressionWithTypeArguments: + return getTypeFromTypeReference(node); + case SyntaxKind.TypeQuery: + return getTypeFromTypeQueryNode(node); + case SyntaxKind.ArrayType: + case SyntaxKind.JSDocArrayType: + return getTypeFromArrayTypeNode(node); + case SyntaxKind.TupleType: + return getTypeFromTupleTypeNode(node); + case SyntaxKind.UnionType: + case SyntaxKind.JSDocUnionType: + return getTypeFromUnionTypeNode(node, aliasSymbol, aliasTypeArguments); + case SyntaxKind.IntersectionType: + return getTypeFromIntersectionTypeNode(node, aliasSymbol, aliasTypeArguments); + case SyntaxKind.ParenthesizedType: + case SyntaxKind.JSDocNullableType: + case SyntaxKind.JSDocNonNullableType: + case SyntaxKind.JSDocConstructorType: + case SyntaxKind.JSDocThisType: + case SyntaxKind.JSDocOptionalType: + return getTypeFromTypeNode((node).type); + case SyntaxKind.JSDocRecordType: + return getTypeFromTypeNode((node as JSDocRecordType).literal); + case SyntaxKind.FunctionType: + case SyntaxKind.ConstructorType: + case SyntaxKind.TypeLiteral: + case SyntaxKind.JSDocTypeLiteral: + case SyntaxKind.JSDocFunctionType: + return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node, aliasSymbol, aliasTypeArguments); + // This function assumes that an identifier or qualified name is a type expression + // Callers should first ensure this by calling isTypeNode + case SyntaxKind.Identifier: + case SyntaxKind.QualifiedName: + const symbol = getSymbolAtLocation(node); + return symbol && getDeclaredTypeOfSymbol(symbol); + case SyntaxKind.JSDocTupleType: + return getTypeFromJSDocTupleType(node); + case SyntaxKind.JSDocVariadicType: + return getTypeFromJSDocVariadicType(node); + default: + return unknownType; + } + } + function instantiateList(items: T[], mapper: TypeMapper, instantiator: (item: T, mapper: TypeMapper) => T): T[] { if (items && items.length) { const result: T[] = []; @@ -15181,31 +15265,24 @@ namespace ts { } /** - * Checks the return type of an async function to ensure it is a compatible - * Promise implementation. - * @param node The signature to check - * @param returnType The return type for the function - * @remarks - * This checks that an async function has a valid Promise-compatible return type, - * and returns the *awaited type* of the promise. An async function has a valid - * Promise-compatible return type if the resolved value of the return type has a - * construct signature that takes in an `initializer` function that in turn supplies - * a `resolve` function as one of its arguments and results in an object with a - * callable `then` signature. - */ + * Checks the return type of an async function to ensure it is a compatible + * Promise implementation. + * + * This checks that an async function has a valid Promise-compatible return type, + * and returns the *awaited type* of the promise. An async function has a valid + * Promise-compatible return type if the resolved value of the return type has a + * construct signature that takes in an `initializer` function that in turn supplies + * a `resolve` function as one of its arguments and results in an object with a + * callable `then` signature. + * + * @param node The signature to check + */ function checkAsyncFunctionReturnType(node: FunctionLikeDeclaration): Type { if (languageVersion >= ScriptTarget.ES6) { const returnType = getTypeFromTypeNode(node.type); return checkCorrectPromiseType(returnType, node.type, Diagnostics.The_return_type_of_an_async_function_or_method_must_be_the_global_Promise_T_type); } - const globalPromiseConstructorLikeType = getGlobalPromiseConstructorLikeType(); - if (globalPromiseConstructorLikeType === emptyObjectType) { - // If we couldn't resolve the global PromiseConstructorLike type we cannot verify - // compatibility with __awaiter. - return unknownType; - } - // As part of our emit for an async function, we will need to emit the entity name of // the return type annotation as an expression. To meet the necessary runtime semantics // for __awaiter, we must also check that the type of the declaration (e.g. the static @@ -15230,18 +15307,35 @@ namespace ts { // then(...): Promise; // } // - // When we get the type of the `Promise` symbol here, we get the type of the static - // side of the `Promise` class, which would be `{ new (...): Promise }`. + + const promiseName = getEntityNameFromTypeNode(node.type); + const rootName = getFirstIdentifier(promiseName); + + // Mark the root symbol as referenced. + getSymbolLinks(rootName.symbol).referenced = true; const promiseType = getTypeFromTypeNode(node.type); if (promiseType === unknownType && compilerOptions.isolatedModules) { // If we are compiling with isolatedModules, we may not be able to resolve the - // type as a value. As such, we will just return unknownType; + // type as a value. As such, we will just return unknownType. return unknownType; } + const promiseConstructorType = getStaticTypeFromTypeNode(node.type); + + const globalPromiseConstructorLikeType = getGlobalPromiseConstructorLikeType(); + if (globalPromiseConstructorLikeType === emptyObjectType) { + // If we couldn't resolve the global PromiseConstructorLike type we cannot verify + // compatibility with __awaiter. + error(node.type || node.name || node, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type); + return unknownType; + } + + // When we get the type of the `Promise` symbol here, we get the type of the static + // side of the `Promise` class, which would be `{ new (...): Promise }`. + const promiseConstructor = getNodeLinks(node.type).resolvedSymbol; - if (!promiseConstructor || !symbolIsValue(promiseConstructor)) { + if (!promiseConstructor) { // try to fall back to global promise type. const typeName = promiseConstructor ? symbolToString(promiseConstructor) @@ -15259,12 +15353,10 @@ namespace ts { } // Verify there is no local declaration that could collide with the promise constructor. - const promiseName = getEntityNameFromTypeNode(node.type); - const promiseNameOrNamespaceRoot = getFirstIdentifier(promiseName); - const rootSymbol = getSymbol(node.locals, promiseNameOrNamespaceRoot.text, SymbolFlags.Value); + const rootSymbol = getSymbol(node.locals, rootName.text, SymbolFlags.Value); if (rootSymbol) { error(rootSymbol.valueDeclaration, Diagnostics.Duplicate_identifier_0_Compiler_uses_declaration_1_to_support_async_functions, - promiseNameOrNamespaceRoot.text, + rootName.text, getFullyQualifiedName(promiseConstructor)); return unknownType; } @@ -18086,9 +18178,33 @@ namespace ts { function getDiagnosticsWorker(sourceFile: SourceFile): Diagnostic[] { throwIfNonDiagnosticsProducing(); if (sourceFile) { + // Some global diagnostics are deferred until they are needed and + // may not be reported in the firt call to getGlobalDiagnostics. + // We should catch these changes and report them. + const previousGlobalDiagnostics = diagnostics.getGlobalDiagnostics(); + const previousGlobalDiagnosticsSize = previousGlobalDiagnostics.length; + checkSourceFile(sourceFile); - return diagnostics.getDiagnostics(sourceFile.fileName); + + const semanticDiagnostics = diagnostics.getDiagnostics(sourceFile.fileName); + const currentGlobalDiagnostics = diagnostics.getGlobalDiagnostics(); + if (currentGlobalDiagnostics !== previousGlobalDiagnostics) { + // If the arrays are not the same reference, new diagnostics were added. + const deferredGlobalDiagnostics = relativeComplement(previousGlobalDiagnostics, currentGlobalDiagnostics, compareDiagnostics); + return concatenate(deferredGlobalDiagnostics, semanticDiagnostics); + } + else if (previousGlobalDiagnosticsSize === 0 && currentGlobalDiagnostics.length > 0) { + // If the arrays are the same reference, but the length has changed, a single + // new diagnostic was added as DiagnosticCollection attempts to reuse the + // same array. + return concatenate(currentGlobalDiagnostics, semanticDiagnostics); + } + + return semanticDiagnostics; } + + // Global diagnostics are always added when a file is not provided to + // getDiagnostics forEach(host.getSourceFiles(), checkSourceFile); return diagnostics.getDiagnostics(); } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index e63bcbf8474..439e5609566 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -400,8 +400,8 @@ namespace ts { } export function concatenate(array1: T[], array2: T[]): T[] { - if (!array2 || !array2.length) return array1; - if (!array1 || !array1.length) return array2; + if (isEmptyArray(array2)) return array1; + if (isEmptyArray(array1)) return array2; return [...array1, ...array2]; } @@ -443,6 +443,27 @@ namespace ts { return result || array; } + /** + * Gets the relative complement of `arrayA` with respect to `b`, returning the elements that + * are not present in `arrayA` but are present in `arrayB`. Assumes both arrays are sorted + * based on the provided comparer. + */ + export function relativeComplement(arrayA: T[] | undefined, arrayB: T[] | undefined, comparer: (x: T, y: T) => Comparison = compareValues, offsetA: number = 0, offsetB: number = 0): T[] | undefined { + if (!arrayB || !arrayA || arrayB.length === 0 || arrayA.length === 0) return arrayB; + const result: T[] = []; + outer: for (; offsetB < arrayB.length; offsetB++) { + inner: for (; offsetA < arrayA.length; offsetA++) { + switch (comparer(arrayB[offsetB], arrayA[offsetA])) { + case Comparison.LessThan: break inner; + case Comparison.EqualTo: continue outer; + case Comparison.GreaterThan: continue inner; + } + } + result.push(arrayB[offsetB]); + } + return result; + } + export function sum(array: any[], prop: string): number { let result = 0; for (const v of array) { @@ -505,12 +526,12 @@ namespace ts { * @param array A sorted array whose first element must be no larger than number * @param number The value to be searched for in the array. */ - export function binarySearch(array: T[], value: T, comparer?: (v1: T, v2: T) => number): number { + export function binarySearch(array: T[], value: T, comparer?: (v1: T, v2: T) => number, offset?: number): number { if (!array || array.length === 0) { return -1; } - let low = 0; + let low = offset || 0; let high = array.length - 1; comparer = comparer !== undefined ? comparer @@ -829,6 +850,10 @@ namespace ts { return Array.isArray ? Array.isArray(value) : value instanceof Array; } + export function isEmptyArray(value: any[] | undefined): boolean { + return !value || !value.length; + } + export function memoize(callback: () => T): () => T { let value: T; return () => { From 763e78dc066bc26e2e4caa08d600b702e1f2fd4f Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Thu, 6 Oct 2016 10:33:44 -0700 Subject: [PATCH 2/3] Simplify async return type checking --- src/compiler/checker.ts | 209 +++++------------- src/compiler/diagnosticMessages.json | 2 +- src/compiler/transformers/ts.ts | 15 +- src/compiler/utilities.ts | 35 ++- .../asyncAliasReturnType_es5.errors.txt | 10 + .../asyncAwaitIsolatedModules_es5.js | 1 + .../asyncFunctionDeclaration15_es5.errors.txt | 24 +- 7 files changed, 108 insertions(+), 188 deletions(-) create mode 100644 tests/baselines/reference/asyncAliasReturnType_es5.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9840df7d805..da5bd3a6590 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5787,90 +5787,6 @@ namespace ts { } } - function getStaticTypeFromTypeNode(node: TypeNode) { - switch (node.kind) { - case SyntaxKind.AnyKeyword: - case SyntaxKind.JSDocAllType: - case SyntaxKind.JSDocUnknownType: - return anyType; - case SyntaxKind.StringKeyword: - return stringType; - case SyntaxKind.NumberKeyword: - return numberType; - case SyntaxKind.BooleanKeyword: - return booleanType; - case SyntaxKind.SymbolKeyword: - return esSymbolType; - case SyntaxKind.VoidKeyword: - return voidType; - case SyntaxKind.UndefinedKeyword: - return undefinedType; - case SyntaxKind.NullKeyword: - return nullType; - case SyntaxKind.NeverKeyword: - return neverType; - case SyntaxKind.JSDocNullKeyword: - return nullType; - case SyntaxKind.JSDocUndefinedKeyword: - return undefinedType; - case SyntaxKind.JSDocNeverKeyword: - return neverType; - case SyntaxKind.ThisType: - case SyntaxKind.ThisKeyword: - return getTypeFromThisTypeNode(node); - case SyntaxKind.LiteralType: - return getTypeFromLiteralTypeNode(node); - case SyntaxKind.JSDocLiteralType: - return getTypeFromLiteralTypeNode((node).literal); - case SyntaxKind.TypeReference: - case SyntaxKind.JSDocTypeReference: - return getTypeFromTypeReference(node); - case SyntaxKind.TypePredicate: - return booleanType; - case SyntaxKind.ExpressionWithTypeArguments: - return getTypeFromTypeReference(node); - case SyntaxKind.TypeQuery: - return getTypeFromTypeQueryNode(node); - case SyntaxKind.ArrayType: - case SyntaxKind.JSDocArrayType: - return getTypeFromArrayTypeNode(node); - case SyntaxKind.TupleType: - return getTypeFromTupleTypeNode(node); - case SyntaxKind.UnionType: - case SyntaxKind.JSDocUnionType: - return getTypeFromUnionTypeNode(node, aliasSymbol, aliasTypeArguments); - case SyntaxKind.IntersectionType: - return getTypeFromIntersectionTypeNode(node, aliasSymbol, aliasTypeArguments); - case SyntaxKind.ParenthesizedType: - case SyntaxKind.JSDocNullableType: - case SyntaxKind.JSDocNonNullableType: - case SyntaxKind.JSDocConstructorType: - case SyntaxKind.JSDocThisType: - case SyntaxKind.JSDocOptionalType: - return getTypeFromTypeNode((node).type); - case SyntaxKind.JSDocRecordType: - return getTypeFromTypeNode((node as JSDocRecordType).literal); - case SyntaxKind.FunctionType: - case SyntaxKind.ConstructorType: - case SyntaxKind.TypeLiteral: - case SyntaxKind.JSDocTypeLiteral: - case SyntaxKind.JSDocFunctionType: - return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node, aliasSymbol, aliasTypeArguments); - // This function assumes that an identifier or qualified name is a type expression - // Callers should first ensure this by calling isTypeNode - case SyntaxKind.Identifier: - case SyntaxKind.QualifiedName: - const symbol = getSymbolAtLocation(node); - return symbol && getDeclaredTypeOfSymbol(symbol); - case SyntaxKind.JSDocTupleType: - return getTypeFromJSDocTupleType(node); - case SyntaxKind.JSDocVariadicType: - return getTypeFromJSDocVariadicType(node); - default: - return unknownType; - } - } - function instantiateList(items: T[], mapper: TypeMapper, instantiator: (item: T, mapper: TypeMapper) => T): T[] { if (items && items.length) { const result: T[] = []; @@ -15308,56 +15224,55 @@ namespace ts { // } // - const promiseName = getEntityNameFromTypeNode(node.type); - const rootName = getFirstIdentifier(promiseName); - - // Mark the root symbol as referenced. - getSymbolLinks(rootName.symbol).referenced = true; + // Always mark the type node as referenced if it points to a value + markTypeNodeAsReferenced(node.type); + const promiseConstructorName = getEntityNameFromTypeNode(node.type); const promiseType = getTypeFromTypeNode(node.type); - if (promiseType === unknownType && compilerOptions.isolatedModules) { - // If we are compiling with isolatedModules, we may not be able to resolve the - // type as a value. As such, we will just return unknownType. + if (promiseType === unknownType) { + if (!compilerOptions.isolatedModules) { + if (promiseConstructorName) { + error(node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, entityNameToString(promiseConstructorName)); + } + else { + error(node.type, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type); + } + } return unknownType; } - const promiseConstructorType = getStaticTypeFromTypeNode(node.type); + if (promiseConstructorName === undefined) { + error(node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, typeToString(promiseType)); + return unknownType; + } + + const promiseConstructorSymbol = resolveEntityName(promiseConstructorName, SymbolFlags.Value, /*ignoreErrors*/ true); + const promiseConstructorType = promiseConstructorSymbol ? getTypeOfSymbol(promiseConstructorSymbol) : unknownType; + if (promiseConstructorType === unknownType) { + error(node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, entityNameToString(promiseConstructorName)); + return unknownType; + } const globalPromiseConstructorLikeType = getGlobalPromiseConstructorLikeType(); if (globalPromiseConstructorLikeType === emptyObjectType) { // If we couldn't resolve the global PromiseConstructorLike type we cannot verify // compatibility with __awaiter. - error(node.type || node.name || node, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type); + error(node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, entityNameToString(promiseConstructorName)); return unknownType; } - // When we get the type of the `Promise` symbol here, we get the type of the static - // side of the `Promise` class, which would be `{ new (...): Promise }`. - - const promiseConstructor = getNodeLinks(node.type).resolvedSymbol; - if (!promiseConstructor) { - // try to fall back to global promise type. - const typeName = promiseConstructor - ? symbolToString(promiseConstructor) - : typeToString(promiseType); - return checkCorrectPromiseType(promiseType, node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type, typeName); - } - - // If the Promise constructor, resolved locally, is an alias symbol we should mark it as referenced. - checkReturnTypeAnnotationAsExpression(node); - - // Validate the promise constructor type. - const promiseConstructorType = getTypeOfSymbol(promiseConstructor); - if (!checkTypeAssignableTo(promiseConstructorType, globalPromiseConstructorLikeType, node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type)) { + if (!checkTypeAssignableTo(promiseConstructorType, globalPromiseConstructorLikeType, node.type, + Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value)) { return unknownType; } // Verify there is no local declaration that could collide with the promise constructor. - const rootSymbol = getSymbol(node.locals, rootName.text, SymbolFlags.Value); - if (rootSymbol) { - error(rootSymbol.valueDeclaration, Diagnostics.Duplicate_identifier_0_Compiler_uses_declaration_1_to_support_async_functions, + const rootName = promiseConstructorName && getFirstIdentifier(promiseConstructorName); + const collidingSymbol = getSymbol(node.locals, rootName.text, SymbolFlags.Value); + if (collidingSymbol) { + error(collidingSymbol.valueDeclaration, Diagnostics.Duplicate_identifier_0_Compiler_uses_declaration_1_to_support_async_functions, rootName.text, - getFullyQualifiedName(promiseConstructor)); + entityNameToString(promiseConstructorName)); return unknownType; } @@ -15415,44 +15330,19 @@ namespace ts { errorInfo); } - /** Checks a type reference node as an expression. */ - function checkTypeNodeAsExpression(node: TypeNode) { - // When we are emitting type metadata for decorators, we need to try to check the type - // as if it were an expression so that we can emit the type in a value position when we - // serialize the type metadata. - if (node && node.kind === SyntaxKind.TypeReference) { - const root = getFirstIdentifier((node).typeName); - const meaning = root.parent.kind === SyntaxKind.TypeReference ? SymbolFlags.Type : SymbolFlags.Namespace; - // Resolve type so we know which symbol is referenced - const rootSymbol = resolveName(root, root.text, meaning | SymbolFlags.Alias, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined); - // Resolved symbol is alias - if (rootSymbol && rootSymbol.flags & SymbolFlags.Alias) { - const aliasTarget = resolveAlias(rootSymbol); - // If alias has value symbol - mark alias as referenced - if (aliasTarget.flags & SymbolFlags.Value && !isConstEnumOrConstEnumOnlyModule(resolveAlias(rootSymbol))) { - markAliasSymbolAsReferenced(rootSymbol); - } - } - } - } - /** - * Checks the type annotation of an accessor declaration or property declaration as - * an expression if it is a type reference to a type with a value declaration. - */ - function checkTypeAnnotationAsExpression(node: VariableLikeDeclaration) { - checkTypeNodeAsExpression((node).type); - } - - function checkReturnTypeAnnotationAsExpression(node: FunctionLikeDeclaration) { - checkTypeNodeAsExpression(node.type); - } - - /** Checks the type annotation of the parameters of a function/method or the constructor of a class as expressions */ - function checkParameterTypeAnnotationsAsExpressions(node: FunctionLikeDeclaration) { - // ensure all type annotations with a value declaration are checked as an expression - for (const parameter of node.parameters) { - checkTypeAnnotationAsExpression(parameter); + * If a TypeNode can be resolved to a value symbol imported from an external module, it is + * marked as referenced to prevent import elision. + */ + function markTypeNodeAsReferenced(node: TypeNode) { + const typeName = node && getEntityNameFromTypeNode(node); + const rootName = typeName && getFirstIdentifier(typeName); + const rootSymbol = rootName && resolveName(rootName, rootName.text, (typeName.kind === SyntaxKind.Identifier ? SymbolFlags.Type : SymbolFlags.Namespace) | SymbolFlags.Alias, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined); + if (rootSymbol + && rootSymbol.flags & SymbolFlags.Alias + && symbolIsValue(rootSymbol) + && !isConstEnumOrConstEnumOnlyModule(resolveAlias(rootSymbol))) { + markAliasSymbolAsReferenced(rootSymbol); } } @@ -15478,20 +15368,25 @@ namespace ts { case SyntaxKind.ClassDeclaration: const constructor = getFirstConstructorWithBody(node); if (constructor) { - checkParameterTypeAnnotationsAsExpressions(constructor); + for (const parameter of constructor.parameters) { + markTypeNodeAsReferenced(parameter.type); + } } break; case SyntaxKind.MethodDeclaration: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: - checkParameterTypeAnnotationsAsExpressions(node); - checkReturnTypeAnnotationAsExpression(node); + for (const parameter of (node).parameters) { + markTypeNodeAsReferenced(parameter.type); + } + + markTypeNodeAsReferenced((node).type); break; case SyntaxKind.PropertyDeclaration: case SyntaxKind.Parameter: - checkTypeAnnotationAsExpression(node); + markTypeNodeAsReferenced((node).type); break; } } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 71d7a978d1b..815ca9a9d67 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -163,7 +163,7 @@ "category": "Error", "code": 1054 }, - "Type '{0}' is not a valid async function return type.": { + "Type '{0}' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value.": { "category": "Error", "code": 1055 }, diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 2419ffa4c3b..352d6f6f1dc 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -2329,15 +2329,16 @@ namespace ts { } function getPromiseConstructor(type: TypeNode) { - const typeName = getEntityNameFromTypeNode(type); - if (typeName && isEntityName(typeName)) { - const serializationKind = resolver.getTypeReferenceSerializationKind(typeName); - if (serializationKind === TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue - || serializationKind === TypeReferenceSerializationKind.Unknown) { - return typeName; + if (type) { + const typeName = getEntityNameFromTypeNode(type); + if (typeName && isEntityName(typeName)) { + const serializationKind = resolver.getTypeReferenceSerializationKind(typeName); + if (serializationKind === TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue + || serializationKind === TypeReferenceSerializationKind.Unknown) { + return typeName; + } } } - return undefined; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 62813e9447c..b6d81e3f242 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -503,6 +503,17 @@ namespace ts { return getFullWidth(name) === 0 ? "(Missing)" : getTextOfNode(name); } + export function entityNameToString(name: EntityNameOrEntityNameExpression): string { + switch (name.kind) { + case SyntaxKind.Identifier: + return getFullWidth(name) === 0 ? unescapeIdentifier((name).text) : getTextOfNode(name); + case SyntaxKind.QualifiedName: + return entityNameToString((name).left) + "." + entityNameToString((name).right); + case SyntaxKind.PropertyAccessExpression: + return entityNameToString((name).expression) + "." + entityNameToString((name).name); + } + } + export function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): Diagnostic { const sourceFile = getSourceFileOfNode(node); const span = getErrorSpanForNode(sourceFile, node); @@ -1054,17 +1065,19 @@ namespace ts { } export function getEntityNameFromTypeNode(node: TypeNode): EntityNameOrEntityNameExpression { - if (node) { - switch (node.kind) { - case SyntaxKind.TypeReference: - return (node).typeName; - case SyntaxKind.ExpressionWithTypeArguments: - Debug.assert(isEntityNameExpression((node).expression)); - return (node).expression; - case SyntaxKind.Identifier: - case SyntaxKind.QualifiedName: - return (node); - } + switch (node.kind) { + case SyntaxKind.TypeReference: + case SyntaxKind.JSDocTypeReference: + return (node).typeName; + + case SyntaxKind.ExpressionWithTypeArguments: + return isEntityNameExpression((node).expression) + ? (node).expression + : undefined; + + case SyntaxKind.Identifier: + case SyntaxKind.QualifiedName: + return (node); } return undefined; diff --git a/tests/baselines/reference/asyncAliasReturnType_es5.errors.txt b/tests/baselines/reference/asyncAliasReturnType_es5.errors.txt new file mode 100644 index 00000000000..5e524e265e2 --- /dev/null +++ b/tests/baselines/reference/asyncAliasReturnType_es5.errors.txt @@ -0,0 +1,10 @@ +tests/cases/conformance/async/es5/asyncAliasReturnType_es5.ts(3,21): error TS1055: Type 'PromiseAlias' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. + + +==== tests/cases/conformance/async/es5/asyncAliasReturnType_es5.ts (1 errors) ==== + type PromiseAlias = Promise; + + async function f(): PromiseAlias { + ~~~~~~~~~~~~~~~~~~ +!!! error TS1055: Type 'PromiseAlias' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. + } \ No newline at end of file diff --git a/tests/baselines/reference/asyncAwaitIsolatedModules_es5.js b/tests/baselines/reference/asyncAwaitIsolatedModules_es5.js index 7eb2157c026..d1d1db3e329 100644 --- a/tests/baselines/reference/asyncAwaitIsolatedModules_es5.js +++ b/tests/baselines/reference/asyncAwaitIsolatedModules_es5.js @@ -77,6 +77,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) { } }; var _this = this; +var missing_1 = require("missing"); function f0() { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/]; diff --git a/tests/baselines/reference/asyncFunctionDeclaration15_es5.errors.txt b/tests/baselines/reference/asyncFunctionDeclaration15_es5.errors.txt index 3ea03927aa9..5fa33233912 100644 --- a/tests/baselines/reference/asyncFunctionDeclaration15_es5.errors.txt +++ b/tests/baselines/reference/asyncFunctionDeclaration15_es5.errors.txt @@ -1,9 +1,9 @@ -tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(6,23): error TS1055: Type '{}' is not a valid async function return type. -tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(7,23): error TS1055: Type 'any' is not a valid async function return type. -tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(8,23): error TS1055: Type 'number' is not a valid async function return type. -tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(9,23): error TS1055: Type 'PromiseLike' is not a valid async function return type. -tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(10,23): error TS1055: Type 'typeof Thenable' is not a valid async function return type. -tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(10,23): error TS1055: Type 'typeof Thenable' is not a valid async function return type. +tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(6,23): error TS1055: Type '{}' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. +tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(7,23): error TS1055: Type 'any' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. +tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(8,23): error TS1055: Type 'number' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. +tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(9,23): error TS1055: Type 'PromiseLike' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. +tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(10,23): error TS1055: Type 'typeof Thenable' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. +tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(10,23): error TS1055: Type 'typeof Thenable' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. Type 'Thenable' is not assignable to type 'PromiseLike'. Types of property 'then' are incompatible. Type '() => void' is not assignable to type '{ (onfulfilled?: (value: any) => any, onrejected?: (reason: any) => any): PromiseLike; (onfulfilled: (value: any) => any, onrejected: (reason: any) => TResult | PromiseLike): PromiseLike; (onfulfilled: (value: any) => TResult | PromiseLike, onrejected?: (reason: any) => TResult | PromiseLike): PromiseLike; (onfulfilled: (value: any) => TResult1 | PromiseLike, onrejected: (reason: any) => TResult2 | PromiseLike): PromiseLike; }'. @@ -20,21 +20,21 @@ tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration1 async function fn1() { } // valid: Promise async function fn2(): { } { } // error ~~~ -!!! error TS1055: Type '{}' is not a valid async function return type. +!!! error TS1055: Type '{}' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. async function fn3(): any { } // error ~~~ -!!! error TS1055: Type 'any' is not a valid async function return type. +!!! error TS1055: Type 'any' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. async function fn4(): number { } // error ~~~~~~ -!!! error TS1055: Type 'number' is not a valid async function return type. +!!! error TS1055: Type 'number' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. async function fn5(): PromiseLike { } // error ~~~~~~~~~~~~~~~~~ -!!! error TS1055: Type 'PromiseLike' is not a valid async function return type. +!!! error TS1055: Type 'PromiseLike' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. async function fn6(): Thenable { } // error ~~~~~~~~ -!!! error TS1055: Type 'typeof Thenable' is not a valid async function return type. +!!! error TS1055: Type 'typeof Thenable' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. ~~~~~~~~ -!!! error TS1055: Type 'typeof Thenable' is not a valid async function return type. +!!! error TS1055: Type 'typeof Thenable' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. !!! error TS1055: Type 'Thenable' is not assignable to type 'PromiseLike'. !!! error TS1055: Types of property 'then' are incompatible. !!! error TS1055: Type '() => void' is not assignable to type '{ (onfulfilled?: (value: any) => any, onrejected?: (reason: any) => any): PromiseLike; (onfulfilled: (value: any) => any, onrejected: (reason: any) => TResult | PromiseLike): PromiseLike; (onfulfilled: (value: any) => TResult | PromiseLike, onrejected?: (reason: any) => TResult | PromiseLike): PromiseLike; (onfulfilled: (value: any) => TResult1 | PromiseLike, onrejected: (reason: any) => TResult2 | PromiseLike): PromiseLike; }'. From 5f401dac703536b0fb5109eab98242ebe9cd9804 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Thu, 6 Oct 2016 17:55:47 -0700 Subject: [PATCH 3/3] Fix lint warnings. --- src/compiler/core.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 439e5609566..e81e4d470e2 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -448,7 +448,7 @@ namespace ts { * are not present in `arrayA` but are present in `arrayB`. Assumes both arrays are sorted * based on the provided comparer. */ - export function relativeComplement(arrayA: T[] | undefined, arrayB: T[] | undefined, comparer: (x: T, y: T) => Comparison = compareValues, offsetA: number = 0, offsetB: number = 0): T[] | undefined { + export function relativeComplement(arrayA: T[] | undefined, arrayB: T[] | undefined, comparer: (x: T, y: T) => Comparison = compareValues, offsetA = 0, offsetB = 0): T[] | undefined { if (!arrayB || !arrayA || arrayB.length === 0 || arrayA.length === 0) return arrayB; const result: T[] = []; outer: for (; offsetB < arrayB.length; offsetB++) {