[binutils-gdb] Rewrite Rust character printer

Tom Tromey tromey@sourceware.org
Sat Jan 31 19:27:23 GMT 2026


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=d26406ed1bf129482a92f05e0f6c405f502225e0

commit d26406ed1bf129482a92f05e0f6c405f502225e0
Author: Tom Tromey <tom@tromey.com>
Date:   Mon Dec 22 13:20:40 2025 -0700

    Rewrite Rust character printer
    
    This adds a Rust-specific subclass of wchar_printer and arranges to
    use it.
    
    Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=20164

Diff:
---
 gdb/rust-lang.c                    | 94 ++++++++++++++++++++++++++++----------
 gdb/testsuite/gdb.rust/expr.exp    |  4 +-
 gdb/testsuite/gdb.rust/unicode.exp |  3 +-
 3 files changed, 73 insertions(+), 28 deletions(-)

diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c
index 83786ff6e85..1695fd73344 100644
--- a/gdb/rust-lang.c
+++ b/gdb/rust-lang.c
@@ -38,6 +38,8 @@
 #include "cli/cli-style.h"
 #include "parser-defs.h"
 #include "rust-exp.h"
+#include "char-print.h"
+#include "extract-store-integer.h"
 
 /* See rust-lang.h.  */
 
@@ -390,6 +392,69 @@ rust_array_like_element_type (struct type *type)
 
 

 
+/* A wchar_printer specialized for Rust syntax.  */
+class rust_wchar_printer : public wchar_printer
+{
+  using wchar_printer::wchar_printer;
+
+  bool printable (gdb_wchar_t w) const override;
+  void print_char (gdb_wchar_t w) override;
+  void print_escape (const gdb_byte *orig, int orig_len) override;
+};
+
+bool
+rust_wchar_printer::printable (gdb_wchar_t w) const
+{
+  return (gdb_iswprint (w)
+	  || w == LCST ('\n') || w == LCST ('\r')
+	  || w == LCST ('\t') || w == LCST ('\0'));
+}
+
+void
+rust_wchar_printer::print_char (gdb_wchar_t w)
+{
+  if (w == LCST ('\\'))
+    m_file.write (LCST ("\\\\"));
+  else if (w == gdb_btowc (m_quoter))
+    {
+      m_file.write (LCST ('\\'));
+      m_file.write (w);
+    }
+  else if (w == LCST ('\n'))
+    m_file.write (LCST ("\\n"));
+  else if (w == LCST ('\r'))
+    m_file.write (LCST ("\\r"));
+  else if (w == LCST ('\t'))
+    m_file.write (LCST ("\\t"));
+  else if (w == LCST ('\0'))
+    m_file.write (LCST ("\\0"));
+  else
+    m_file.write (w);
+}
+
+void
+rust_wchar_printer::print_escape (const gdb_byte *orig, int orig_len)
+{
+  int i;
+
+  for (i = 0; i + m_width <= orig_len; i += m_width)
+    {
+      ULONGEST value = extract_unsigned_integer (&orig[i], m_width,
+						 m_byte_order);
+      if (value <= 255)
+	gdb_printf (&m_file, "\\x%02x", (int) value);
+      else
+	gdb_printf (&m_file, "\\u{%06lx}", (unsigned long) value);
+    }
+
+  /* If we somehow have extra bytes, print them now.  */
+  while (i < orig_len)
+    {
+      gdb_printf (&m_file, "\\x%02x", orig[i] & 0xff);
+      ++i;
+    }
+}
+
 /* See language.h.  */
 
 void
@@ -417,9 +482,8 @@ rust_language::printstr (struct ui_file *stream, struct type *type,
 	}
     }
 
-  /* This is not ideal as it doesn't use our character printer.  */
-  generic_printstr (stream, type, string, length, encoding, force_ellipses,
-		    '"', 0, options);
+  rust_wchar_printer printer (type, '"', encoding);
+  printer.print (stream, string, length, force_ellipses, 0, options);
 }
 
 

@@ -1776,29 +1840,11 @@ rust_language::printchar (int ch, struct type *chtype,
 			  struct ui_file *stream) const
 {
   if (!rust_chartype_p (chtype))
+    generic_emit_char (ch, chtype, stream, target_charset (chtype->arch ()));
+  else
     {
-      generic_emit_char (ch, chtype, stream,
-			 target_charset (chtype->arch ()));
-      return;
+      rust_wchar_printer (chtype, '\'').print (ch, stream);
     }
-  gdb_puts ("'", stream);
-  if (ch == '\\')
-    gdb_printf (stream, "\\%c", ch);
-  else if (ch == '\n')
-    gdb_puts ("\\n", stream);
-  else if (ch == '\r')
-    gdb_puts ("\\r", stream);
-  else if (ch == '\t')
-    gdb_puts ("\\t", stream);
-  else if (ch == '\0')
-    gdb_puts ("\\0", stream);
-  else if (ch >= 32 && ch <= 127 && c_isprint (ch))
-    gdb_putc (ch, stream);
-  else if (ch <= 255)
-    gdb_printf (stream, "\\x%02x", ch);
-  else
-    gdb_printf (stream, "\\u{%06x}", ch);
-  gdb_puts ("'", stream);
 }
 
 /* See language.h.  */
diff --git a/gdb/testsuite/gdb.rust/expr.exp b/gdb/testsuite/gdb.rust/expr.exp
index e3306c0f478..3eb72cf01b3 100644
--- a/gdb/testsuite/gdb.rust/expr.exp
+++ b/gdb/testsuite/gdb.rust/expr.exp
@@ -122,8 +122,8 @@ gdb_test "print \[0;1\]" " = \\\[0\\\]"
 gdb_test "print b\"hi rust\"" " = b\"hi rust\""
 # This isn't rusty syntax yet, but that's another bug -- this is just
 # testing that byte escapes work properly.
-gdb_test "print b\"\\xddhi bob\"" " = b\"\\\\335hi bob\""
-gdb_test "print b\"has\\0nul\"" " = b\"has\\\\000nul\""
+gdb_test "print b\"\\xddhi bob\"" " = b\"\\\\xddhi bob\""
+gdb_test "print b\"has\\0nul\"" " = b\"has\\\\0nul\""
 
 gdb_test "print br##\"hi\"##" " = b\"hi\""
 gdb_test "print br##\"hi" "Unexpected EOF in string"
diff --git a/gdb/testsuite/gdb.rust/unicode.exp b/gdb/testsuite/gdb.rust/unicode.exp
index 401fb1f3502..b9951013738 100644
--- a/gdb/testsuite/gdb.rust/unicode.exp
+++ b/gdb/testsuite/gdb.rust/unicode.exp
@@ -39,8 +39,7 @@ if {![runto ${srcfile}:$line]} {
 
 gdb_test "print 𝕯" " = 98" "print D"
 gdb_test "print \"𝕯\"" " = \"𝕯\"" "print D in string"
-# This output is maybe not ideal, but it also isn't incorrect.
-gdb_test "print '𝕯'" " = 120175 '\\\\u\\\{01d56f\\\}'" \
+gdb_test "print '𝕯'" " = 120175 '𝕯'" \
     "print D as char"
 gdb_test "print cç" " = 97" "print cc"


More information about the Gdb-cvs mailing list