diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json
index 6196191328016..078bd600771d4 100644
--- a/src/compiler/diagnosticMessages.json
+++ b/src/compiler/diagnosticMessages.json
@@ -4261,5 +4261,13 @@
"Add missing typeof": {
"category": "Message",
"code": 95052
+ },
+ "Remove unused label": {
+ "category": "Message",
+ "code": 95053
+ },
+ "Remove all unused labels": {
+ "category": "Message",
+ "code": 95054
}
}
diff --git a/src/harness/tsconfig.json b/src/harness/tsconfig.json
index 4fb96dbe93dc1..359701a02cb3a 100644
--- a/src/harness/tsconfig.json
+++ b/src/harness/tsconfig.json
@@ -106,6 +106,7 @@
"../services/codefixes/fixForgottenThisPropertyAccess.ts",
"../services/codefixes/fixUnusedIdentifier.ts",
"../services/codefixes/fixUnreachableCode.ts",
+ "../services/codefixes/fixUnusedLabel.ts",
"../services/codefixes/fixJSDocTypes.ts",
"../services/codefixes/fixAwaitInSyncFunction.ts",
"../services/codefixes/disableJsDiagnostics.ts",
diff --git a/src/server/tsconfig.json b/src/server/tsconfig.json
index 50e06eb3f2339..8ae6974baf062 100644
--- a/src/server/tsconfig.json
+++ b/src/server/tsconfig.json
@@ -102,6 +102,7 @@
"../services/codefixes/fixForgottenThisPropertyAccess.ts",
"../services/codefixes/fixUnusedIdentifier.ts",
"../services/codefixes/fixUnreachableCode.ts",
+ "../services/codefixes/fixUnusedLabel.ts",
"../services/codefixes/fixJSDocTypes.ts",
"../services/codefixes/fixAwaitInSyncFunction.ts",
"../services/codefixes/disableJsDiagnostics.ts",
diff --git a/src/server/tsconfig.library.json b/src/server/tsconfig.library.json
index 43e8fd1857b9a..922af11e87963 100644
--- a/src/server/tsconfig.library.json
+++ b/src/server/tsconfig.library.json
@@ -108,6 +108,7 @@
"../services/codefixes/fixForgottenThisPropertyAccess.ts",
"../services/codefixes/fixUnusedIdentifier.ts",
"../services/codefixes/fixUnreachableCode.ts",
+ "../services/codefixes/fixUnusedLabel.ts",
"../services/codefixes/fixJSDocTypes.ts",
"../services/codefixes/fixAwaitInSyncFunction.ts",
"../services/codefixes/disableJsDiagnostics.ts",
diff --git a/src/services/codefixes/fixUnusedLabel.ts b/src/services/codefixes/fixUnusedLabel.ts
new file mode 100644
index 0000000000000..b99f72d68397e
--- /dev/null
+++ b/src/services/codefixes/fixUnusedLabel.ts
@@ -0,0 +1,25 @@
+/* @internal */
+namespace ts.codefix {
+ const fixId = "fixUnusedLabel";
+ const errorCodes = [Diagnostics.Unused_label.code];
+ registerCodeFix({
+ errorCodes,
+ getCodeActions(context) {
+ const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, context.span.start));
+ return [createCodeFixAction(fixId, changes, Diagnostics.Remove_unused_label, fixId, Diagnostics.Remove_all_unused_labels)];
+ },
+ fixIds: [fixId],
+ getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => doChange(changes, diag.file, diag.start)),
+ });
+
+ function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, start: number): void {
+ const token = getTokenAtPosition(sourceFile, start, /*includeJsDocComment*/ false);
+ const labeledStatement = cast(token.parent, isLabeledStatement);
+ const pos = token.getStart(sourceFile);
+ const statementPos = labeledStatement.statement.getStart(sourceFile);
+ // If label is on a separate line, just delete the rest of that line, but not the indentation of the labeled statement.
+ const end = positionsAreOnSameLine(pos, statementPos, sourceFile) ? statementPos
+ : skipTrivia(sourceFile.text, findChildOfKind(labeledStatement, SyntaxKind.ColonToken, sourceFile)!.end, /*stopAfterLineBreak*/ true);
+ changes.deleteRange(sourceFile, { pos, end });
+ }
+}
diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json
index d78086504c926..7e1ccc9c3afc0 100644
--- a/src/services/tsconfig.json
+++ b/src/services/tsconfig.json
@@ -99,6 +99,7 @@
"codefixes/fixForgottenThisPropertyAccess.ts",
"codefixes/fixUnusedIdentifier.ts",
"codefixes/fixUnreachableCode.ts",
+ "codefixes/fixUnusedLabel.ts",
"codefixes/fixJSDocTypes.ts",
"codefixes/fixAwaitInSyncFunction.ts",
"codefixes/disableJsDiagnostics.ts",
diff --git a/tests/cases/fourslash/codeFixUnusedLabel.ts b/tests/cases/fourslash/codeFixUnusedLabel.ts
new file mode 100644
index 0000000000000..0feea173b0c2b
--- /dev/null
+++ b/tests/cases/fourslash/codeFixUnusedLabel.ts
@@ -0,0 +1,11 @@
+///
+
+// @noUnusedLocals: true
+
+/////* a */label/* b */:/* c */while (1) {}
+
+verify.codeFix({
+ description: "Remove unused label",
+ newFileContent:
+`/* a */while (1) {}`,
+});
diff --git a/tests/cases/fourslash/codeFixUnusedLabel_all.ts b/tests/cases/fourslash/codeFixUnusedLabel_all.ts
new file mode 100644
index 0000000000000..2769e0a65eed9
--- /dev/null
+++ b/tests/cases/fourslash/codeFixUnusedLabel_all.ts
@@ -0,0 +1,21 @@
+///
+
+// @noUnusedLocals: true
+
+////label1: while (1) {}
+////
+////function f() {
+////label2:
+//// while (1) {}
+////}
+
+verify.codeFixAll({
+ fixId: "fixUnusedLabel",
+ fixAllDescription: "Remove all unused labels",
+ newFileContent:
+`while (1) {}
+
+function f() {
+ while (1) {}
+}`,
+});