Skip to content

Commit

Permalink
Fix getReturnTypeFromBody widening
Browse files Browse the repository at this point in the history
  • Loading branch information
rbuckton committed Nov 13, 2017
1 parent b9dbf5d commit ae11ae5
Show file tree
Hide file tree
Showing 60 changed files with 3,865 additions and 427 deletions.
73 changes: 45 additions & 28 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3360,8 +3360,13 @@ namespace ts {
writeAnonymousType(<ObjectType>type, nextFlags);
}
else if (type.flags & TypeFlags.UniqueESSymbol) {
writeKeyword(writer, SyntaxKind.UniqueKeyword);
writeSpace(writer);
if (flags & TypeFormatFlags.AllowUniqueESSymbolType) {
writeKeyword(writer, SyntaxKind.UniqueKeyword);
writeSpace(writer);
}
else {
writer.reportInaccessibleUniqueSymbolError();
}
writeKeyword(writer, SyntaxKind.SymbolKeyword);
}
else if (type.flags & TypeFlags.StringOrNumberLiteral) {
Expand Down Expand Up @@ -8365,9 +8370,7 @@ namespace ts {
}

function getESSymbolLikeTypeForNode(node: Node) {
if (isVariableDeclaration(node) ? isConst(node) && isIdentifier(node.name) && isVariableDeclarationInVariableStatement(node) :
isPropertyDeclaration(node) ? hasReadonlyModifier(node) && hasStaticModifier(node) :
isPropertySignature(node) && hasReadonlyModifier(node)) {
if (isValidESSymbolDeclaration(node)) {
const symbol = getSymbolOfNode(node);
const links = getSymbolLinks(symbol);
return links.type || (links.type = createUniqueESSymbolType(symbol));
Expand Down Expand Up @@ -17694,9 +17697,6 @@ namespace ts {
// the native Promise<T> type later in this function.
type = checkAwaitedType(type, /*errorNode*/ func, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
}

// widen 'unique symbol' types when we infer the return type.
type = getWidenedUniqueESSymbolType(type);
}
else {
let types: Type[];
Expand Down Expand Up @@ -17728,37 +17728,49 @@ namespace ts {
: voidType; // Normal function
}
}

// Return a union of the return expression types.
type = getUnionType(types, /*subtypeReduction*/ true);

// widen 'unique symbol' types when we infer the return type.
type = getWidenedUniqueESSymbolType(type);

if (functionFlags & FunctionFlags.Generator) { // AsyncGenerator function or Generator function
type = functionFlags & FunctionFlags.Async
? createAsyncIterableIteratorType(type) // AsyncGenerator function
: createIterableIteratorType(type); // Generator function
}
}

if (!contextualSignature) {
reportErrorsFromWidening(func, type);
}

if (isUnitType(type) &&
!(contextualSignature &&
isLiteralContextualType(
contextualSignature === getSignatureFromDeclaration(func) ? type : getReturnTypeOfSignature(contextualSignature)))) {
type = getWidenedLiteralType(type);
if (isUnitType(type)) {
let contextualType = !contextualSignature ? undefined :
contextualSignature === getSignatureFromDeclaration(func) ? type :
getReturnTypeOfSignature(contextualSignature);
if (contextualType) {
switch (functionFlags & FunctionFlags.AsyncGenerator) {
case FunctionFlags.AsyncGenerator:
contextualType = getIteratedTypeOfGenerator(contextualType, /*isAsyncGenerator*/ true);
break;
case FunctionFlags.Generator:
contextualType = getIteratedTypeOfGenerator(contextualType, /*isAsyncGenerator*/ false);
break;
case FunctionFlags.Async:
contextualType = getPromisedTypeOfPromise(contextualType);
break;
}
}
type = getWidenedLiteralLikeTypeForContextualType(type, contextualType);
}

const widenedType = getWidenedType(type);
// From within an async function you can return either a non-promise value or a promise. Any
// Promise/A+ compatible implementation will always assimilate any foreign promise, so the
// return type of the body is awaited type of the body, wrapped in a native Promise<T> type.
return (functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async
? createPromiseReturnType(func, widenedType) // Async function
: widenedType; // Generator function, AsyncGenerator function, or normal function
switch (functionFlags & FunctionFlags.AsyncGenerator) {
case FunctionFlags.AsyncGenerator:
return createAsyncIterableIteratorType(widenedType);
case FunctionFlags.Generator:
return createIterableIteratorType(widenedType);
case FunctionFlags.Async:
// From within an async function you can return either a non-promise value or a promise. Any
// Promise/A+ compatible implementation will always assimilate any foreign promise, so the
// return type of the body is awaited type of the body, wrapped in a native Promise<T> type.
return createPromiseType(widenedType);
default:
return widenedType;
}
}

function checkAndAggregateYieldOperandTypes(func: FunctionLikeDeclaration, checkMode: CheckMode): Type[] {
Expand Down Expand Up @@ -24627,9 +24639,14 @@ namespace ts {
let type = symbol && !(symbol.flags & (SymbolFlags.TypeLiteral | SymbolFlags.Signature))
? getWidenedLiteralType(getTypeOfSymbol(symbol))
: unknownType;
if (type.flags & TypeFlags.UniqueESSymbol &&
type.symbol === symbol) {
flags |= TypeFormatFlags.AllowUniqueESSymbolType;
}
if (flags & TypeFormatFlags.AddUndefined) {
type = getNullableType(type, TypeFlags.Undefined);
}

getSymbolDisplayBuilder().buildTypeDisplay(type, writer, enclosingDeclaration, flags);
}

Expand Down
15 changes: 13 additions & 2 deletions src/compiler/declarationEmitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ namespace ts {
const writer = <EmitTextWriterWithSymbolWriter>createTextWriter(newLine);
writer.trackSymbol = trackSymbol;
writer.reportInaccessibleThisError = reportInaccessibleThisError;
writer.reportInaccessibleUniqueSymbolError = reportInaccessibleUniqueSymbolError;
writer.reportPrivateInBaseOfClassExpression = reportPrivateInBaseOfClassExpression;
writer.writeKeyword = writer.write;
writer.writeOperator = writer.write;
Expand Down Expand Up @@ -322,11 +323,21 @@ namespace ts {
}
}

function reportInaccessibleUniqueSymbolError() {
if (errorNameNode) {
reportedDeclarationError = true;
emitterDiagnostics.add(createDiagnosticForNode(errorNameNode, Diagnostics.The_inferred_type_of_0_references_an_inaccessible_1_type_A_type_annotation_is_necessary,
declarationNameToString(errorNameNode),
"unique symbol"));
}
}

function reportInaccessibleThisError() {
if (errorNameNode) {
reportedDeclarationError = true;
emitterDiagnostics.add(createDiagnosticForNode(errorNameNode, Diagnostics.The_inferred_type_of_0_references_an_inaccessible_this_type_A_type_annotation_is_necessary,
declarationNameToString(errorNameNode)));
emitterDiagnostics.add(createDiagnosticForNode(errorNameNode, Diagnostics.The_inferred_type_of_0_references_an_inaccessible_1_type_A_type_annotation_is_necessary,
declarationNameToString(errorNameNode),
"this"));
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1804,7 +1804,7 @@
"category": "Error",
"code": 2526
},
"The inferred type of '{0}' references an inaccessible 'this' type. A type annotation is necessary.": {
"The inferred type of '{0}' references an inaccessible '{1}' type. A type annotation is necessary.": {
"category": "Error",
"code": 2527
},
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2857,6 +2857,7 @@ namespace ts {
trackSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): void;
reportInaccessibleThisError(): void;
reportPrivateInBaseOfClassExpression(propertyName: string): void;
reportInaccessibleUniqueSymbolError(): void;
}

export const enum TypeFormatFlags {
Expand All @@ -2877,6 +2878,7 @@ namespace ts {
InArrayType = 1 << 15, // Writing an array element type
UseAliasDefinedOutsideCurrentScope = 1 << 16, // For a `type T = ... ` defined in a different file, write `T` instead of its value,
// even though `T` can't be accessed in the current scope.
AllowUniqueESSymbolType = 1 << 17,
}

export const enum SymbolFormatFlags {
Expand Down
7 changes: 7 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ namespace ts {
clear: () => str = "",
trackSymbol: noop,
reportInaccessibleThisError: noop,
reportInaccessibleUniqueSymbolError: noop,
reportPrivateInBaseOfClassExpression: noop,
};
}
Expand Down Expand Up @@ -945,6 +946,12 @@ namespace ts {
&& node.parent.parent.kind === SyntaxKind.VariableStatement;
}

export function isValidESSymbolDeclaration(node: Node) {
return isVariableDeclaration(node) ? isConst(node) && isIdentifier(node.name) && isVariableDeclarationInVariableStatement(node) :
isPropertyDeclaration(node) ? hasReadonlyModifier(node) && hasStaticModifier(node) :
isPropertySignature(node) && hasReadonlyModifier(node);
}

export function introducesArgumentsExoticObject(node: Node) {
switch (node.kind) {
case SyntaxKind.MethodDeclaration:
Expand Down
1 change: 1 addition & 0 deletions src/services/codefixes/inferFromUsage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ namespace ts.codefix {
},
reportInaccessibleThisError: () => { typeIsAccessible = false; },
reportPrivateInBaseOfClassExpression: () => { typeIsAccessible = false; },
reportInaccessibleUniqueSymbolError: () => { typeIsAccessible = false; }
};
}
writer.clear();
Expand Down
1 change: 1 addition & 0 deletions src/services/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1130,6 +1130,7 @@ namespace ts {
clear: resetWriter,
trackSymbol: noop,
reportInaccessibleThisError: noop,
reportInaccessibleUniqueSymbolError: noop,
reportPrivateInBaseOfClassExpression: noop,
};

Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/YieldStarExpression4_es6.types
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
=== tests/cases/conformance/es6/yieldExpressions/YieldStarExpression4_es6.ts ===
function *g() {
>g : () => IterableIterator<undefined>
>g : () => IterableIterator<any>

yield * [];
>yield * [] : any
Expand Down
2 changes: 2 additions & 0 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1826,6 +1826,7 @@ declare namespace ts {
trackSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): void;
reportInaccessibleThisError(): void;
reportPrivateInBaseOfClassExpression(propertyName: string): void;
reportInaccessibleUniqueSymbolError(): void;
}
enum TypeFormatFlags {
None = 0,
Expand All @@ -1844,6 +1845,7 @@ declare namespace ts {
WriteClassExpressionAsTypeLiteral = 16384,
InArrayType = 32768,
UseAliasDefinedOutsideCurrentScope = 65536,
AllowUniqueESSymbolType = 131072,
}
enum SymbolFormatFlags {
None = 0,
Expand Down
2 changes: 2 additions & 0 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1826,6 +1826,7 @@ declare namespace ts {
trackSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): void;
reportInaccessibleThisError(): void;
reportPrivateInBaseOfClassExpression(propertyName: string): void;
reportInaccessibleUniqueSymbolError(): void;
}
enum TypeFormatFlags {
None = 0,
Expand All @@ -1844,6 +1845,7 @@ declare namespace ts {
WriteClassExpressionAsTypeLiteral = 16384,
InArrayType = 32768,
UseAliasDefinedOutsideCurrentScope = 65536,
AllowUniqueESSymbolType = 131072,
}
enum SymbolFormatFlags {
None = 0,
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/asyncImportNestedYield.types
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
=== tests/cases/compiler/asyncImportNestedYield.ts ===
async function* foo() {
>foo : () => AsyncIterableIterator<"foo">
>foo : () => AsyncIterableIterator<string>

import((await import(yield "foo")).default);
>import((await import(yield "foo")).default) : Promise<any>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class C3 {
>C3 : C3

async * f() {
>f : () => AsyncIterableIterator<1>
>f : () => AsyncIterableIterator<number>

const x = yield 1;
>x : any
Expand All @@ -50,14 +50,14 @@ class C5 {
>C5 : C5

async * f() {
>f : () => AsyncIterableIterator<1>
>f : () => AsyncIterableIterator<number>

const x = yield* (async function*() { yield 1; })();
>x : any
>yield* (async function*() { yield 1; })() : any
>(async function*() { yield 1; })() : AsyncIterableIterator<1>
>(async function*() { yield 1; }) : () => AsyncIterableIterator<1>
>async function*() { yield 1; } : () => AsyncIterableIterator<1>
>(async function*() { yield 1; })() : AsyncIterableIterator<number>
>(async function*() { yield 1; }) : () => AsyncIterableIterator<number>
>async function*() { yield 1; } : () => AsyncIterableIterator<number>
>yield 1 : any
>1 : 1
}
Expand All @@ -80,7 +80,7 @@ class C7 {
>C7 : C7

async * f() {
>f : () => AsyncIterableIterator<1>
>f : () => AsyncIterableIterator<number>

return 1;
>1 : 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class C3 {
>C3 : C3

async * f() {
>f : () => AsyncIterableIterator<1>
>f : () => AsyncIterableIterator<number>

const x = yield 1;
>x : any
Expand All @@ -50,14 +50,14 @@ class C5 {
>C5 : C5

async * f() {
>f : () => AsyncIterableIterator<1>
>f : () => AsyncIterableIterator<number>

const x = yield* (async function*() { yield 1; })();
>x : any
>yield* (async function*() { yield 1; })() : any
>(async function*() { yield 1; })() : AsyncIterableIterator<1>
>(async function*() { yield 1; }) : () => AsyncIterableIterator<1>
>async function*() { yield 1; } : () => AsyncIterableIterator<1>
>(async function*() { yield 1; })() : AsyncIterableIterator<number>
>(async function*() { yield 1; }) : () => AsyncIterableIterator<number>
>async function*() { yield 1; } : () => AsyncIterableIterator<number>
>yield 1 : any
>1 : 1
}
Expand All @@ -80,7 +80,7 @@ class C7 {
>C7 : C7

async * f() {
>f : () => AsyncIterableIterator<1>
>f : () => AsyncIterableIterator<number>

return 1;
>1 : 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class C3 {
>C3 : C3

async * f() {
>f : () => AsyncIterableIterator<1>
>f : () => AsyncIterableIterator<number>

const x = yield 1;
>x : any
Expand All @@ -50,14 +50,14 @@ class C5 {
>C5 : C5

async * f() {
>f : () => AsyncIterableIterator<1>
>f : () => AsyncIterableIterator<number>

const x = yield* (async function*() { yield 1; })();
>x : any
>yield* (async function*() { yield 1; })() : any
>(async function*() { yield 1; })() : AsyncIterableIterator<1>
>(async function*() { yield 1; }) : () => AsyncIterableIterator<1>
>async function*() { yield 1; } : () => AsyncIterableIterator<1>
>(async function*() { yield 1; })() : AsyncIterableIterator<number>
>(async function*() { yield 1; }) : () => AsyncIterableIterator<number>
>async function*() { yield 1; } : () => AsyncIterableIterator<number>
>yield 1 : any
>1 : 1
}
Expand All @@ -80,7 +80,7 @@ class C7 {
>C7 : C7

async * f() {
>f : () => AsyncIterableIterator<1>
>f : () => AsyncIterableIterator<number>

return 1;
>1 : 1
Expand Down
Loading

0 comments on commit ae11ae5

Please sign in to comment.