arphaman created this revision.
arphaman added reviewers: ilya-biryukov, ioeric, hokein, jkorous.
Herald added subscribers: dexonsmith, MaskRay.

This patch adds support for diagnostic message capitalization to Clangd. This 
is enabled when '-capitialize-diagnostic-text'  is provided to Clangd.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50154

Files:
  clangd/ClangdLSPServer.cpp
  clangd/ClangdLSPServer.h
  clangd/ClangdUnit.cpp
  clangd/Diagnostics.cpp
  clangd/Diagnostics.h
  clangd/tool/ClangdMain.cpp
  test/clangd/capitalize-diagnostics.test

Index: test/clangd/capitalize-diagnostics.test
===================================================================
--- /dev/null
+++ test/clangd/capitalize-diagnostics.test
@@ -0,0 +1,42 @@
+# RUN: clangd -capitialize-diagnostic-text -lit-test < %s | FileCheck -strict-whitespace %s
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
+---
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"void foo() { }\nvoid foo() { }"}}}
+#      CHECK:  "method": "textDocument/publishDiagnostics",
+# CHECK-NEXT:  "params": {
+# CHECK-NEXT:    "diagnostics": [
+# CHECK-NEXT:      {
+# CHECK-NEXT:        "message": "Redefinition of 'foo'\n\nfoo.c:1:6: note: previous definition is here",
+# CHECK-NEXT:        "range": {
+# CHECK-NEXT:          "end": {
+# CHECK-NEXT:            "character": 8,
+# CHECK-NEXT:            "line": 1
+# CHECK-NEXT:          },
+# CHECK-NEXT:          "start": {
+# CHECK-NEXT:            "character": 5,
+# CHECK-NEXT:            "line": 1
+# CHECK-NEXT:          }
+# CHECK-NEXT:        },
+# CHECK-NEXT:        "severity": 1
+# CHECK-NEXT:      },
+# CHECK-NEXT:      {
+# CHECK-NEXT:        "message": "Previous definition is here\n\nfoo.c:2:6: error: redefinition of 'foo'",
+# CHECK-NEXT:        "range": {
+# CHECK-NEXT:          "end": {
+# CHECK-NEXT:            "character": 8,
+# CHECK-NEXT:            "line": 0
+# CHECK-NEXT:          },
+# CHECK-NEXT:          "start": {
+# CHECK-NEXT:            "character": 5,
+# CHECK-NEXT:            "line": 0
+# CHECK-NEXT:          }
+# CHECK-NEXT:        },
+# CHECK-NEXT:        "severity": 3
+# CHECK-NEXT:      }
+# CHECK-NEXT:    ],
+# CHECK-NEXT:    "uri": "file://{{.*}}/foo.c"
+# CHECK-NEXT:  }
+---
+{"jsonrpc":"2.0","id":5,"method":"shutdown"}
+---
+{"jsonrpc":"2.0","method":"exit"}
Index: clangd/tool/ClangdMain.cpp
===================================================================
--- clangd/tool/ClangdMain.cpp
+++ clangd/tool/ClangdMain.cpp
@@ -185,6 +185,12 @@
                                 "'compile_commands.json' files")),
     llvm::cl::init(FilesystemCompileArgs), llvm::cl::Hidden);
 
+static llvm::cl::opt<bool> CapitalizeDiagnosticText(
+    "capitialize-diagnostic-text",
+    llvm::cl::desc(
+        "Capitalize the first character in the diagnostic's message"),
+    llvm::cl::init(false));
+
 int main(int argc, char *argv[]) {
   llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
   llvm::cl::SetVersionPrinter([](llvm::raw_ostream &OS) {
@@ -300,9 +306,12 @@
     CCOpts.IncludeIndicator.NoInsert.clear();
   }
 
+  clangd::DiagnosticOptions DiagOpts;
+  DiagOpts.CapitalizeDiagnosticText = CapitalizeDiagnosticText;
+
   // Initialize and run ClangdLSPServer.
   ClangdLSPServer LSPServer(
-      Out, CCOpts, CompileCommandsDirPath,
+      Out, CCOpts, DiagOpts, CompileCommandsDirPath,
       /*ShouldUseInMemoryCDB=*/CompileArgsFrom == LSPCompileArgs, Opts);
   constexpr int NoShutdownRequestErrorCode = 1;
   llvm::set_thread_name("clangd.main");
Index: clangd/Diagnostics.h
===================================================================
--- clangd/Diagnostics.h
+++ clangd/Diagnostics.h
@@ -23,6 +23,11 @@
 namespace clang {
 namespace clangd {
 
+struct DiagnosticOptions {
+  /// Capitalize the first character in the diagnostic's message.
+  bool CapitalizeDiagnosticText = false;
+};
+
 /// Contains basic information about a diagnostic.
 struct DiagBase {
   std::string Message;
@@ -65,7 +70,7 @@
 /// file do not have a corresponding LSP diagnostic, but can still be included
 /// as part of their main diagnostic's message.
 void toLSPDiags(
-    const Diag &D,
+    const Diag &D, const DiagnosticOptions &DiagOpts,
     llvm::function_ref<void(clangd::Diagnostic, llvm::ArrayRef<Fix>)> OutFn);
 
 /// Convert from clang diagnostic level to LSP severity.
Index: clangd/Diagnostics.cpp
===================================================================
--- clangd/Diagnostics.cpp
+++ clangd/Diagnostics.cpp
@@ -145,6 +145,14 @@
   OS << diagLeveltoString(D.Severity) << ": " << D.Message;
 }
 
+/// Applies common text transformations to a diagnostic's string.
+std::string capitalizeIfNeeded(std::string Message,
+                               const DiagnosticOptions &DiagOpts) {
+  if (DiagOpts.CapitalizeDiagnosticText && !Message.empty())
+    Message[0] = llvm::toUpper(Message[0]);
+  return Message;
+}
+
 /// Returns a message sent to LSP for the main diagnostic in \p D.
 /// The message includes all the notes with their corresponding locations.
 /// However, notes with fix-its are excluded as those usually only contain a
@@ -157,29 +165,30 @@
 ///
 ///     dir1/dir2/dir3/../../dir4/header.h:12:23
 ///     note: candidate function not viable: requires 3 arguments
-std::string mainMessage(const Diag &D) {
+std::string mainMessage(const Diag &D, const DiagnosticOptions &DiagOpts) {
   std::string Result;
   llvm::raw_string_ostream OS(Result);
   OS << D.Message;
   for (auto &Note : D.Notes) {
     OS << "\n\n";
     printDiag(OS, Note);
   }
   OS.flush();
-  return Result;
+  return capitalizeIfNeeded(std::move(Result), DiagOpts);
 }
 
 /// Returns a message sent to LSP for the note of the main diagnostic.
 /// The message includes the main diagnostic to provide the necessary context
 /// for the user to understand the note.
-std::string noteMessage(const Diag &Main, const DiagBase &Note) {
+std::string noteMessage(const Diag &Main, const DiagBase &Note,
+                        const DiagnosticOptions &DiagOpts) {
   std::string Result;
   llvm::raw_string_ostream OS(Result);
   OS << Note.Message;
   OS << "\n\n";
   printDiag(OS, Main);
   OS.flush();
-  return Result;
+  return capitalizeIfNeeded(std::move(Result), DiagOpts);
 }
 } // namespace
 
@@ -222,7 +231,7 @@
 }
 
 void toLSPDiags(
-    const Diag &D,
+    const Diag &D, const DiagnosticOptions &DiagOpts,
     llvm::function_ref<void(clangd::Diagnostic, llvm::ArrayRef<Fix>)> OutFn) {
   auto FillBasicFields = [](const DiagBase &D) -> clangd::Diagnostic {
     clangd::Diagnostic Res;
@@ -233,15 +242,15 @@
 
   {
     clangd::Diagnostic Main = FillBasicFields(D);
-    Main.message = mainMessage(D);
+    Main.message = mainMessage(D, DiagOpts);
     OutFn(std::move(Main), D.Fixes);
   }
 
   for (auto &Note : D.Notes) {
     if (!Note.InsideMainFile)
       continue;
     clangd::Diagnostic Res = FillBasicFields(Note);
-    Res.message = noteMessage(D, Note);
+    Res.message = noteMessage(D, Note, DiagOpts);
     OutFn(std::move(Res), llvm::ArrayRef<Fix>());
   }
 }
Index: clangd/ClangdUnit.cpp
===================================================================
--- clangd/ClangdUnit.cpp
+++ clangd/ClangdUnit.cpp
@@ -279,7 +279,7 @@
   // reporting them.
   IgnoreDiagnostics IgnoreDiagnostics;
   IntrusiveRefCntPtr<DiagnosticsEngine> CommandLineDiagsEngine =
-      CompilerInstance::createDiagnostics(new DiagnosticOptions,
+      CompilerInstance::createDiagnostics(new clang::DiagnosticOptions,
                                           &IgnoreDiagnostics, false);
   std::unique_ptr<CompilerInvocation> CI = createInvocationFromCommandLine(
       ArgStrs, CommandLineDiagsEngine, Inputs.FS);
Index: clangd/ClangdLSPServer.h
===================================================================
--- clangd/ClangdLSPServer.h
+++ clangd/ClangdLSPServer.h
@@ -34,6 +34,7 @@
   /// loaded only from \p CompileCommandsDir. Otherwise, clangd will look
   /// for compile_commands.json in all parent directories of each file.
   ClangdLSPServer(JSONOutput &Out, const clangd::CodeCompleteOptions &CCOpts,
+                  const clangd::DiagnosticOptions &DiagOpts,
                   llvm::Optional<Path> CompileCommandsDir,
                   bool ShouldUseInMemoryCDB, const ClangdServer::Options &Opts);
 
@@ -155,6 +156,8 @@
   RealFileSystemProvider FSProvider;
   /// Options used for code completion
   clangd::CodeCompleteOptions CCOpts;
+  /// Options used for diagnostics.
+  clangd::DiagnosticOptions DiagOpts;
   /// The supported kinds of the client.
   SymbolKindBitset SupportedSymbolKinds;
 
Index: clangd/ClangdLSPServer.cpp
===================================================================
--- clangd/ClangdLSPServer.cpp
+++ clangd/ClangdLSPServer.cpp
@@ -436,13 +436,15 @@
 
 ClangdLSPServer::ClangdLSPServer(JSONOutput &Out,
                                  const clangd::CodeCompleteOptions &CCOpts,
+                                 const clangd::DiagnosticOptions &DiagOpts,
                                  llvm::Optional<Path> CompileCommandsDir,
                                  bool ShouldUseInMemoryCDB,
                                  const ClangdServer::Options &Opts)
     : Out(Out), CDB(ShouldUseInMemoryCDB ? CompilationDB::makeInMemory()
                                          : CompilationDB::makeDirectoryBased(
                                                std::move(CompileCommandsDir))),
-      CCOpts(CCOpts), SupportedSymbolKinds(defaultSymbolKinds()),
+      CCOpts(CCOpts), DiagOpts(DiagOpts),
+      SupportedSymbolKinds(defaultSymbolKinds()),
       Server(CDB.getCDB(), FSProvider, /*DiagConsumer=*/*this, Opts) {}
 
 bool ClangdLSPServer::run(std::FILE *In, JSONStreamStyle InputStyle) {
@@ -485,17 +487,18 @@
 
   DiagnosticToReplacementMap LocalFixIts; // Temporary storage
   for (auto &Diag : Diagnostics) {
-    toLSPDiags(Diag, [&](clangd::Diagnostic Diag, llvm::ArrayRef<Fix> Fixes) {
-      DiagnosticsJSON.push_back(json::Object{
-          {"range", Diag.range},
-          {"severity", Diag.severity},
-          {"message", Diag.message},
-      });
-
-      auto &FixItsForDiagnostic = LocalFixIts[Diag];
-      std::copy(Fixes.begin(), Fixes.end(),
-                std::back_inserter(FixItsForDiagnostic));
-    });
+    toLSPDiags(Diag, DiagOpts,
+               [&](clangd::Diagnostic Diag, llvm::ArrayRef<Fix> Fixes) {
+                 DiagnosticsJSON.push_back(json::Object{
+                     {"range", Diag.range},
+                     {"severity", Diag.severity},
+                     {"message", Diag.message},
+                 });
+
+                 auto &FixItsForDiagnostic = LocalFixIts[Diag];
+                 std::copy(Fixes.begin(), Fixes.end(),
+                           std::back_inserter(FixItsForDiagnostic));
+               });
   }
 
   // Cache FixIts
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to