about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTrevor Gross <tmgross@umich.edu>2024-07-18 20:33:22 -0400
committerTrevor Gross <tmgross@umich.edu>2024-07-21 12:05:02 -0500
commit827970ebe9fcce27ada9f71977895666f56032be (patch)
tree9ec67fb6ff05ab816f184e3f226c29d6597258f0
parent68fb25e2eb4898551037a62b753bc9702f868fe3 (diff)
downloadrust-827970ebe9fcce27ada9f71977895666f56032be.tar.gz
rust-827970ebe9fcce27ada9f71977895666f56032be.zip
Implement `debug_more_non_exhaustive`
Add a `.finish_non_exhaustive()` method to `DebugTuple`, `DebugSet`,
`DebugList`, and `DebugMap`. This indicates that the structures have
remaining items with `..`.

This implements the ACP at
<https://github.com/rust-lang/libs-team/issues/248>.
-rw-r--r--library/core/src/fmt/builders.rs200
-rw-r--r--library/core/tests/fmt/builders.rs322
-rw-r--r--library/core/tests/lib.rs1
3 files changed, 521 insertions, 2 deletions
diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs
index 944f7c08507..1d04320831d 100644
--- a/library/core/src/fmt/builders.rs
+++ b/library/core/src/fmt/builders.rs
@@ -360,6 +360,51 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> {
         self
     }
 
+    /// Marks the tuple struct as non-exhaustive, indicating to the reader that there are some
+    /// other fields that are not shown in the debug representation.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(debug_more_non_exhaustive)]
+    ///
+    /// use std::fmt;
+    ///
+    /// struct Foo(i32, String);
+    ///
+    /// impl fmt::Debug for Foo {
+    ///     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+    ///         fmt.debug_tuple("Foo")
+    ///            .field(&self.0)
+    ///            .finish_non_exhaustive() // Show that some other field(s) exist.
+    ///     }
+    /// }
+    ///
+    /// assert_eq!(
+    ///     format!("{:?}", Foo(10, "secret!".to_owned())),
+    ///     "Foo(10, ..)",
+    /// );
+    /// ```
+    #[unstable(feature = "debug_more_non_exhaustive", issue = "127942")]
+    pub fn finish_non_exhaustive(&mut self) -> fmt::Result {
+        self.result = self.result.and_then(|_| {
+            if self.fields > 0 {
+                if self.is_pretty() {
+                    let mut slot = None;
+                    let mut state = Default::default();
+                    let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut state);
+                    writer.write_str("..\n")?;
+                    self.fmt.write_str(")")
+                } else {
+                    self.fmt.write_str(", ..)")
+                }
+            } else {
+                self.fmt.write_str("(..)")
+            }
+        });
+        self.result
+    }
+
     /// Finishes output and returns any error encountered.
     ///
     /// # Examples
@@ -554,6 +599,56 @@ impl<'a, 'b: 'a> DebugSet<'a, 'b> {
         self
     }
 
+    /// Marks the set as non-exhaustive, indicating to the reader that there are some other
+    /// elements that are not shown in the debug representation.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(debug_more_non_exhaustive)]
+    ///
+    /// use std::fmt;
+    ///
+    /// struct Foo(Vec<i32>);
+    ///
+    /// impl fmt::Debug for Foo {
+    ///     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+    ///         // Print at most two elements, abbreviate the rest
+    ///         let mut f = fmt.debug_set();
+    ///         let mut f = f.entries(self.0.iter().take(2));
+    ///         if self.0.len() > 2 {
+    ///             f.finish_non_exhaustive()
+    ///         } else {
+    ///             f.finish()
+    ///         }
+    ///     }
+    /// }
+    ///
+    /// assert_eq!(
+    ///     format!("{:?}", Foo(vec![1, 2, 3, 4])),
+    ///     "{1, 2, ..}",
+    /// );
+    /// ```
+    #[unstable(feature = "debug_more_non_exhaustive", issue = "127942")]
+    pub fn finish_non_exhaustive(&mut self) -> fmt::Result {
+        self.inner.result = self.inner.result.and_then(|_| {
+            if self.inner.has_fields {
+                if self.inner.is_pretty() {
+                    let mut slot = None;
+                    let mut state = Default::default();
+                    let mut writer = PadAdapter::wrap(self.inner.fmt, &mut slot, &mut state);
+                    writer.write_str("..\n")?;
+                    self.inner.fmt.write_str("}")
+                } else {
+                    self.inner.fmt.write_str(", ..}")
+                }
+            } else {
+                self.inner.fmt.write_str("..}")
+            }
+        });
+        self.inner.result
+    }
+
     /// Finishes output and returns any error encountered.
     ///
     /// # Examples
@@ -697,6 +792,55 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> {
         self
     }
 
+    /// Marks the list as non-exhaustive, indicating to the reader that there are some other
+    /// elements that are not shown in the debug representation.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(debug_more_non_exhaustive)]
+    ///
+    /// use std::fmt;
+    ///
+    /// struct Foo(Vec<i32>);
+    ///
+    /// impl fmt::Debug for Foo {
+    ///     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+    ///         // Print at most two elements, abbreviate the rest
+    ///         let mut f = fmt.debug_list();
+    ///         let mut f = f.entries(self.0.iter().take(2));
+    ///         if self.0.len() > 2 {
+    ///             f.finish_non_exhaustive()
+    ///         } else {
+    ///             f.finish()
+    ///         }
+    ///     }
+    /// }
+    ///
+    /// assert_eq!(
+    ///     format!("{:?}", Foo(vec![1, 2, 3, 4])),
+    ///     "[1, 2, ..]",
+    /// );
+    /// ```
+    #[unstable(feature = "debug_more_non_exhaustive", issue = "127942")]
+    pub fn finish_non_exhaustive(&mut self) -> fmt::Result {
+        self.inner.result.and_then(|_| {
+            if self.inner.has_fields {
+                if self.inner.is_pretty() {
+                    let mut slot = None;
+                    let mut state = Default::default();
+                    let mut writer = PadAdapter::wrap(self.inner.fmt, &mut slot, &mut state);
+                    writer.write_str("..\n")?;
+                    self.inner.fmt.write_str("]")
+                } else {
+                    self.inner.fmt.write_str(", ..]")
+                }
+            } else {
+                self.inner.fmt.write_str("..]")
+            }
+        })
+    }
+
     /// Finishes output and returns any error encountered.
     ///
     /// # Examples
@@ -973,6 +1117,62 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
         self
     }
 
+    /// Marks the map as non-exhaustive, indicating to the reader that there are some other
+    /// entries that are not shown in the debug representation.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(debug_more_non_exhaustive)]
+    ///
+    /// use std::fmt;
+    ///
+    /// struct Foo(Vec<(String, i32)>);
+    ///
+    /// impl fmt::Debug for Foo {
+    ///     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+    ///         // Print at most two elements, abbreviate the rest
+    ///         let mut f = fmt.debug_map();
+    ///         let mut f = f.entries(self.0.iter().take(2).map(|&(ref k, ref v)| (k, v)));
+    ///         if self.0.len() > 2 {
+    ///             f.finish_non_exhaustive()
+    ///         } else {
+    ///             f.finish()
+    ///         }
+    ///     }
+    /// }
+    ///
+    /// assert_eq!(
+    ///     format!("{:?}", Foo(vec![
+    ///         ("A".to_string(), 10),
+    ///         ("B".to_string(), 11),
+    ///         ("C".to_string(), 12),
+    ///     ])),
+    ///     r#"{"A": 10, "B": 11, ..}"#,
+    /// );
+    /// ```
+    #[unstable(feature = "debug_more_non_exhaustive", issue = "127942")]
+    pub fn finish_non_exhaustive(&mut self) -> fmt::Result {
+        self.result = self.result.and_then(|_| {
+            assert!(!self.has_key, "attempted to finish a map with a partial entry");
+
+            if self.has_fields {
+                if self.is_pretty() {
+                    let mut slot = None;
+                    let mut state = Default::default();
+                    let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut state);
+                    writer.write_str("..\n")?;
+                    self.fmt.write_str("}")
+                } else {
+                    self.fmt.write_str(", ..}")
+                }
+            } else {
+                self.fmt.write_str("..}")
+            }
+        });
+        self.result
+    }
+
     /// Finishes output and returns any error encountered.
     ///
     /// # Panics
diff --git a/library/core/tests/fmt/builders.rs b/library/core/tests/fmt/builders.rs
index 7b73f138151..ba4801f5912 100644
--- a/library/core/tests/fmt/builders.rs
+++ b/library/core/tests/fmt/builders.rs
@@ -261,6 +261,80 @@ mod debug_tuple {
             format!("{Bar:#?}")
         );
     }
+
+    #[test]
+    fn test_empty_non_exhaustive() {
+        struct Foo;
+
+        impl fmt::Debug for Foo {
+            fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+                fmt.debug_tuple("Foo").finish_non_exhaustive()
+            }
+        }
+
+        assert_eq!("Foo(..)", format!("{Foo:?}"));
+        assert_eq!("Foo(..)", format!("{Foo:#?}"));
+    }
+
+    #[test]
+    fn test_multiple_and_non_exhaustive() {
+        struct Foo;
+
+        impl fmt::Debug for Foo {
+            fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+                fmt.debug_tuple("Foo")
+                    .field(&true)
+                    .field(&format_args!("{}/{}", 10, 20))
+                    .finish_non_exhaustive()
+            }
+        }
+
+        assert_eq!("Foo(true, 10/20, ..)", format!("{Foo:?}"));
+        assert_eq!(
+            "Foo(
+    true,
+    10/20,
+    ..
+)",
+            format!("{Foo:#?}")
+        );
+    }
+
+    #[test]
+    fn test_nested_non_exhaustive() {
+        struct Foo;
+
+        impl fmt::Debug for Foo {
+            fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+                fmt.debug_tuple("Foo")
+                    .field(&true)
+                    .field(&format_args!("{}/{}", 10, 20))
+                    .finish_non_exhaustive()
+            }
+        }
+
+        struct Bar;
+
+        impl fmt::Debug for Bar {
+            fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+                fmt.debug_tuple("Bar").field(&Foo).field(&"world").finish_non_exhaustive()
+            }
+        }
+
+        assert_eq!(r#"Bar(Foo(true, 10/20, ..), "world", ..)"#, format!("{Bar:?}"));
+        assert_eq!(
+            r#"Bar(
+    Foo(
+        true,
+        10/20,
+        ..
+    ),
+    "world",
+    ..
+)"#,
+            format!("{Bar:#?}")
+        );
+    }
 }
 
 mod debug_map {
@@ -371,8 +445,7 @@ mod debug_map {
         }
 
         assert_eq!(
-            r#"{"foo": {"bar": true, 10: 10/20}, \
-                    {"bar": true, 10: 10/20}: "world"}"#,
+            r#"{"foo": {"bar": true, 10: 10/20}, {"bar": true, 10: 10/20}: "world"}"#,
             format!("{Bar:?}")
         );
         assert_eq!(
@@ -471,6 +544,103 @@ mod debug_map {
 
         let _ = format!("{Foo:?}");
     }
+
+    #[test]
+    fn test_empty_non_exhaustive() {
+        struct Foo;
+
+        impl fmt::Debug for Foo {
+            fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+                fmt.debug_map().finish_non_exhaustive()
+            }
+        }
+
+        assert_eq!("{..}", format!("{Foo:?}"));
+        assert_eq!("{..}", format!("{Foo:#?}"));
+    }
+
+    #[test]
+    fn test_multiple_and_non_exhaustive() {
+        struct Entry;
+
+        impl fmt::Debug for Entry {
+            fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+                fmt.debug_map()
+                    .entry(&"bar", &true)
+                    .entry(&10, &format_args!("{}/{}", 10, 20))
+                    .finish_non_exhaustive()
+            }
+        }
+
+        struct KeyValue;
+
+        impl fmt::Debug for KeyValue {
+            fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+                fmt.debug_map()
+                    .key(&"bar")
+                    .value(&true)
+                    .key(&10)
+                    .value(&format_args!("{}/{}", 10, 20))
+                    .finish_non_exhaustive()
+            }
+        }
+
+        assert_eq!(format!("{Entry:?}"), format!("{KeyValue:?}"));
+        assert_eq!(format!("{Entry:#?}"), format!("{KeyValue:#?}"));
+
+        assert_eq!(r#"{"bar": true, 10: 10/20, ..}"#, format!("{Entry:?}"));
+        assert_eq!(
+            r#"{
+    "bar": true,
+    10: 10/20,
+    ..
+}"#,
+            format!("{Entry:#?}")
+        );
+    }
+
+    #[test]
+    fn test_nested_non_exhaustive() {
+        struct Foo;
+
+        impl fmt::Debug for Foo {
+            fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+                fmt.debug_map()
+                    .entry(&"bar", &true)
+                    .entry(&10, &format_args!("{}/{}", 10, 20))
+                    .finish_non_exhaustive()
+            }
+        }
+
+        struct Bar;
+
+        impl fmt::Debug for Bar {
+            fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+                fmt.debug_map().entry(&"foo", &Foo).entry(&Foo, &"world").finish_non_exhaustive()
+            }
+        }
+
+        assert_eq!(
+            r#"{"foo": {"bar": true, 10: 10/20, ..}, {"bar": true, 10: 10/20, ..}: "world", ..}"#,
+            format!("{Bar:?}")
+        );
+        assert_eq!(
+            r#"{
+    "foo": {
+        "bar": true,
+        10: 10/20,
+        ..
+    },
+    {
+        "bar": true,
+        10: 10/20,
+        ..
+    }: "world",
+    ..
+}"#,
+            format!("{Bar:#?}")
+        );
+    }
 }
 
 mod debug_set {
@@ -559,6 +729,80 @@ mod debug_set {
             format!("{Bar:#?}")
         );
     }
+
+    #[test]
+    fn test_empty_non_exhaustive() {
+        struct Foo;
+
+        impl fmt::Debug for Foo {
+            fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+                fmt.debug_set().finish_non_exhaustive()
+            }
+        }
+
+        assert_eq!("{..}", format!("{Foo:?}"));
+        assert_eq!("{..}", format!("{Foo:#?}"));
+    }
+
+    #[test]
+    fn test_multiple_and_non_exhaustive() {
+        struct Foo;
+
+        impl fmt::Debug for Foo {
+            fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+                fmt.debug_set()
+                    .entry(&true)
+                    .entry(&format_args!("{}/{}", 10, 20))
+                    .finish_non_exhaustive()
+            }
+        }
+
+        assert_eq!("{true, 10/20, ..}", format!("{Foo:?}"));
+        assert_eq!(
+            "{
+    true,
+    10/20,
+    ..
+}",
+            format!("{Foo:#?}")
+        );
+    }
+
+    #[test]
+    fn test_nested_non_exhaustive() {
+        struct Foo;
+
+        impl fmt::Debug for Foo {
+            fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+                fmt.debug_set()
+                    .entry(&true)
+                    .entry(&format_args!("{}/{}", 10, 20))
+                    .finish_non_exhaustive()
+            }
+        }
+
+        struct Bar;
+
+        impl fmt::Debug for Bar {
+            fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+                fmt.debug_set().entry(&Foo).entry(&"world").finish_non_exhaustive()
+            }
+        }
+
+        assert_eq!(r#"{{true, 10/20, ..}, "world", ..}"#, format!("{Bar:?}"));
+        assert_eq!(
+            r#"{
+    {
+        true,
+        10/20,
+        ..
+    },
+    "world",
+    ..
+}"#,
+            format!("{Bar:#?}")
+        );
+    }
 }
 
 mod debug_list {
@@ -647,6 +891,80 @@ mod debug_list {
             format!("{Bar:#?}")
         );
     }
+
+    #[test]
+    fn test_empty_non_exhaustive() {
+        struct Foo;
+
+        impl fmt::Debug for Foo {
+            fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+                fmt.debug_list().finish_non_exhaustive()
+            }
+        }
+
+        assert_eq!("[..]", format!("{Foo:?}"));
+        assert_eq!("[..]", format!("{Foo:#?}"));
+    }
+
+    #[test]
+    fn test_multiple_non_exhaustive() {
+        struct Foo;
+
+        impl fmt::Debug for Foo {
+            fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+                fmt.debug_list()
+                    .entry(&true)
+                    .entry(&format_args!("{}/{}", 10, 20))
+                    .finish_non_exhaustive()
+            }
+        }
+
+        assert_eq!("[true, 10/20, ..]", format!("{Foo:?}"));
+        assert_eq!(
+            "[
+    true,
+    10/20,
+    ..
+]",
+            format!("{Foo:#?}")
+        );
+    }
+
+    #[test]
+    fn test_nested_non_exhaustive() {
+        struct Foo;
+
+        impl fmt::Debug for Foo {
+            fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+                fmt.debug_list()
+                    .entry(&true)
+                    .entry(&format_args!("{}/{}", 10, 20))
+                    .finish_non_exhaustive()
+            }
+        }
+
+        struct Bar;
+
+        impl fmt::Debug for Bar {
+            fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+                fmt.debug_list().entry(&Foo).entry(&"world").finish_non_exhaustive()
+            }
+        }
+
+        assert_eq!(r#"[[true, 10/20, ..], "world", ..]"#, format!("{Bar:?}"));
+        assert_eq!(
+            r#"[
+    [
+        true,
+        10/20,
+        ..
+    ],
+    "world",
+    ..
+]"#,
+            format!("{Bar:#?}")
+        );
+    }
 }
 
 #[test]
diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs
index 83a615fcd8b..0f6ba7e4c66 100644
--- a/library/core/tests/lib.rs
+++ b/library/core/tests/lib.rs
@@ -29,6 +29,7 @@
 #![feature(core_io_borrowed_buf)]
 #![feature(core_private_bignum)]
 #![feature(core_private_diy_float)]
+#![feature(debug_more_non_exhaustive)]
 #![feature(dec2flt)]
 #![feature(duration_consts_float)]
 #![feature(duration_constants)]