Skip to content

Commit 6c7697a

Browse files
committed
Merge branch 'master' of github.com:microsoft/TypeScript
2 parents 7b9ca8a + b58c9f4 commit 6c7697a

File tree

46 files changed

+1570
-296
lines changed

Some content is hidden

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

46 files changed

+1570
-296
lines changed

src/compiler/binder.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -985,7 +985,7 @@ namespace ts {
985985
return initFlowNode({ flags: FlowFlags.SwitchClause, antecedent, switchStatement, clauseStart, clauseEnd });
986986
}
987987

988-
function createFlowMutation(flags: FlowFlags, antecedent: FlowNode, node: Node): FlowNode {
988+
function createFlowMutation(flags: FlowFlags, antecedent: FlowNode, node: Expression | VariableDeclaration | ArrayBindingElement): FlowNode {
989989
setFlowNodeReferenced(antecedent);
990990
const result = initFlowNode({ flags, antecedent, node });
991991
if (currentExceptionTarget) {
@@ -1341,7 +1341,7 @@ namespace ts {
13411341
// is potentially an assertion and is therefore included in the control flow.
13421342
if (node.expression.kind === SyntaxKind.CallExpression) {
13431343
const call = <CallExpression>node.expression;
1344-
if (isDottedName(call.expression)) {
1344+
if (isDottedName(call.expression) && call.expression.kind !== SyntaxKind.SuperKeyword) {
13451345
currentFlow = createFlowCall(currentFlow, call);
13461346
}
13471347
}
@@ -1747,6 +1747,9 @@ namespace ts {
17471747
}
17481748
else {
17491749
bindEachChild(node);
1750+
if (node.expression.kind === SyntaxKind.SuperKeyword) {
1751+
currentFlow = createFlowCall(currentFlow, node);
1752+
}
17501753
}
17511754
}
17521755
if (node.expression.kind === SyntaxKind.PropertyAccessExpression) {
@@ -2464,6 +2467,9 @@ namespace ts {
24642467
node.flowNode = currentFlow;
24652468
}
24662469
return checkStrictModeIdentifier(<Identifier>node);
2470+
case SyntaxKind.SuperKeyword:
2471+
node.flowNode = currentFlow;
2472+
break;
24672473
case SyntaxKind.PrivateIdentifier:
24682474
return checkPrivateIdentifier(node as PrivateIdentifier);
24692475
case SyntaxKind.PropertyAccessExpression:

src/compiler/checker.ts

Lines changed: 72 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,7 @@ namespace ts {
911911
const sharedFlowNodes: FlowNode[] = [];
912912
const sharedFlowTypes: FlowType[] = [];
913913
const flowNodeReachable: (boolean | undefined)[] = [];
914+
const flowNodePostSuper: (boolean | undefined)[] = [];
914915
const potentialThisCollisions: Node[] = [];
915916
const potentialNewTargetCollisions: Node[] = [];
916917
const potentialWeakMapCollisions: Node[] = [];
@@ -5949,6 +5950,7 @@ namespace ts {
59495950
}
59505951
}
59515952

5953+
59525954
// Synthesize declarations for a symbol - might be an Interface, a Class, a Namespace, a Type, a Variable (const, let, or var), an Alias
59535955
// or a merge of some number of those.
59545956
// An interesting challenge is ensuring that when classes merge with namespaces and interfaces, is keeping
@@ -6317,7 +6319,10 @@ namespace ts {
63176319
const baseTypes = getBaseTypes(classType);
63186320
const implementsTypes = getImplementsTypes(classType);
63196321
const staticType = getTypeOfSymbol(symbol);
6320-
const staticBaseType = getBaseConstructorTypeOfClass(staticType as InterfaceType);
6322+
const isClass = !!staticType.symbol?.valueDeclaration && isClassLike(staticType.symbol.valueDeclaration);
6323+
const staticBaseType = isClass
6324+
? getBaseConstructorTypeOfClass(staticType as InterfaceType)
6325+
: anyType;
63216326
const heritageClauses = [
63226327
...!length(baseTypes) ? [] : [createHeritageClause(SyntaxKind.ExtendsKeyword, map(baseTypes, b => serializeBaseType(b, staticBaseType, localName)))],
63236328
...!length(implementsTypes) ? [] : [createHeritageClause(SyntaxKind.ImplementsKeyword, map(implementsTypes, b => serializeBaseType(b, staticBaseType, localName)))]
@@ -6353,7 +6358,17 @@ namespace ts {
63536358
const staticMembers = flatMap(
63546359
filter(getPropertiesOfType(staticType), p => !(p.flags & SymbolFlags.Prototype) && p.escapedName !== "prototype" && !isNamespaceMember(p)),
63556360
p => serializePropertySymbolForClass(p, /*isStatic*/ true, staticBaseType));
6356-
const constructors = serializeSignatures(SignatureKind.Construct, staticType, baseTypes[0], SyntaxKind.Constructor) as ConstructorDeclaration[];
6361+
// When we encounter an `X.prototype.y` assignment in a JS file, we bind `X` as a class regardless as to whether
6362+
// the value is ever initialized with a class or function-like value. For cases where `X` could never be
6363+
// created via `new`, we will inject a `private constructor()` declaration to indicate it is not createable.
6364+
const isNonConstructableClassLikeInJsFile =
6365+
!isClass &&
6366+
!!symbol.valueDeclaration &&
6367+
isInJSFile(symbol.valueDeclaration) &&
6368+
!some(getSignaturesOfType(staticType, SignatureKind.Construct));
6369+
const constructors = isNonConstructableClassLikeInJsFile ?
6370+
[createConstructor(/*decorators*/ undefined, createModifiersFromModifierFlags(ModifierFlags.Private), [], /*body*/ undefined)] :
6371+
serializeSignatures(SignatureKind.Construct, staticType, baseTypes[0], SyntaxKind.Constructor) as ConstructorDeclaration[];
63576372
for (const c of constructors) {
63586373
// A constructor's return type and type parameters are supposed to be controlled by the enclosing class declaration
63596374
// `signatureToSignatureDeclarationHelper` appends them regardless, so for now we delete them here
@@ -20134,7 +20149,7 @@ namespace ts {
2013420149
noCacheCheck = false;
2013520150
}
2013620151
if (flags & (FlowFlags.Assignment | FlowFlags.Condition | FlowFlags.ArrayMutation)) {
20137-
flow = (<FlowAssignment | FlowCondition | FlowArrayMutation | PreFinallyFlow>flow).antecedent;
20152+
flow = (<FlowAssignment | FlowCondition | FlowArrayMutation>flow).antecedent;
2013820153
}
2013920154
else if (flags & FlowFlags.Call) {
2014020155
const signature = getEffectsSignature((<FlowCall>flow).node);
@@ -20184,6 +20199,51 @@ namespace ts {
2018420199
}
2018520200
}
2018620201

20202+
// Return true if the given flow node is preceded by a 'super(...)' call in every possible code path
20203+
// leading to the node.
20204+
function isPostSuperFlowNode(flow: FlowNode, noCacheCheck: boolean): boolean {
20205+
while (true) {
20206+
const flags = flow.flags;
20207+
if (flags & FlowFlags.Shared) {
20208+
if (!noCacheCheck) {
20209+
const id = getFlowNodeId(flow);
20210+
const postSuper = flowNodePostSuper[id];
20211+
return postSuper !== undefined ? postSuper : (flowNodePostSuper[id] = isPostSuperFlowNode(flow, /*noCacheCheck*/ true));
20212+
}
20213+
noCacheCheck = false;
20214+
}
20215+
if (flags & (FlowFlags.Assignment | FlowFlags.Condition | FlowFlags.ArrayMutation | FlowFlags.SwitchClause)) {
20216+
flow = (<FlowAssignment | FlowCondition | FlowArrayMutation | FlowSwitchClause>flow).antecedent;
20217+
}
20218+
else if (flags & FlowFlags.Call) {
20219+
if ((<FlowCall>flow).node.expression.kind === SyntaxKind.SuperKeyword) {
20220+
return true;
20221+
}
20222+
flow = (<FlowCall>flow).antecedent;
20223+
}
20224+
else if (flags & FlowFlags.BranchLabel) {
20225+
// A branching point is post-super if every branch is post-super.
20226+
return every((<FlowLabel>flow).antecedents, f => isPostSuperFlowNode(f, /*noCacheCheck*/ false));
20227+
}
20228+
else if (flags & FlowFlags.LoopLabel) {
20229+
// A loop is post-super if the control flow path that leads to the top is post-super.
20230+
flow = (<FlowLabel>flow).antecedents![0];
20231+
}
20232+
else if (flags & FlowFlags.ReduceLabel) {
20233+
const target = (<FlowReduceLabel>flow).target;
20234+
const saveAntecedents = target.antecedents;
20235+
target.antecedents = (<FlowReduceLabel>flow).antecedents;
20236+
const result = isPostSuperFlowNode((<FlowReduceLabel>flow).antecedent, /*noCacheCheck*/ false);
20237+
target.antecedents = saveAntecedents;
20238+
return result;
20239+
}
20240+
else {
20241+
// Unreachable nodes are considered post-super to silence errors
20242+
return !!(flags & FlowFlags.Unreachable);
20243+
}
20244+
}
20245+
}
20246+
2018720247
function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType = declaredType, flowContainer?: Node, couldBeUninitialized?: boolean) {
2018820248
let key: string | undefined;
2018920249
let keySet = false;
@@ -21583,31 +21643,10 @@ namespace ts {
2158321643
}
2158421644
}
2158521645

21586-
function findFirstSuperCall(n: Node): SuperCall | undefined {
21587-
if (isSuperCall(n)) {
21588-
return n;
21589-
}
21590-
else if (isFunctionLike(n)) {
21591-
return undefined;
21592-
}
21593-
return forEachChild(n, findFirstSuperCall);
21594-
}
21595-
21596-
/**
21597-
* Return a cached result if super-statement is already found.
21598-
* Otherwise, find a super statement in a given constructor function and cache the result in the node-links of the constructor
21599-
*
21600-
* @param constructor constructor-function to look for super statement
21601-
*/
21602-
function getSuperCallInConstructor(constructor: ConstructorDeclaration): SuperCall | undefined {
21603-
const links = getNodeLinks(constructor);
21604-
21605-
// Only trying to find super-call if we haven't yet tried to find one. Once we try, we will record the result
21606-
if (links.hasSuperCall === undefined) {
21607-
links.superCall = findFirstSuperCall(constructor.body!);
21608-
links.hasSuperCall = links.superCall ? true : false;
21609-
}
21610-
return links.superCall!;
21646+
function findFirstSuperCall(node: Node): SuperCall | undefined {
21647+
return isSuperCall(node) ? node :
21648+
isFunctionLike(node) ? undefined :
21649+
forEachChild(node, findFirstSuperCall);
2161121650
}
2161221651

2161321652
/**
@@ -21630,17 +21669,7 @@ namespace ts {
2163021669
// If a containing class does not have extends clause or the class extends null
2163121670
// skip checking whether super statement is called before "this" accessing.
2163221671
if (baseTypeNode && !classDeclarationExtendsNull(containingClassDecl)) {
21633-
const superCall = getSuperCallInConstructor(<ConstructorDeclaration>container);
21634-
21635-
// We should give an error in the following cases:
21636-
// - No super-call
21637-
// - "this" is accessing before super-call.
21638-
// i.e super(this)
21639-
// this.x; super();
21640-
// We want to make sure that super-call is done before accessing "this" so that
21641-
// "this" is not accessed as a parameter of the super-call.
21642-
if (!superCall || superCall.end > node.pos) {
21643-
// In ES6, super inside constructor of class-declaration has to precede "this" accessing
21672+
if (node.flowNode && !isPostSuperFlowNode(node.flowNode, /*noCacheCheck*/ false)) {
2164421673
error(node, diagnosticMessage);
2164521674
}
2164621675
}
@@ -21865,7 +21894,8 @@ namespace ts {
2186521894
function checkSuperExpression(node: Node): Type {
2186621895
const isCallExpression = node.parent.kind === SyntaxKind.CallExpression && (<CallExpression>node.parent).expression === node;
2186721896

21868-
let container = getSuperContainer(node, /*stopOnFunctions*/ true);
21897+
const immediateContainer = getSuperContainer(node, /*stopOnFunctions*/ true);
21898+
let container = immediateContainer;
2186921899
let needToCaptureLexicalThis = false;
2187021900

2187121901
// adjust the container reference in case if super is used inside arrow functions with arbitrarily deep nesting
@@ -21901,7 +21931,7 @@ namespace ts {
2190121931
return errorType;
2190221932
}
2190321933

21904-
if (!isCallExpression && container.kind === SyntaxKind.Constructor) {
21934+
if (!isCallExpression && immediateContainer.kind === SyntaxKind.Constructor) {
2190521935
checkThisBeforeSuper(node, container, Diagnostics.super_must_be_called_before_accessing_a_property_of_super_in_the_constructor_of_a_derived_class);
2190621936
}
2190721937

@@ -29898,7 +29928,7 @@ namespace ts {
2989829928
if (getClassExtendsHeritageElement(containingClassDecl)) {
2989929929
captureLexicalThis(node.parent, containingClassDecl);
2990029930
const classExtendsNull = classDeclarationExtendsNull(containingClassDecl);
29901-
const superCall = getSuperCallInConstructor(node);
29931+
const superCall = findFirstSuperCall(node.body!);
2990229932
if (superCall) {
2990329933
if (classExtendsNull) {
2990429934
error(superCall, Diagnostics.A_constructor_cannot_contain_a_super_call_when_its_class_extends_null);

src/compiler/core.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ namespace ts {
150150
* returns a falsey value, then returns false.
151151
* If no such value is found, the callback is applied to each element of array and `true` is returned.
152152
*/
153-
export function every<T>(array: readonly T[], callback: (element: T, index: number) => boolean): boolean {
153+
export function every<T>(array: readonly T[] | undefined, callback: (element: T, index: number) => boolean): boolean {
154154
if (array) {
155155
for (let i = 0; i < array.length; i++) {
156156
if (!callback(array[i], i)) {

src/compiler/transformers/es2015.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2583,7 +2583,7 @@ namespace ts {
25832583
&& i < numInitialPropertiesWithoutYield) {
25842584
numInitialPropertiesWithoutYield = i;
25852585
}
2586-
if (property.name!.kind === SyntaxKind.ComputedPropertyName) {
2586+
if (Debug.checkDefined(property.name).kind === SyntaxKind.ComputedPropertyName) {
25872587
numInitialProperties = i;
25882588
break;
25892589
}

src/compiler/transformers/taggedTemplate.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace ts {
88
export function processTaggedTemplateExpression(
99
context: TransformationContext,
1010
node: TaggedTemplateExpression,
11-
visitor: ((node: Node) => VisitResult<Node>) | undefined,
11+
visitor: Visitor,
1212
currentSourceFile: SourceFile,
1313
recordTaggedTemplateString: (temp: Identifier) => void,
1414
level: ProcessLevel) {
@@ -24,7 +24,9 @@ namespace ts {
2424
const rawStrings: Expression[] = [];
2525
const template = node.template;
2626

27-
if (level === ProcessLevel.LiftRestriction && !hasInvalidEscape(template)) return node;
27+
if (level === ProcessLevel.LiftRestriction && !hasInvalidEscape(template)) {
28+
return visitEachChild(node, visitor, context);
29+
}
2830

2931
if (isNoSubstitutionTemplateLiteral(template)) {
3032
cookedStrings.push(createTemplateCooked(template));

src/compiler/types.ts

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2796,34 +2796,21 @@ namespace ts {
27962796
}
27972797

27982798
export type FlowNode =
2799-
| AfterFinallyFlow
2800-
| PreFinallyFlow
28012799
| FlowStart
28022800
| FlowLabel
28032801
| FlowAssignment
28042802
| FlowCall
28052803
| FlowCondition
28062804
| FlowSwitchClause
2807-
| FlowArrayMutation;
2805+
| FlowArrayMutation
2806+
| FlowCall
2807+
| FlowReduceLabel;
28082808

28092809
export interface FlowNodeBase {
28102810
flags: FlowFlags;
28112811
id?: number; // Node id used by flow type cache in checker
28122812
}
28132813

2814-
export interface FlowLock {
2815-
locked?: boolean;
2816-
}
2817-
2818-
export interface AfterFinallyFlow extends FlowNodeBase, FlowLock {
2819-
antecedent: FlowNode;
2820-
}
2821-
2822-
export interface PreFinallyFlow extends FlowNodeBase {
2823-
antecedent: FlowNode;
2824-
lock: FlowLock;
2825-
}
2826-
28272814
// FlowStart represents the start of a control flow. For a function expression or arrow
28282815
// function, the node property references the function (which in turn has a flowNode
28292816
// property for the containing control flow).
@@ -4321,8 +4308,6 @@ namespace ts {
43214308
resolvedJsxElementAttributesType?: Type; // resolved element attributes type of a JSX openinglike element
43224309
resolvedJsxElementAllAttributesType?: Type; // resolved all element attributes type of a JSX openinglike element
43234310
resolvedJSDocType?: Type; // Resolved type of a JSDoc type reference
4324-
hasSuperCall?: boolean; // recorded result when we try to find super-call. We only try to find one if this flag is undefined, indicating that we haven't made an attempt.
4325-
superCall?: SuperCall; // Cached first super-call found in the constructor. Used in checking whether super is called before this-accessing
43264311
switchTypes?: Type[]; // Cached array of switch case expression types
43274312
jsxNamespace?: Symbol | false; // Resolved jsx namespace symbol for this node
43284313
contextFreeType?: Type; // Cached context-free type used by the first pass of inference; used when a function's return is partially contextually sensitive

0 commit comments

Comments
 (0)