Skip to content

Commit 05d398e

Browse files
committed
Merge branch 'master' into named-tuple-members
2 parents 9adfb14 + 415b2e7 commit 05d398e

File tree

67 files changed

+2115
-537
lines changed

Some content is hidden

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

67 files changed

+2115
-537
lines changed

CONTRIBUTING.md

+203-216
Large diffs are not rendered by default.

src/compiler/binder.ts

+8-2
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

+77-43
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[] = [];
@@ -5969,6 +5970,7 @@ namespace ts {
59695970
}
59705971
}
59715972

5973+
59725974
// Synthesize declarations for a symbol - might be an Interface, a Class, a Namespace, a Type, a Variable (const, let, or var), an Alias
59735975
// or a merge of some number of those.
59745976
// An interesting challenge is ensuring that when classes merge with namespaces and interfaces, is keeping
@@ -6337,7 +6339,10 @@ namespace ts {
63376339
const baseTypes = getBaseTypes(classType);
63386340
const implementsTypes = getImplementsTypes(classType);
63396341
const staticType = getTypeOfSymbol(symbol);
6340-
const staticBaseType = getBaseConstructorTypeOfClass(staticType as InterfaceType);
6342+
const isClass = !!staticType.symbol?.valueDeclaration && isClassLike(staticType.symbol.valueDeclaration);
6343+
const staticBaseType = isClass
6344+
? getBaseConstructorTypeOfClass(staticType as InterfaceType)
6345+
: anyType;
63416346
const heritageClauses = [
63426347
...!length(baseTypes) ? [] : [createHeritageClause(SyntaxKind.ExtendsKeyword, map(baseTypes, b => serializeBaseType(b, staticBaseType, localName)))],
63436348
...!length(implementsTypes) ? [] : [createHeritageClause(SyntaxKind.ImplementsKeyword, map(implementsTypes, b => serializeBaseType(b, staticBaseType, localName)))]
@@ -6373,7 +6378,17 @@ namespace ts {
63736378
const staticMembers = flatMap(
63746379
filter(getPropertiesOfType(staticType), p => !(p.flags & SymbolFlags.Prototype) && p.escapedName !== "prototype" && !isNamespaceMember(p)),
63756380
p => serializePropertySymbolForClass(p, /*isStatic*/ true, staticBaseType));
6376-
const constructors = serializeSignatures(SignatureKind.Construct, staticType, baseTypes[0], SyntaxKind.Constructor) as ConstructorDeclaration[];
6381+
// When we encounter an `X.prototype.y` assignment in a JS file, we bind `X` as a class regardless as to whether
6382+
// the value is ever initialized with a class or function-like value. For cases where `X` could never be
6383+
// created via `new`, we will inject a `private constructor()` declaration to indicate it is not createable.
6384+
const isNonConstructableClassLikeInJsFile =
6385+
!isClass &&
6386+
!!symbol.valueDeclaration &&
6387+
isInJSFile(symbol.valueDeclaration) &&
6388+
!some(getSignaturesOfType(staticType, SignatureKind.Construct));
6389+
const constructors = isNonConstructableClassLikeInJsFile ?
6390+
[createConstructor(/*decorators*/ undefined, createModifiersFromModifierFlags(ModifierFlags.Private), [], /*body*/ undefined)] :
6391+
serializeSignatures(SignatureKind.Construct, staticType, baseTypes[0], SyntaxKind.Constructor) as ConstructorDeclaration[];
63776392
for (const c of constructors) {
63786393
// A constructor's return type and type parameters are supposed to be controlled by the enclosing class declaration
63796394
// `signatureToSignatureDeclarationHelper` appends them regardless, so for now we delete them here
@@ -20193,7 +20208,7 @@ namespace ts {
2019320208
noCacheCheck = false;
2019420209
}
2019520210
if (flags & (FlowFlags.Assignment | FlowFlags.Condition | FlowFlags.ArrayMutation)) {
20196-
flow = (<FlowAssignment | FlowCondition | FlowArrayMutation | PreFinallyFlow>flow).antecedent;
20211+
flow = (<FlowAssignment | FlowCondition | FlowArrayMutation>flow).antecedent;
2019720212
}
2019820213
else if (flags & FlowFlags.Call) {
2019920214
const signature = getEffectsSignature((<FlowCall>flow).node);
@@ -20243,6 +20258,51 @@ namespace ts {
2024320258
}
2024420259
}
2024520260

20261+
// Return true if the given flow node is preceded by a 'super(...)' call in every possible code path
20262+
// leading to the node.
20263+
function isPostSuperFlowNode(flow: FlowNode, noCacheCheck: boolean): boolean {
20264+
while (true) {
20265+
const flags = flow.flags;
20266+
if (flags & FlowFlags.Shared) {
20267+
if (!noCacheCheck) {
20268+
const id = getFlowNodeId(flow);
20269+
const postSuper = flowNodePostSuper[id];
20270+
return postSuper !== undefined ? postSuper : (flowNodePostSuper[id] = isPostSuperFlowNode(flow, /*noCacheCheck*/ true));
20271+
}
20272+
noCacheCheck = false;
20273+
}
20274+
if (flags & (FlowFlags.Assignment | FlowFlags.Condition | FlowFlags.ArrayMutation | FlowFlags.SwitchClause)) {
20275+
flow = (<FlowAssignment | FlowCondition | FlowArrayMutation | FlowSwitchClause>flow).antecedent;
20276+
}
20277+
else if (flags & FlowFlags.Call) {
20278+
if ((<FlowCall>flow).node.expression.kind === SyntaxKind.SuperKeyword) {
20279+
return true;
20280+
}
20281+
flow = (<FlowCall>flow).antecedent;
20282+
}
20283+
else if (flags & FlowFlags.BranchLabel) {
20284+
// A branching point is post-super if every branch is post-super.
20285+
return every((<FlowLabel>flow).antecedents, f => isPostSuperFlowNode(f, /*noCacheCheck*/ false));
20286+
}
20287+
else if (flags & FlowFlags.LoopLabel) {
20288+
// A loop is post-super if the control flow path that leads to the top is post-super.
20289+
flow = (<FlowLabel>flow).antecedents![0];
20290+
}
20291+
else if (flags & FlowFlags.ReduceLabel) {
20292+
const target = (<FlowReduceLabel>flow).target;
20293+
const saveAntecedents = target.antecedents;
20294+
target.antecedents = (<FlowReduceLabel>flow).antecedents;
20295+
const result = isPostSuperFlowNode((<FlowReduceLabel>flow).antecedent, /*noCacheCheck*/ false);
20296+
target.antecedents = saveAntecedents;
20297+
return result;
20298+
}
20299+
else {
20300+
// Unreachable nodes are considered post-super to silence errors
20301+
return !!(flags & FlowFlags.Unreachable);
20302+
}
20303+
}
20304+
}
20305+
2024620306
function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType = declaredType, flowContainer?: Node, couldBeUninitialized?: boolean) {
2024720307
let key: string | undefined;
2024820308
let keySet = false;
@@ -21642,31 +21702,10 @@ namespace ts {
2164221702
}
2164321703
}
2164421704

21645-
function findFirstSuperCall(n: Node): SuperCall | undefined {
21646-
if (isSuperCall(n)) {
21647-
return n;
21648-
}
21649-
else if (isFunctionLike(n)) {
21650-
return undefined;
21651-
}
21652-
return forEachChild(n, findFirstSuperCall);
21653-
}
21654-
21655-
/**
21656-
* Return a cached result if super-statement is already found.
21657-
* Otherwise, find a super statement in a given constructor function and cache the result in the node-links of the constructor
21658-
*
21659-
* @param constructor constructor-function to look for super statement
21660-
*/
21661-
function getSuperCallInConstructor(constructor: ConstructorDeclaration): SuperCall | undefined {
21662-
const links = getNodeLinks(constructor);
21663-
21664-
// Only trying to find super-call if we haven't yet tried to find one. Once we try, we will record the result
21665-
if (links.hasSuperCall === undefined) {
21666-
links.superCall = findFirstSuperCall(constructor.body!);
21667-
links.hasSuperCall = links.superCall ? true : false;
21668-
}
21669-
return links.superCall!;
21705+
function findFirstSuperCall(node: Node): SuperCall | undefined {
21706+
return isSuperCall(node) ? node :
21707+
isFunctionLike(node) ? undefined :
21708+
forEachChild(node, findFirstSuperCall);
2167021709
}
2167121710

2167221711
/**
@@ -21689,17 +21728,7 @@ namespace ts {
2168921728
// If a containing class does not have extends clause or the class extends null
2169021729
// skip checking whether super statement is called before "this" accessing.
2169121730
if (baseTypeNode && !classDeclarationExtendsNull(containingClassDecl)) {
21692-
const superCall = getSuperCallInConstructor(<ConstructorDeclaration>container);
21693-
21694-
// We should give an error in the following cases:
21695-
// - No super-call
21696-
// - "this" is accessing before super-call.
21697-
// i.e super(this)
21698-
// this.x; super();
21699-
// We want to make sure that super-call is done before accessing "this" so that
21700-
// "this" is not accessed as a parameter of the super-call.
21701-
if (!superCall || superCall.end > node.pos) {
21702-
// In ES6, super inside constructor of class-declaration has to precede "this" accessing
21731+
if (node.flowNode && !isPostSuperFlowNode(node.flowNode, /*noCacheCheck*/ false)) {
2170321732
error(node, diagnosticMessage);
2170421733
}
2170521734
}
@@ -21924,7 +21953,8 @@ namespace ts {
2192421953
function checkSuperExpression(node: Node): Type {
2192521954
const isCallExpression = node.parent.kind === SyntaxKind.CallExpression && (<CallExpression>node.parent).expression === node;
2192621955

21927-
let container = getSuperContainer(node, /*stopOnFunctions*/ true);
21956+
const immediateContainer = getSuperContainer(node, /*stopOnFunctions*/ true);
21957+
let container = immediateContainer;
2192821958
let needToCaptureLexicalThis = false;
2192921959

2193021960
// adjust the container reference in case if super is used inside arrow functions with arbitrarily deep nesting
@@ -21960,7 +21990,7 @@ namespace ts {
2196021990
return errorType;
2196121991
}
2196221992

21963-
if (!isCallExpression && container.kind === SyntaxKind.Constructor) {
21993+
if (!isCallExpression && immediateContainer.kind === SyntaxKind.Constructor) {
2196421994
checkThisBeforeSuper(node, container, Diagnostics.super_must_be_called_before_accessing_a_property_of_super_in_the_constructor_of_a_derived_class);
2196521995
}
2196621996

@@ -22498,6 +22528,10 @@ namespace ts {
2249822528
}
2249922529
}
2250022530

22531+
function isCircularMappedProperty(symbol: Symbol) {
22532+
return !!(getCheckFlags(symbol) & CheckFlags.Mapped && !(<MappedSymbol>symbol).type && findResolutionCycleStartIndex(symbol, TypeSystemPropertyName.Type) >= 0);
22533+
}
22534+
2250122535
function getTypeOfPropertyOfContextualType(type: Type, name: __String) {
2250222536
return mapType(type, t => {
2250322537
if (isGenericMappedType(t)) {
@@ -22511,7 +22545,7 @@ namespace ts {
2251122545
else if (t.flags & TypeFlags.StructuredType) {
2251222546
const prop = getPropertyOfType(t, name);
2251322547
if (prop) {
22514-
return getTypeOfSymbol(prop);
22548+
return isCircularMappedProperty(prop) ? undefined : getTypeOfSymbol(prop);
2251522549
}
2251622550
if (isTupleType(t)) {
2251722551
const restType = getRestTypeOfTupleType(t);
@@ -29999,7 +30033,7 @@ namespace ts {
2999930033
if (getClassExtendsHeritageElement(containingClassDecl)) {
3000030034
captureLexicalThis(node.parent, containingClassDecl);
3000130035
const classExtendsNull = classDeclarationExtendsNull(containingClassDecl);
30002-
const superCall = getSuperCallInConstructor(node);
30036+
const superCall = findFirstSuperCall(node.body!);
3000330037
if (superCall) {
3000430038
if (classExtendsNull) {
3000530039
error(superCall, Diagnostics.A_constructor_cannot_contain_a_super_call_when_its_class_extends_null);

src/compiler/core.ts

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

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

+4-2
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

+3-18
Original file line numberDiff line numberDiff line change
@@ -2803,34 +2803,21 @@ namespace ts {
28032803
}
28042804

28052805
export type FlowNode =
2806-
| AfterFinallyFlow
2807-
| PreFinallyFlow
28082806
| FlowStart
28092807
| FlowLabel
28102808
| FlowAssignment
28112809
| FlowCall
28122810
| FlowCondition
28132811
| FlowSwitchClause
2814-
| FlowArrayMutation;
2812+
| FlowArrayMutation
2813+
| FlowCall
2814+
| FlowReduceLabel;
28152815

28162816
export interface FlowNodeBase {
28172817
flags: FlowFlags;
28182818
id?: number; // Node id used by flow type cache in checker
28192819
}
28202820

2821-
export interface FlowLock {
2822-
locked?: boolean;
2823-
}
2824-
2825-
export interface AfterFinallyFlow extends FlowNodeBase, FlowLock {
2826-
antecedent: FlowNode;
2827-
}
2828-
2829-
export interface PreFinallyFlow extends FlowNodeBase {
2830-
antecedent: FlowNode;
2831-
lock: FlowLock;
2832-
}
2833-
28342821
// FlowStart represents the start of a control flow. For a function expression or arrow
28352822
// function, the node property references the function (which in turn has a flowNode
28362823
// 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

src/harness/fourslashImpl.ts

+6
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,12 @@ namespace FourSlash {
515515
}
516516
}
517517

518+
public verifyOrganizeImports(newContent: string) {
519+
const changes = this.languageService.organizeImports({ fileName: this.activeFile.fileName, type: "file" }, this.formatCodeSettings, ts.emptyOptions);
520+
this.applyChanges(changes);
521+
this.verifyFileContent(this.activeFile.fileName, newContent);
522+
}
523+
518524
private raiseError(message: string): never {
519525
throw new Error(this.messageAtLastKnownMarker(message));
520526
}

src/harness/fourslashInterfaceImpl.ts

+4
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,10 @@ namespace FourSlashInterface {
560560
public noMoveToNewFile(): void {
561561
this.state.noMoveToNewFile();
562562
}
563+
564+
public organizeImports(newContent: string) {
565+
this.state.verifyOrganizeImports(newContent);
566+
}
563567
}
564568

565569
export class Edit {

0 commit comments

Comments
 (0)