Index: clangd/Diagnostics.h
===================================================================
--- clangd/Diagnostics.h
+++ clangd/Diagnostics.h
@@ -64,6 +64,8 @@
 
 /// A top-level diagnostic that may have Notes and Fixes.
 struct Diag : DiagBase {
+  // Diagnostic enum ID (member of clang::diag).
+  unsigned ID;
   /// Elaborate on the problem, usually pointing to a related piece of code.
   std::vector<Note> Notes;
   /// *Alternative* fixes for this diagnostic, one should be chosen.
Index: clangd/Diagnostics.cpp
===================================================================
--- clangd/Diagnostics.cpp
+++ clangd/Diagnostics.cpp
@@ -10,6 +10,8 @@
 #include "Compiler.h"
 #include "Logger.h"
 #include "SourceCode.h"
+#include "clang/Basic/AllDiagnostics.h"
+#include "clang/Basic/DiagnosticIDs.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Lex/Lexer.h"
 #include "llvm/Support/Capacity.h"
@@ -18,9 +20,31 @@
 
 namespace clang {
 namespace clangd {
-
 namespace {
 
+const char* getDiagnosticCode(unsigned ID) {
+  switch (ID) {
+#define DIAG(ENUM, CLASS, DEFAULT_MAPPING, DESC, GROPU, SFINAE, NOWERROR,      \
+             SHOWINSYSHEADER, CATEGORY)                                        \
+  case clang::diag::ENUM:                                                      \
+    return #ENUM;
+#include "clang/Basic/DiagnosticCommonKinds.inc"
+#include "clang/Basic/DiagnosticDriverKinds.inc"
+#include "clang/Basic/DiagnosticFrontendKinds.inc"
+#include "clang/Basic/DiagnosticSerializationKinds.inc"
+#include "clang/Basic/DiagnosticLexKinds.inc"
+#include "clang/Basic/DiagnosticParseKinds.inc"
+#include "clang/Basic/DiagnosticASTKinds.inc"
+#include "clang/Basic/DiagnosticCommentKinds.inc"
+#include "clang/Basic/DiagnosticSemaKinds.inc"
+#include "clang/Basic/DiagnosticAnalysisKinds.inc"
+#include "clang/Basic/DiagnosticRefactoringKinds.inc"
+#undef DIAG
+  default:
+    return nullptr;
+  }
+}
+
 bool mentionsMainFile(const Diag &D) {
   if (D.InsideMainFile)
     return true;
@@ -253,6 +277,8 @@
   {
     clangd::Diagnostic Main = FillBasicFields(D);
     Main.message = mainMessage(D);
+    if (auto* Name = getDiagnosticCode(D.ID))
+      Main.code = Name;
     if (Opts.EmbedFixesInDiagnostics) {
       Main.codeActions.emplace();
       for (const auto &Fix : D.Fixes)
@@ -372,6 +398,7 @@
     flushLastDiag();
 
     LastDiag = Diag();
+    LastDiag->ID = Info.getID();
     FillDiagBase(*LastDiag);
 
     if (!Info.getFixItHints().empty())
Index: clangd/Protocol.h
===================================================================
--- clangd/Protocol.h
+++ clangd/Protocol.h
@@ -566,8 +566,7 @@
   int severity = 0;
 
   /// The diagnostic's code. Can be omitted.
-  /// Note: Not currently used by clangd
-  // std::string code;
+  std::string code;
 
   /// A human-readable string describing the source of this
   /// diagnostic, e.g. 'typescript' or 'super lint'.
Index: clangd/Protocol.cpp
===================================================================
--- clangd/Protocol.cpp
+++ clangd/Protocol.cpp
@@ -369,6 +369,8 @@
     Diag["category"] = *D.category;
   if (D.codeActions)
     Diag["codeActions"] = D.codeActions;
+  if (!D.code.empty())
+    Diag["code"] = D.code;
   return std::move(Diag);
 }
 
@@ -378,6 +380,7 @@
     return false;
   O.map("severity", R.severity);
   O.map("category", R.category);
+  O.map("code", R.code);
   return true;
 }
 
Index: test/clangd/compile-commands-path-in-initialize.test
===================================================================
--- test/clangd/compile-commands-path-in-initialize.test
+++ test/clangd/compile-commands-path-in-initialize.test
@@ -21,6 +21,7 @@
 # CHECK-NEXT:   "params": {
 # CHECK-NEXT:     "diagnostics": [
 # CHECK-NEXT:       {
+# CHECK-NEXT:         "code": "warn_pragma_message",
 # CHECK-NEXT:         "message": "MACRO is one",
 ---
 {"jsonrpc":"2.0","id":10000,"method":"shutdown"}
Index: test/clangd/diagnostic-category.test
===================================================================
--- test/clangd/diagnostic-category.test
+++ test/clangd/diagnostic-category.test
@@ -7,6 +7,7 @@
 # CHECK-NEXT:     "diagnostics": [
 # CHECK-NEXT:      {
 # CHECK-NEXT:        "category": "Semantic Issue",
+# CHECK-NEXT:        "code": "err_use_with_wrong_tag",
 # CHECK-NEXT:        "message": "Use of 'Point' with tag type that does not match previous declaration (fix available)\n\nfoo.c:1:8: note: previous use is here",
 # CHECK-NEXT:        "range": {
 # CHECK-NEXT:          "end": {
Index: test/clangd/diagnostics.test
===================================================================
--- test/clangd/diagnostics.test
+++ test/clangd/diagnostics.test
@@ -6,6 +6,7 @@
 # CHECK-NEXT:  "params": {
 # CHECK-NEXT:    "diagnostics": [
 # CHECK-NEXT:      {
+# CHECK-NEXT:        "code": "ext_main_returns_nonint",
 # CHECK-NEXT:        "message": "Return type of 'main' is not 'int' (fix available)",
 # CHECK-NEXT:        "range": {
 # CHECK-NEXT:          "end": {
Index: test/clangd/did-change-configuration-params.test
===================================================================
--- test/clangd/did-change-configuration-params.test
+++ test/clangd/did-change-configuration-params.test
@@ -24,6 +24,7 @@
 # CHECK-NEXT:  "params": {
 # CHECK-NEXT:    "diagnostics": [
 # CHECK-NEXT:      {
+# CHECK-NEXT:        "code": "warn_uninit_var",
 # CHECK-NEXT:        "message": "Variable 'i' is uninitialized when used here (fix available)",
 # CHECK-NEXT:        "range": {
 # CHECK-NEXT:          "end": {
Index: test/clangd/execute-command.test
===================================================================
--- test/clangd/execute-command.test
+++ test/clangd/execute-command.test
@@ -6,6 +6,7 @@
 # CHECK-NEXT:  "params": {
 # CHECK-NEXT:    "diagnostics": [
 # CHECK-NEXT:      {
+# CHECK-NEXT:        "code": "warn_condition_is_assignment",
 # CHECK-NEXT:        "message": "Using the result of an assignment as a condition without parentheses (fixes available)",
 # CHECK-NEXT:        "range": {
 # CHECK-NEXT:          "end": {
Index: test/clangd/fixits-codeaction.test
===================================================================
--- test/clangd/fixits-codeaction.test
+++ test/clangd/fixits-codeaction.test
@@ -6,6 +6,7 @@
 # CHECK-NEXT:  "params": {
 # CHECK-NEXT:    "diagnostics": [
 # CHECK-NEXT:      {
+# CHECK-NEXT:        "code": "warn_condition_is_assignment",
 # CHECK-NEXT:        "message": "Using the result of an assignment as a condition without parentheses (fixes available)",
 # CHECK-NEXT:        "range": {
 # CHECK-NEXT:          "end": {
@@ -23,13 +24,14 @@
 # CHECK-NEXT:    "uri": "file://{{.*}}/foo.c"
 # CHECK-NEXT:  }
 ---
-{"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"test:///foo.c"},"range":{"start":{"line":0,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 37}},"severity":2,"message":"Using the result of an assignment as a condition without parentheses (fixes available)"}]}}}
+{"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"test:///foo.c"},"range":{"start":{"line":0,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 37}},"severity":2,"message":"Using the result of an assignment as a condition without parentheses (fixes available)", "code": "warn_condition_is_assignment"}]}}}
 #      CHECK:  "id": 2,
 # CHECK-NEXT:  "jsonrpc": "2.0",
 # CHECK-NEXT:  "result": [
 # CHECK-NEXT:    {
 # CHECK-NEXT:      "diagnostics": [
 # CHECK-NEXT:        {
+# CHECK-NEXT:          "code": "warn_condition_is_assignment",
 # CHECK-NEXT:          "message": "Using the result of an assignment as a condition without parentheses (fixes available)",
 # CHECK-NEXT:          "range": {
 # CHECK-NEXT:            "end": {
@@ -82,6 +84,7 @@
 # CHECK-NEXT:    {
 # CHECK-NEXT:      "diagnostics": [
 # CHECK-NEXT:        {
+# CHECK-NEXT:          "code": "warn_condition_is_assignment",
 # CHECK-NEXT:          "message": "Using the result of an assignment as a condition without parentheses (fixes available)",
 # CHECK-NEXT:          "range": {
 # CHECK-NEXT:            "end": {
Index: test/clangd/fixits-command.test
===================================================================
--- test/clangd/fixits-command.test
+++ test/clangd/fixits-command.test
@@ -6,6 +6,7 @@
 # CHECK-NEXT:  "params": {
 # CHECK-NEXT:    "diagnostics": [
 # CHECK-NEXT:      {
+# CHECK-NEXT:        "code": "warn_condition_is_assignment",
 # CHECK-NEXT:        "message": "Using the result of an assignment as a condition without parentheses (fixes available)",
 # CHECK-NEXT:        "range": {
 # CHECK-NEXT:          "end": {
Index: test/clangd/fixits-embed-in-diagnostic.test
===================================================================
--- test/clangd/fixits-embed-in-diagnostic.test
+++ test/clangd/fixits-embed-in-diagnostic.test
@@ -6,6 +6,7 @@
 # CHECK-NEXT:  "params": {
 # CHECK-NEXT:    "diagnostics": [
 # CHECK-NEXT:      {
+# CHECK-NEXT:        "code": "err_use_with_wrong_tag",
 # CHECK-NEXT:        "codeActions": [
 # CHECK-NEXT:          {
 # CHECK-NEXT:            "edit": {
Index: unittests/clangd/DiagnosticsTests.cpp
===================================================================
--- unittests/clangd/DiagnosticsTests.cpp
+++ unittests/clangd/DiagnosticsTests.cpp
@@ -12,6 +12,7 @@
 #include "TestIndex.h"
 #include "TestTU.h"
 #include "index/MemIndex.h"
+#include "clang/Basic/DiagnosticSema.h"
 #include "llvm/Support/ScopedPrinter.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
@@ -216,6 +217,7 @@
 
 TEST(DiagnosticsTest, ToLSP) {
   clangd::Diag D;
+  D.ID = clang::diag::err_enum_class_reference;
   D.Message = "something terrible happened";
   D.Range = {pos(1, 2), pos(3, 4)};
   D.InsideMainFile = true;
@@ -284,6 +286,8 @@
       LSPDiags,
       ElementsAre(Pair(EqualToLSPDiag(MainLSP), ElementsAre(EqualToFix(F))),
                   Pair(EqualToLSPDiag(NoteInMainLSP), IsEmpty())));
+  EXPECT_EQ(LSPDiags[0].first.code, "err_enum_class_reference");
+  EXPECT_EQ(LSPDiags[1].first.code, "");
 }
 
 struct SymbolWithHeader {