mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-24 04:30:53 -06:00
feat(26217): Add missing member fix should work for missing function (#41215)
* feat(26217): Add missing function declaration QF * use codeFixAvailable instead of codeFix * add diagnostics messages 'Method not implemented.' and 'Function not implemented.' * use codeFixAvailable instead of codeFix * fix signature types
This commit is contained in:
parent
66ba15a3fd
commit
0c58edeb29
@ -6195,6 +6195,23 @@
|
||||
"category": "Message",
|
||||
"code": 95155
|
||||
},
|
||||
"Add missing function declaration '{0}'": {
|
||||
"category": "Message",
|
||||
"code": 95156
|
||||
},
|
||||
"Add all missing function declarations": {
|
||||
"category": "Message",
|
||||
"code": 95157
|
||||
},
|
||||
"Method not implemented.": {
|
||||
"category": "Message",
|
||||
"code": 95158
|
||||
},
|
||||
"Function not implemented.": {
|
||||
"category": "Message",
|
||||
"code": 95159
|
||||
},
|
||||
|
||||
|
||||
"No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": {
|
||||
"category": "Error",
|
||||
|
||||
@ -1,34 +1,39 @@
|
||||
/* @internal */
|
||||
namespace ts.codefix {
|
||||
const fixName = "addMissingMember";
|
||||
const fixMissingMember = "fixMissingMember";
|
||||
const fixMissingFunctionDeclaration = "fixMissingFunctionDeclaration";
|
||||
const errorCodes = [
|
||||
Diagnostics.Property_0_does_not_exist_on_type_1.code,
|
||||
Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code,
|
||||
Diagnostics.Property_0_is_missing_in_type_1_but_required_in_type_2.code,
|
||||
Diagnostics.Type_0_is_missing_the_following_properties_from_type_1_Colon_2.code,
|
||||
Diagnostics.Type_0_is_missing_the_following_properties_from_type_1_Colon_2_and_3_more.code
|
||||
Diagnostics.Type_0_is_missing_the_following_properties_from_type_1_Colon_2_and_3_more.code,
|
||||
Diagnostics.Cannot_find_name_0.code
|
||||
];
|
||||
const fixId = "addMissingMember";
|
||||
|
||||
registerCodeFix({
|
||||
errorCodes,
|
||||
getCodeActions(context) {
|
||||
const info = getInfo(context.sourceFile, context.span.start, context.program.getTypeChecker(), context.program);
|
||||
const typeChecker = context.program.getTypeChecker();
|
||||
const info = getInfo(context.sourceFile, context.span.start, typeChecker, context.program);
|
||||
if (!info) {
|
||||
return undefined;
|
||||
}
|
||||
if (info.kind === InfoKind.Function) {
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addFunctionDeclaration(t, context, info));
|
||||
return [createCodeFixAction(fixMissingFunctionDeclaration, changes, [Diagnostics.Add_missing_function_declaration_0, info.token.text], fixMissingFunctionDeclaration, Diagnostics.Add_all_missing_function_declarations)];
|
||||
}
|
||||
if (info.kind === InfoKind.Enum) {
|
||||
const { token, parentDeclaration } = info;
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addEnumMemberDeclaration(t, context.program.getTypeChecker(), token, parentDeclaration));
|
||||
return [createCodeFixAction(fixName, changes, [Diagnostics.Add_missing_enum_member_0, token.text], fixId, Diagnostics.Add_all_missing_members)];
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addEnumMemberDeclaration(t, context.program.getTypeChecker(), info));
|
||||
return [createCodeFixAction(fixMissingMember, changes, [Diagnostics.Add_missing_enum_member_0, info.token.text], fixMissingMember, Diagnostics.Add_all_missing_members)];
|
||||
}
|
||||
return concatenate(getActionsForMissingMethodDeclaration(context, info), getActionsForMissingMemberDeclaration(context, info));
|
||||
},
|
||||
fixIds: [fixId],
|
||||
fixIds: [fixMissingMember, fixMissingFunctionDeclaration],
|
||||
getAllCodeActions: context => {
|
||||
const { program } = context;
|
||||
const { program, fixId } = context;
|
||||
const checker = program.getTypeChecker();
|
||||
const seen = new Map<string, true>();
|
||||
|
||||
const typeDeclToMembers = new Map<ClassOrInterface, ClassOrInterfaceInfo[]>();
|
||||
|
||||
return createCombinedCodeActions(textChanges.ChangeTracker.with(context, changes => {
|
||||
@ -38,14 +43,23 @@ namespace ts.codefix {
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.kind === InfoKind.Enum) {
|
||||
const { token, parentDeclaration } = info;
|
||||
addEnumMemberDeclaration(changes, checker, token, parentDeclaration);
|
||||
if (fixId === fixMissingFunctionDeclaration) {
|
||||
if (info.kind === InfoKind.Function) {
|
||||
addFunctionDeclaration(changes, context, info);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const { parentDeclaration, token } = info;
|
||||
const infos = getOrUpdate(typeDeclToMembers, parentDeclaration, () => []);
|
||||
if (!infos.some(i => i.token.text === token.text)) infos.push(info);
|
||||
if (info.kind === InfoKind.Enum) {
|
||||
addEnumMemberDeclaration(changes, checker, info);
|
||||
}
|
||||
|
||||
if (info.kind === InfoKind.ClassOrInterface) {
|
||||
const { parentDeclaration, token } = info;
|
||||
const infos = getOrUpdate(typeDeclToMembers, parentDeclaration, () => []);
|
||||
if (!infos.some(i => i.token.text === token.text)) {
|
||||
infos.push(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -61,7 +75,7 @@ namespace ts.codefix {
|
||||
const { parentDeclaration, declSourceFile, modifierFlags, token, call, isJSFile } = info;
|
||||
// Always prefer to add a method declaration if possible.
|
||||
if (call && !isPrivateIdentifier(token)) {
|
||||
addMethodDeclaration(context, changes, call, token.text, modifierFlags & ModifierFlags.Static, parentDeclaration, declSourceFile, isJSFile);
|
||||
addMethodDeclaration(context, changes, call, token, modifierFlags & ModifierFlags.Static, parentDeclaration, declSourceFile);
|
||||
}
|
||||
else {
|
||||
if (isJSFile && !isInterfaceDeclaration(parentDeclaration)) {
|
||||
@ -78,12 +92,15 @@ namespace ts.codefix {
|
||||
},
|
||||
});
|
||||
|
||||
const enum InfoKind { Enum, ClassOrInterface }
|
||||
const enum InfoKind { Enum, ClassOrInterface, Function }
|
||||
type Info = EnumInfo | ClassOrInterfaceInfo | FunctionInfo;
|
||||
|
||||
interface EnumInfo {
|
||||
readonly kind: InfoKind.Enum;
|
||||
readonly token: Identifier;
|
||||
readonly parentDeclaration: EnumDeclaration;
|
||||
}
|
||||
|
||||
interface ClassOrInterfaceInfo {
|
||||
readonly kind: InfoKind.ClassOrInterface;
|
||||
readonly call: CallExpression | undefined;
|
||||
@ -93,23 +110,56 @@ namespace ts.codefix {
|
||||
readonly declSourceFile: SourceFile;
|
||||
readonly isJSFile: boolean;
|
||||
}
|
||||
type Info = EnumInfo | ClassOrInterfaceInfo;
|
||||
|
||||
function getInfo(tokenSourceFile: SourceFile, tokenPos: number, checker: TypeChecker, program: Program): Info | undefined {
|
||||
interface FunctionInfo {
|
||||
readonly kind: InfoKind.Function;
|
||||
readonly call: CallExpression;
|
||||
readonly token: Identifier;
|
||||
readonly sourceFile: SourceFile;
|
||||
readonly modifierFlags: ModifierFlags;
|
||||
readonly parentDeclaration: SourceFile | ModuleDeclaration;
|
||||
}
|
||||
|
||||
function getInfo(sourceFile: SourceFile, tokenPos: number, checker: TypeChecker, program: Program): Info | undefined {
|
||||
// The identifier of the missing property. eg:
|
||||
// this.missing = 1;
|
||||
// ^^^^^^^
|
||||
const token = getTokenAtPosition(tokenSourceFile, tokenPos);
|
||||
const token = getTokenAtPosition(sourceFile, tokenPos);
|
||||
if (!isIdentifier(token) && !isPrivateIdentifier(token)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const { parent } = token;
|
||||
if (!isPropertyAccessExpression(parent)) return undefined;
|
||||
if (isIdentifier(token) && isCallExpression(parent)) {
|
||||
return { kind: InfoKind.Function, token, call: parent, sourceFile, modifierFlags: ModifierFlags.None, parentDeclaration: sourceFile };
|
||||
}
|
||||
|
||||
if (!isPropertyAccessExpression(parent)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const leftExpressionType = skipConstraint(checker.getTypeAtLocation(parent.expression));
|
||||
const { symbol } = leftExpressionType;
|
||||
if (!symbol || !symbol.declarations) return undefined;
|
||||
if (!symbol || !symbol.declarations) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (isIdentifier(token) && isCallExpression(parent.parent)) {
|
||||
const moduleDeclaration = find(symbol.declarations, isModuleDeclaration);
|
||||
const moduleDeclarationSourceFile = moduleDeclaration?.getSourceFile();
|
||||
if (moduleDeclaration && moduleDeclarationSourceFile && !program.isSourceFileFromExternalLibrary(moduleDeclarationSourceFile)) {
|
||||
return { kind: InfoKind.Function, token, call: parent.parent, sourceFile, modifierFlags: ModifierFlags.Export, parentDeclaration: moduleDeclaration };
|
||||
}
|
||||
|
||||
const moduleSourceFile = find(symbol.declarations, isSourceFile);
|
||||
if (sourceFile.commonJsModuleIndicator) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (moduleSourceFile && !program.isSourceFileFromExternalLibrary(moduleSourceFile)) {
|
||||
return { kind: InfoKind.Function, token, call: parent.parent, sourceFile: moduleSourceFile, modifierFlags: ModifierFlags.Export, parentDeclaration: moduleSourceFile };
|
||||
}
|
||||
}
|
||||
|
||||
const classDeclaration = find(symbol.declarations, isClassLike);
|
||||
// Don't suggest adding private identifiers to anything other than a class.
|
||||
@ -157,7 +207,7 @@ namespace ts.codefix {
|
||||
const diagnostic = modifierFlags & ModifierFlags.Static ? Diagnostics.Initialize_static_property_0 :
|
||||
isPrivateIdentifier(token) ? Diagnostics.Declare_a_private_field_named_0 : Diagnostics.Initialize_property_0_in_the_constructor;
|
||||
|
||||
return createCodeFixAction(fixName, changes, [diagnostic, token.text], fixId, Diagnostics.Add_all_missing_members);
|
||||
return createCodeFixAction(fixMissingMember, changes, [diagnostic, token.text], fixMissingMember, Diagnostics.Add_all_missing_members);
|
||||
}
|
||||
|
||||
function addMissingMemberInJs(changeTracker: textChanges.ChangeTracker, declSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, token: Identifier | PrivateIdentifier, makeStatic: boolean): void {
|
||||
@ -207,13 +257,13 @@ namespace ts.codefix {
|
||||
const typeNode = getTypeNode(context.program.getTypeChecker(), parentDeclaration, token);
|
||||
const addPropertyDeclarationChanges = (modifierFlags: ModifierFlags) => textChanges.ChangeTracker.with(context, t => addPropertyDeclaration(t, declSourceFile, parentDeclaration, memberName, typeNode, modifierFlags));
|
||||
|
||||
const actions = [createCodeFixAction(fixName, addPropertyDeclarationChanges(modifierFlags & ModifierFlags.Static), [isStatic ? Diagnostics.Declare_static_property_0 : Diagnostics.Declare_property_0, memberName], fixId, Diagnostics.Add_all_missing_members)];
|
||||
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)];
|
||||
if (isStatic || isPrivateIdentifier(token)) {
|
||||
return actions;
|
||||
}
|
||||
|
||||
if (modifierFlags & ModifierFlags.Private) {
|
||||
actions.unshift(createCodeFixActionWithoutFixAll(fixName, addPropertyDeclarationChanges(ModifierFlags.Private), [Diagnostics.Declare_private_property_0, memberName]));
|
||||
actions.unshift(createCodeFixActionWithoutFixAll(fixMissingMember, addPropertyDeclarationChanges(ModifierFlags.Private), [Diagnostics.Declare_private_property_0, memberName]));
|
||||
}
|
||||
|
||||
actions.push(createAddIndexSignatureAction(context, declSourceFile, parentDeclaration, token.text, typeNode));
|
||||
@ -282,11 +332,11 @@ namespace ts.codefix {
|
||||
|
||||
const changes = textChanges.ChangeTracker.with(context, t => t.insertNodeAtClassStart(declSourceFile, classDeclaration, indexSignature));
|
||||
// No fixId here because code-fix-all currently only works on adding individual named properties.
|
||||
return createCodeFixActionWithoutFixAll(fixName, changes, [Diagnostics.Add_index_signature_for_property_0, tokenName]);
|
||||
return createCodeFixActionWithoutFixAll(fixMissingMember, changes, [Diagnostics.Add_index_signature_for_property_0, tokenName]);
|
||||
}
|
||||
|
||||
function getActionsForMissingMethodDeclaration(context: CodeFixContext, info: ClassOrInterfaceInfo): CodeFixAction[] | undefined {
|
||||
const { parentDeclaration, declSourceFile, modifierFlags, token, call, isJSFile } = info;
|
||||
const { parentDeclaration, declSourceFile, modifierFlags, token, call } = info;
|
||||
if (call === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
@ -297,10 +347,10 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
const methodName = token.text;
|
||||
const addMethodDeclarationChanges = (modifierFlags: ModifierFlags) => textChanges.ChangeTracker.with(context, t => addMethodDeclaration(context, t, call, methodName, modifierFlags, parentDeclaration, declSourceFile, isJSFile));
|
||||
const actions = [createCodeFixAction(fixName, addMethodDeclarationChanges(modifierFlags & ModifierFlags.Static), [modifierFlags & ModifierFlags.Static ? Diagnostics.Declare_static_method_0 : Diagnostics.Declare_method_0, methodName], fixId, Diagnostics.Add_all_missing_members)];
|
||||
const addMethodDeclarationChanges = (modifierFlags: ModifierFlags) => textChanges.ChangeTracker.with(context, t => addMethodDeclaration(context, t, call, token, modifierFlags, parentDeclaration, declSourceFile));
|
||||
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)];
|
||||
if (modifierFlags & ModifierFlags.Private) {
|
||||
actions.unshift(createCodeFixActionWithoutFixAll(fixName, addMethodDeclarationChanges(ModifierFlags.Private), [Diagnostics.Declare_private_method_0, methodName]));
|
||||
actions.unshift(createCodeFixActionWithoutFixAll(fixMissingMember, addMethodDeclarationChanges(ModifierFlags.Private), [Diagnostics.Declare_private_method_0, methodName]));
|
||||
}
|
||||
return actions;
|
||||
}
|
||||
@ -309,14 +359,13 @@ namespace ts.codefix {
|
||||
context: CodeFixContextBase,
|
||||
changes: textChanges.ChangeTracker,
|
||||
callExpression: CallExpression,
|
||||
methodName: string,
|
||||
name: Identifier,
|
||||
modifierFlags: ModifierFlags,
|
||||
parentDeclaration: ClassOrInterface,
|
||||
sourceFile: SourceFile,
|
||||
isJSFile: boolean
|
||||
): void {
|
||||
const importAdder = createImportAdder(sourceFile, context.program, context.preferences, context.host);
|
||||
const methodDeclaration = createMethodFromCallExpression(context, importAdder, callExpression, methodName, modifierFlags, parentDeclaration, isJSFile);
|
||||
const methodDeclaration = createSignatureDeclarationFromCallExpression(SyntaxKind.MethodDeclaration, context, importAdder, callExpression, name, modifierFlags, parentDeclaration) as MethodDeclaration;
|
||||
const containingMethodDeclaration = findAncestor(callExpression, n => isMethodDeclaration(n) || isConstructorDeclaration(n));
|
||||
if (containingMethodDeclaration && containingMethodDeclaration.parent === parentDeclaration) {
|
||||
changes.insertNodeAfter(sourceFile, containingMethodDeclaration, methodDeclaration);
|
||||
@ -327,27 +376,33 @@ namespace ts.codefix {
|
||||
importAdder.writeFixes(changes);
|
||||
}
|
||||
|
||||
function addEnumMemberDeclaration(changes: textChanges.ChangeTracker, checker: TypeChecker, token: Identifier, enumDeclaration: EnumDeclaration) {
|
||||
function addEnumMemberDeclaration(changes: textChanges.ChangeTracker, checker: TypeChecker, { token, parentDeclaration }: EnumInfo) {
|
||||
/**
|
||||
* create initializer only literal enum that has string initializer.
|
||||
* value of initializer is a string literal that equal to name of enum member.
|
||||
* numeric enum or empty enum will not create initializer.
|
||||
*/
|
||||
const hasStringInitializer = some(enumDeclaration.members, member => {
|
||||
const hasStringInitializer = some(parentDeclaration.members, member => {
|
||||
const type = checker.getTypeAtLocation(member);
|
||||
return !!(type && type.flags & TypeFlags.StringLike);
|
||||
});
|
||||
|
||||
const enumMember = factory.createEnumMember(token, hasStringInitializer ? factory.createStringLiteral(token.text) : undefined);
|
||||
changes.replaceNode(enumDeclaration.getSourceFile(), enumDeclaration, factory.updateEnumDeclaration(
|
||||
enumDeclaration,
|
||||
enumDeclaration.decorators,
|
||||
enumDeclaration.modifiers,
|
||||
enumDeclaration.name,
|
||||
concatenate(enumDeclaration.members, singleElementArray(enumMember))
|
||||
changes.replaceNode(parentDeclaration.getSourceFile(), parentDeclaration, factory.updateEnumDeclaration(
|
||||
parentDeclaration,
|
||||
parentDeclaration.decorators,
|
||||
parentDeclaration.modifiers,
|
||||
parentDeclaration.name,
|
||||
concatenate(parentDeclaration.members, singleElementArray(enumMember))
|
||||
), {
|
||||
leadingTriviaOption: textChanges.LeadingTriviaOption.IncludeAll,
|
||||
trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude
|
||||
});
|
||||
}
|
||||
|
||||
function addFunctionDeclaration(changes: textChanges.ChangeTracker, context: CodeFixContextBase, info: FunctionInfo) {
|
||||
const importAdder = createImportAdder(context.sourceFile, context.program, context.preferences, context.host);
|
||||
const functionDeclaration = createSignatureDeclarationFromCallExpression(SyntaxKind.FunctionDeclaration, context, importAdder, info.call, info.token, info.modifierFlags, info.parentDeclaration) as FunctionDeclaration;
|
||||
changes.insertNodeAtEndOfScope(info.sourceFile, info.parentDeclaration, functionDeclaration);
|
||||
}
|
||||
}
|
||||
|
||||
@ -247,41 +247,69 @@ namespace ts.codefix {
|
||||
);
|
||||
}
|
||||
|
||||
export function createMethodFromCallExpression(
|
||||
export function createSignatureDeclarationFromCallExpression(
|
||||
kind: SyntaxKind.MethodDeclaration | SyntaxKind.FunctionDeclaration,
|
||||
context: CodeFixContextBase,
|
||||
importAdder: ImportAdder,
|
||||
call: CallExpression,
|
||||
methodName: string,
|
||||
name: Identifier,
|
||||
modifierFlags: ModifierFlags,
|
||||
contextNode: Node,
|
||||
inJs: boolean
|
||||
): MethodDeclaration {
|
||||
const body = !isInterfaceDeclaration(contextNode);
|
||||
const { typeArguments, arguments: args, parent } = call;
|
||||
contextNode: Node
|
||||
) {
|
||||
const quotePreference = getQuotePreference(context.sourceFile, context.preferences);
|
||||
const scriptTarget = getEmitScriptTarget(context.program.getCompilerOptions());
|
||||
const checker = context.program.getTypeChecker();
|
||||
const tracker = getNoopSymbolTrackerWithResolver(context);
|
||||
const types = map(args, arg =>
|
||||
typeToAutoImportableTypeNode(checker, importAdder, checker.getBaseTypeOfLiteralType(checker.getTypeAtLocation(arg)), contextNode, scriptTarget, /*flags*/ undefined, tracker));
|
||||
const checker = context.program.getTypeChecker();
|
||||
const isJs = isInJSFile(contextNode);
|
||||
const { typeArguments, arguments: args, parent } = call;
|
||||
|
||||
const contextualType = isJs ? undefined : checker.getContextualType(call);
|
||||
const names = map(args, arg =>
|
||||
isIdentifier(arg) ? arg.text : isPropertyAccessExpression(arg) && isIdentifier(arg.name) ? arg.name.text : undefined);
|
||||
const contextualType = checker.getContextualType(call);
|
||||
const returnType = (inJs || !contextualType) ? undefined : checker.typeToTypeNode(contextualType, contextNode, /*flags*/ undefined, tracker);
|
||||
const quotePreference = getQuotePreference(context.sourceFile, context.preferences);
|
||||
return factory.createMethodDeclaration(
|
||||
const types = isJs ? [] : map(args, arg =>
|
||||
typeToAutoImportableTypeNode(checker, importAdder, checker.getBaseTypeOfLiteralType(checker.getTypeAtLocation(arg)), contextNode, scriptTarget, /*flags*/ undefined, tracker));
|
||||
|
||||
const modifiers = modifierFlags
|
||||
? factory.createNodeArray(factory.createModifiersFromModifierFlags(modifierFlags))
|
||||
: undefined;
|
||||
const asteriskToken = isYieldExpression(parent)
|
||||
? factory.createToken(SyntaxKind.AsteriskToken)
|
||||
: undefined;
|
||||
const typeParameters = isJs || typeArguments === undefined
|
||||
? undefined
|
||||
: map(typeArguments, (_, i) =>
|
||||
factory.createTypeParameterDeclaration(CharacterCodes.T + typeArguments.length - 1 <= CharacterCodes.Z ? String.fromCharCode(CharacterCodes.T + i) : `T${i}`));
|
||||
const parameters = createDummyParameters(args.length, names, types, /*minArgumentCount*/ undefined, isJs);
|
||||
const type = isJs || contextualType === undefined
|
||||
? undefined
|
||||
: checker.typeToTypeNode(contextualType, contextNode, /*flags*/ undefined, tracker);
|
||||
|
||||
if (kind === SyntaxKind.MethodDeclaration) {
|
||||
return factory.createMethodDeclaration(
|
||||
/*decorators*/ undefined,
|
||||
modifiers,
|
||||
asteriskToken,
|
||||
name,
|
||||
/*questionToken*/ undefined,
|
||||
typeParameters,
|
||||
parameters,
|
||||
type,
|
||||
isInterfaceDeclaration(contextNode) ? undefined : createStubbedMethodBody(quotePreference)
|
||||
);
|
||||
}
|
||||
return factory.createFunctionDeclaration(
|
||||
/*decorators*/ undefined,
|
||||
/*modifiers*/ modifierFlags ? factory.createNodeArray(factory.createModifiersFromModifierFlags(modifierFlags)) : undefined,
|
||||
/*asteriskToken*/ isYieldExpression(parent) ? factory.createToken(SyntaxKind.AsteriskToken) : undefined,
|
||||
methodName,
|
||||
/*questionToken*/ undefined,
|
||||
/*typeParameters*/ inJs ? undefined : map(typeArguments, (_, i) =>
|
||||
factory.createTypeParameterDeclaration(CharacterCodes.T + typeArguments!.length - 1 <= CharacterCodes.Z ? String.fromCharCode(CharacterCodes.T + i) : `T${i}`)),
|
||||
/*parameters*/ createDummyParameters(args.length, names, types, /*minArgumentCount*/ undefined, inJs),
|
||||
/*type*/ returnType,
|
||||
body ? createStubbedMethodBody(quotePreference) : undefined);
|
||||
modifiers,
|
||||
asteriskToken,
|
||||
name,
|
||||
typeParameters,
|
||||
parameters,
|
||||
type,
|
||||
createStubbedBody(Diagnostics.Function_not_implemented.message, quotePreference)
|
||||
);
|
||||
}
|
||||
|
||||
export function typeToAutoImportableTypeNode(checker: TypeChecker, importAdder: ImportAdder, type: Type, contextNode: Node, scriptTarget: ScriptTarget, flags?: NodeBuilderFlags, tracker?: SymbolTracker): TypeNode | undefined {
|
||||
export function typeToAutoImportableTypeNode(checker: TypeChecker, importAdder: ImportAdder, type: Type, contextNode: Node | undefined, scriptTarget: ScriptTarget, flags?: NodeBuilderFlags, tracker?: SymbolTracker): TypeNode | undefined {
|
||||
const typeNode = checker.typeToTypeNode(type, contextNode, flags, tracker);
|
||||
if (typeNode && isImportTypeNode(typeNode)) {
|
||||
const importableReference = tryGetAutoImportableReferenceFromTypeNode(typeNode, scriptTarget);
|
||||
@ -381,14 +409,18 @@ namespace ts.codefix {
|
||||
createStubbedMethodBody(quotePreference));
|
||||
}
|
||||
|
||||
function createStubbedMethodBody(quotePreference: QuotePreference): Block {
|
||||
function createStubbedMethodBody(quotePreference: QuotePreference) {
|
||||
return createStubbedBody(Diagnostics.Method_not_implemented.message, quotePreference);
|
||||
}
|
||||
|
||||
export function createStubbedBody(text: string, quotePreference: QuotePreference): Block {
|
||||
return factory.createBlock(
|
||||
[factory.createThrowStatement(
|
||||
factory.createNewExpression(
|
||||
factory.createIdentifier("Error"),
|
||||
/*typeArguments*/ undefined,
|
||||
// TODO Handle auto quote preference.
|
||||
[factory.createStringLiteral("Method not implemented.", /*isSingleQuote*/ quotePreference === QuotePreference.Single)]))],
|
||||
[factory.createStringLiteral(text, /*isSingleQuote*/ quotePreference === QuotePreference.Single)]))],
|
||||
/*multiline*/ true);
|
||||
}
|
||||
|
||||
|
||||
@ -2,4 +2,14 @@
|
||||
|
||||
////[x, y()] = [0, () => 1];
|
||||
|
||||
verify.not.codeFixAvailable();
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: [ts.Diagnostics.Add_missing_function_declaration_0.message, "y"],
|
||||
newFileContent:
|
||||
`[x, y()] = [0, () => 1];
|
||||
|
||||
function y() {
|
||||
throw new Error("Function not implemented.");
|
||||
}
|
||||
`
|
||||
});
|
||||
@ -0,0 +1,15 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////foo();
|
||||
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: [ts.Diagnostics.Add_missing_function_declaration_0.message, "foo"],
|
||||
newFileContent:
|
||||
`foo();
|
||||
|
||||
function foo() {
|
||||
throw new Error("Function not implemented.");
|
||||
}
|
||||
`
|
||||
});
|
||||
@ -0,0 +1,22 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////namespace Foo {
|
||||
//// export const x = 0;
|
||||
////}
|
||||
////
|
||||
////Foo.test<string, number>();
|
||||
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: [ts.Diagnostics.Add_missing_function_declaration_0.message, "test"],
|
||||
newFileContent:
|
||||
`namespace Foo {
|
||||
export const x = 0;
|
||||
|
||||
export function test<T, U>() {
|
||||
throw new Error("Function not implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
Foo.test<string, number>();`
|
||||
});
|
||||
@ -0,0 +1,23 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @filename: /test.ts
|
||||
////export const x = 1;
|
||||
|
||||
// @filename: /foo.ts
|
||||
////import * as test from "./test";
|
||||
////test.foo();
|
||||
|
||||
goTo.file("/foo.ts");
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: [ts.Diagnostics.Add_missing_function_declaration_0.message, "foo"],
|
||||
newFileContent: {
|
||||
"/test.ts":
|
||||
`export const x = 1;
|
||||
|
||||
export function foo() {
|
||||
throw new Error("Function not implemented.");
|
||||
}
|
||||
`
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,28 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @filename: /test.ts
|
||||
////export const x = 1;
|
||||
|
||||
// @filename: /foo.ts
|
||||
////import * as test from "./test";
|
||||
////test.foo();
|
||||
////test.foo();
|
||||
////test.foo();
|
||||
|
||||
goTo.file("/foo.ts");
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: [ts.Diagnostics.Add_missing_function_declaration_0.message, "foo"],
|
||||
applyChanges: true,
|
||||
newFileContent: {
|
||||
"/test.ts":
|
||||
`export const x = 1;
|
||||
|
||||
export function foo() {
|
||||
throw new Error("Function not implemented.");
|
||||
}
|
||||
`
|
||||
}
|
||||
});
|
||||
|
||||
verify.not.codeFixAvailable();
|
||||
@ -0,0 +1,23 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @filename: /test.ts
|
||||
////export const x = 1;
|
||||
|
||||
// @filename: /foo.ts
|
||||
////import * as test from "./test";
|
||||
////test.foo(1, "", { x: 1, y: 1 });
|
||||
|
||||
goTo.file("/foo.ts");
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: [ts.Diagnostics.Add_missing_function_declaration_0.message, "foo"],
|
||||
newFileContent: {
|
||||
"/test.ts":
|
||||
`export const x = 1;
|
||||
|
||||
export function foo(arg0: number, arg1: string, arg2: { x: number; y: number; }) {
|
||||
throw new Error("Function not implemented.");
|
||||
}
|
||||
`
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,23 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @filename: /test.ts
|
||||
////export const x = 1;
|
||||
|
||||
// @filename: /foo.ts
|
||||
////import * as test from "./test";
|
||||
////const foo: string = test.foo();
|
||||
|
||||
goTo.file("/foo.ts");
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: [ts.Diagnostics.Add_missing_function_declaration_0.message, "foo"],
|
||||
newFileContent: {
|
||||
"/test.ts":
|
||||
`export const x = 1;
|
||||
|
||||
export function foo(): string {
|
||||
throw new Error("Function not implemented.");
|
||||
}
|
||||
`
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,23 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @filename: /test.ts
|
||||
////export const x = 1;
|
||||
|
||||
// @filename: /foo.ts
|
||||
////import * as test from "./test";
|
||||
////test.foo<string, number>();
|
||||
|
||||
goTo.file("/foo.ts");
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: [ts.Diagnostics.Add_missing_function_declaration_0.message, "foo"],
|
||||
newFileContent: {
|
||||
"/test.ts":
|
||||
`export const x = 1;
|
||||
|
||||
export function foo<T, U>() {
|
||||
throw new Error("Function not implemented.");
|
||||
}
|
||||
`
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,12 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @moduleResolution: node
|
||||
// @filename: /node_modules/test/index.js
|
||||
////export const x = 1;
|
||||
|
||||
// @filename: /foo.ts
|
||||
////import * as test from "test";
|
||||
////test.foo();
|
||||
|
||||
goTo.file("/foo.ts");
|
||||
verify.not.codeFixAvailable();
|
||||
@ -0,0 +1,22 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////foo();
|
||||
////foo();
|
||||
////foo();
|
||||
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: [ts.Diagnostics.Add_missing_function_declaration_0.message, "foo"],
|
||||
applyChanges: true,
|
||||
newFileContent:
|
||||
`foo();
|
||||
foo();
|
||||
foo();
|
||||
|
||||
function foo() {
|
||||
throw new Error("Function not implemented.");
|
||||
}
|
||||
`
|
||||
});
|
||||
|
||||
verify.not.codeFixAvailable();
|
||||
@ -0,0 +1,15 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////foo(1, "", { x: 1, y: 1 });
|
||||
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: [ts.Diagnostics.Add_missing_function_declaration_0.message, "foo"],
|
||||
newFileContent:
|
||||
`foo(1, "", { x: 1, y: 1 });
|
||||
|
||||
function foo(arg0: number, arg1: string, arg2: { x: number; y: number; }) {
|
||||
throw new Error("Function not implemented.");
|
||||
}
|
||||
`
|
||||
});
|
||||
@ -0,0 +1,15 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////const test: string = foo();
|
||||
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: [ts.Diagnostics.Add_missing_function_declaration_0.message, "foo"],
|
||||
newFileContent:
|
||||
`const test: string = foo();
|
||||
|
||||
function foo(): string {
|
||||
throw new Error("Function not implemented.");
|
||||
}
|
||||
`
|
||||
});
|
||||
@ -0,0 +1,15 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////foo<string, number>();
|
||||
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: [ts.Diagnostics.Add_missing_function_declaration_0.message, "foo"],
|
||||
newFileContent:
|
||||
`foo<string, number>();
|
||||
|
||||
function foo<T, U>() {
|
||||
throw new Error("Function not implemented.");
|
||||
}
|
||||
`
|
||||
});
|
||||
@ -0,0 +1,22 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////namespace Foo {
|
||||
//// export const x = 0;
|
||||
////}
|
||||
////
|
||||
////Foo.test();
|
||||
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: [ts.Diagnostics.Add_missing_function_declaration_0.message, "test"],
|
||||
newFileContent:
|
||||
`namespace Foo {
|
||||
export const x = 0;
|
||||
|
||||
export function test() {
|
||||
throw new Error("Function not implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
Foo.test();`
|
||||
});
|
||||
@ -0,0 +1,29 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////namespace Foo {
|
||||
//// export const x = 0;
|
||||
////}
|
||||
////
|
||||
////Foo.test();
|
||||
////Foo.test();
|
||||
////Foo.test();
|
||||
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: [ts.Diagnostics.Add_missing_function_declaration_0.message, "test"],
|
||||
applyChanges: true,
|
||||
newFileContent:
|
||||
`namespace Foo {
|
||||
export const x = 0;
|
||||
|
||||
export function test() {
|
||||
throw new Error("Function not implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
Foo.test();
|
||||
Foo.test();
|
||||
Foo.test();`
|
||||
});
|
||||
|
||||
verify.not.codeFixAvailable();
|
||||
@ -0,0 +1,22 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////namespace Foo {
|
||||
//// export const x = 0;
|
||||
////}
|
||||
////
|
||||
////Foo.test(1, "", { x: 1, y: 1 });
|
||||
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: [ts.Diagnostics.Add_missing_function_declaration_0.message, "test"],
|
||||
newFileContent:
|
||||
`namespace Foo {
|
||||
export const x = 0;
|
||||
|
||||
export function test(arg0: number, arg1: string, arg2: { x: number; y: number; }) {
|
||||
throw new Error("Function not implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
Foo.test(1, "", { x: 1, y: 1 });`
|
||||
});
|
||||
@ -0,0 +1,22 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////namespace Foo {
|
||||
//// export const x = 0;
|
||||
////}
|
||||
////
|
||||
////const test: string = Foo.test();
|
||||
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: [ts.Diagnostics.Add_missing_function_declaration_0.message, "test"],
|
||||
newFileContent:
|
||||
`namespace Foo {
|
||||
export const x = 0;
|
||||
|
||||
export function test(): string {
|
||||
throw new Error("Function not implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
const test: string = Foo.test();`
|
||||
});
|
||||
@ -0,0 +1,49 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @filename: /test.ts
|
||||
////export const x = 1;
|
||||
|
||||
// @filename: /foo.ts
|
||||
////import * as test from "./test";
|
||||
////
|
||||
////namespace Foo {
|
||||
//// export const x = 0;
|
||||
////}
|
||||
////
|
||||
////test.f();
|
||||
////Foo.f();
|
||||
////f();
|
||||
|
||||
goTo.file("/foo.ts");
|
||||
verify.codeFixAll({
|
||||
fixId: "fixMissingFunctionDeclaration",
|
||||
fixAllDescription: ts.Diagnostics.Add_all_missing_function_declarations.message,
|
||||
newFileContent: {
|
||||
"/test.ts":
|
||||
`export const x = 1;
|
||||
|
||||
export function f() {
|
||||
throw new Error("Function not implemented.");
|
||||
}
|
||||
`,
|
||||
"/foo.ts":
|
||||
`import * as test from "./test";
|
||||
|
||||
namespace Foo {
|
||||
export const x = 0;
|
||||
|
||||
export function f() {
|
||||
throw new Error("Function not implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
test.f();
|
||||
Foo.f();
|
||||
f();
|
||||
|
||||
function f() {
|
||||
throw new Error("Function not implemented.");
|
||||
}
|
||||
`
|
||||
}
|
||||
});
|
||||
@ -6,4 +6,18 @@
|
||||
////}
|
||||
////Foo.test();
|
||||
|
||||
verify.not.codeFixAvailable();
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: [ts.Diagnostics.Add_missing_function_declaration_0.message, "test"],
|
||||
applyChanges: true,
|
||||
newFileContent:
|
||||
`interface Foo {}
|
||||
namespace Foo {
|
||||
export function bar() { }
|
||||
|
||||
export function test() {
|
||||
throw new Error("Function not implemented.");
|
||||
}
|
||||
}
|
||||
Foo.test();`
|
||||
});
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
////En.A;
|
||||
|
||||
verify.codeFixAll({
|
||||
fixId: "addMissingMember",
|
||||
fixId: "fixMissingMember",
|
||||
fixAllDescription: "Add all missing members",
|
||||
newFileContent:
|
||||
`class C {
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
////}
|
||||
|
||||
verify.codeFixAll({
|
||||
fixId: "addMissingMember",
|
||||
fixId: "fixMissingMember",
|
||||
fixAllDescription: "Add all missing members",
|
||||
newFileContent:
|
||||
`class C {
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
////}
|
||||
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: "Add async modifier to containing function",
|
||||
newFileContent:
|
||||
`async function f() {
|
||||
|
||||
@ -6,4 +6,6 @@
|
||||
////declare var x: number;
|
||||
////export = x;
|
||||
|
||||
verify.not.codeFixAvailable(); // See GH#20191
|
||||
verify.codeFixAvailable([
|
||||
{ description: "Add missing function declaration 'valueOf'" }
|
||||
]);
|
||||
|
||||
@ -18,5 +18,6 @@
|
||||
//// "types": "bin/lib/libfile.d.ts"
|
||||
//// }
|
||||
|
||||
// No fix because this accesses a nested node_modules
|
||||
verify.not.codeFixAvailable();
|
||||
verify.codeFixAvailable([
|
||||
{ description: "Add missing function declaration 'f1'" }
|
||||
]);
|
||||
|
||||
@ -3,4 +3,6 @@
|
||||
// @noImplicitAny: true
|
||||
//// function ...q) {}} f(10);
|
||||
|
||||
verify.not.codeFixAvailable();
|
||||
verify.codeFixAvailable([
|
||||
{ description: "Add missing function declaration 'f'" }
|
||||
]);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user