From d5268c81e7d6f7d8bbb84216063d55fcbee7edfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=96=87=E7=92=90?= Date: Sun, 24 Jun 2018 23:04:43 +0800 Subject: [PATCH 1/4] add quick fix for add missing enum member --- src/compiler/diagnosticMessages.json | 8 ++ src/services/codefixes/fixAddMissingMember.ts | 109 ++++++++++++++---- .../reference/api/tsserverlibrary.d.ts | 2 + .../fourslash/codeFixAddMissingEnumMember1.ts | 16 +++ .../fourslash/codeFixAddMissingEnumMember2.ts | 16 +++ .../fourslash/codeFixAddMissingEnumMember3.ts | 21 ++++ .../fourslash/codeFixAddMissingEnumMember4.ts | 17 +++ .../fourslash/codeFixAddMissingEnumMember5.ts | 17 +++ .../fourslash/codeFixAddMissingEnumMember6.ts | 17 +++ .../fourslash/codeFixAddMissingEnumMember7.ts | 15 +++ .../codeFixUndeclaredPropertyAccesses.ts | 4 +- 11 files changed, 217 insertions(+), 25 deletions(-) create mode 100644 tests/cases/fourslash/codeFixAddMissingEnumMember1.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingEnumMember2.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingEnumMember3.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingEnumMember4.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingEnumMember5.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingEnumMember6.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingEnumMember7.ts diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 7d08fd5caf1..a4f7f8b82e5 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -4418,5 +4418,13 @@ "Remove braces from arrow function": { "category": "Message", "code": 95060 + }, + "Add missing enum member '{0}'": { + "category": "Message", + "code": 95061 + }, + "Add all missing enum members": { + "category": "Message", + "code": 95062 } } diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index 7dbb43b94c7..4a7c5ece9d8 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -11,11 +11,15 @@ namespace ts.codefix { getCodeActions(context) { const info = getInfo(context.sourceFile, context.span.start, context.program.getTypeChecker()); if (!info) return undefined; - const { classDeclaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info; - const methodCodeAction = call && getActionForMethodDeclaration(context, classDeclarationSourceFile, classDeclaration, token, call, makeStatic, inJs, context.preferences); + + if (isEnumInfo(info)) { + return singleElementArray(getActionForEnumMemberDeclaration(context, info.enumDeclarationSourceFile, info.declaration, info.token)); + } + const { declaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info; + const methodCodeAction = call && getActionForMethodDeclaration(context, classDeclarationSourceFile, declaration, token, call, makeStatic, inJs, context.preferences); const addMember = inJs ? - singleElementArray(getActionsForAddMissingMemberInJavaScriptFile(context, classDeclarationSourceFile, classDeclaration, token.text, makeStatic)) : - getActionsForAddMissingMemberInTypeScriptFile(context, classDeclarationSourceFile, classDeclaration, token, makeStatic); + singleElementArray(getActionsForAddMissingMemberInJavaScriptFile(context, classDeclarationSourceFile, declaration, token.text, makeStatic)) : + getActionsForAddMissingMemberInTypeScriptFile(context, classDeclarationSourceFile, declaration, token, makeStatic); return concatenate(singleElementArray(methodCodeAction), addMember); }, fixIds: [fixId], @@ -23,31 +27,44 @@ namespace ts.codefix { const seenNames = createMap(); 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 (isEnumInfo(info)) { + const { token, declaration, enumDeclarationSourceFile } = info; + addEnumMemberDeclaration(changes, checker, token, declaration, enumDeclarationSourceFile); } else { - if (inJs) { - addMissingMemberInJs(changes, classDeclarationSourceFile, classDeclaration, token.text, makeStatic); + const { declaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info; + // Always prefer to add a method declaration if possible. + if (call) { + addMethodDeclaration(context, changes, classDeclarationSourceFile, declaration, 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, declaration, token.text, makeStatic); + } + else { + const typeNode = getTypeNode(program.getTypeChecker(), declaration, token); + addPropertyDeclaration(changes, classDeclarationSourceFile, declaration, token.text, typeNode, makeStatic); + } } } }); }, }); - interface Info { token: Identifier; classDeclaration: ClassLikeDeclaration; makeStatic: boolean; classDeclarationSourceFile: SourceFile; inJs: boolean; call: CallExpression | undefined; } + interface EnumInfo { token: Identifier; declaration: EnumDeclaration; enumDeclarationSourceFile: SourceFile; } + interface ClassInfo { token: Identifier; declaration: ClassLikeDeclaration; makeStatic: boolean; classDeclarationSourceFile: SourceFile; inJs: boolean; call: CallExpression | undefined; } + type Info = EnumInfo | ClassInfo; + + function isEnumInfo (info: Info): info is EnumInfo { + return isEnumDeclaration(info.declaration); + } + function getInfo(tokenSourceFile: SourceFile, tokenPos: number, checker: TypeChecker): Info | undefined { // The identifier of the missing property. eg: // this.missing = 1; @@ -62,15 +79,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 { token, declaration: classDeclaration, makeStatic, classDeclarationSourceFile, inJs, call }; + } + const enumDeclaration = find(symbol.declarations, isEnumDeclaration); + if (enumDeclaration) { + return { token, declaration: enumDeclaration, enumDeclarationSourceFile: enumDeclaration.getSourceFile() }; + } + return undefined; } function getActionsForAddMissingMemberInJavaScriptFile(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, makeStatic: boolean): CodeFixAction | undefined { @@ -188,6 +211,16 @@ namespace ts.codefix { return createCodeFixAction(fixName, changes, [makeStatic ? Diagnostics.Declare_static_method_0 : Diagnostics.Declare_method_0, token.text], fixId, Diagnostics.Add_all_missing_members); } + function getActionForEnumMemberDeclaration( + context: CodeFixContext, + enumDeclarationSourceFile: SourceFile, + enumDeclaration: EnumDeclaration, + token: Identifier + ): CodeFixAction | undefined { + const changes = textChanges.ChangeTracker.with(context, t => addEnumMemberDeclaration(t, context.program.getTypeChecker(), token, enumDeclaration, enumDeclarationSourceFile)); + return createCodeFixAction(fixName, changes, [Diagnostics.Add_missing_enum_member_0, token.text], fixId, Diagnostics.Add_all_missing_enum_members); + } + function addMethodDeclaration( context: CodeFixContextBase, changeTracker: textChanges.ChangeTracker, @@ -209,4 +242,32 @@ namespace ts.codefix { changeTracker.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, methodDeclaration); } } + + function createEnumMemberFromEnumDeclaration(checker: TypeChecker, token: Identifier, enumDeclaration: EnumDeclaration) { + /** + * create initializer only string enum. + * value of initializer is a string literal that equal to name of enum member. + * literal enum or empty enum will not create initializer. + */ + const firstMember = firstOrUndefined(enumDeclaration.members); + let enumMemberInitializer: Expression | undefined; + if (firstMember && firstMember.initializer) { + const memberType = checker.getTypeAtLocation(firstMember.initializer); + if (memberType && memberType.flags & TypeFlags.StringLike) { + enumMemberInitializer = createStringLiteral(token.text); + } + } + return createEnumMember(token, enumMemberInitializer); + } + + function addEnumMemberDeclaration(changes: textChanges.ChangeTracker, checker: TypeChecker, token: Identifier, enumDeclaration: EnumDeclaration, file: SourceFile) { + const enumMember = createEnumMemberFromEnumDeclaration(checker, token, enumDeclaration); + changes.replaceNode(file, enumDeclaration, updateEnumDeclaration( + enumDeclaration, + enumDeclaration.decorators, + enumDeclaration.modifiers, + enumDeclaration.name, + concatenate(enumDeclaration.members, singleElementArray(enumMember)) + )); + } } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 43a49150e19..5998d1dcc14 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -5914,6 +5914,8 @@ declare namespace ts { Add_or_remove_braces_in_an_arrow_function: DiagnosticMessage; Add_braces_to_arrow_function: DiagnosticMessage; Remove_braces_from_arrow_function: DiagnosticMessage; + Add_missing_enum_member_0: DiagnosticMessage; + Add_all_missing_enum_members: DiagnosticMessage; }; } declare namespace ts { diff --git a/tests/cases/fourslash/codeFixAddMissingEnumMember1.ts b/tests/cases/fourslash/codeFixAddMissingEnumMember1.ts new file mode 100644 index 00000000000..e99b653ee08 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingEnumMember1.ts @@ -0,0 +1,16 @@ +/// + +////enum E { +//// a +////} +////E.b + +verify.codeFix({ + description: "Add missing enum member 'b'", + newFileContent: `enum E { + a, + b +} +E.b` +}); + diff --git a/tests/cases/fourslash/codeFixAddMissingEnumMember2.ts b/tests/cases/fourslash/codeFixAddMissingEnumMember2.ts new file mode 100644 index 00000000000..16e456beba7 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingEnumMember2.ts @@ -0,0 +1,16 @@ +/// + +////enum E { +//// a = 1 +////} +////E.b + +verify.codeFix({ + description: "Add missing enum member 'b'", + newFileContent: `enum E { + a = 1, + b +} +E.b` +}); + diff --git a/tests/cases/fourslash/codeFixAddMissingEnumMember3.ts b/tests/cases/fourslash/codeFixAddMissingEnumMember3.ts new file mode 100644 index 00000000000..b6848021d1f --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingEnumMember3.ts @@ -0,0 +1,21 @@ +/// + +////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` +}); + + diff --git a/tests/cases/fourslash/codeFixAddMissingEnumMember4.ts b/tests/cases/fourslash/codeFixAddMissingEnumMember4.ts new file mode 100644 index 00000000000..a4e20401556 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingEnumMember4.ts @@ -0,0 +1,17 @@ +/// + +////enum E { +//// a = "a", +////} +////E.b + +verify.codeFix({ + description: "Add missing enum member 'b'", + newFileContent: `enum E { + a = "a", + b = "b" +} +E.b` +}); + + diff --git a/tests/cases/fourslash/codeFixAddMissingEnumMember5.ts b/tests/cases/fourslash/codeFixAddMissingEnumMember5.ts new file mode 100644 index 00000000000..9d0fe55859e --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingEnumMember5.ts @@ -0,0 +1,17 @@ +/// + +////enum E { +//// a = "a" + "-", +////} +////E.b + +verify.codeFix({ + description: "Add missing enum member 'b'", + newFileContent: `enum E { + a = "a" + "-", + b = "b" +} +E.b` +}); + + diff --git a/tests/cases/fourslash/codeFixAddMissingEnumMember6.ts b/tests/cases/fourslash/codeFixAddMissingEnumMember6.ts new file mode 100644 index 00000000000..71155a81736 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingEnumMember6.ts @@ -0,0 +1,17 @@ +/// + +////enum E { +//// a = "b" +////} +////E.b + +verify.codeFix({ + description: "Add missing enum member 'b'", + newFileContent: `enum E { + a = "b", + b = "b" +} +E.b` +}); + + diff --git a/tests/cases/fourslash/codeFixAddMissingEnumMember7.ts b/tests/cases/fourslash/codeFixAddMissingEnumMember7.ts new file mode 100644 index 00000000000..4b18502b8a5 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingEnumMember7.ts @@ -0,0 +1,15 @@ +/// + +////enum E { +////} +////E.a + +verify.codeFix({ + description: "Add missing enum member 'a'", + newFileContent: `enum E { + a +} +E.a` +}); + + diff --git a/tests/cases/fourslash/codeFixUndeclaredPropertyAccesses.ts b/tests/cases/fourslash/codeFixUndeclaredPropertyAccesses.ts index 2576dbb1fb7..b83a4c857e3 100644 --- a/tests/cases/fourslash/codeFixUndeclaredPropertyAccesses.ts +++ b/tests/cases/fourslash/codeFixUndeclaredPropertyAccesses.ts @@ -14,4 +14,6 @@ //// let t: T; //// t.x; -verify.not.codeFixAvailable(); +verify.codeFixAvailable([{ + description: "Add missing enum member 'c'" +}]); From f426e048c432af65e34bc5a968a72a38a9a1bcfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=96=87=E7=92=90?= Date: Tue, 26 Jun 2018 10:42:34 +0800 Subject: [PATCH 2/4] accept baseline --- tests/baselines/reference/api/tsserverlibrary.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 8f9077cf203..d77ae004ae9 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -5913,10 +5913,10 @@ declare namespace ts { Add_or_remove_braces_in_an_arrow_function: DiagnosticMessage; Add_braces_to_arrow_function: DiagnosticMessage; Remove_braces_from_arrow_function: DiagnosticMessage; - Add_missing_enum_member_0: DiagnosticMessage; - Add_all_missing_enum_members: 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 { From d6f5fc6518a81386b09fcbcdb46aec16053b5df8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=96=87=E7=92=90?= Date: Wed, 27 Jun 2018 21:03:50 +0800 Subject: [PATCH 3/4] add Initializer if enum value is another enum --- src/services/codefixes/fixAddMissingMember.ts | 78 +++++++------------ .../codeFixAddMissingEnumMember10.ts | 25 ++++++ .../codeFixAddMissingEnumMember11.ts | 32 ++++++++ .../fourslash/codeFixAddMissingEnumMember8.ts | 21 +++++ .../fourslash/codeFixAddMissingEnumMember9.ts | 25 ++++++ 5 files changed, 133 insertions(+), 48 deletions(-) create mode 100644 tests/cases/fourslash/codeFixAddMissingEnumMember10.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingEnumMember11.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingEnumMember8.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingEnumMember9.ts diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index 4a7c5ece9d8..bb0dd5a75a5 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -12,14 +12,16 @@ namespace ts.codefix { const info = getInfo(context.sourceFile, context.span.start, context.program.getTypeChecker()); if (!info) return undefined; - if (isEnumInfo(info)) { - return singleElementArray(getActionForEnumMemberDeclaration(context, info.enumDeclarationSourceFile, info.declaration, info.token)); + 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 { declaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info; - const methodCodeAction = call && getActionForMethodDeclaration(context, classDeclarationSourceFile, declaration, token, call, makeStatic, inJs, context.preferences); + const { classDeclaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info; + const methodCodeAction = call && getActionForMethodDeclaration(context, classDeclarationSourceFile, classDeclaration, token, call, makeStatic, inJs, context.preferences); const addMember = inJs ? - singleElementArray(getActionsForAddMissingMemberInJavaScriptFile(context, classDeclarationSourceFile, declaration, token.text, makeStatic)) : - getActionsForAddMissingMemberInTypeScriptFile(context, classDeclarationSourceFile, declaration, token, makeStatic); + singleElementArray(getActionsForAddMissingMemberInJavaScriptFile(context, classDeclarationSourceFile, classDeclaration, token.text, makeStatic)) : + getActionsForAddMissingMemberInTypeScriptFile(context, classDeclarationSourceFile, classDeclaration, token, makeStatic); return concatenate(singleElementArray(methodCodeAction), addMember); }, fixIds: [fixId], @@ -33,23 +35,23 @@ namespace ts.codefix { return; } - if (isEnumInfo(info)) { - const { token, declaration, enumDeclarationSourceFile } = info; - addEnumMemberDeclaration(changes, checker, token, declaration, enumDeclarationSourceFile); + if (info.kind === InfoKind.enum) { + const { token, enumDeclaration } = info; + addEnumMemberDeclaration(changes, checker, token, enumDeclaration); } else { - const { declaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info; + const { classDeclaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info; // Always prefer to add a method declaration if possible. if (call) { - addMethodDeclaration(context, changes, classDeclarationSourceFile, declaration, token, call, makeStatic, inJs, preferences); + addMethodDeclaration(context, changes, classDeclarationSourceFile, classDeclaration, token, call, makeStatic, inJs, preferences); } else { if (inJs) { - addMissingMemberInJs(changes, classDeclarationSourceFile, declaration, token.text, makeStatic); + addMissingMemberInJs(changes, classDeclarationSourceFile, classDeclaration, token.text, makeStatic); } else { - const typeNode = getTypeNode(program.getTypeChecker(), declaration, token); - addPropertyDeclaration(changes, classDeclarationSourceFile, declaration, token.text, typeNode, makeStatic); + const typeNode = getTypeNode(program.getTypeChecker(), classDeclaration, token); + addPropertyDeclaration(changes, classDeclarationSourceFile, classDeclaration, token.text, typeNode, makeStatic); } } } @@ -57,14 +59,11 @@ namespace ts.codefix { }, }); - interface EnumInfo { token: Identifier; declaration: EnumDeclaration; enumDeclarationSourceFile: SourceFile; } - interface ClassInfo { token: Identifier; declaration: 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 isEnumInfo (info: Info): info is EnumInfo { - return isEnumDeclaration(info.declaration); - } - function getInfo(tokenSourceFile: SourceFile, tokenPos: number, checker: TypeChecker): Info | undefined { // The identifier of the missing property. eg: // this.missing = 1; @@ -87,11 +86,11 @@ namespace ts.codefix { const classDeclarationSourceFile = classDeclaration.getSourceFile(); const inJs = isSourceFileJavaScript(classDeclarationSourceFile); const call = tryCast(parent.parent, isCallExpression); - return { token, declaration: classDeclaration, makeStatic, classDeclarationSourceFile, inJs, call }; + return { kind: InfoKind.class, token, classDeclaration, makeStatic, classDeclarationSourceFile, inJs, call }; } const enumDeclaration = find(symbol.declarations, isEnumDeclaration); if (enumDeclaration) { - return { token, declaration: enumDeclaration, enumDeclarationSourceFile: enumDeclaration.getSourceFile() }; + return { kind: InfoKind.enum, token, enumDeclaration }; } return undefined; } @@ -211,16 +210,6 @@ namespace ts.codefix { return createCodeFixAction(fixName, changes, [makeStatic ? Diagnostics.Declare_static_method_0 : Diagnostics.Declare_method_0, token.text], fixId, Diagnostics.Add_all_missing_members); } - function getActionForEnumMemberDeclaration( - context: CodeFixContext, - enumDeclarationSourceFile: SourceFile, - enumDeclaration: EnumDeclaration, - token: Identifier - ): CodeFixAction | undefined { - const changes = textChanges.ChangeTracker.with(context, t => addEnumMemberDeclaration(t, context.program.getTypeChecker(), token, enumDeclaration, enumDeclarationSourceFile)); - return createCodeFixAction(fixName, changes, [Diagnostics.Add_missing_enum_member_0, token.text], fixId, Diagnostics.Add_all_missing_enum_members); - } - function addMethodDeclaration( context: CodeFixContextBase, changeTracker: textChanges.ChangeTracker, @@ -243,26 +232,19 @@ namespace ts.codefix { } } - function createEnumMemberFromEnumDeclaration(checker: TypeChecker, token: Identifier, enumDeclaration: EnumDeclaration) { + function addEnumMemberDeclaration(changes: textChanges.ChangeTracker, checker: TypeChecker, token: Identifier, enumDeclaration: EnumDeclaration) { /** - * create initializer only string enum. + * create initializer only literal enum that has string initializer. * value of initializer is a string literal that equal to name of enum member. - * literal enum or empty enum will not create initializer. + * numeric enum or empty enum will not create initializer. */ - const firstMember = firstOrUndefined(enumDeclaration.members); - let enumMemberInitializer: Expression | undefined; - if (firstMember && firstMember.initializer) { - const memberType = checker.getTypeAtLocation(firstMember.initializer); - if (memberType && memberType.flags & TypeFlags.StringLike) { - enumMemberInitializer = createStringLiteral(token.text); - } - } - return createEnumMember(token, enumMemberInitializer); - } + const hasStringInitializer = some(enumDeclaration.members, member => { + const type = checker.getTypeAtLocation(member); + return !!(type && type.flags & (TypeFlags.StringLike | TypeFlags.Enum)); + }); - function addEnumMemberDeclaration(changes: textChanges.ChangeTracker, checker: TypeChecker, token: Identifier, enumDeclaration: EnumDeclaration, file: SourceFile) { - const enumMember = createEnumMemberFromEnumDeclaration(checker, token, enumDeclaration); - changes.replaceNode(file, enumDeclaration, updateEnumDeclaration( + const enumMember = createEnumMember(token, hasStringInitializer ? createStringLiteral(token.text) : undefined); + changes.replaceNode(enumDeclaration.getSourceFile(), enumDeclaration, updateEnumDeclaration( enumDeclaration, enumDeclaration.decorators, enumDeclaration.modifiers, diff --git a/tests/cases/fourslash/codeFixAddMissingEnumMember10.ts b/tests/cases/fourslash/codeFixAddMissingEnumMember10.ts new file mode 100644 index 00000000000..0335c9ef552 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingEnumMember10.ts @@ -0,0 +1,25 @@ +/// + +////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 = "b" +} +A.b` +}); diff --git a/tests/cases/fourslash/codeFixAddMissingEnumMember11.ts b/tests/cases/fourslash/codeFixAddMissingEnumMember11.ts new file mode 100644 index 00000000000..6a085fc285a --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingEnumMember11.ts @@ -0,0 +1,32 @@ +/// + +////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 = "c" +} +B.c` +}); + diff --git a/tests/cases/fourslash/codeFixAddMissingEnumMember8.ts b/tests/cases/fourslash/codeFixAddMissingEnumMember8.ts new file mode 100644 index 00000000000..fdbbfa68255 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingEnumMember8.ts @@ -0,0 +1,21 @@ +/// + +////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` +}); + + diff --git a/tests/cases/fourslash/codeFixAddMissingEnumMember9.ts b/tests/cases/fourslash/codeFixAddMissingEnumMember9.ts new file mode 100644 index 00000000000..03bde7c1fce --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingEnumMember9.ts @@ -0,0 +1,25 @@ +/// + +////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 = "b" +} +A.b` +}); From d8cdd7552e57ea159866d946255cbcbe49565998 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=96=87=E7=92=90?= Date: Thu, 28 Jun 2018 09:36:26 +0800 Subject: [PATCH 4/4] do not handle enum type member --- src/services/codefixes/fixAddMissingMember.ts | 2 +- tests/cases/fourslash/codeFixAddMissingEnumMember10.ts | 2 +- tests/cases/fourslash/codeFixAddMissingEnumMember11.ts | 3 +-- tests/cases/fourslash/codeFixAddMissingEnumMember9.ts | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index bb0dd5a75a5..cd94a910716 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -240,7 +240,7 @@ namespace ts.codefix { */ const hasStringInitializer = some(enumDeclaration.members, member => { const type = checker.getTypeAtLocation(member); - return !!(type && type.flags & (TypeFlags.StringLike | TypeFlags.Enum)); + return !!(type && type.flags & TypeFlags.StringLike); }); const enumMember = createEnumMember(token, hasStringInitializer ? createStringLiteral(token.text) : undefined); diff --git a/tests/cases/fourslash/codeFixAddMissingEnumMember10.ts b/tests/cases/fourslash/codeFixAddMissingEnumMember10.ts index 0335c9ef552..dba3051fee9 100644 --- a/tests/cases/fourslash/codeFixAddMissingEnumMember10.ts +++ b/tests/cases/fourslash/codeFixAddMissingEnumMember10.ts @@ -19,7 +19,7 @@ verify.codeFix({ } enum A { a = E.c, - b = "b" + b } A.b` }); diff --git a/tests/cases/fourslash/codeFixAddMissingEnumMember11.ts b/tests/cases/fourslash/codeFixAddMissingEnumMember11.ts index 6a085fc285a..4928b52dba6 100644 --- a/tests/cases/fourslash/codeFixAddMissingEnumMember11.ts +++ b/tests/cases/fourslash/codeFixAddMissingEnumMember11.ts @@ -25,8 +25,7 @@ enum A { } enum B { b = A.a, - c = "c" + c } B.c` }); - diff --git a/tests/cases/fourslash/codeFixAddMissingEnumMember9.ts b/tests/cases/fourslash/codeFixAddMissingEnumMember9.ts index 03bde7c1fce..acc27a6a4ea 100644 --- a/tests/cases/fourslash/codeFixAddMissingEnumMember9.ts +++ b/tests/cases/fourslash/codeFixAddMissingEnumMember9.ts @@ -19,7 +19,7 @@ verify.codeFix({ } enum A { a = E.a, - b = "b" + b } A.b` });