• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/* @internal */
2namespace ts.codefix {
3    const fixMissingMember = "fixMissingMember";
4    const fixMissingProperties = "fixMissingProperties";
5    const fixMissingAttributes = "fixMissingAttributes";
6    const fixMissingFunctionDeclaration = "fixMissingFunctionDeclaration";
7
8    const errorCodes = [
9        Diagnostics.Property_0_does_not_exist_on_type_1.code,
10        Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code,
11        Diagnostics.Property_0_is_missing_in_type_1_but_required_in_type_2.code,
12        Diagnostics.Type_0_is_missing_the_following_properties_from_type_1_Colon_2.code,
13        Diagnostics.Type_0_is_missing_the_following_properties_from_type_1_Colon_2_and_3_more.code,
14        Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1.code,
15        Diagnostics.Cannot_find_name_0.code
16    ];
17
18    enum InfoKind {
19        TypeLikeDeclaration,
20        Enum,
21        Function,
22        ObjectLiteral,
23        JsxAttributes,
24        Signature,
25    }
26
27    registerCodeFix({
28        errorCodes,
29        getCodeActions(context) {
30            const typeChecker = context.program.getTypeChecker();
31            const info = getInfo(context.sourceFile, context.span.start, context.errorCode, typeChecker, context.program);
32            if (!info) {
33                return undefined;
34            }
35            if (info.kind === InfoKind.ObjectLiteral) {
36                const changes = textChanges.ChangeTracker.with(context, t => addObjectLiteralProperties(t, context, info));
37                return [createCodeFixAction(fixMissingProperties, changes, Diagnostics.Add_missing_properties, fixMissingProperties, Diagnostics.Add_all_missing_properties)];
38            }
39            if (info.kind === InfoKind.JsxAttributes) {
40                const changes = textChanges.ChangeTracker.with(context, t => addJsxAttributes(t, context, info));
41                return [createCodeFixAction(fixMissingAttributes, changes, Diagnostics.Add_missing_attributes, fixMissingAttributes, Diagnostics.Add_all_missing_attributes)];
42            }
43            if (info.kind === InfoKind.Function || info.kind === InfoKind.Signature) {
44                const changes = textChanges.ChangeTracker.with(context, t => addFunctionDeclaration(t, context, info));
45                return [createCodeFixAction(fixMissingFunctionDeclaration, changes, [Diagnostics.Add_missing_function_declaration_0, info.token.text], fixMissingFunctionDeclaration, Diagnostics.Add_all_missing_function_declarations)];
46            }
47            if (info.kind === InfoKind.Enum) {
48                const changes = textChanges.ChangeTracker.with(context, t => addEnumMemberDeclaration(t, context.program.getTypeChecker(), info));
49                return [createCodeFixAction(fixMissingMember, changes, [Diagnostics.Add_missing_enum_member_0, info.token.text], fixMissingMember, Diagnostics.Add_all_missing_members)];
50            }
51            return concatenate(getActionsForMissingMethodDeclaration(context, info), getActionsForMissingMemberDeclaration(context, info));
52        },
53        fixIds: [fixMissingMember, fixMissingFunctionDeclaration, fixMissingProperties, fixMissingAttributes],
54        getAllCodeActions: context => {
55            const { program, fixId } = context;
56            const checker = program.getTypeChecker();
57            const seen = new Map<string, true>();
58            const typeDeclToMembers = new Map<ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode, TypeLikeDeclarationInfo[]>();
59
60            return createCombinedCodeActions(textChanges.ChangeTracker.with(context, changes => {
61                eachDiagnostic(context, errorCodes, diag => {
62                    const info = getInfo(diag.file, diag.start, diag.code, checker, context.program);
63                    if (!info || !addToSeen(seen, getNodeId(info.parentDeclaration) + "#" + info.token.text)) {
64                        return;
65                    }
66                    if (fixId === fixMissingFunctionDeclaration && (info.kind === InfoKind.Function || info.kind === InfoKind.Signature)) {
67                        addFunctionDeclaration(changes, context, info);
68                    }
69                    else if (fixId === fixMissingProperties && info.kind === InfoKind.ObjectLiteral) {
70                        addObjectLiteralProperties(changes, context, info);
71                    }
72                    else if (fixId === fixMissingAttributes && info.kind === InfoKind.JsxAttributes) {
73                        addJsxAttributes(changes, context, info);
74                    }
75                    else {
76                        if (info.kind === InfoKind.Enum) {
77                            addEnumMemberDeclaration(changes, checker, info);
78                        }
79                        if (info.kind === InfoKind.TypeLikeDeclaration) {
80                            const { parentDeclaration, token } = info;
81                            const infos = getOrUpdate(typeDeclToMembers, parentDeclaration, () => []);
82                            if (!infos.some(i => i.token.text === token.text)) {
83                                infos.push(info);
84                            }
85                        }
86                    }
87                });
88
89                typeDeclToMembers.forEach((infos, declaration) => {
90                    const supers = isTypeLiteralNode(declaration) ? undefined : getAllSupers(declaration, checker);
91                    for (const info of infos) {
92                        // If some superclass added this property, don't add it again.
93                        if (supers?.some(superClassOrInterface => {
94                            const superInfos = typeDeclToMembers.get(superClassOrInterface);
95                            return !!superInfos && superInfos.some(({ token }) => token.text === info.token.text);
96                        })) continue;
97
98                        const { parentDeclaration, declSourceFile, modifierFlags, token, call, isJSFile } = info;
99                        // Always prefer to add a method declaration if possible.
100                        if (call && !isPrivateIdentifier(token)) {
101                            addMethodDeclaration(context, changes, call, token, modifierFlags & ModifierFlags.Static, parentDeclaration, declSourceFile);
102                        }
103                        else {
104                            if (isJSFile && !isInterfaceDeclaration(parentDeclaration) && !isTypeLiteralNode(parentDeclaration)) {
105                                addMissingMemberInJs(changes, declSourceFile, parentDeclaration, token, !!(modifierFlags & ModifierFlags.Static));
106                            }
107                            else {
108                                const typeNode = getTypeNode(checker, parentDeclaration, token);
109                                addPropertyDeclaration(changes, declSourceFile, parentDeclaration, token.text, typeNode, modifierFlags & ModifierFlags.Static);
110                            }
111                        }
112                    }
113                });
114            }));
115        },
116    });
117
118    type Info = TypeLikeDeclarationInfo | EnumInfo | FunctionInfo | ObjectLiteralInfo | JsxAttributesInfo | SignatureInfo;
119
120    interface EnumInfo {
121        readonly kind: InfoKind.Enum;
122        readonly token: Identifier;
123        readonly parentDeclaration: EnumDeclaration;
124    }
125
126    interface TypeLikeDeclarationInfo {
127        readonly kind: InfoKind.TypeLikeDeclaration;
128        readonly call: CallExpression | undefined;
129        readonly token: Identifier | PrivateIdentifier;
130        readonly modifierFlags: ModifierFlags;
131        readonly parentDeclaration: ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode;
132        readonly declSourceFile: SourceFile;
133        readonly isJSFile: boolean;
134    }
135
136    interface FunctionInfo {
137        readonly kind: InfoKind.Function;
138        readonly call: CallExpression;
139        readonly token: Identifier;
140        readonly sourceFile: SourceFile;
141        readonly modifierFlags: ModifierFlags;
142        readonly parentDeclaration: SourceFile | ModuleDeclaration | ReturnStatement;
143    }
144
145    interface ObjectLiteralInfo {
146        readonly kind: InfoKind.ObjectLiteral;
147        readonly token: Identifier;
148        readonly properties: Symbol[];
149        readonly parentDeclaration: ObjectLiteralExpression;
150        readonly indentation?: number;
151    }
152
153    interface JsxAttributesInfo {
154        readonly kind: InfoKind.JsxAttributes;
155        readonly token: Identifier;
156        readonly attributes: Symbol[];
157        readonly parentDeclaration: JsxOpeningLikeElement;
158    }
159
160    interface SignatureInfo {
161        readonly kind: InfoKind.Signature;
162        readonly token: Identifier;
163        readonly signature: Signature;
164        readonly sourceFile: SourceFile;
165        readonly parentDeclaration: Node;
166    }
167
168    function getInfo(sourceFile: SourceFile, tokenPos: number, errorCode: number, checker: TypeChecker, program: Program): Info | undefined {
169        // The identifier of the missing property. eg:
170        // this.missing = 1;
171        //      ^^^^^^^
172        const token = getTokenAtPosition(sourceFile, tokenPos);
173        const parent = token.parent;
174
175        if (errorCode === Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1.code) {
176            if (!(token.kind === SyntaxKind.OpenBraceToken && isObjectLiteralExpression(parent) && isCallExpression(parent.parent))) return undefined;
177
178            const argIndex = findIndex(parent.parent.arguments, arg => arg === parent);
179            if (argIndex < 0) return undefined;
180
181            const signature = checker.getResolvedSignature(parent.parent);
182            if (!(signature && signature.declaration && signature.parameters[argIndex])) return undefined;
183
184            const param = signature.parameters[argIndex].valueDeclaration;
185            if (!(param && isParameter(param) && isIdentifier(param.name))) return undefined;
186
187            const properties = arrayFrom(checker.getUnmatchedProperties(checker.getTypeAtLocation(parent), checker.getParameterType(signature, argIndex), /* requireOptionalProperties */ false, /* matchDiscriminantProperties */ false));
188            if (!length(properties)) return undefined;
189            return { kind: InfoKind.ObjectLiteral, token: param.name, properties, parentDeclaration: parent };
190        }
191
192        if (!isMemberName(token)) return undefined;
193
194        if (isIdentifier(token) && hasInitializer(parent) && parent.initializer && isObjectLiteralExpression(parent.initializer)) {
195            const properties = arrayFrom(checker.getUnmatchedProperties(checker.getTypeAtLocation(parent.initializer), checker.getTypeAtLocation(token), /* requireOptionalProperties */ false, /* matchDiscriminantProperties */ false));
196            if (!length(properties)) return undefined;
197
198            return { kind: InfoKind.ObjectLiteral, token, properties, parentDeclaration: parent.initializer };
199        }
200
201        if (isIdentifier(token) && isJsxOpeningLikeElement(token.parent)) {
202            const target = getEmitScriptTarget(program.getCompilerOptions());
203            const attributes = getUnmatchedAttributes(checker, target, token.parent);
204            if (!length(attributes)) return undefined;
205            return { kind: InfoKind.JsxAttributes, token, attributes, parentDeclaration: token.parent };
206        }
207
208        if (isIdentifier(token)) {
209            const type = checker.getContextualType(token);
210            if (type && getObjectFlags(type) & ObjectFlags.Anonymous) {
211                const signature = firstOrUndefined(checker.getSignaturesOfType(type, SignatureKind.Call));
212                if (signature === undefined) return undefined;
213                return { kind: InfoKind.Signature, token, signature, sourceFile, parentDeclaration: findScope(token) };
214            }
215            if (isCallExpression(parent) && parent.expression === token) {
216                return { kind: InfoKind.Function, token, call: parent, sourceFile, modifierFlags: ModifierFlags.None, parentDeclaration: findScope(token) };
217            }
218        }
219
220        if (!isPropertyAccessExpression(parent)) return undefined;
221
222        const leftExpressionType = skipConstraint(checker.getTypeAtLocation(parent.expression));
223        const symbol = leftExpressionType.symbol;
224        if (!symbol || !symbol.declarations) return undefined;
225
226        if (isIdentifier(token) && isCallExpression(parent.parent)) {
227            const moduleDeclaration = find(symbol.declarations, isModuleDeclaration);
228            const moduleDeclarationSourceFile = moduleDeclaration?.getSourceFile();
229            if (moduleDeclaration && moduleDeclarationSourceFile && !isSourceFileFromLibrary(program, moduleDeclarationSourceFile)) {
230                return { kind: InfoKind.Function, token, call: parent.parent, sourceFile, modifierFlags: ModifierFlags.Export, parentDeclaration: moduleDeclaration };
231            }
232
233            const moduleSourceFile = find(symbol.declarations, isSourceFile);
234            if (sourceFile.commonJsModuleIndicator) return undefined;
235
236            if (moduleSourceFile && !isSourceFileFromLibrary(program, moduleSourceFile)) {
237                return { kind: InfoKind.Function, token, call: parent.parent, sourceFile: moduleSourceFile, modifierFlags: ModifierFlags.Export, parentDeclaration: moduleSourceFile };
238            }
239        }
240
241        const classDeclaration = find(symbol.declarations, isClassLike);
242        // Don't suggest adding private identifiers to anything other than a class.
243        if (!classDeclaration && isPrivateIdentifier(token)) return undefined;
244
245        // Prefer to change the class instead of the interface if they are merged
246        const declaration = classDeclaration || find(symbol.declarations, d => isInterfaceDeclaration(d) || isTypeLiteralNode(d)) as InterfaceDeclaration | TypeLiteralNode | undefined;
247        if (declaration && !isSourceFileFromLibrary(program, declaration.getSourceFile())) {
248            const makeStatic = !isTypeLiteralNode(declaration) && ((leftExpressionType as TypeReference).target || leftExpressionType) !== checker.getDeclaredTypeOfSymbol(symbol);
249            if (makeStatic && (isPrivateIdentifier(token) || isInterfaceDeclaration(declaration))) return undefined;
250
251            const declSourceFile = declaration.getSourceFile();
252            const modifierFlags = isTypeLiteralNode(declaration) ? ModifierFlags.None :
253                (makeStatic ? ModifierFlags.Static : ModifierFlags.None) | (startsWithUnderscore(token.text) ? ModifierFlags.Private : ModifierFlags.None);
254            const isJSFile = isSourceFileJS(declSourceFile);
255            const call = tryCast(parent.parent, isCallExpression);
256            return { kind: InfoKind.TypeLikeDeclaration, token, call, modifierFlags, parentDeclaration: declaration, declSourceFile, isJSFile };
257        }
258
259        const enumDeclaration = find(symbol.declarations, isEnumDeclaration);
260        if (enumDeclaration && !(leftExpressionType.flags & TypeFlags.EnumLike) && !isPrivateIdentifier(token) && !isSourceFileFromLibrary(program, enumDeclaration.getSourceFile())) {
261            return { kind: InfoKind.Enum, token, parentDeclaration: enumDeclaration };
262        }
263
264        return undefined;
265    }
266
267    function getActionsForMissingMemberDeclaration(context: CodeFixContext, info: TypeLikeDeclarationInfo): CodeFixAction[] | undefined {
268        return info.isJSFile ? singleElementArray(createActionForAddMissingMemberInJavascriptFile(context, info)) :
269            createActionsForAddMissingMemberInTypeScriptFile(context, info);
270    }
271
272    function createActionForAddMissingMemberInJavascriptFile(context: CodeFixContext, { parentDeclaration, declSourceFile, modifierFlags, token }: TypeLikeDeclarationInfo): CodeFixAction | undefined {
273        if (isInterfaceDeclaration(parentDeclaration) || isTypeLiteralNode(parentDeclaration)) {
274            return undefined;
275        }
276
277        const changes = textChanges.ChangeTracker.with(context, t => addMissingMemberInJs(t, declSourceFile, parentDeclaration, token, !!(modifierFlags & ModifierFlags.Static)));
278        if (changes.length === 0) {
279            return undefined;
280        }
281
282        const diagnostic = modifierFlags & ModifierFlags.Static ? Diagnostics.Initialize_static_property_0 :
283            isPrivateIdentifier(token) ? Diagnostics.Declare_a_private_field_named_0 : Diagnostics.Initialize_property_0_in_the_constructor;
284
285        return createCodeFixAction(fixMissingMember, changes, [diagnostic, token.text], fixMissingMember, Diagnostics.Add_all_missing_members);
286    }
287
288    function addMissingMemberInJs(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, token: Identifier | PrivateIdentifier, makeStatic: boolean): void {
289        const tokenName = token.text;
290        if (makeStatic) {
291            if (classDeclaration.kind === SyntaxKind.ClassExpression) {
292                return;
293            }
294            const className = classDeclaration.name!.getText();
295            const staticInitialization = initializePropertyToUndefined(factory.createIdentifier(className), tokenName);
296            changeTracker.insertNodeAfter(sourceFile, classDeclaration, staticInitialization);
297        }
298        else if (isPrivateIdentifier(token)) {
299            const property = factory.createPropertyDeclaration(
300                /*modifiers*/ undefined,
301                tokenName,
302                /*questionToken*/ undefined,
303                /*type*/ undefined,
304                /*initializer*/ undefined);
305
306            const lastProp = getNodeToInsertPropertyAfter(classDeclaration);
307            if (lastProp) {
308                changeTracker.insertNodeAfter(sourceFile, lastProp, property);
309            }
310            else {
311                changeTracker.insertMemberAtStart(sourceFile, classDeclaration, property);
312            }
313        }
314        else {
315            const classConstructor = getFirstConstructorWithBody(classDeclaration);
316            if (!classConstructor) {
317                return;
318            }
319            const propertyInitialization = initializePropertyToUndefined(factory.createThis(), tokenName);
320            changeTracker.insertNodeAtConstructorEnd(sourceFile, classConstructor, propertyInitialization);
321        }
322    }
323
324    function initializePropertyToUndefined(obj: Expression, propertyName: string) {
325        return factory.createExpressionStatement(factory.createAssignment(factory.createPropertyAccessExpression(obj, propertyName), createUndefined()));
326    }
327
328    function createActionsForAddMissingMemberInTypeScriptFile(context: CodeFixContext, { parentDeclaration, declSourceFile, modifierFlags, token }: TypeLikeDeclarationInfo): CodeFixAction[] | undefined {
329        const memberName = token.text;
330        const isStatic = modifierFlags & ModifierFlags.Static;
331        const typeNode = getTypeNode(context.program.getTypeChecker(), parentDeclaration, token);
332        const addPropertyDeclarationChanges = (modifierFlags: ModifierFlags) => textChanges.ChangeTracker.with(context, t => addPropertyDeclaration(t, declSourceFile, parentDeclaration, memberName, typeNode, modifierFlags));
333
334        const actions = [createCodeFixAction(fixMissingMember, addPropertyDeclarationChanges(modifierFlags & ModifierFlags.Static), [isStatic ? Diagnostics.Declare_static_property_0 : Diagnostics.Declare_property_0, memberName], fixMissingMember, Diagnostics.Add_all_missing_members)];
335        if (isStatic || isPrivateIdentifier(token)) {
336            return actions;
337        }
338
339        if (modifierFlags & ModifierFlags.Private) {
340            actions.unshift(createCodeFixActionWithoutFixAll(fixMissingMember, addPropertyDeclarationChanges(ModifierFlags.Private), [Diagnostics.Declare_private_property_0, memberName]));
341        }
342
343        actions.push(createAddIndexSignatureAction(context, declSourceFile, parentDeclaration, token.text, typeNode));
344        return actions;
345    }
346
347    function getTypeNode(checker: TypeChecker, node: ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode, token: Node) {
348        let typeNode: TypeNode | undefined;
349        if (token.parent.parent.kind === SyntaxKind.BinaryExpression) {
350            const binaryExpression = token.parent.parent as BinaryExpression;
351            const otherExpression = token.parent === binaryExpression.left ? binaryExpression.right : binaryExpression.left;
352            const widenedType = checker.getWidenedType(checker.getBaseTypeOfLiteralType(checker.getTypeAtLocation(otherExpression)));
353            typeNode = checker.typeToTypeNode(widenedType, node, NodeBuilderFlags.NoTruncation);
354        }
355        else {
356            const contextualType = checker.getContextualType(token.parent as Expression);
357            typeNode = contextualType ? checker.typeToTypeNode(contextualType, /*enclosingDeclaration*/ undefined, NodeBuilderFlags.NoTruncation) : undefined;
358        }
359        return typeNode || factory.createKeywordTypeNode(SyntaxKind.AnyKeyword);
360    }
361
362    function addPropertyDeclaration(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, node: ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode, tokenName: string, typeNode: TypeNode, modifierFlags: ModifierFlags): void {
363        const modifiers = modifierFlags ? factory.createNodeArray(factory.createModifiersFromModifierFlags(modifierFlags)) : undefined;
364
365        const property = isClassLike(node)
366            ? factory.createPropertyDeclaration(modifiers, tokenName, /*questionToken*/ undefined, typeNode, /*initializer*/ undefined)
367            : factory.createPropertySignature(/*modifiers*/ undefined, tokenName, /*questionToken*/ undefined, typeNode);
368
369        const lastProp = getNodeToInsertPropertyAfter(node);
370        if (lastProp) {
371            changeTracker.insertNodeAfter(sourceFile, lastProp, property);
372        }
373        else {
374            changeTracker.insertMemberAtStart(sourceFile, node, property);
375        }
376    }
377
378    // Gets the last of the first run of PropertyDeclarations, or undefined if the class does not start with a PropertyDeclaration.
379    function getNodeToInsertPropertyAfter(node: ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode): PropertyDeclaration | undefined {
380        let res: PropertyDeclaration | undefined;
381        for (const member of node.members) {
382            if (!isPropertyDeclaration(member)) break;
383            res = member;
384        }
385        return res;
386    }
387
388    function createAddIndexSignatureAction(context: CodeFixContext, sourceFile: SourceFile, node: ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode, tokenName: string, typeNode: TypeNode): CodeFixAction {
389        // Index signatures cannot have the static modifier.
390        const stringTypeNode = factory.createKeywordTypeNode(SyntaxKind.StringKeyword);
391        const indexingParameter = factory.createParameterDeclaration(
392            /*modifiers*/ undefined,
393            /*dotDotDotToken*/ undefined,
394            "x",
395            /*questionToken*/ undefined,
396            stringTypeNode,
397            /*initializer*/ undefined);
398        const indexSignature = factory.createIndexSignature(
399            /*modifiers*/ undefined,
400            [indexingParameter],
401            typeNode);
402
403        const changes = textChanges.ChangeTracker.with(context, t => t.insertMemberAtStart(sourceFile, node, indexSignature));
404        // No fixId here because code-fix-all currently only works on adding individual named properties.
405        return createCodeFixActionWithoutFixAll(fixMissingMember, changes, [Diagnostics.Add_index_signature_for_property_0, tokenName]);
406    }
407
408    function getActionsForMissingMethodDeclaration(context: CodeFixContext, info: TypeLikeDeclarationInfo): CodeFixAction[] | undefined {
409        const { parentDeclaration, declSourceFile, modifierFlags, token, call } = info;
410        if (call === undefined) {
411            return undefined;
412        }
413
414        // Private methods are not implemented yet.
415        if (isPrivateIdentifier(token)) {
416            return undefined;
417        }
418
419        const methodName = token.text;
420        const addMethodDeclarationChanges = (modifierFlags: ModifierFlags) => textChanges.ChangeTracker.with(context, t => addMethodDeclaration(context, t, call, token, modifierFlags, parentDeclaration, declSourceFile));
421        const actions = [createCodeFixAction(fixMissingMember, addMethodDeclarationChanges(modifierFlags & ModifierFlags.Static), [modifierFlags & ModifierFlags.Static ? Diagnostics.Declare_static_method_0 : Diagnostics.Declare_method_0, methodName], fixMissingMember, Diagnostics.Add_all_missing_members)];
422        if (modifierFlags & ModifierFlags.Private) {
423            actions.unshift(createCodeFixActionWithoutFixAll(fixMissingMember, addMethodDeclarationChanges(ModifierFlags.Private), [Diagnostics.Declare_private_method_0, methodName]));
424        }
425        return actions;
426    }
427
428    function addMethodDeclaration(
429        context: CodeFixContextBase,
430        changes: textChanges.ChangeTracker,
431        callExpression: CallExpression,
432        name: Identifier,
433        modifierFlags: ModifierFlags,
434        parentDeclaration: ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode,
435        sourceFile: SourceFile,
436    ): void {
437        const importAdder = createImportAdder(sourceFile, context.program, context.preferences, context.host);
438        const kind = isClassLike(parentDeclaration) ? SyntaxKind.MethodDeclaration : SyntaxKind.MethodSignature;
439        const signatureDeclaration = createSignatureDeclarationFromCallExpression(kind, context, importAdder, callExpression, name, modifierFlags, parentDeclaration) as MethodDeclaration;
440        const containingMethodDeclaration = tryGetContainingMethodDeclaration(parentDeclaration, callExpression);
441        if (containingMethodDeclaration) {
442            changes.insertNodeAfter(sourceFile, containingMethodDeclaration, signatureDeclaration);
443        }
444        else {
445            changes.insertMemberAtStart(sourceFile, parentDeclaration, signatureDeclaration);
446        }
447        importAdder.writeFixes(changes);
448    }
449
450    function addEnumMemberDeclaration(changes: textChanges.ChangeTracker, checker: TypeChecker, { token, parentDeclaration }: EnumInfo) {
451        /**
452         * create initializer only literal enum that has string initializer.
453         * value of initializer is a string literal that equal to name of enum member.
454         * numeric enum or empty enum will not create initializer.
455         */
456        const hasStringInitializer = some(parentDeclaration.members, member => {
457            const type = checker.getTypeAtLocation(member);
458            return !!(type && type.flags & TypeFlags.StringLike);
459        });
460
461        const enumMember = factory.createEnumMember(token, hasStringInitializer ? factory.createStringLiteral(token.text) : undefined);
462        changes.replaceNode(parentDeclaration.getSourceFile(), parentDeclaration, factory.updateEnumDeclaration(
463            parentDeclaration,
464            parentDeclaration.modifiers,
465            parentDeclaration.name,
466            concatenate(parentDeclaration.members, singleElementArray(enumMember))
467        ), {
468            leadingTriviaOption: textChanges.LeadingTriviaOption.IncludeAll,
469            trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude
470        });
471    }
472
473    function addFunctionDeclaration(changes: textChanges.ChangeTracker, context: CodeFixContextBase, info: FunctionInfo | SignatureInfo) {
474        const quotePreference = getQuotePreference(context.sourceFile, context.preferences);
475        const importAdder = createImportAdder(context.sourceFile, context.program, context.preferences, context.host);
476        const functionDeclaration = info.kind === InfoKind.Function
477            ? createSignatureDeclarationFromCallExpression(SyntaxKind.FunctionDeclaration, context, importAdder, info.call, idText(info.token), info.modifierFlags, info.parentDeclaration)
478            : createSignatureDeclarationFromSignature(SyntaxKind.FunctionDeclaration, context, quotePreference, info.signature, createStubbedBody(Diagnostics.Function_not_implemented.message, quotePreference), info.token, /*modifiers*/ undefined, /*optional*/ undefined, /*enclosingDeclaration*/ undefined, importAdder);
479        if (functionDeclaration === undefined) {
480            Debug.fail("fixMissingFunctionDeclaration codefix got unexpected error.");
481        }
482
483        isReturnStatement(info.parentDeclaration)
484            ? changes.insertNodeBefore(info.sourceFile, info.parentDeclaration, functionDeclaration, /*blankLineBetween*/ true)
485            : changes.insertNodeAtEndOfScope(info.sourceFile, info.parentDeclaration, functionDeclaration);
486        importAdder.writeFixes(changes);
487    }
488
489    function addJsxAttributes(changes: textChanges.ChangeTracker, context: CodeFixContextBase, info: JsxAttributesInfo) {
490        const importAdder = createImportAdder(context.sourceFile, context.program, context.preferences, context.host);
491        const quotePreference = getQuotePreference(context.sourceFile, context.preferences);
492        const checker = context.program.getTypeChecker();
493        const jsxAttributesNode = info.parentDeclaration.attributes;
494        const hasSpreadAttribute = some(jsxAttributesNode.properties, isJsxSpreadAttribute);
495        const attrs = map(info.attributes, attr => {
496            const value = tryGetValueFromType(context, checker, importAdder, quotePreference, checker.getTypeOfSymbol(attr), info.parentDeclaration);
497            const name = factory.createIdentifier(attr.name);
498            const jsxAttribute = factory.createJsxAttribute(name, factory.createJsxExpression(/*dotDotDotToken*/ undefined, value));
499            // formattingScanner requires the Identifier to have a context for scanning attributes with "-" (data-foo).
500            setParent(name, jsxAttribute);
501            return jsxAttribute;
502        });
503        const jsxAttributes = factory.createJsxAttributes(hasSpreadAttribute ? [...attrs, ...jsxAttributesNode.properties] : [...jsxAttributesNode.properties, ...attrs]);
504        const options = { prefix: jsxAttributesNode.pos === jsxAttributesNode.end ? " " : undefined };
505        changes.replaceNode(context.sourceFile, jsxAttributesNode, jsxAttributes, options);
506        importAdder.writeFixes(changes);
507    }
508
509    function addObjectLiteralProperties(changes: textChanges.ChangeTracker, context: CodeFixContextBase, info: ObjectLiteralInfo) {
510        const importAdder = createImportAdder(context.sourceFile, context.program, context.preferences, context.host);
511        const quotePreference = getQuotePreference(context.sourceFile, context.preferences);
512        const target = getEmitScriptTarget(context.program.getCompilerOptions());
513        const checker = context.program.getTypeChecker();
514        const props = map(info.properties, prop => {
515            const initializer = tryGetValueFromType(context, checker, importAdder, quotePreference, checker.getTypeOfSymbol(prop), info.parentDeclaration);
516            return factory.createPropertyAssignment(createPropertyNameFromSymbol(prop, target, quotePreference, checker), initializer);
517        });
518        const options = {
519            leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude,
520            trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude,
521            indentation: info.indentation
522        };
523        changes.replaceNode(context.sourceFile, info.parentDeclaration, factory.createObjectLiteralExpression([...info.parentDeclaration.properties, ...props], /*multiLine*/ true), options);
524        importAdder.writeFixes(changes);
525    }
526
527    function tryGetValueFromType(context: CodeFixContextBase, checker: TypeChecker, importAdder: ImportAdder, quotePreference: QuotePreference, type: Type, enclosingDeclaration: Node | undefined): Expression {
528        if (type.flags & TypeFlags.AnyOrUnknown) {
529            return createUndefined();
530        }
531        if (type.flags & (TypeFlags.String | TypeFlags.TemplateLiteral)) {
532            return factory.createStringLiteral("", /* isSingleQuote */ quotePreference === QuotePreference.Single);
533        }
534        if (type.flags & TypeFlags.Number) {
535            return factory.createNumericLiteral(0);
536        }
537        if (type.flags & TypeFlags.BigInt) {
538            return factory.createBigIntLiteral("0n");
539        }
540        if (type.flags & TypeFlags.Boolean) {
541            return factory.createFalse();
542        }
543        if (type.flags & TypeFlags.EnumLike) {
544            const enumMember = type.symbol.exports ? firstOrUndefined(arrayFrom(type.symbol.exports.values())) : type.symbol;
545            const name = checker.symbolToExpression(type.symbol.parent ? type.symbol.parent : type.symbol, SymbolFlags.Value, /*enclosingDeclaration*/ undefined, /*flags*/ undefined);
546            return enumMember === undefined || name === undefined ? factory.createNumericLiteral(0) : factory.createPropertyAccessExpression(name, checker.symbolToString(enumMember));
547        }
548        if (type.flags & TypeFlags.NumberLiteral) {
549            return factory.createNumericLiteral((type as NumberLiteralType).value);
550        }
551        if (type.flags & TypeFlags.BigIntLiteral) {
552            return factory.createBigIntLiteral((type as BigIntLiteralType).value);
553        }
554        if (type.flags & TypeFlags.StringLiteral) {
555            return factory.createStringLiteral((type as StringLiteralType).value, /* isSingleQuote */ quotePreference === QuotePreference.Single);
556        }
557        if (type.flags & TypeFlags.BooleanLiteral) {
558            return (type === checker.getFalseType() || type === checker.getFalseType(/*fresh*/ true)) ? factory.createFalse() : factory.createTrue();
559        }
560        if (type.flags & TypeFlags.Null) {
561            return factory.createNull();
562        }
563        if (type.flags & TypeFlags.Union) {
564            const expression = firstDefined((type as UnionType).types, t => tryGetValueFromType(context, checker, importAdder, quotePreference, t, enclosingDeclaration));
565            return expression ?? createUndefined();
566        }
567        if (checker.isArrayLikeType(type)) {
568            return factory.createArrayLiteralExpression();
569        }
570        if (isObjectLiteralType(type)) {
571            const props = map(checker.getPropertiesOfType(type), prop => {
572                const initializer = prop.valueDeclaration ? tryGetValueFromType(context, checker, importAdder, quotePreference, checker.getTypeAtLocation(prop.valueDeclaration), enclosingDeclaration) : createUndefined();
573                return factory.createPropertyAssignment(prop.name, initializer);
574            });
575            return factory.createObjectLiteralExpression(props, /*multiLine*/ true);
576        }
577        if (getObjectFlags(type) & ObjectFlags.Anonymous) {
578            const decl = find(type.symbol.declarations || emptyArray, or(isFunctionTypeNode, isMethodSignature, isMethodDeclaration));
579            if (decl === undefined) return createUndefined();
580
581            const signature = checker.getSignaturesOfType(type, SignatureKind.Call);
582            if (signature === undefined) return createUndefined();
583
584            const func = createSignatureDeclarationFromSignature(SyntaxKind.FunctionExpression, context, quotePreference, signature[0],
585                createStubbedBody(Diagnostics.Function_not_implemented.message, quotePreference), /*name*/ undefined, /*modifiers*/ undefined, /*optional*/ undefined, /*enclosingDeclaration*/ enclosingDeclaration, importAdder) as FunctionExpression | undefined;
586            return func ?? createUndefined();
587        }
588        if (getObjectFlags(type) & ObjectFlags.Class) {
589            const classDeclaration = getClassLikeDeclarationOfSymbol(type.symbol);
590            if (classDeclaration === undefined || hasAbstractModifier(classDeclaration)) return createUndefined();
591
592            const constructorDeclaration = getFirstConstructorWithBody(classDeclaration);
593            if (constructorDeclaration && length(constructorDeclaration.parameters)) return createUndefined();
594
595            return factory.createNewExpression(factory.createIdentifier(type.symbol.name), /*typeArguments*/ undefined, /*argumentsArray*/ undefined);
596        }
597        return createUndefined();
598    }
599
600    function createUndefined() {
601        return factory.createIdentifier("undefined");
602    }
603
604    function isObjectLiteralType(type: Type) {
605        return (type.flags & TypeFlags.Object) &&
606            ((getObjectFlags(type) & ObjectFlags.ObjectLiteral) || (type.symbol && tryCast(singleOrUndefined(type.symbol.declarations), isTypeLiteralNode)));
607    }
608
609    function getUnmatchedAttributes(checker: TypeChecker, target: ScriptTarget, source: JsxOpeningLikeElement) {
610        const attrsType = checker.getContextualType(source.attributes);
611        if (attrsType === undefined) return emptyArray;
612
613        const targetProps = attrsType.getProperties();
614        if (!length(targetProps)) return emptyArray;
615
616        const seenNames = new Set<__String>();
617        for (const sourceProp of source.attributes.properties) {
618            if (isJsxAttribute(sourceProp)) {
619                seenNames.add(sourceProp.name.escapedText);
620            }
621            if (isJsxSpreadAttribute(sourceProp)) {
622                const type = checker.getTypeAtLocation(sourceProp.expression);
623                for (const prop of type.getProperties()) {
624                    seenNames.add(prop.escapedName);
625                }
626            }
627        }
628        return filter(targetProps, targetProp =>
629            isIdentifierText(targetProp.name, target, LanguageVariant.JSX) && !((targetProp.flags & SymbolFlags.Optional || getCheckFlags(targetProp) & CheckFlags.Partial) || seenNames.has(targetProp.escapedName)));
630    }
631
632    function tryGetContainingMethodDeclaration(node: ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode, callExpression: CallExpression) {
633        if (isTypeLiteralNode(node)) {
634            return undefined;
635        }
636        const declaration = findAncestor(callExpression, n => isMethodDeclaration(n) || isConstructorDeclaration(n));
637        return declaration && declaration.parent === node ? declaration : undefined;
638    }
639
640    function createPropertyNameFromSymbol(symbol: Symbol, target: ScriptTarget, quotePreference: QuotePreference, checker: TypeChecker) {
641        if (isTransientSymbol(symbol)) {
642            const prop = checker.symbolToNode(symbol, SymbolFlags.Value, /*enclosingDeclaration*/ undefined, NodeBuilderFlags.WriteComputedProps);
643            if (prop && isComputedPropertyName(prop)) return prop;
644        }
645        return createPropertyNameNodeForIdentifierOrLiteral(symbol.name, target, quotePreference === QuotePreference.Single);
646    }
647
648    function findScope(node: Node) {
649        if (findAncestor(node, isJsxExpression)) {
650            const returnStatement = findAncestor(node.parent, isReturnStatement);
651            if (returnStatement) return returnStatement;
652        }
653        return getSourceFileOfNode(node);
654    }
655}
656