Skip to content

Commit 143edff

Browse files
committed
Merge remote-tracking branch 'origin/master' into usePromise
2 parents 3f00197 + f673f48 commit 143edff

24 files changed

+863
-17
lines changed

src/compiler/binder.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1045,7 +1045,35 @@ namespace ts {
10451045
if (node.finallyBlock) {
10461046
// in finally flow is combined from pre-try/flow from try/flow from catch
10471047
// pre-flow is necessary to make sure that finally is reachable even if finally flows in both try and finally blocks are unreachable
1048-
addAntecedent(preFinallyLabel, preTryFlow);
1048+
1049+
// also for finally blocks we inject two extra edges into the flow graph.
1050+
// first -> edge that connects pre-try flow with the label at the beginning of the finally block, it has lock associated with it
1051+
// second -> edge that represents post-finally flow.
1052+
// these edges are used in following scenario:
1053+
// let a; (1)
1054+
// try { a = someOperation(); (2)}
1055+
// finally { (3) console.log(a) } (4)
1056+
// (5) a
1057+
1058+
// flow graph for this case looks roughly like this (arrows show ):
1059+
// (1-pre-try-flow) <--.. <-- (2-post-try-flow)
1060+
// ^ ^
1061+
// |*****(3-pre-finally-label) -----|
1062+
// ^
1063+
// |-- ... <-- (4-post-finally-label) <--- (5)
1064+
// In case when we walk the flow starting from inside the finally block we want to take edge '*****' into account
1065+
// since it ensures that finally is always reachable. However when we start outside the finally block and go through label (5)
1066+
// then edge '*****' should be discarded because label 4 is only reachable if post-finally label-4 is reachable
1067+
// Simply speaking code inside finally block is treated as reachable as pre-try-flow
1068+
// since we conservatively assume that any line in try block can throw or return in which case we'll enter finally.
1069+
// However code after finally is reachable only if control flow was not abrupted in try/catch or finally blocks - it should be composed from
1070+
// final flows of these blocks without taking pre-try flow into account.
1071+
//
1072+
// extra edges that we inject allows to control this behavior
1073+
// if when walking the flow we step on post-finally edge - we can mark matching pre-finally edge as locked so it will be skipped.
1074+
const preFinallyFlow: PreFinallyFlow = { flags: FlowFlags.PreFinally, antecedent: preTryFlow, lock: {} };
1075+
addAntecedent(preFinallyLabel, preFinallyFlow);
1076+
10491077
currentFlow = finishFlowLabel(preFinallyLabel);
10501078
bind(node.finallyBlock);
10511079
// if flow after finally is unreachable - keep it
@@ -1061,6 +1089,11 @@ namespace ts {
10611089
: unreachableFlow;
10621090
}
10631091
}
1092+
if (!(currentFlow.flags & FlowFlags.Unreachable)) {
1093+
const afterFinallyFlow: AfterFinallyFlow = { flags: FlowFlags.AfterFinally, antecedent: currentFlow };
1094+
preFinallyFlow.lock = afterFinallyFlow;
1095+
currentFlow = afterFinallyFlow;
1096+
}
10641097
}
10651098
else {
10661099
currentFlow = finishFlowLabel(preFinallyLabel);

src/compiler/checker.ts

Lines changed: 78 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2690,7 +2690,11 @@ namespace ts {
26902690
writePunctuation(writer, SyntaxKind.ColonToken);
26912691
writeSpace(writer);
26922692

2693-
buildTypeDisplay(getTypeOfSymbol(p), writer, enclosingDeclaration, flags, symbolStack);
2693+
let type = getTypeOfSymbol(p);
2694+
if (isRequiredInitializedParameter(parameterNode)) {
2695+
type = includeFalsyTypes(type, TypeFlags.Undefined);
2696+
}
2697+
buildTypeDisplay(type, writer, enclosingDeclaration, flags, symbolStack);
26942698
}
26952699

26962700
function buildBindingPatternDisplay(bindingPattern: BindingPattern, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
@@ -3271,6 +3275,16 @@ namespace ts {
32713275
return strictNullChecks && optional ? includeFalsyTypes(type, TypeFlags.Undefined) : type;
32723276
}
32733277

3278+
/** remove undefined from the annotated type of a parameter when there is an initializer (that doesn't include undefined) */
3279+
function removeOptionalityFromAnnotation(annotatedType: Type, declaration: VariableLikeDeclaration): Type {
3280+
const annotationIncludesUndefined = strictNullChecks &&
3281+
declaration.kind === SyntaxKind.Parameter &&
3282+
declaration.initializer &&
3283+
getFalsyFlags(annotatedType) & TypeFlags.Undefined &&
3284+
!(getFalsyFlags(checkExpression(declaration.initializer)) & TypeFlags.Undefined);
3285+
return annotationIncludesUndefined ? getNonNullableType(annotatedType) : annotatedType;
3286+
}
3287+
32743288
// Return the inferred type for a variable, parameter, or property declaration
32753289
function getTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration, includeOptionality: boolean): Type {
32763290
if (declaration.flags & NodeFlags.JavaScriptFile) {
@@ -3304,7 +3318,8 @@ namespace ts {
33043318

33053319
// Use type from type annotation if one is present
33063320
if (declaration.type) {
3307-
return addOptionality(getTypeFromTypeNode(declaration.type), /*optional*/ declaration.questionToken && includeOptionality);
3321+
const declaredType = removeOptionalityFromAnnotation(getTypeFromTypeNode(declaration.type), declaration);
3322+
return addOptionality(declaredType, /*optional*/ declaration.questionToken && includeOptionality);
33083323
}
33093324

33103325
if ((compilerOptions.noImplicitAny || declaration.flags & NodeFlags.JavaScriptFile) &&
@@ -5198,6 +5213,12 @@ namespace ts {
51985213
Debug.assert(parameterIndex >= 0);
51995214
return parameterIndex >= signature.minArgumentCount;
52005215
}
5216+
const iife = getImmediatelyInvokedFunctionExpression(node.parent);
5217+
if (iife) {
5218+
return !node.type &&
5219+
!node.dotDotDotToken &&
5220+
indexOf((node.parent as SignatureDeclaration).parameters, node) >= iife.arguments.length;
5221+
}
52015222

52025223
return false;
52035224
}
@@ -7762,16 +7783,36 @@ namespace ts {
77627783
if (target.flags & TypeFlags.Union && containsType(targetTypes, source)) {
77637784
return Ternary.True;
77647785
}
7765-
const len = targetTypes.length;
7766-
for (let i = 0; i < len; i++) {
7767-
const related = isRelatedTo(source, targetTypes[i], reportErrors && i === len - 1);
7786+
for (const type of targetTypes) {
7787+
const related = isRelatedTo(source, type, /*reportErrors*/ false);
77687788
if (related) {
77697789
return related;
77707790
}
77717791
}
7792+
if (reportErrors) {
7793+
const discriminantType = findMatchingDiscriminantType(source, target);
7794+
isRelatedTo(source, discriminantType || targetTypes[targetTypes.length - 1], /*reportErrors*/ true);
7795+
}
77727796
return Ternary.False;
77737797
}
77747798

7799+
function findMatchingDiscriminantType(source: Type, target: UnionOrIntersectionType) {
7800+
const sourceProperties = getPropertiesOfObjectType(source);
7801+
if (sourceProperties) {
7802+
for (const sourceProperty of sourceProperties) {
7803+
if (isDiscriminantProperty(target, sourceProperty.name)) {
7804+
const sourceType = getTypeOfSymbol(sourceProperty);
7805+
for (const type of target.types) {
7806+
const targetType = getTypeOfPropertyOfType(type, sourceProperty.name);
7807+
if (targetType && isRelatedTo(sourceType, targetType)) {
7808+
return type;
7809+
}
7810+
}
7811+
}
7812+
}
7813+
}
7814+
}
7815+
77757816
function typeRelatedToEachType(source: Type, target: UnionOrIntersectionType, reportErrors: boolean): Ternary {
77767817
let result = Ternary.True;
77777818
const targetTypes = target.types;
@@ -8592,7 +8633,7 @@ namespace ts {
85928633
if (flags & TypeFlags.Void) types.push(voidType);
85938634
if (flags & TypeFlags.Undefined) types.push(undefinedType);
85948635
if (flags & TypeFlags.Null) types.push(nullType);
8595-
return getUnionType(types, /*subtypeReduction*/ true);
8636+
return getUnionType(types);
85968637
}
85978638

85988639
function removeDefinitelyFalsyTypes(type: Type): Type {
@@ -9875,7 +9916,19 @@ namespace ts {
98759916
}
98769917
}
98779918
let type: FlowType;
9878-
if (flow.flags & FlowFlags.Assignment) {
9919+
if (flow.flags & FlowFlags.AfterFinally) {
9920+
// block flow edge: finally -> pre-try (for larger explanation check comment in binder.ts - bindTryStatement
9921+
(<AfterFinallyFlow>flow).locked = true;
9922+
type = getTypeAtFlowNode((<AfterFinallyFlow>flow).antecedent);
9923+
(<AfterFinallyFlow>flow).locked = false;
9924+
}
9925+
else if (flow.flags & FlowFlags.PreFinally) {
9926+
// locked pre-finally flows are filtered out in getTypeAtFlowBranchLabel
9927+
// so here just redirect to antecedent
9928+
flow = (<PreFinallyFlow>flow).antecedent;
9929+
continue;
9930+
}
9931+
else if (flow.flags & FlowFlags.Assignment) {
98799932
type = getTypeAtFlowAssignment(<FlowAssignment>flow);
98809933
if (!type) {
98819934
flow = (<FlowAssignment>flow).antecedent;
@@ -10031,6 +10084,12 @@ namespace ts {
1003110084
let subtypeReduction = false;
1003210085
let seenIncomplete = false;
1003310086
for (const antecedent of flow.antecedents) {
10087+
if (antecedent.flags & FlowFlags.PreFinally && (<PreFinallyFlow>antecedent).lock.locked) {
10088+
// if flow correspond to branch from pre-try to finally and this branch is locked - this means that
10089+
// we initially have started following the flow outside the finally block.
10090+
// in this case we should ignore this branch.
10091+
continue;
10092+
}
1003410093
const flowType = getTypeAtFlowNode(antecedent);
1003510094
const type = getTypeFromFlowType(flowType);
1003610095
// If the type at a particular antecedent path is the declared type and the
@@ -20710,6 +20769,13 @@ namespace ts {
2071020769
return false;
2071120770
}
2071220771

20772+
function isRequiredInitializedParameter(parameter: ParameterDeclaration) {
20773+
return strictNullChecks &&
20774+
!isOptionalParameter(parameter) &&
20775+
parameter.initializer &&
20776+
!(getModifierFlags(parameter) & ModifierFlags.ParameterPropertyModifier);
20777+
}
20778+
2071320779
function getNodeCheckFlags(node: Node): NodeCheckFlags {
2071420780
node = getParseTreeNode(node);
2071520781
return node ? getNodeLinks(node).flags : undefined;
@@ -20801,10 +20867,12 @@ namespace ts {
2080120867
function writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter) {
2080220868
// Get type of the symbol if this is the valid symbol otherwise get type at location
2080320869
const symbol = getSymbolOfNode(declaration);
20804-
const type = symbol && !(symbol.flags & (SymbolFlags.TypeLiteral | SymbolFlags.Signature))
20870+
let type = symbol && !(symbol.flags & (SymbolFlags.TypeLiteral | SymbolFlags.Signature))
2080520871
? getWidenedLiteralType(getTypeOfSymbol(symbol))
2080620872
: unknownType;
20807-
20873+
if (flags & TypeFormatFlags.AddUndefined) {
20874+
type = includeFalsyTypes(type, TypeFlags.Undefined);
20875+
}
2080820876
getSymbolDisplayBuilder().buildTypeDisplay(type, writer, enclosingDeclaration, flags);
2080920877
}
2081020878

@@ -20903,6 +20971,7 @@ namespace ts {
2090320971
isTopLevelValueImportEqualsWithEntityName,
2090420972
isDeclarationVisible,
2090520973
isImplementationOfOverload,
20974+
isRequiredInitializedParameter,
2090620975
writeTypeOfDeclaration,
2090720976
writeReturnTypeOfSignatureDeclaration,
2090820977
writeTypeOfExpression,

src/compiler/declarationEmitter.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,13 +324,20 @@ namespace ts {
324324
function writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration, type: TypeNode, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) {
325325
writer.getSymbolAccessibilityDiagnostic = getSymbolAccessibilityDiagnostic;
326326
write(": ");
327-
if (type) {
327+
328+
// use the checker's type, not the declared type,
329+
// for non-optional initialized parameters that aren't a parameter property
330+
const shouldUseResolverType = declaration.kind === SyntaxKind.Parameter &&
331+
resolver.isRequiredInitializedParameter(declaration as ParameterDeclaration);
332+
if (type && !shouldUseResolverType) {
328333
// Write the type
329334
emitType(type);
330335
}
331336
else {
332337
errorNameNode = declaration.name;
333-
resolver.writeTypeOfDeclaration(declaration, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, writer);
338+
const format = TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue |
339+
(shouldUseResolverType ? TypeFormatFlags.AddUndefined : 0);
340+
resolver.writeTypeOfDeclaration(declaration, enclosingDeclaration, format, writer);
334341
errorNameNode = undefined;
335342
}
336343
}

src/compiler/types.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2071,10 +2071,25 @@
20712071
ArrayMutation = 1 << 8, // Potential array mutation
20722072
Referenced = 1 << 9, // Referenced as antecedent once
20732073
Shared = 1 << 10, // Referenced as antecedent more than once
2074+
PreFinally = 1 << 11, // Injected edge that links pre-finally label and pre-try flow
2075+
AfterFinally = 1 << 12, // Injected edge that links post-finally flow with the rest of the graph
20742076
Label = BranchLabel | LoopLabel,
20752077
Condition = TrueCondition | FalseCondition
20762078
}
20772079

2080+
export interface FlowLock {
2081+
locked?: boolean;
2082+
}
2083+
2084+
export interface AfterFinallyFlow extends FlowNode, FlowLock {
2085+
antecedent: FlowNode;
2086+
}
2087+
2088+
export interface PreFinallyFlow extends FlowNode {
2089+
antecedent: FlowNode;
2090+
lock: FlowLock;
2091+
}
2092+
20782093
export interface FlowNode {
20792094
flags: FlowFlags;
20802095
id?: number; // Node id used by flow type cache in checker
@@ -2474,7 +2489,8 @@
24742489
InFirstTypeArgument = 0x00000100, // Writing first type argument of the instantiated type
24752490
InTypeAlias = 0x00000200, // Writing type in type alias declaration
24762491
UseTypeAliasValue = 0x00000400, // Serialize the type instead of using type-alias. This is needed when we emit declaration file.
2477-
SuppressAnyReturnType = 0x00000800, // If the return type is any-like, don't offer a return type.
2492+
SuppressAnyReturnType = 0x00000800, // If the return type is any-like, don't offer a return type.
2493+
AddUndefined = 0x00001000, // Add undefined to types of initialized, non-optional parameters
24782494
}
24792495

24802496
export const enum SymbolFormatFlags {
@@ -2579,6 +2595,7 @@
25792595
isDeclarationVisible(node: Declaration): boolean;
25802596
collectLinkedAliases(node: Identifier): Node[];
25812597
isImplementationOfOverload(node: FunctionLikeDeclaration): boolean;
2598+
isRequiredInitializedParameter(node: ParameterDeclaration): boolean;
25822599
writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void;
25832600
writeReturnTypeOfSignatureDeclaration(signatureDeclaration: SignatureDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void;
25842601
writeTypeOfExpression(expr: Expression, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void;

tests/baselines/reference/contextuallyTypedIife.types

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -253,17 +253,17 @@ let eleven = (o => o.a(11))({ a: function(n) { return n; } });
253253
// missing arguments
254254
(function(x, undefined) { return x; })(42);
255255
>(function(x, undefined) { return x; })(42) : number
256-
>(function(x, undefined) { return x; }) : (x: number, undefined: any) => number
257-
>function(x, undefined) { return x; } : (x: number, undefined: any) => number
256+
>(function(x, undefined) { return x; }) : (x: number, undefined?: any) => number
257+
>function(x, undefined) { return x; } : (x: number, undefined?: any) => number
258258
>x : number
259259
>undefined : any
260260
>x : number
261261
>42 : 42
262262

263263
((x, y, z) => 42)();
264264
>((x, y, z) => 42)() : number
265-
>((x, y, z) => 42) : (x: any, y: any, z: any) => number
266-
>(x, y, z) => 42 : (x: any, y: any, z: any) => number
265+
>((x, y, z) => 42) : (x?: any, y?: any, z?: any) => number
266+
>(x, y, z) => 42 : (x?: any, y?: any, z?: any) => number
267267
>x : any
268268
>y : any
269269
>z : any

0 commit comments

Comments
 (0)