1/* @internal */ 2namespace ts.codefix { 3 const errorCodes = [ 4 Diagnostics.Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2.code, 5 Diagnostics.Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1.code, 6 ]; 7 const fixId = "fixClassDoesntImplementInheritedAbstractMember"; 8 registerCodeFix({ 9 errorCodes, 10 getCodeActions(context) { 11 const { sourceFile, span } = context; 12 const changes = textChanges.ChangeTracker.with(context, t => 13 addMissingMembers(getClass(sourceFile, span.start), sourceFile, context, t, context.preferences)); 14 return changes.length === 0 ? undefined : [createCodeFixAction(fixId, changes, Diagnostics.Implement_inherited_abstract_class, fixId, Diagnostics.Implement_all_inherited_abstract_classes)]; 15 }, 16 fixIds: [fixId], 17 getAllCodeActions: context => { 18 const seenClassDeclarations = new Map<string, true>(); 19 return codeFixAll(context, errorCodes, (changes, diag) => { 20 const classDeclaration = getClass(diag.file, diag.start); 21 if (addToSeen(seenClassDeclarations, getNodeId(classDeclaration))) { 22 addMissingMembers(classDeclaration, context.sourceFile, context, changes, context.preferences); 23 } 24 }); 25 }, 26 }); 27 28 function getClass(sourceFile: SourceFile, pos: number): ClassLikeDeclaration { 29 // Token is the identifier in the case of a class declaration 30 // or the class keyword token in the case of a class expression. 31 const token = getTokenAtPosition(sourceFile, pos); 32 return cast(token.parent, isClassLike); 33 } 34 35 function addMissingMembers(classDeclaration: ClassLikeDeclaration, sourceFile: SourceFile, context: TypeConstructionContext, changeTracker: textChanges.ChangeTracker, preferences: UserPreferences): void { 36 const extendsNode = getEffectiveBaseTypeNode(classDeclaration)!; 37 const checker = context.program.getTypeChecker(); 38 const instantiatedExtendsType = checker.getTypeAtLocation(extendsNode); 39 40 // Note that this is ultimately derived from a map indexed by symbol names, 41 // so duplicates cannot occur. 42 const abstractAndNonPrivateExtendsSymbols = checker.getPropertiesOfType(instantiatedExtendsType).filter(symbolPointsToNonPrivateAndAbstractMember); 43 44 const importAdder = createImportAdder(sourceFile, context.program, preferences, context.host); 45 createMissingMemberNodes(classDeclaration, abstractAndNonPrivateExtendsSymbols, sourceFile, context, preferences, importAdder, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member)); 46 importAdder.writeFixes(changeTracker); 47 } 48 49 function symbolPointsToNonPrivateAndAbstractMember(symbol: Symbol): boolean { 50 // See `codeFixClassExtendAbstractProtectedProperty.ts` in https://github.com/Microsoft/TypeScript/pull/11547/files 51 // (now named `codeFixClassExtendAbstractPrivateProperty.ts`) 52 const flags = getSyntacticModifierFlags(first(symbol.getDeclarations()!)); 53 return !(flags & ModifierFlags.Private) && !!(flags & ModifierFlags.Abstract); 54 } 55} 56