mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-07-02 06:35:09 -05:00
Merge pull request #25182 from Kingwl/fix-missing-enum-member
add quick fix for add missing enum member
This commit is contained in:
@@ -4446,5 +4446,13 @@
|
||||
"Convert named export to default export": {
|
||||
"category": "Message",
|
||||
"code": 95062
|
||||
},
|
||||
"Add missing enum member '{0}'": {
|
||||
"category": "Message",
|
||||
"code": 95063
|
||||
},
|
||||
"Add all missing enum members": {
|
||||
"category": "Message",
|
||||
"code": 95064
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,12 @@ namespace ts.codefix {
|
||||
getCodeActions(context) {
|
||||
const info = getInfo(context.sourceFile, context.span.start, context.program.getTypeChecker());
|
||||
if (!info) return undefined;
|
||||
|
||||
if (info.kind === InfoKind.enum) {
|
||||
const { token, enumDeclaration } = info;
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addEnumMemberDeclaration(t, context.program.getTypeChecker(), token, enumDeclaration));
|
||||
return singleElementArray(createCodeFixAction(fixName, changes, [Diagnostics.Add_missing_enum_member_0, token.text], fixId, Diagnostics.Add_all_missing_enum_members));
|
||||
}
|
||||
const { classDeclaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info;
|
||||
const methodCodeAction = call && getActionForMethodDeclaration(context, classDeclarationSourceFile, classDeclaration, token, call, makeStatic, inJs, context.preferences);
|
||||
const addMember = inJs ?
|
||||
@@ -23,31 +29,41 @@ namespace ts.codefix {
|
||||
const seenNames = createMap<true>();
|
||||
return codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
const { program, preferences } = context;
|
||||
const info = getInfo(diag.file, diag.start, program.getTypeChecker());
|
||||
if (!info) return;
|
||||
const { classDeclaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info;
|
||||
if (!addToSeen(seenNames, token.text)) {
|
||||
const checker = program.getTypeChecker();
|
||||
const info = getInfo(diag.file, diag.start, checker);
|
||||
if (!info || !addToSeen(seenNames, info.token.text)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Always prefer to add a method declaration if possible.
|
||||
if (call) {
|
||||
addMethodDeclaration(context, changes, classDeclarationSourceFile, classDeclaration, token, call, makeStatic, inJs, preferences);
|
||||
if (info.kind === InfoKind.enum) {
|
||||
const { token, enumDeclaration } = info;
|
||||
addEnumMemberDeclaration(changes, checker, token, enumDeclaration);
|
||||
}
|
||||
else {
|
||||
if (inJs) {
|
||||
addMissingMemberInJs(changes, classDeclarationSourceFile, classDeclaration, token.text, makeStatic);
|
||||
const { classDeclaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info;
|
||||
// Always prefer to add a method declaration if possible.
|
||||
if (call) {
|
||||
addMethodDeclaration(context, changes, classDeclarationSourceFile, classDeclaration, token, call, makeStatic, inJs, preferences);
|
||||
}
|
||||
else {
|
||||
const typeNode = getTypeNode(program.getTypeChecker(), classDeclaration, token);
|
||||
addPropertyDeclaration(changes, classDeclarationSourceFile, classDeclaration, token.text, typeNode, makeStatic);
|
||||
if (inJs) {
|
||||
addMissingMemberInJs(changes, classDeclarationSourceFile, classDeclaration, token.text, makeStatic);
|
||||
}
|
||||
else {
|
||||
const typeNode = getTypeNode(program.getTypeChecker(), classDeclaration, token);
|
||||
addPropertyDeclaration(changes, classDeclarationSourceFile, classDeclaration, token.text, typeNode, makeStatic);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
interface Info { token: Identifier; classDeclaration: ClassLikeDeclaration; makeStatic: boolean; classDeclarationSourceFile: SourceFile; inJs: boolean; call: CallExpression | undefined; }
|
||||
enum InfoKind { enum, class }
|
||||
interface EnumInfo { kind: InfoKind.enum; token: Identifier; enumDeclaration: EnumDeclaration; }
|
||||
interface ClassInfo { kind: InfoKind.class; token: Identifier; classDeclaration: ClassLikeDeclaration; makeStatic: boolean; classDeclarationSourceFile: SourceFile; inJs: boolean; call: CallExpression | undefined; }
|
||||
type Info = EnumInfo | ClassInfo;
|
||||
|
||||
function getInfo(tokenSourceFile: SourceFile, tokenPos: number, checker: TypeChecker): Info | undefined {
|
||||
// The identifier of the missing property. eg:
|
||||
// this.missing = 1;
|
||||
@@ -62,15 +78,21 @@ namespace ts.codefix {
|
||||
|
||||
const leftExpressionType = skipConstraint(checker.getTypeAtLocation(parent.expression)!);
|
||||
const { symbol } = leftExpressionType;
|
||||
const classDeclaration = symbol && symbol.declarations && find(symbol.declarations, isClassLike);
|
||||
if (!classDeclaration) return undefined;
|
||||
if (!symbol || !symbol.declarations) return undefined;
|
||||
|
||||
const makeStatic = (leftExpressionType as TypeReference).target !== checker.getDeclaredTypeOfSymbol(symbol);
|
||||
const classDeclarationSourceFile = classDeclaration.getSourceFile();
|
||||
const inJs = isSourceFileJavaScript(classDeclarationSourceFile);
|
||||
const call = tryCast(parent.parent, isCallExpression);
|
||||
|
||||
return { token, classDeclaration, makeStatic, classDeclarationSourceFile, inJs, call };
|
||||
const classDeclaration = find(symbol.declarations, isClassLike);
|
||||
if (classDeclaration) {
|
||||
const makeStatic = (leftExpressionType as TypeReference).target !== checker.getDeclaredTypeOfSymbol(symbol);
|
||||
const classDeclarationSourceFile = classDeclaration.getSourceFile();
|
||||
const inJs = isSourceFileJavaScript(classDeclarationSourceFile);
|
||||
const call = tryCast(parent.parent, isCallExpression);
|
||||
return { kind: InfoKind.class, token, classDeclaration, makeStatic, classDeclarationSourceFile, inJs, call };
|
||||
}
|
||||
const enumDeclaration = find(symbol.declarations, isEnumDeclaration);
|
||||
if (enumDeclaration) {
|
||||
return { kind: InfoKind.enum, token, enumDeclaration };
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getActionsForAddMissingMemberInJavaScriptFile(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, makeStatic: boolean): CodeFixAction | undefined {
|
||||
@@ -209,4 +231,25 @@ namespace ts.codefix {
|
||||
changeTracker.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, methodDeclaration);
|
||||
}
|
||||
}
|
||||
|
||||
function addEnumMemberDeclaration(changes: textChanges.ChangeTracker, checker: TypeChecker, token: Identifier, enumDeclaration: EnumDeclaration) {
|
||||
/**
|
||||
* 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 type = checker.getTypeAtLocation(member);
|
||||
return !!(type && type.flags & TypeFlags.StringLike);
|
||||
});
|
||||
|
||||
const enumMember = createEnumMember(token, hasStringInitializer ? createStringLiteral(token.text) : undefined);
|
||||
changes.replaceNode(enumDeclaration.getSourceFile(), enumDeclaration, updateEnumDeclaration(
|
||||
enumDeclaration,
|
||||
enumDeclaration.decorators,
|
||||
enumDeclaration.modifiers,
|
||||
enumDeclaration.name,
|
||||
concatenate(enumDeclaration.members, singleElementArray(enumMember))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5946,6 +5946,8 @@ declare namespace ts {
|
||||
Remove_braces_from_arrow_function: DiagnosticMessage;
|
||||
Convert_default_export_to_named_export: DiagnosticMessage;
|
||||
Convert_named_export_to_default_export: DiagnosticMessage;
|
||||
Add_missing_enum_member_0: DiagnosticMessage;
|
||||
Add_all_missing_enum_members: DiagnosticMessage;
|
||||
};
|
||||
}
|
||||
declare namespace ts {
|
||||
|
||||
16
tests/cases/fourslash/codeFixAddMissingEnumMember1.ts
Normal file
16
tests/cases/fourslash/codeFixAddMissingEnumMember1.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////enum E {
|
||||
//// a
|
||||
////}
|
||||
////E.b
|
||||
|
||||
verify.codeFix({
|
||||
description: "Add missing enum member 'b'",
|
||||
newFileContent: `enum E {
|
||||
a,
|
||||
b
|
||||
}
|
||||
E.b`
|
||||
});
|
||||
|
||||
25
tests/cases/fourslash/codeFixAddMissingEnumMember10.ts
Normal file
25
tests/cases/fourslash/codeFixAddMissingEnumMember10.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////enum E {
|
||||
//// a,
|
||||
//// b = 1,
|
||||
//// c = "123"
|
||||
////}
|
||||
////enum A {
|
||||
//// a = E.c
|
||||
////}
|
||||
////A.b
|
||||
|
||||
verify.codeFix({
|
||||
description: "Add missing enum member 'b'",
|
||||
newFileContent: `enum E {
|
||||
a,
|
||||
b = 1,
|
||||
c = "123"
|
||||
}
|
||||
enum A {
|
||||
a = E.c,
|
||||
b
|
||||
}
|
||||
A.b`
|
||||
});
|
||||
31
tests/cases/fourslash/codeFixAddMissingEnumMember11.ts
Normal file
31
tests/cases/fourslash/codeFixAddMissingEnumMember11.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////enum E {
|
||||
//// a,
|
||||
//// b = 1,
|
||||
//// c = "123"
|
||||
////}
|
||||
////enum A {
|
||||
//// a = E.c
|
||||
////}
|
||||
////enum B {
|
||||
//// b = A.a
|
||||
////}
|
||||
////B.c
|
||||
|
||||
verify.codeFix({
|
||||
description: "Add missing enum member 'c'",
|
||||
newFileContent: `enum E {
|
||||
a,
|
||||
b = 1,
|
||||
c = "123"
|
||||
}
|
||||
enum A {
|
||||
a = E.c
|
||||
}
|
||||
enum B {
|
||||
b = A.a,
|
||||
c
|
||||
}
|
||||
B.c`
|
||||
});
|
||||
16
tests/cases/fourslash/codeFixAddMissingEnumMember2.ts
Normal file
16
tests/cases/fourslash/codeFixAddMissingEnumMember2.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////enum E {
|
||||
//// a = 1
|
||||
////}
|
||||
////E.b
|
||||
|
||||
verify.codeFix({
|
||||
description: "Add missing enum member 'b'",
|
||||
newFileContent: `enum E {
|
||||
a = 1,
|
||||
b
|
||||
}
|
||||
E.b`
|
||||
});
|
||||
|
||||
21
tests/cases/fourslash/codeFixAddMissingEnumMember3.ts
Normal file
21
tests/cases/fourslash/codeFixAddMissingEnumMember3.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////enum E {
|
||||
//// a,
|
||||
//// b = 1,
|
||||
//// c
|
||||
////}
|
||||
////E.d
|
||||
|
||||
verify.codeFix({
|
||||
description: "Add missing enum member 'd'",
|
||||
newFileContent: `enum E {
|
||||
a,
|
||||
b = 1,
|
||||
c,
|
||||
d
|
||||
}
|
||||
E.d`
|
||||
});
|
||||
|
||||
|
||||
17
tests/cases/fourslash/codeFixAddMissingEnumMember4.ts
Normal file
17
tests/cases/fourslash/codeFixAddMissingEnumMember4.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////enum E {
|
||||
//// a = "a",
|
||||
////}
|
||||
////E.b
|
||||
|
||||
verify.codeFix({
|
||||
description: "Add missing enum member 'b'",
|
||||
newFileContent: `enum E {
|
||||
a = "a",
|
||||
b = "b"
|
||||
}
|
||||
E.b`
|
||||
});
|
||||
|
||||
|
||||
17
tests/cases/fourslash/codeFixAddMissingEnumMember5.ts
Normal file
17
tests/cases/fourslash/codeFixAddMissingEnumMember5.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////enum E {
|
||||
//// a = "a" + "-",
|
||||
////}
|
||||
////E.b
|
||||
|
||||
verify.codeFix({
|
||||
description: "Add missing enum member 'b'",
|
||||
newFileContent: `enum E {
|
||||
a = "a" + "-",
|
||||
b = "b"
|
||||
}
|
||||
E.b`
|
||||
});
|
||||
|
||||
|
||||
17
tests/cases/fourslash/codeFixAddMissingEnumMember6.ts
Normal file
17
tests/cases/fourslash/codeFixAddMissingEnumMember6.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////enum E {
|
||||
//// a = "b"
|
||||
////}
|
||||
////E.b
|
||||
|
||||
verify.codeFix({
|
||||
description: "Add missing enum member 'b'",
|
||||
newFileContent: `enum E {
|
||||
a = "b",
|
||||
b = "b"
|
||||
}
|
||||
E.b`
|
||||
});
|
||||
|
||||
|
||||
15
tests/cases/fourslash/codeFixAddMissingEnumMember7.ts
Normal file
15
tests/cases/fourslash/codeFixAddMissingEnumMember7.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////enum E {
|
||||
////}
|
||||
////E.a
|
||||
|
||||
verify.codeFix({
|
||||
description: "Add missing enum member 'a'",
|
||||
newFileContent: `enum E {
|
||||
a
|
||||
}
|
||||
E.a`
|
||||
});
|
||||
|
||||
|
||||
21
tests/cases/fourslash/codeFixAddMissingEnumMember8.ts
Normal file
21
tests/cases/fourslash/codeFixAddMissingEnumMember8.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////enum E {
|
||||
//// a,
|
||||
//// b = 1,
|
||||
//// c = "123"
|
||||
////}
|
||||
////E.d
|
||||
|
||||
verify.codeFix({
|
||||
description: "Add missing enum member 'd'",
|
||||
newFileContent: `enum E {
|
||||
a,
|
||||
b = 1,
|
||||
c = "123",
|
||||
d = "d"
|
||||
}
|
||||
E.d`
|
||||
});
|
||||
|
||||
|
||||
25
tests/cases/fourslash/codeFixAddMissingEnumMember9.ts
Normal file
25
tests/cases/fourslash/codeFixAddMissingEnumMember9.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////enum E {
|
||||
//// a,
|
||||
//// b = 1,
|
||||
//// c = "123"
|
||||
////}
|
||||
////enum A {
|
||||
//// a = E.a
|
||||
////}
|
||||
////A.b
|
||||
|
||||
verify.codeFix({
|
||||
description: "Add missing enum member 'b'",
|
||||
newFileContent: `enum E {
|
||||
a,
|
||||
b = 1,
|
||||
c = "123"
|
||||
}
|
||||
enum A {
|
||||
a = E.a,
|
||||
b
|
||||
}
|
||||
A.b`
|
||||
});
|
||||
@@ -14,4 +14,6 @@
|
||||
//// let t: T<number>;
|
||||
//// t.x;
|
||||
|
||||
verify.not.codeFixAvailable();
|
||||
verify.codeFixAvailable([{
|
||||
description: "Add missing enum member 'c'"
|
||||
}]);
|
||||
|
||||
Reference in New Issue
Block a user