1/* @internal */ 2namespace ts.codefix { 3 const fixId = "classSuperMustPrecedeThisAccess"; 4 const errorCodes = [Diagnostics.super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class.code]; 5 registerCodeFix({ 6 errorCodes, 7 getCodeActions(context) { 8 const { sourceFile, span } = context; 9 const nodes = getNodes(sourceFile, span.start); 10 if (!nodes) return undefined; 11 const { constructor, superCall } = nodes; 12 const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, constructor, superCall)); 13 return [createCodeFixAction(fixId, changes, Diagnostics.Make_super_call_the_first_statement_in_the_constructor, fixId, Diagnostics.Make_all_super_calls_the_first_statement_in_their_constructor)]; 14 }, 15 fixIds: [fixId], 16 getAllCodeActions(context) { 17 const { sourceFile } = context; 18 const seenClasses = new Map<string, true>(); // Ensure we only do this once per class. 19 return codeFixAll(context, errorCodes, (changes, diag) => { 20 const nodes = getNodes(diag.file, diag.start); 21 if (!nodes) return; 22 const { constructor, superCall } = nodes; 23 if (addToSeen(seenClasses, getNodeId(constructor.parent))) { 24 doChange(changes, sourceFile, constructor, superCall); 25 } 26 }); 27 }, 28 }); 29 30 function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, constructor: ConstructorDeclaration, superCall: ExpressionStatement): void { 31 changes.insertNodeAtConstructorStart(sourceFile, constructor, superCall); 32 changes.delete(sourceFile, superCall); 33 } 34 35 function getNodes(sourceFile: SourceFile, pos: number): { readonly constructor: ConstructorDeclaration, readonly superCall: ExpressionStatement } | undefined { 36 const token = getTokenAtPosition(sourceFile, pos); 37 if (token.kind !== SyntaxKind.ThisKeyword) return undefined; 38 const constructor = getContainingFunction(token) as ConstructorDeclaration; 39 const superCall = findSuperCall(constructor.body!); 40 // figure out if the `this` access is actually inside the supercall 41 // i.e. super(this.a), since in that case we won't suggest a fix 42 return superCall && !superCall.expression.arguments.some(arg => isPropertyAccessExpression(arg) && arg.expression === token) ? { constructor, superCall } : undefined; 43 } 44 45 function findSuperCall(n: Node): ExpressionStatement & { expression: CallExpression } | undefined { 46 return isExpressionStatement(n) && isSuperCall(n.expression) 47 ? n as ExpressionStatement & { expression: CallExpression } 48 : isFunctionLike(n) 49 ? undefined 50 : forEachChild(n, findSuperCall); 51 } 52} 53