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:
Oleksandr T 2021-01-09 02:10:50 +02:00 committed by GitHub
parent 66ba15a3fd
commit 0c58edeb29
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 590 additions and 76 deletions

View File

@ -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",

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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.");
}
`
});

View File

@ -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.");
}
`
});

View File

@ -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>();`
});

View File

@ -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.");
}
`
}
});

View File

@ -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();

View File

@ -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.");
}
`
}
});

View File

@ -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.");
}
`
}
});

View File

@ -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.");
}
`
}
});

View File

@ -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();

View File

@ -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();

View File

@ -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.");
}
`
});

View File

@ -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.");
}
`
});

View File

@ -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.");
}
`
});

View File

@ -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();`
});

View File

@ -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();

View File

@ -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 });`
});

View File

@ -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();`
});

View File

@ -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.");
}
`
}
});

View File

@ -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();`
});

View File

@ -26,7 +26,7 @@
////En.A;
verify.codeFixAll({
fixId: "addMissingMember",
fixId: "fixMissingMember",
fixAllDescription: "Add all missing members",
newFileContent:
`class C {

View File

@ -14,7 +14,7 @@
////}
verify.codeFixAll({
fixId: "addMissingMember",
fixId: "fixMissingMember",
fixAllDescription: "Add all missing members",
newFileContent:
`class C {

View File

@ -7,6 +7,7 @@
////}
verify.codeFix({
index: 0,
description: "Add async modifier to containing function",
newFileContent:
`async function f() {

View File

@ -6,4 +6,6 @@
////declare var x: number;
////export = x;
verify.not.codeFixAvailable(); // See GH#20191
verify.codeFixAvailable([
{ description: "Add missing function declaration 'valueOf'" }
]);

View File

@ -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'" }
]);

View File

@ -3,4 +3,6 @@
// @noImplicitAny: true
//// function ...q) {}} f(10);
verify.not.codeFixAvailable();
verify.codeFixAvailable([
{ description: "Add missing function declaration 'f'" }
]);