Skip to content

Commit 0fbfa63

Browse files
committed
Merge remote-tracking branch 'upstream/master'
* upstream/master: Use control flow analysis to check 'super(...)' call before 'this' access (microsoft#38612) LEGO: check in for master to temporary branch. Make `processTaggedTemplateExpression` visit a returned node goToDefinition: find only the value if it's the RHS of an assignment Fix regression organize imports duplicates comments (microsoft#38599) Fix crash in JS declaration emit (microsoft#38508)
2 parents e87e6af + 3c1f37e commit 0fbfa63

File tree

46 files changed

+1564
-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

+1564
-296
lines changed

src/compiler/binder.ts

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

1006-
function createFlowMutation(flags: FlowFlags, antecedent: FlowNode, node: Node): FlowNode {
1006+
function createFlowMutation(flags: FlowFlags, antecedent: FlowNode, node: Expression | VariableDeclaration | ArrayBindingElement): FlowNode {
10071007
setFlowNodeReferenced(antecedent);
10081008
const result = initFlowNode({ flags, antecedent, node });
10091009
if (currentExceptionTarget) {
@@ -1359,7 +1359,7 @@ namespace ts {
13591359
// is potentially an assertion and is therefore included in the control flow.
13601360
if (node.expression.kind === SyntaxKind.CallExpression) {
13611361
const call = <CallExpression>node.expression;
1362-
if (isDottedName(call.expression)) {
1362+
if (isDottedName(call.expression) && call.expression.kind !== SyntaxKind.SuperKeyword) {
13631363
currentFlow = createFlowCall(currentFlow, call);
13641364
}
13651365
}
@@ -1765,6 +1765,9 @@ namespace ts {
17651765
}
17661766
else {
17671767
bindEachChild(node);
1768+
if (node.expression.kind === SyntaxKind.SuperKeyword) {
1769+
currentFlow = createFlowCall(currentFlow, node);
1770+
}
17681771
}
17691772
}
17701773
if (node.expression.kind === SyntaxKind.PropertyAccessExpression) {
@@ -2496,6 +2499,9 @@ namespace ts {
24962499
node.flowNode = currentFlow;
24972500
}
24982501
return checkStrictModeIdentifier(<Identifier>node);
2502+
case SyntaxKind.SuperKeyword:
2503+
node.flowNode = currentFlow;
2504+
break;
24992505
case SyntaxKind.PrivateIdentifier:
25002506
return checkPrivateIdentifier(node as PrivateIdentifier);
25012507
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[] = [];
@@ -5952,6 +5953,7 @@ namespace ts {
59525953
}
59535954
}
59545955

5956+
59555957
// Synthesize declarations for a symbol - might be an Interface, a Class, a Namespace, a Type, a Variable (const, let, or var), an Alias
59565958
// or a merge of some number of those.
59575959
// An interesting challenge is ensuring that when classes merge with namespaces and interfaces, is keeping
@@ -6320,7 +6322,10 @@ namespace ts {
63206322
const baseTypes = getBaseTypes(classType);
63216323
const implementsTypes = getImplementsTypes(classType);
63226324
const staticType = getTypeOfSymbol(symbol);
6323-
const staticBaseType = getBaseConstructorTypeOfClass(staticType as InterfaceType);
6325+
const isClass = !!staticType.symbol?.valueDeclaration && isClassLike(staticType.symbol.valueDeclaration);
6326+
const staticBaseType = isClass
6327+
? getBaseConstructorTypeOfClass(staticType as InterfaceType)
6328+
: anyType;
63246329
const heritageClauses = [
63256330
...!length(baseTypes) ? [] : [createHeritageClause(SyntaxKind.ExtendsKeyword, map(baseTypes, b => serializeBaseType(b, staticBaseType, localName)))],
63266331
...!length(implementsTypes) ? [] : [createHeritageClause(SyntaxKind.ImplementsKeyword, map(implementsTypes, b => serializeBaseType(b, staticBaseType, localName)))]
@@ -6356,7 +6361,17 @@ namespace ts {
63566361
const staticMembers = flatMap(
63576362
filter(getPropertiesOfType(staticType), p => !(p.flags & SymbolFlags.Prototype) && p.escapedName !== "prototype" && !isNamespaceMember(p)),
63586363
p => serializePropertySymbolForClass(p, /*isStatic*/ true, staticBaseType));
6359-
const constructors = serializeSignatures(SignatureKind.Construct, staticType, baseTypes[0], SyntaxKind.Constructor) as ConstructorDeclaration[];
6364+
// When we encounter an `X.prototype.y` assignment in a JS file, we bind `X` as a class regardless as to whether
6365+
// the value is ever initialized with a class or function-like value. For cases where `X` could never be
6366+
// created via `new`, we will inject a `private constructor()` declaration to indicate it is not createable.
6367+
const isNonConstructableClassLikeInJsFile =
6368+
!isClass &&
6369+
!!symbol.valueDeclaration &&
6370+
isInJSFile(symbol.valueDeclaration) &&
6371+
!some(getSignaturesOfType(staticType, SignatureKind.Construct));
6372+
const constructors = isNonConstructableClassLikeInJsFile ?
6373+
[createConstructor(/*decorators*/ undefined, createModifiersFromModifierFlags(ModifierFlags.Private), [], /*body*/ undefined)] :
6374+
serializeSignatures(SignatureKind.Construct, staticType, baseTypes[0], SyntaxKind.Constructor) as ConstructorDeclaration[];
63606375
for (const c of constructors) {
63616376
// A constructor's return type and type parameters are supposed to be controlled by the enclosing class declaration
63626377
// `signatureToSignatureDeclarationHelper` appends them regardless, so for now we delete them here
@@ -20137,7 +20152,7 @@ namespace ts {
2013720152
noCacheCheck = false;
2013820153
}
2013920154
if (flags & (FlowFlags.Assignment | FlowFlags.Condition | FlowFlags.ArrayMutation)) {
20140-
flow = (<FlowAssignment | FlowCondition | FlowArrayMutation | PreFinallyFlow>flow).antecedent;
20155+
flow = (<FlowAssignment | FlowCondition | FlowArrayMutation>flow).antecedent;
2014120156
}
2014220157
else if (flags & FlowFlags.Call) {
2014320158
const signature = getEffectsSignature((<FlowCall>flow).node);
@@ -20187,6 +20202,51 @@ namespace ts {
2018720202
}
2018820203
}
2018920204

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

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

2161621655
/**
@@ -21633,17 +21672,7 @@ namespace ts {
2163321672
// If a containing class does not have extends clause or the class extends null
2163421673
// skip checking whether super statement is called before "this" accessing.
2163521674
if (baseTypeNode && !classDeclarationExtendsNull(containingClassDecl)) {
21636-
const superCall = getSuperCallInConstructor(<ConstructorDeclaration>container);
21637-
21638-
// We should give an error in the following cases:
21639-
// - No super-call
21640-
// - "this" is accessing before super-call.
21641-
// i.e super(this)
21642-
// this.x; super();
21643-
// We want to make sure that super-call is done before accessing "this" so that
21644-
// "this" is not accessed as a parameter of the super-call.
21645-
if (!superCall || superCall.end > node.pos) {
21646-
// In ES6, super inside constructor of class-declaration has to precede "this" accessing
21675+
if (node.flowNode && !isPostSuperFlowNode(node.flowNode, /*noCacheCheck*/ false)) {
2164721676
error(node, diagnosticMessage);
2164821677
}
2164921678
}
@@ -21868,7 +21897,8 @@ namespace ts {
2186821897
function checkSuperExpression(node: Node): Type {
2186921898
const isCallExpression = node.parent.kind === SyntaxKind.CallExpression && (<CallExpression>node.parent).expression === node;
2187021899

21871-
let container = getSuperContainer(node, /*stopOnFunctions*/ true);
21900+
const immediateContainer = getSuperContainer(node, /*stopOnFunctions*/ true);
21901+
let container = immediateContainer;
2187221902
let needToCaptureLexicalThis = false;
2187321903

2187421904
// adjust the container reference in case if super is used inside arrow functions with arbitrarily deep nesting
@@ -21904,7 +21934,7 @@ namespace ts {
2190421934
return errorType;
2190521935
}
2190621936

21907-
if (!isCallExpression && container.kind === SyntaxKind.Constructor) {
21937+
if (!isCallExpression && immediateContainer.kind === SyntaxKind.Constructor) {
2190821938
checkThisBeforeSuper(node, container, Diagnostics.super_must_be_called_before_accessing_a_property_of_super_in_the_constructor_of_a_derived_class);
2190921939
}
2191021940

@@ -29901,7 +29931,7 @@ namespace ts {
2990129931
if (getClassExtendsHeritageElement(containingClassDecl)) {
2990229932
captureLexicalThis(node.parent, containingClassDecl);
2990329933
const classExtendsNull = classDeclarationExtendsNull(containingClassDecl);
29904-
const superCall = getSuperCallInConstructor(node);
29934+
const superCall = findFirstSuperCall(node.body!);
2990529935
if (superCall) {
2990629936
if (classExtendsNull) {
2990729937
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
@@ -2799,34 +2799,21 @@ namespace ts {
27992799
}
28002800

28012801
export type FlowNode =
2802-
| AfterFinallyFlow
2803-
| PreFinallyFlow
28042802
| FlowStart
28052803
| FlowLabel
28062804
| FlowAssignment
28072805
| FlowCall
28082806
| FlowCondition
28092807
| FlowSwitchClause
2810-
| FlowArrayMutation;
2808+
| FlowArrayMutation
2809+
| FlowCall
2810+
| FlowReduceLabel;
28112811

28122812
export interface FlowNodeBase {
28132813
flags: FlowFlags;
28142814
id?: number; // Node id used by flow type cache in checker
28152815
}
28162816

2817-
export interface FlowLock {
2818-
locked?: boolean;
2819-
}
2820-
2821-
export interface AfterFinallyFlow extends FlowNodeBase, FlowLock {
2822-
antecedent: FlowNode;
2823-
}
2824-
2825-
export interface PreFinallyFlow extends FlowNodeBase {
2826-
antecedent: FlowNode;
2827-
lock: FlowLock;
2828-
}
2829-
28302817
// FlowStart represents the start of a control flow. For a function expression or arrow
28312818
// function, the node property references the function (which in turn has a flowNode
28322819
// property for the containing control flow).
@@ -4329,8 +4316,6 @@ namespace ts {
43294316
resolvedJsxElementAttributesType?: Type; // resolved element attributes type of a JSX openinglike element
43304317
resolvedJsxElementAllAttributesType?: Type; // resolved all element attributes type of a JSX openinglike element
43314318
resolvedJSDocType?: Type; // Resolved type of a JSDoc type reference
4332-
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.
4333-
superCall?: SuperCall; // Cached first super-call found in the constructor. Used in checking whether super is called before this-accessing
43344319
switchTypes?: Type[]; // Cached array of switch case expression types
43354320
jsxNamespace?: Symbol | false; // Resolved jsx namespace symbol for this node
43364321
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)