From 225d439d26b86a6d655f2e3cb2c873566e3d5bef Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Sat, 6 Feb 2021 15:11:41 -0500 Subject: [PATCH] Empty Unstructured::choose param should return Err, not panic. --- CHANGELOG.md | 1 + src/error.rs | 6 ++++++ src/unstructured.rs | 34 ++++++++++++++++++++++------------ 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 861b504..2902ca5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ Released YYYY-MM-DD. ### Changed * Rename `Unstructured#get_bytes` to `Unstructured#bytes`. [#70](https://github.com/rust-fuzz/arbitrary/pull/70) +* Passing an empty slice of choices to `Unstructured#choose` returns an error. Previously it would panic. [71](https://github.com/rust-fuzz/arbitrary/pull/71) ### Removed diff --git a/src/error.rs b/src/error.rs index 8cbd076..f590c12 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,6 +4,8 @@ use std::{error, fmt}; #[derive(Debug, Clone, Copy)] #[non_exhaustive] pub enum Error { + /// No choices were provided to the Unstructured::choose call + EmptyChoose, /// There was not enough underlying data to fulfill some request for raw /// bytes. NotEnoughData, @@ -14,6 +16,10 @@ pub enum Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { + Error::EmptyChoose => write!( + f, + "`arbitrary::Unstructured::choose` must be given a non-empty set of choices" + ), Error::NotEnoughData => write!( f, "There is not enough underlying raw data to construct an `Arbitrary` instance" diff --git a/src/unstructured.rs b/src/unstructured.rs index b80ac4b..9e2f497 100644 --- a/src/unstructured.rs +++ b/src/unstructured.rs @@ -345,29 +345,39 @@ impl<'a> Unstructured<'a> { /// This should only be used inside of `Arbitrary` implementations. /// /// Returns an error if there is not enough underlying data to make a - /// choice. + /// choice or if no choices are provided. /// - /// # Panics + /// # Examples /// - /// Panics if `choices` is empty. + /// Selecting from an array of choices: /// - /// # Example + /// ``` + /// use arbitrary::Unstructured; + /// + /// let mut u = Unstructured::new(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); + /// let choices = ['a', 'b', 'c', 'd', 'e', 'f', 'g']; + /// + /// let choice = u.choose(&choices).unwrap(); + /// + /// println!("chose {}", choice); + /// ``` + /// + /// An error is returned if no choices are provided: /// /// ``` /// use arbitrary::Unstructured; /// /// let mut u = Unstructured::new(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); + /// let choices: [char; 0] = []; /// - /// let choices = ['a', 'b', 'c', 'd', 'e', 'f', 'g']; - /// if let Ok(ch) = u.choose(&choices) { - /// println!("chose {}", ch); - /// } + /// let result = u.choose(&choices); + /// + /// assert!(result.is_err()); /// ``` pub fn choose<'b, T>(&mut self, choices: &'b [T]) -> Result<&'b T> { - assert!( - !choices.is_empty(), - "`arbitrary::Unstructured::choose` must be given a non-empty set of choices" - ); + if choices.is_empty() { + return Err(Error::EmptyChoose); + } let idx = self.int_in_range(0..=choices.len() - 1)?; Ok(&choices[idx]) }