[Lldb-commits] [lldb] [lldb] Assert lack of trailing period or newlines in diagnostics (PR #191447)

Jonas Devlieghere via lldb-commits lldb-commits at lists.llvm.org
Fri Apr 17 10:04:19 PDT 2026


https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/191447

>From 0e4852aad5ced62068a01c0a6eb18b2dbd45f2b5 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Fri, 10 Apr 2026 16:19:45 +0100
Subject: [PATCH 1/2] [lldb] Assert lack of trailing period or newlines in
 diagnostics

This PR adds an assert to CommandReturnObject::{AppendNote,
AppendWarning} to ensure the diagnostics don't end with a newline, which
is added by the function, or a period, which goes against the coding
standards.

We should do the same thing for AppendError, but currently there are
still too many violations that need to be cleaned up and if the compiler
emits non-compliant diagnostics we may not be able to do this at all.
---
 lldb/source/Commands/CommandObjectDWIMPrint.cpp   |  2 +-
 lldb/source/Commands/CommandObjectDisassemble.cpp |  4 ++--
 lldb/source/Commands/CommandObjectSource.cpp      |  4 ++--
 lldb/source/Commands/CommandObjectThread.cpp      |  2 +-
 lldb/source/Interpreter/CommandInterpreter.cpp    | 15 +++++----------
 lldb/source/Interpreter/CommandReturnObject.cpp   | 10 ++++++++++
 lldb/source/Target/Thread.cpp                     |  2 +-
 .../Commands/command-disassemble-process.yaml     | 12 ++++++------
 lldb/test/Shell/Commands/command-disassemble.s    |  8 ++++----
 .../command-list-reach-beginning-of-file.test     |  2 +-
 .../Commands/command-list-reach-end-of-file.test  |  6 +++---
 lldb/test/Shell/Driver/LocalLLDBInit.test         |  2 +-
 12 files changed, 37 insertions(+), 32 deletions(-)

diff --git a/lldb/source/Commands/CommandObjectDWIMPrint.cpp b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
index 27bd71c21ad3f..0772fe21fff85 100644
--- a/lldb/source/Commands/CommandObjectDWIMPrint.cpp
+++ b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
@@ -126,7 +126,7 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
       result.AppendNote(
           "object description requested, but type doesn't implement "
           "a custom object description. Consider using \"p\" instead of "
-          "\"po\" (this note will only be shown once per debug session).\n");
+          "\"po\" (this note will only be shown once per debug session).");
       note_shown = true;
     }
   };
diff --git a/lldb/source/Commands/CommandObjectDisassemble.cpp b/lldb/source/Commands/CommandObjectDisassemble.cpp
index 265f8ff643125..8f696087bcf97 100644
--- a/lldb/source/Commands/CommandObjectDisassemble.cpp
+++ b/lldb/source/Commands/CommandObjectDisassemble.cpp
@@ -254,12 +254,12 @@ CommandObjectDisassemble::CheckRangeSize(std::vector<AddressRange> ranges,
     return ranges;
 
   StreamString msg;
-  msg << "Not disassembling " << what << " because it is very large ";
+  msg << "not disassembling " << what << " because it is very large ";
   for (const AddressRange &r : ranges)
     r.Dump(&msg, &GetTarget(), Address::DumpStyleLoadAddress,
            Address::DumpStyleFileAddress);
   msg << ". To disassemble specify an instruction count limit, start/stop "
-         "addresses or use the --force option.";
+         "addresses or use the --force option";
   return llvm::createStringError(msg.GetString());
 }
 
diff --git a/lldb/source/Commands/CommandObjectSource.cpp b/lldb/source/Commands/CommandObjectSource.cpp
index 3e323f518a267..1785575c7899c 100644
--- a/lldb/source/Commands/CommandObjectSource.cpp
+++ b/lldb/source/Commands/CommandObjectSource.cpp
@@ -497,7 +497,7 @@ class CommandObjectSourceInfo : public CommandObjectParsed {
         displayed_something = true;
     }
     if (!displayed_something) {
-      result.AppendErrorWithFormat("No source filenames matched '%s'.\n",
+      result.AppendErrorWithFormat("no source filenames matched '%s'",
                                    filename);
       return false;
     }
@@ -1068,7 +1068,7 @@ class CommandObjectSourceList : public CommandObjectParsed {
                 "Reached {0} of the file, no more to page",
                 m_options.reverse ? "beginning" : "end");
           } else {
-            result.AppendNote("No source available");
+            result.AppendNote("no source available");
           }
         }
 
diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp
index 1ddc62348d744..1391a612cb72f 100644
--- a/lldb/source/Commands/CommandObjectThread.cpp
+++ b/lldb/source/Commands/CommandObjectThread.cpp
@@ -1911,7 +1911,7 @@ class CommandObjectThreadJump : public CommandObjectParsed {
 
       if (!file) {
         result.AppendErrorWithFormat(
-            "No source file available for the current location.");
+            "no source file available for the current location");
         return;
       }
 
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index abbc2992b19e8..ee34feaf157a1 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -104,16 +104,11 @@ using namespace lldb_private;
 static const char *k_white_space = " \t\v";
 
 static constexpr const char *InitFileWarning =
-    "There is a .lldbinit file in the current directory which is not being "
-    "read.\n"
-    "To silence this warning without sourcing in the local .lldbinit,\n"
-    "add the following to the lldbinit file in your home directory:\n"
-    "    settings set target.load-cwd-lldbinit false\n"
-    "To allow lldb to source .lldbinit files in the current working "
-    "directory,\n"
-    "set the value of this variable to true.  Only do so if you understand "
-    "and\n"
-    "accept the security risk.";
+    R"(there is a .lldbinit file in the current directory which is not being read.
+To silence this warning without sourcing in the local .lldbinit, add the following to the lldbinit file in your home directory:
+    settings set target.load-cwd-lldbinit false\n"
+To allow lldb to source .lldbinit files in the current working directory, set the value of this variable to true.
+Only do so if you understand and accept the security risk)";
 
 const char *CommandInterpreter::g_no_argument = "<no-argument>";
 const char *CommandInterpreter::g_need_argument = "<need-argument>";
diff --git a/lldb/source/Interpreter/CommandReturnObject.cpp b/lldb/source/Interpreter/CommandReturnObject.cpp
index 59def88132c47..fa722b5ba56cd 100644
--- a/lldb/source/Interpreter/CommandReturnObject.cpp
+++ b/lldb/source/Interpreter/CommandReturnObject.cpp
@@ -33,6 +33,12 @@ static llvm::raw_ostream &note(Stream &strm) {
          << "note: ";
 }
 
+static llvm::StringRef validate(llvm::StringRef diagnostic) {
+  assert(!diagnostic.ends_with('\n') && !diagnostic.ends_with('.') &&
+         "diagnostics should end without a period/newline");
+  return diagnostic.trim("\n.");
+}
+
 static void DumpStringToStreamWithNewline(Stream &strm, const std::string &s) {
   bool add_newline = false;
   if (!s.empty()) {
@@ -75,12 +81,14 @@ void CommandReturnObject::AppendMessage(llvm::StringRef in_string) {
 }
 
 void CommandReturnObject::AppendNote(llvm::StringRef in_string) {
+  in_string = validate(in_string);
   if (in_string.empty())
     return;
   note(GetOutputStream()) << in_string.rtrim() << '\n';
 }
 
 void CommandReturnObject::AppendWarning(llvm::StringRef in_string) {
+  in_string = validate(in_string);
   if (in_string.empty())
     return;
   warning(GetErrorStream()) << in_string.rtrim() << '\n';
@@ -93,6 +101,8 @@ void CommandReturnObject::AppendError(llvm::StringRef in_string) {
   // Workaround to deal with already fully formatted compiler diagnostics.
   llvm::StringRef msg(in_string.rtrim());
   msg.consume_front("error: ");
+
+  // FIXME: We should call validate here.
   error(GetErrorStream()) << msg << '\n';
 }
 
diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp
index c199fd236f5cd..03d3525bacbdc 100644
--- a/lldb/source/Target/Thread.cpp
+++ b/lldb/source/Target/Thread.cpp
@@ -1898,7 +1898,7 @@ Status Thread::JumpToLine(const FileSpec &file, uint32_t line,
         "first location:\n",
         file.GetFilename(), line);
     DumpAddressList(sstr, candidates, target);
-    *warnings = std::string(sstr.GetString());
+    *warnings = std::string(sstr.GetString().trim('\n'));
   }
 
   if (!reg_ctx->SetPC(dest))
diff --git a/lldb/test/Shell/Commands/command-disassemble-process.yaml b/lldb/test/Shell/Commands/command-disassemble-process.yaml
index 931e0b93e3b67..de780bf0e0486 100644
--- a/lldb/test/Shell/Commands/command-disassemble-process.yaml
+++ b/lldb/test/Shell/Commands/command-disassemble-process.yaml
@@ -70,15 +70,15 @@
 
 # INVALID: error: Could not find function bounds for address 0xdead
 
-# BIG: error: Not disassembling the current function because it is very large [0x0000000000004002-0x0000000000005f42). To disassemble specify an instruction count limit, start/stop addresses or use the --force option.
+# BIG: error: not disassembling the current function because it is very large [0x0000000000004002-0x0000000000005f42). To disassemble specify an instruction count limit, start/stop addresses or use the --force option
 
 --- !ELF
-FileHeader:      
+FileHeader:
   Class:           ELFCLASS64
   Data:            ELFDATA2LSB
   Type:            ET_EXEC
   Machine:         EM_X86_64
-Sections:        
+Sections:
   - Name:            .text
     Type:            SHT_PROGBITS
     Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
@@ -130,14 +130,14 @@ Streams:
       Version Info:    0x00000000
       Feature Info:    0x00000000
   - Type:            ThreadList
-    Threads:         
+    Threads:
       - Thread Id:       0x000074F3
         Context:         0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B001000000000006CAE000000006B7FC05A0000C81D415A0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A2BF9E5A6B7F0000000000000000000000000000000000008850C14BFD7F00009850C14BFD7F00000100000000000000B04AC14BFD7F0000000000000000000060812D01000000000800000000000000B065E05A6B7F00008004400000000000E050C14BFD7F00000000000000000000000000000000000004400000000000007F03FFFF0000FFFFFFFFFFFF000000000000000000000000801F00006B7F00000400000000000000B84CC14BFD7F0000304D405A6B7F0000C84DC14BFD7F0000C0AA405A6B7F00004F033D0000000000B84DC14BFD7F0000E84DC14BFD7F0000000000000000000000000000000000000070E05A6B7F000078629E5A6B7F0000C81D415A6B7F0000804F9E5A6B7F00000000000001000000E603000001000000E093115A6B7F0000804EC14BFD7F0000584EC14BFD7F000099ADC05A6B7F00000100000000000000AAAAD77D0000000002000000000000000800000000000000B065E05A6B7F0000E6B7C05A6B7F0000010000006B7F0000884DC14BFD7F0000106F7C5A6B7F0000984EC14BFD7F0000488B7C5A6B7F0000C4A71CB90000000001000000000000000800000000000000B065E05A6B7F000048B6C05A6B7F0000702AE25A6B7F0000D84DC14BFD7F000030489E5A6B7F0000E84EC14BFD7F0000E05E9E5A6B7F00000991F0460000000001000000000000000800000000000000B065E05A6B7F000048B6C05A6B7F00000100000000000000284EC14BFD7F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-        Stack:           
+        Stack:
           Start of Memory Range: 0x00007FFD4BC15080
           Content:         30044000000000000000000000000000
   - Type:            MemoryList
-    Memory Ranges:   
+    Memory Ranges:
       - Start of Memory Range: 0x00007FFD4BC15080
         Content:         30044000000000000000000000000000
 ...
diff --git a/lldb/test/Shell/Commands/command-disassemble.s b/lldb/test/Shell/Commands/command-disassemble.s
index 14f416d221231..486a1a183abc9 100644
--- a/lldb/test/Shell/Commands/command-disassemble.s
+++ b/lldb/test/Shell/Commands/command-disassemble.s
@@ -51,7 +51,7 @@
 # CHECK-NEXT: (lldb) disassemble --address 0xdeadb
 # CHECK-NEXT: error: Could not find function bounds for address 0xdeadb
 # CHECK-NEXT: (lldb) disassemble --address 0x100
-# CHECK-NEXT: error: Not disassembling the function because it is very large [0x0000000000000040-0x0000000000002040). To disassemble specify an instruction count limit, start/stop addresses or use the --force option.
+# CHECK-NEXT: error: not disassembling the function because it is very large [0x0000000000000040-0x0000000000002040). To disassemble specify an instruction count limit, start/stop addresses or use the --force option
 # CHECK-NEXT: (lldb) disassemble --address 0x100 --count 3
 # CHECK-NEXT: command-disassemble.s.tmp`very_long:
 # CHECK-NEXT: command-disassemble.s.tmp[0x40] <+0>: int    $0x2a
@@ -82,10 +82,10 @@
 # CHECK-NEXT: (lldb) disassemble --name case2
 # CHECK-NEXT: command-disassemble.s.tmp`n1::case2:
 # CHECK-NEXT: command-disassemble.s.tmp[0x2044] <+0>: int    $0x32
-# CHECK-NEXT: warning: Not disassembling a function because it is very large [0x0000000000002046-0x0000000000004046). To disassemble specify an instruction count limit, start/stop addresses or use the --force option.
+# CHECK-NEXT: warning: not disassembling a function because it is very large [0x0000000000002046-0x0000000000004046). To disassemble specify an instruction count limit, start/stop addresses or use the --force option
 # CHECK-NEXT: (lldb) disassemble --name case3
-# CHECK-NEXT: error: Not disassembling a function because it is very large [0x0000000000006046-0x0000000000007046)[0x0000000000009046-0x000000000000a050). To disassemble specify an instruction count limit, start/stop addresses or use the --force option.
-# CHECK-NEXT: Not disassembling a function because it is very large [0x0000000000004046-0x0000000000006046). To disassemble specify an instruction count limit, start/stop addresses or use the --force option.
+# CHECK-NEXT: error: not disassembling a function because it is very large [0x0000000000006046-0x0000000000007046)[0x0000000000009046-0x000000000000a050). To disassemble specify an instruction count limit, start/stop addresses or use the --force option
+# CHECK-NEXT: not disassembling a function because it is very large [0x0000000000004046-0x0000000000006046). To disassemble specify an instruction count limit, start/stop addresses or use the --force option
 # CHECK-NEXT: (lldb) disassemble --name case3 --count 3
 # CHECK-NEXT: command-disassemble.s.tmp`n2::case3:
 # CHECK-NEXT: command-disassemble.s.tmp[0x6046] <-12288>: int    $0x2a
diff --git a/lldb/test/Shell/Commands/command-list-reach-beginning-of-file.test b/lldb/test/Shell/Commands/command-list-reach-beginning-of-file.test
index 9987efedd8020..a9163cd3bf0ab 100644
--- a/lldb/test/Shell/Commands/command-list-reach-beginning-of-file.test
+++ b/lldb/test/Shell/Commands/command-list-reach-beginning-of-file.test
@@ -4,7 +4,7 @@
 # RUN: %lldb %t.out -b -s %s 2>&1 | FileCheck %s
 
 list
-# CHECK: note: No source available
+# CHECK: note: no source available
 
 b main
 # CHECK: Breakpoint 1:
diff --git a/lldb/test/Shell/Commands/command-list-reach-end-of-file.test b/lldb/test/Shell/Commands/command-list-reach-end-of-file.test
index edf4c521a9e76..160fa6b7fd562 100644
--- a/lldb/test/Shell/Commands/command-list-reach-end-of-file.test
+++ b/lldb/test/Shell/Commands/command-list-reach-end-of-file.test
@@ -4,7 +4,7 @@
 # RUN: %lldb %t.out -b -s %s 2>&1 | FileCheck %s
 
 list
-# CHECK: note: No source available 
+# CHECK: note: no source available
 
 b main
 # CHECK: Breakpoint 1:
@@ -21,8 +21,8 @@ list
 list
 # CHECK: return 0;
 
-list 
+list
 # CHECK: note: Reached end of the file, no more to page
 
-list 
+list
 # CHECK: note: Reached end of the file, no more to page
diff --git a/lldb/test/Shell/Driver/LocalLLDBInit.test b/lldb/test/Shell/Driver/LocalLLDBInit.test
index 2aa8c527929f9..c10e90f104563 100644
--- a/lldb/test/Shell/Driver/LocalLLDBInit.test
+++ b/lldb/test/Shell/Driver/LocalLLDBInit.test
@@ -9,7 +9,7 @@
 # RUN: env HOME=%t.home %lldb-init -local-lldbinit -o 'settings show frame-format' 2>&1 | FileCheck %s --check-prefix=ALLOWINIT --check-prefix=NOINIT
 # RUN: %lldb -o 'settings show frame-format' 2>&1 | FileCheck %s --check-prefix=NOINIT --check-prefix=CHECK
 
-# WARNINIT: warning: There is a .lldbinit file in the current directory which is not being read.
+# WARNINIT: warning: there is a .lldbinit file in the current directory which is not being read.
 # NOINIT-NOT: There is a .lldbinit file in the current directory which is not being read.
 # CHECK-NOT: bogus
 # ALLOWINIT: name 'prlnt' is not defined

>From 83ed35a955edcf9c2f32c2d21fc7e356e69c8968 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Fri, 17 Apr 2026 10:04:02 -0700
Subject: [PATCH 2/2] Address review feedback

---
 .../Commands/CommandObjectDWIMPrint.cpp       |  2 +-
 .../Interpreter/CommandReturnObject.cpp       | 20 +++++++++++++++----
 lldb/test/Shell/Driver/LocalLLDBInit.test     |  2 +-
 3 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/lldb/source/Commands/CommandObjectDWIMPrint.cpp b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
index 0772fe21fff85..7785a64acc261 100644
--- a/lldb/source/Commands/CommandObjectDWIMPrint.cpp
+++ b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
@@ -126,7 +126,7 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
       result.AppendNote(
           "object description requested, but type doesn't implement "
           "a custom object description. Consider using \"p\" instead of "
-          "\"po\" (this note will only be shown once per debug session).");
+          "\"po\" (this note will only be shown once per debug session)");
       note_shown = true;
     }
   };
diff --git a/lldb/source/Interpreter/CommandReturnObject.cpp b/lldb/source/Interpreter/CommandReturnObject.cpp
index fa722b5ba56cd..a6b0d56c66cca 100644
--- a/lldb/source/Interpreter/CommandReturnObject.cpp
+++ b/lldb/source/Interpreter/CommandReturnObject.cpp
@@ -33,9 +33,21 @@ static llvm::raw_ostream &note(Stream &strm) {
          << "note: ";
 }
 
-static llvm::StringRef validate(llvm::StringRef diagnostic) {
+static llvm::StringRef validate_diagnostic(llvm::StringRef diagnostic) {
+  // This class is already adding the prefix.
+  assert(!diagnostic.starts_with("warning:") &&
+         !diagnostic.starts_with("error:") &&
+         !diagnostic.starts_with("note:") &&
+         "diagnostics shouldn't duplicate error:/warning:/note:");
+
+  // https://llvm.org/docs/CodingStandards.html#error-and-warning-messages
   assert(!diagnostic.ends_with('\n') && !diagnostic.ends_with('.') &&
          "diagnostics should end without a period/newline");
+
+  // Handing the case where the assert doesn't hold goes against the idea of
+  // them being pre-conditions. However this isn't really a matter of internal
+  // consistency and therefore we prioritize a consistent user experience over
+  // purity.
   return diagnostic.trim("\n.");
 }
 
@@ -81,14 +93,14 @@ void CommandReturnObject::AppendMessage(llvm::StringRef in_string) {
 }
 
 void CommandReturnObject::AppendNote(llvm::StringRef in_string) {
-  in_string = validate(in_string);
+  in_string = validate_diagnostic(in_string);
   if (in_string.empty())
     return;
   note(GetOutputStream()) << in_string.rtrim() << '\n';
 }
 
 void CommandReturnObject::AppendWarning(llvm::StringRef in_string) {
-  in_string = validate(in_string);
+  in_string = validate_diagnostic(in_string);
   if (in_string.empty())
     return;
   warning(GetErrorStream()) << in_string.rtrim() << '\n';
@@ -102,7 +114,7 @@ void CommandReturnObject::AppendError(llvm::StringRef in_string) {
   llvm::StringRef msg(in_string.rtrim());
   msg.consume_front("error: ");
 
-  // FIXME: We should call validate here.
+  // FIXME: We should call validate_diagnostic here.
   error(GetErrorStream()) << msg << '\n';
 }
 
diff --git a/lldb/test/Shell/Driver/LocalLLDBInit.test b/lldb/test/Shell/Driver/LocalLLDBInit.test
index c10e90f104563..e8ca232f9dfe4 100644
--- a/lldb/test/Shell/Driver/LocalLLDBInit.test
+++ b/lldb/test/Shell/Driver/LocalLLDBInit.test
@@ -10,7 +10,7 @@
 # RUN: %lldb -o 'settings show frame-format' 2>&1 | FileCheck %s --check-prefix=NOINIT --check-prefix=CHECK
 
 # WARNINIT: warning: there is a .lldbinit file in the current directory which is not being read.
-# NOINIT-NOT: There is a .lldbinit file in the current directory which is not being read.
+# NOINIT-NOT: there is a .lldbinit file in the current directory which is not being read.
 # CHECK-NOT: bogus
 # ALLOWINIT: name 'prlnt' is not defined
 # ALLOWINIT: bogus



More information about the lldb-commits mailing list