diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9cdd702a..947af635 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -50,9 +50,10 @@ jobs: profile: minimal components: clippy override: true - - uses: actions-rs/clippy-check@v1 - with: - token: ${{ github.token }} + - run: cargo clippy -- -D warnings + - run: cargo clippy --features integer128 -- -D warnings + - run: cargo clippy --features indexmap -- -D warnings + - run: cargo clippy --all-features -- -D warnings rustfmt: name: "Format: stable" diff --git a/src/de/mod.rs b/src/de/mod.rs index 8c0ebbc5..8fe87cbd 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -40,14 +40,14 @@ impl<'de> Deserializer<'de> { // Cannot implement trait here since output is tied to input lifetime 'de. #[allow(clippy::should_implement_trait)] pub fn from_str(input: &'de str) -> SpannedResult { - Self::from_str_with_options(input, Options::default()) + Self::from_str_with_options(input, &Options::default()) } pub fn from_bytes(input: &'de [u8]) -> SpannedResult { - Self::from_bytes_with_options(input, Options::default()) + Self::from_bytes_with_options(input, &Options::default()) } - pub fn from_str_with_options(input: &'de str, options: Options) -> SpannedResult { + pub fn from_str_with_options(input: &'de str, options: &Options) -> SpannedResult { let mut deserializer = Deserializer { parser: Parser::new(input)?, newtype_variant: false, @@ -61,21 +61,34 @@ impl<'de> Deserializer<'de> { Ok(deserializer) } - pub fn from_bytes_with_options(input: &'de [u8], options: Options) -> SpannedResult { - Self::from_str_with_options( - str::from_utf8(input).map_err(|error| SpannedError::from_utf8_error(error, input))?, - options, - ) + pub fn from_bytes_with_options(input: &'de [u8], options: &Options) -> SpannedResult { + let err = match str::from_utf8(input) { + Ok(input) => return Self::from_str_with_options(input, options), + Err(err) => err, + }; + + // FIXME: use [`utf8_chunks`](https://github.com/rust-lang/rust/issues/99543) once stabilised + #[allow(clippy::expect_used)] + let valid_input = + str::from_utf8(&input[..err.valid_up_to()]).expect("source is valid up to error"); + + Err(SpannedError { + code: err.into(), + position: Position::from_offset(valid_input, valid_input.len()), + }) } + #[must_use] pub fn remainder(&self) -> &'de str { self.parser.src() } + #[must_use] pub fn span_error(&self, code: Error) -> SpannedError { self.parser.span_error(code) } + #[must_use] pub fn extensions(&self) -> Extensions { self.parser.exts } @@ -661,7 +674,11 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { self.newtype_variant = false; let mut canary_buffer = [0u8; SERDE_FLATTEN_CANARY.len()]; - let _ = write!(canary_buffer.as_mut(), "{}", VisitorExpecting(&visitor)); + std::mem::drop(write!( + canary_buffer.as_mut(), + "{}", + VisitorExpecting(&visitor) + )); let terminator = if canary_buffer == SERDE_FLATTEN_CANARY { Terminator::MapAsStruct } else { diff --git a/src/de/tests.rs b/src/de/tests.rs index 981b4336..62f9a8ee 100644 --- a/src/de/tests.rs +++ b/src/de/tests.rs @@ -14,6 +14,12 @@ struct EmptyStruct1; #[derive(Debug, PartialEq, Deserialize)] struct EmptyStruct2 {} +#[derive(Debug, PartialEq, Deserialize)] +struct NewType(i32); + +#[derive(Debug, PartialEq, Deserialize)] +struct TupleStruct(f32, f32); + #[derive(Clone, Copy, Debug, PartialEq, Deserialize)] struct MyStruct { x: f32, @@ -48,15 +54,9 @@ fn test_struct() { assert_eq!(Ok(my_struct), from_str("MyStruct(x:4,y:7,)")); assert_eq!(Ok(my_struct), from_str("(x:4,y:7)")); - #[derive(Debug, PartialEq, Deserialize)] - struct NewType(i32); - assert_eq!(Ok(NewType(42)), from_str("NewType(42)")); assert_eq!(Ok(NewType(33)), from_str("(33)")); - #[derive(Debug, PartialEq, Deserialize)] - struct TupleStruct(f32, f32); - assert_eq!(Ok(TupleStruct(2.0, 5.0)), from_str("TupleStruct(2,5,)")); assert_eq!(Ok(TupleStruct(3.0, 4.0)), from_str("(3,4)")); } @@ -165,22 +165,29 @@ fn err(kind: Error, line: usize, col: usize) -> SpannedResult { fn test_err_wrong_value() { use std::collections::HashMap; - use self::Error::*; - - assert_eq!(from_str::("'c'"), err(ExpectedFloat, 1, 1)); - assert_eq!(from_str::("'c'"), err(ExpectedString, 1, 1)); - assert_eq!(from_str::>("'c'"), err(ExpectedMap, 1, 1)); - assert_eq!(from_str::<[u8; 5]>("'c'"), err(ExpectedStructLike, 1, 1)); - assert_eq!(from_str::>("'c'"), err(ExpectedArray, 1, 1)); - assert_eq!(from_str::("'c'"), err(ExpectedIdentifier, 1, 1)); + assert_eq!(from_str::("'c'"), err(Error::ExpectedFloat, 1, 1)); + assert_eq!(from_str::("'c'"), err(Error::ExpectedString, 1, 1)); + assert_eq!( + from_str::>("'c'"), + err(Error::ExpectedMap, 1, 1) + ); + assert_eq!( + from_str::<[u8; 5]>("'c'"), + err(Error::ExpectedStructLike, 1, 1) + ); + assert_eq!(from_str::>("'c'"), err(Error::ExpectedArray, 1, 1)); + assert_eq!( + from_str::("'c'"), + err(Error::ExpectedIdentifier, 1, 1) + ); assert_eq!( from_str::("'c'"), - err(ExpectedNamedStructLike("MyStruct"), 1, 1) + err(Error::ExpectedNamedStructLike("MyStruct"), 1, 1) ); assert_eq!( from_str::("NotMyStruct(x: 4, y: 2)"), err( - ExpectedDifferentStructName { + Error::ExpectedDifferentStructName { expected: "MyStruct", found: String::from("NotMyStruct") }, @@ -188,16 +195,22 @@ fn test_err_wrong_value() { 12 ) ); - assert_eq!(from_str::<(u8, bool)>("'c'"), err(ExpectedStructLike, 1, 1)); - assert_eq!(from_str::("notabool"), err(ExpectedBoolean, 1, 1)); + assert_eq!( + from_str::<(u8, bool)>("'c'"), + err(Error::ExpectedStructLike, 1, 1) + ); + assert_eq!( + from_str::("notabool"), + err(Error::ExpectedBoolean, 1, 1) + ); assert_eq!( from_str::("MyStruct(\n x: true)"), - err(ExpectedFloat, 2, 8) + err(Error::ExpectedFloat, 2, 8) ); assert_eq!( from_str::("MyStruct(\n x: 3.5, \n y:)"), - err(ExpectedFloat, 3, 7) + err(Error::ExpectedFloat, 3, 7) ); } @@ -341,7 +354,7 @@ fn test_byte_stream() { #[test] fn test_numbers() { assert_eq!( - Ok(vec![1234, 12345, 123456, 1234567, 555_555]), + Ok(vec![1234, 12345, 123_456, 1_234_567, 555_555]), from_str("[1_234, 12_345, 1_2_3_4_5_6, 1_234_567, 5_55_55_5]"), ); } diff --git a/src/error.rs b/src/error.rs index 0175cd1e..f688c8e1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -122,11 +122,11 @@ impl fmt::Display for SpannedError { } impl fmt::Display for Error { + #[allow(clippy::too_many_lines)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { Error::Fmt => f.write_str("Formatting RON failed"), - Error::Io(ref s) => f.write_str(s), - Error::Message(ref s) => f.write_str(s), + Error::Io(ref s) | Error::Message(ref s) => f.write_str(s), #[allow(deprecated)] Error::Base64Error(ref e) => fmt::Display::fmt(e, f), Error::Eof => f.write_str("Unexpected end of RON"), @@ -144,7 +144,7 @@ impl fmt::Display for Error { Error::FloatUnderscore => f.write_str("Unexpected underscore in float"), Error::ExpectedInteger => f.write_str("Expected integer"), Error::ExpectedOption => f.write_str("Expected option"), - Error::ExpectedOptionEnd => f.write_str("Expected closing `)`"), + Error::ExpectedOptionEnd | Error::ExpectedStructLikeEnd => f.write_str("Expected closing `)`"), Error::ExpectedMap => f.write_str("Expected opening `{`"), Error::ExpectedMapColon => f.write_str("Expected colon"), Error::ExpectedMapEnd => f.write_str("Expected closing `}`"), @@ -165,7 +165,6 @@ impl fmt::Display for Error { write!(f, "Expected opening `(` for struct {}", Identifier(name)) } } - Error::ExpectedStructLikeEnd => f.write_str("Expected closing `)`"), Error::ExpectedUnit => f.write_str("Expected unit"), Error::ExpectedString => f.write_str("Expected string"), Error::ExpectedByteString => f.write_str("Expected byte string"), @@ -289,16 +288,12 @@ pub struct Position { pub line: usize, pub col: usize, } + impl Position { pub(crate) fn from_offset(src: &str, cursor: usize) -> Position { let src = &src[..cursor]; let line = 1 + src.chars().filter(|&c| c == '\n').count(); - let col = 1 + src - .rsplit('\n') - .next() - .expect("rsplit always yields at least one value") - .chars() - .count(); + let col = 1 + src.chars().rev().take_while(|&c| c != '\n').count(); Position { line, col } } } @@ -334,32 +329,30 @@ impl de::Error for Error { impl<'a> fmt::Display for UnexpectedSerdeTypeValue<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use de::Unexpected::*; - match self.0 { - Bool(b) => write!(f, "the boolean `{}`", b), - Unsigned(i) => write!(f, "the unsigned integer `{}`", i), - Signed(i) => write!(f, "the signed integer `{}`", i), - Float(n) => write!(f, "the floating point number `{}`", n), - Char(c) => write!(f, "the UTF-8 character `{}`", c), - Str(s) => write!(f, "the string {:?}", s), - Bytes(b) => write!(f, "the byte string b\"{}\"", { + de::Unexpected::Bool(b) => write!(f, "the boolean `{}`", b), + de::Unexpected::Unsigned(i) => write!(f, "the unsigned integer `{}`", i), + de::Unexpected::Signed(i) => write!(f, "the signed integer `{}`", i), + de::Unexpected::Float(n) => write!(f, "the floating point number `{}`", n), + de::Unexpected::Char(c) => write!(f, "the UTF-8 character `{}`", c), + de::Unexpected::Str(s) => write!(f, "the string {:?}", s), + de::Unexpected::Bytes(b) => write!(f, "the byte string b\"{}\"", { b.iter() .flat_map(|c| std::ascii::escape_default(*c)) .map(char::from) .collect::() }), - Unit => write!(f, "a unit value"), - Option => write!(f, "an optional value"), - NewtypeStruct => write!(f, "a newtype struct"), - Seq => write!(f, "a sequence"), - Map => write!(f, "a map"), - Enum => write!(f, "an enum"), - UnitVariant => write!(f, "a unit variant"), - NewtypeVariant => write!(f, "a newtype variant"), - TupleVariant => write!(f, "a tuple variant"), - StructVariant => write!(f, "a struct variant"), - Other(other) => f.write_str(other), + de::Unexpected::Unit => write!(f, "a unit value"), + de::Unexpected::Option => write!(f, "an optional value"), + de::Unexpected::NewtypeStruct => write!(f, "a newtype struct"), + de::Unexpected::Seq => write!(f, "a sequence"), + de::Unexpected::Map => write!(f, "a map"), + de::Unexpected::Enum => write!(f, "an enum"), + de::Unexpected::UnitVariant => write!(f, "a unit variant"), + de::Unexpected::NewtypeVariant => write!(f, "a newtype variant"), + de::Unexpected::TupleVariant => write!(f, "a tuple variant"), + de::Unexpected::StructVariant => write!(f, "a struct variant"), + de::Unexpected::Other(other) => f.write_str(other), } } } @@ -443,16 +436,6 @@ impl From for Error { } } -impl SpannedError { - pub(crate) fn from_utf8_error(error: Utf8Error, src: &[u8]) -> Self { - let src = str::from_utf8(&src[..error.valid_up_to()]).expect("source is valid up to error"); - Self { - code: error.into(), - position: Position::from_offset(src, error.valid_up_to()), - } - } -} - struct OneOf { alts: &'static [&'static str], none: &'static str, diff --git a/src/extensions.rs b/src/extensions.rs index 0da2efe9..3e1d1dc6 100644 --- a/src/extensions.rs +++ b/src/extensions.rs @@ -11,6 +11,7 @@ bitflags::bitflags! { impl Extensions { /// Creates an extension flag from an ident. + #[must_use] pub fn from_ident(ident: &str) -> Option { match ident { "unwrap_newtypes" => Some(Extensions::UNWRAP_NEWTYPES), diff --git a/src/lib.rs b/src/lib.rs index 638af2cb..6b34ed2e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,18 @@ +#![deny(clippy::correctness)] +#![deny(clippy::suspicious)] +#![deny(clippy::complexity)] +#![deny(clippy::perf)] +#![deny(clippy::style)] +#![warn(clippy::pedantic)] +#![cfg_attr(not(test), deny(clippy::unwrap_used))] +#![cfg_attr(not(test), deny(clippy::expect_used))] +#![deny(clippy::panic)] +#![warn(clippy::todo)] +#![deny(clippy::unimplemented)] +#![deny(clippy::unreachable)] +#![deny(unsafe_code)] +#![allow(clippy::module_name_repetitions)] +#![allow(clippy::missing_errors_doc)] #![doc = include_str!("../README.md")] #![doc(html_root_url = "https://docs.rs/ron/0.8.1")] diff --git a/src/options.rs b/src/options.rs index 2d18b59d..94e18651 100644 --- a/src/options.rs +++ b/src/options.rs @@ -109,7 +109,7 @@ impl Options { where T: de::Deserialize<'a>, { - self.from_bytes(s.as_bytes()) + self.from_str_seed(s, std::marker::PhantomData) } /// A convenience function for building a deserializer @@ -142,7 +142,15 @@ impl Options { where S: de::DeserializeSeed<'a, Value = T>, { - self.from_bytes_seed(s.as_bytes(), seed) + let mut deserializer = Deserializer::from_str_with_options(s, self)?; + + let value = seed + .deserialize(&mut deserializer) + .map_err(|e| deserializer.span_error(e))?; + + deserializer.end().map_err(|e| deserializer.span_error(e))?; + + Ok(value) } /// A convenience function for building a deserializer @@ -152,7 +160,7 @@ impl Options { where S: de::DeserializeSeed<'a, Value = T>, { - let mut deserializer = Deserializer::from_bytes_with_options(s, self.clone())?; + let mut deserializer = Deserializer::from_bytes_with_options(s, self)?; let value = seed .deserialize(&mut deserializer) @@ -173,7 +181,7 @@ impl Options { W: fmt::Write, T: ?Sized + ser::Serialize, { - let mut s = Serializer::with_options(writer, None, self.clone())?; + let mut s = Serializer::with_options(writer, None, self)?; value.serialize(&mut s) } @@ -183,7 +191,7 @@ impl Options { W: fmt::Write, T: ?Sized + ser::Serialize, { - let mut s = Serializer::with_options(writer, Some(config), self.clone())?; + let mut s = Serializer::with_options(writer, Some(config), self)?; value.serialize(&mut s) } @@ -197,7 +205,7 @@ impl Options { T: ?Sized + ser::Serialize, { let mut output = String::new(); - let mut s = Serializer::with_options(&mut output, None, self.clone())?; + let mut s = Serializer::with_options(&mut output, None, self)?; value.serialize(&mut s)?; Ok(output) } @@ -208,7 +216,7 @@ impl Options { T: ?Sized + ser::Serialize, { let mut output = String::new(); - let mut s = Serializer::with_options(&mut output, Some(config), self.clone())?; + let mut s = Serializer::with_options(&mut output, Some(config), self)?; value.serialize(&mut s)?; Ok(output) } diff --git a/src/parse.rs b/src/parse.rs index c2772ed1..b4a1500a 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -134,6 +134,10 @@ impl<'a> Parser<'a> { Ok(c) } + pub fn skip_next(&mut self) { + std::mem::drop(self.next()); + } + pub fn peek(&self) -> Option { self.src().chars().next() } @@ -220,6 +224,42 @@ impl<'a> Parser<'a> { /// actual parsing of ron tokens impl<'a> Parser<'a> { + fn parse_integer_digits( + &mut self, + s: &str, + base: u8, + f: fn(&mut T, u8) -> bool, + ) -> Result { + let mut num_acc = T::from_u8(0); + + for (i, c) in s.chars().enumerate() { + if c == '_' { + continue; + } + + if num_acc.checked_mul_ext(base) { + self.advance(s.len()); + return Err(Error::IntegerOutOfBounds); + } + + let digit = Self::decode_hex(c)?; + + if digit >= base { + self.advance(i); + return Err(Error::InvalidIntegerDigit { digit: c, base }); + } + + if f(&mut num_acc, digit) { + self.advance(s.len()); + return Err(Error::IntegerOutOfBounds); + } + } + + self.advance(s.len()); + + Ok(num_acc) + } + fn parse_integer(&mut self, sign: i8) -> Result { let base = match () { _ if self.consume_str("0b") => 2, @@ -238,61 +278,26 @@ impl<'a> Parser<'a> { return Err(Error::UnderscoreAtBeginning); } - fn calc_num( - parser: &mut Parser, - s: &str, - base: u8, - f: fn(&mut T, u8) -> bool, - ) -> Result { - let mut num_acc = T::from_u8(0); - - for (i, c) in s.chars().enumerate() { - if c == '_' { - continue; - } - - if num_acc.checked_mul_ext(base) { - parser.advance(s.len()); - return Err(Error::IntegerOutOfBounds); - } - - let digit = parser.decode_hex(c)?; - - if digit >= base { - parser.advance(i); - return Err(Error::InvalidIntegerDigit { digit: c, base }); - } - - if f(&mut num_acc, digit) { - parser.advance(s.len()); - return Err(Error::IntegerOutOfBounds); - } - } - - parser.advance(s.len()); - - Ok(num_acc) - } - let s = &self.src()[..num_bytes]; if sign > 0 { - calc_num(self, s, base, T::checked_add_ext) + self.parse_integer_digits(s, base, T::checked_add_ext) } else { - calc_num(self, s, base, T::checked_sub_ext) + self.parse_integer_digits(s, base, T::checked_sub_ext) } } + #[allow(clippy::too_many_lines)] pub fn integer(&mut self) -> Result { let src_backup = self.src(); let is_negative = match self.peek_or_eof()? { '+' => { - let _ = self.next(); + self.skip_next(); false } '-' => { - let _ = self.next(); + self.skip_next(); true } 'b' if self.consume_str("b'") => { @@ -417,7 +422,7 @@ impl<'a> Parser<'a> { Err(Error::UnderscoreAtBeginning | Error::InvalidIntegerDigit { .. }) ) { // Advance past the number suffix - let _ = self.identifier(); + self.skip_ident(); } let integer_ron = &src_backup[..src_backup.len() - suffix_bytes.len()]; @@ -788,12 +793,11 @@ impl<'a> Parser<'a> { break; }; - let parsed = match res { - Ok(parsed) => parsed, - Err(_) => { - self.set_cursor(backup_cursor); - return Err(Error::ExpectedFloat); - } + let parsed = if let Ok(parsed) = res { + parsed + } else { + self.set_cursor(backup_cursor); + return Err(Error::ExpectedFloat); }; let float_ron = &self.src[backup_cursor.cursor..self.cursor.cursor]; @@ -831,9 +835,8 @@ impl<'a> Parser<'a> { if len > 0 { self.advance(2 + len); return true; - } else { - return false; } + return false; } if let Some(c) = self.peek() { @@ -927,9 +930,9 @@ impl<'a> Parser<'a> { '+' | '-' => 1, _ => 0, }; - let flen = self.next_chars_while_from(skip, is_float_char); - let ilen = self.next_chars_while_from(skip, is_int_char); - flen > ilen + let valid_float_len = self.next_chars_while_from(skip, is_float_char); + let valid_int_len = self.next_chars_while_from(skip, is_int_char); + valid_float_len > valid_int_len } else { false } @@ -946,9 +949,7 @@ impl<'a> Parser<'a> { } loop { - while self.peek().map_or(false, is_whitespace_char) { - let _ = self.next(); - } + self.advance(self.next_chars_while(is_whitespace_char)); match self.skip_comment()? { None => break, @@ -995,7 +996,7 @@ impl<'a> Parser<'a> { if self.consume_char('"') { let base64_str = self.escaped_string()?; - let base64_result = ParsedByteStr::try_from_base64(base64_str.clone()); + let base64_result = ParsedByteStr::try_from_base64(&base64_str); if cfg!(not(test)) { // FIXME @juntyr: remove in v0.10 @@ -1010,7 +1011,7 @@ impl<'a> Parser<'a> { } } else if self.consume_char('r') { let base64_str = self.raw_string()?; - let base64_result = ParsedByteStr::try_from_base64(base64_str.clone()); + let base64_result = ParsedByteStr::try_from_base64(&base64_str); if cfg!(not(test)) { // FIXME @juntyr: remove in v0.10 @@ -1154,7 +1155,7 @@ impl<'a> Parser<'a> { for _ in 0..2 { n <<= 4; let byte = self.next()?; - let decoded = self.decode_hex(byte)?; + let decoded = Self::decode_hex(byte)?; n |= decoded; } @@ -1162,7 +1163,7 @@ impl<'a> Parser<'a> { } #[inline] - fn decode_hex(&self, c: char) -> Result { + fn decode_hex(c: char) -> Result { if !c.is_ascii() { return Err(Error::InvalidEscape("Non-hex digit found")); } @@ -1238,12 +1239,12 @@ impl<'a> Parser<'a> { if byte == '}' { break; - } else { - let _ = self.next()?; - num_digits += 1; } - let byte = self.decode_hex(byte)?; + self.skip_next(); + num_digits += 1; + + let byte = Self::decode_hex(byte)?; bytes <<= 4; bytes |= u32::from(byte); } @@ -1534,6 +1535,7 @@ impl Float for ParsedFloat { fn parse(float: &str) -> Result { let value = f64::from_str(float).map_err(|_| Error::ExpectedFloat)?; + #[allow(clippy::cast_possible_truncation)] if value.total_cmp(&f64::from(value as f32)).is_eq() { Ok(ParsedFloat::F32(value as f32)) } else { @@ -1553,17 +1555,18 @@ pub enum StructType { Unit, } +#[derive(Copy, Clone)] // GRCOV_EXCL_LINE pub enum NewtypeMode { NoParensMeanUnit, InsideNewtype, } +#[derive(Copy, Clone)] // GRCOV_EXCL_LINE pub enum TupleMode { ImpreciseTupleOrNewtype, DifferentiateNewtype, } -#[derive(Clone)] pub enum ParsedStr<'a> { Allocated(String), Slice(&'a str), @@ -1586,8 +1589,8 @@ impl<'a> ParsedStr<'a> { } impl<'a> ParsedByteStr<'a> { - pub fn try_from_base64(str: ParsedStr<'a>) -> Result { - let base64_str = match &str { + pub fn try_from_base64(str: &ParsedStr<'a>) -> Result { + let base64_str = match str { ParsedStr::Allocated(string) => string.as_str(), ParsedStr::Slice(str) => str, }; diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 84bb29e9..105f1323 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -59,7 +59,6 @@ where /// Pretty serializer state struct Pretty { indent: usize, - sequence_index: Vec, } /// Pretty serializer configuration. @@ -74,6 +73,7 @@ struct Pretty { /// // definitely superior (okay, just joking) /// .indentor("\t".to_owned()); /// ``` +#[allow(clippy::struct_excessive_bools)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(default)] #[non_exhaustive] @@ -113,8 +113,9 @@ pub struct PrettyConfig { impl PrettyConfig { /// Creates a default [`PrettyConfig`]. + #[must_use] pub fn new() -> Self { - Default::default() + Self::default() } /// Limits the pretty-formatting based on the number of indentations. @@ -122,7 +123,8 @@ impl PrettyConfig { /// (indentation level) 6, everything will be put into the same line, /// without pretty formatting. /// - /// Default: [usize::MAX] + /// Default: [`usize::MAX`] + #[must_use] pub fn depth_limit(mut self, depth_limit: usize) -> Self { self.depth_limit = depth_limit; @@ -132,6 +134,7 @@ impl PrettyConfig { /// Configures the newlines used for serialization. /// /// Default: `\r\n` on Windows, `\n` otherwise + #[must_use] pub fn new_line(mut self, new_line: String) -> Self { self.new_line = new_line; @@ -141,6 +144,7 @@ impl PrettyConfig { /// Configures the string sequence used for indentation. /// /// Default: 4 spaces + #[must_use] pub fn indentor(mut self, indentor: String) -> Self { self.indentor = indentor; @@ -150,6 +154,7 @@ impl PrettyConfig { /// Configures the string sequence used to separate items inline. /// /// Default: 1 space + #[must_use] pub fn separator(mut self, separator: String) -> Self { self.separator = separator; @@ -159,6 +164,7 @@ impl PrettyConfig { /// Configures whether to emit struct names. /// /// Default: `false` + #[must_use] pub fn struct_names(mut self, struct_names: bool) -> Self { self.struct_names = struct_names; @@ -171,6 +177,7 @@ impl PrettyConfig { /// newlines or indentations. /// /// Default: `false` + #[must_use] pub fn separate_tuple_members(mut self, separate_tuple_members: bool) -> Self { self.separate_tuple_members = separate_tuple_members; @@ -181,6 +188,7 @@ impl PrettyConfig { /// indicating the index. /// /// Default: `false` + #[must_use] pub fn enumerate_arrays(mut self, enumerate_arrays: bool) -> Self { self.enumerate_arrays = enumerate_arrays; @@ -205,6 +213,7 @@ impl PrettyConfig { /// ``` /// /// Default: `false` + #[must_use] pub fn compact_arrays(mut self, compact_arrays: bool) -> Self { self.compact_arrays = compact_arrays; @@ -213,7 +222,8 @@ impl PrettyConfig { /// Configures extensions /// - /// Default: [Extensions::empty()] + /// Default: [`Extensions::empty()`] + #[must_use] pub fn extensions(mut self, extensions: Extensions) -> Self { self.extensions = extensions; @@ -236,6 +246,7 @@ impl PrettyConfig { /// ``` /// /// Default: `true` + #[must_use] pub fn escape_strings(mut self, escape_strings: bool) -> Self { self.escape_strings = escape_strings; @@ -260,6 +271,7 @@ impl PrettyConfig { /// ``` /// /// Default: `false` + #[must_use] pub fn compact_structs(mut self, compact_structs: bool) -> Self { self.compact_structs = compact_structs; @@ -285,6 +297,7 @@ impl PrettyConfig { /// ``` /// /// Default: `false` + #[must_use] pub fn compact_maps(mut self, compact_maps: bool) -> Self { self.compact_maps = compact_maps; @@ -316,6 +329,7 @@ impl PrettyConfig { /// ``` /// /// Default: `false` + #[must_use] pub fn number_suffixes(mut self, number_suffixes: bool) -> Self { self.number_suffixes = number_suffixes; @@ -368,7 +382,7 @@ impl Serializer { /// Most of the time you can just use [`to_string`] or /// [`to_string_pretty`]. pub fn new(writer: W, config: Option) -> Result { - Self::with_options(writer, config, Options::default()) + Self::with_options(writer, config, &Options::default()) } /// Creates a new [`Serializer`]. @@ -378,7 +392,7 @@ impl Serializer { pub fn with_options( mut writer: W, config: Option, - options: Options, + options: &Options, ) -> Result { if let Some(conf) = &config { if !conf.new_line.chars().all(is_whitespace_char) { @@ -416,15 +430,7 @@ impl Serializer { }; Ok(Serializer { output: writer, - pretty: config.map(|conf| { - ( - conf, - Pretty { - indent: 0, - sequence_index: Vec::new(), - }, - ) - }), + pretty: config.map(|conf| (conf, Pretty { indent: 0 })), default_extensions: options.default_extensions, is_empty: None, newtype_variant: false, @@ -523,7 +529,7 @@ impl Serializer { fn serialize_escaped_str(&mut self, value: &str) -> fmt::Result { self.output.write_str("\"")?; let mut scalar = [0u8; 4]; - for c in value.chars().flat_map(|c| c.escape_debug()) { + for c in value.chars().flat_map(char::escape_debug) { self.output.write_str(c.encode_utf8(&mut scalar))?; } self.output.write_str("\"")?; @@ -537,7 +543,7 @@ impl Serializer { '#' => (count + 1, max.max(count + 1)), _ => (0_usize, max), }); - let hashes = String::from_iter(std::iter::repeat('#').take(num_consecutive_hashes + 1)); + let hashes: String = "#".repeat(num_consecutive_hashes + 1); self.output.write_char('r')?; self.output.write_str(&hashes)?; self.output.write_str("\"")?; @@ -568,7 +574,7 @@ impl Serializer { '#' => (count + 1, max.max(count + 1)), _ => (0_usize, max), }); - let hashes = String::from_iter(std::iter::repeat('#').take(num_consecutive_hashes + 1)); + let hashes: String = "#".repeat(num_consecutive_hashes + 1); self.output.write_str("br")?; self.output.write_str(&hashes)?; self.output.write_str("\"")?; @@ -622,6 +628,7 @@ impl Serializer { Ok(()) } + #[allow(clippy::unused_self)] fn validate_identifier(&self, name: &str) -> Result<()> { if name.is_empty() || !name.chars().all(is_ident_raw_char) { return Err(Error::InvalidIdentifier(name.into())); @@ -632,8 +639,7 @@ impl Serializer { fn struct_names(&self) -> bool { self.pretty .as_ref() - .map(|(pc, _)| pc.struct_names) - .unwrap_or(false) + .map_or(false, |(pc, _)| pc.struct_names) } } @@ -942,10 +948,6 @@ impl<'a, W: fmt::Write> ser::Serializer for &'a mut Serializer { self.start_indent()?; } - if let Some((_, ref mut pretty)) = self.pretty { - pretty.sequence_index.push(0); - } - Compound::try_new(self, false) } @@ -1026,15 +1028,15 @@ impl<'a, W: fmt::Write> ser::Serializer for &'a mut Serializer { self.newtype_variant = false; self.implicit_some_depth = 0; - if !old_newtype_variant { + if old_newtype_variant { + self.validate_identifier(name)?; + } else { if self.struct_names() { self.write_identifier(name)?; } else { self.validate_identifier(name)?; } self.output.write_char('(')?; - } else { - self.validate_identifier(name)?; } if !self.compact_structs() { @@ -1078,6 +1080,7 @@ pub struct Compound<'a, W: fmt::Write> { ser: &'a mut Serializer, state: State, newtype_variant: bool, + sequence_index: usize, } impl<'a, W: fmt::Write> Compound<'a, W> { @@ -1094,6 +1097,7 @@ impl<'a, W: fmt::Write> Compound<'a, W> { ser, state: State::First, newtype_variant, + sequence_index: 0, }) } } @@ -1133,9 +1137,8 @@ impl<'a, W: fmt::Write> ser::SerializeSeq for Compound<'a, W> { if let Some((ref mut config, ref mut pretty)) = self.ser.pretty { if pretty.indent <= config.depth_limit && config.enumerate_arrays { - let index = pretty.sequence_index.last_mut().unwrap(); - write!(self.ser.output, "/*[{}]*/ ", index)?; - *index += 1; + write!(self.ser.output, "/*[{}]*/ ", self.sequence_index)?; + self.sequence_index += 1; } } @@ -1158,10 +1161,6 @@ impl<'a, W: fmt::Write> ser::SerializeSeq for Compound<'a, W> { self.ser.end_indent()?; } - if let Some((_, ref mut pretty)) = self.ser.pretty { - pretty.sequence_index.pop(); - } - // seq always disables `self.newtype_variant` self.ser.output.write_char(']')?; Ok(()) diff --git a/src/ser/tests.rs b/src/ser/tests.rs index e9acaec1..e11a3f6e 100644 --- a/src/ser/tests.rs +++ b/src/ser/tests.rs @@ -10,6 +10,12 @@ struct EmptyStruct1; #[derive(Serialize)] struct EmptyStruct2 {} +#[derive(Serialize)] +struct NewType(i32); + +#[derive(Serialize)] +struct TupleStruct(f32, f32); + #[derive(Serialize)] struct MyStruct { x: f32, @@ -36,14 +42,8 @@ fn test_struct() { assert_eq!(to_string(&my_struct).unwrap(), "(x:4.0,y:7.0)"); - #[derive(Serialize)] - struct NewType(i32); - assert_eq!(to_string(&NewType(42)).unwrap(), "(42)"); - #[derive(Serialize)] - struct TupleStruct(f32, f32); - assert_eq!(to_string(&TupleStruct(2.0, 5.0)).unwrap(), "(2.0,5.0)"); } diff --git a/src/value/map.rs b/src/value/map.rs index 0a3e78d2..cdef2be2 100644 --- a/src/value/map.rs +++ b/src/value/map.rs @@ -11,8 +11,8 @@ use super::Value; /// A [`Value`] to [`Value`] map. /// -/// This structure either uses a [BTreeMap](std::collections::BTreeMap) or the -/// [IndexMap](indexmap::IndexMap) internally. +/// This structure either uses a [`BTreeMap`](std::collections::BTreeMap) or the +/// [`IndexMap`](indexmap::IndexMap) internally. /// The latter can be used by enabling the `indexmap` feature. This can be used /// to preserve the order of the parsed map. #[derive(Clone, Debug, Default, Deserialize, Serialize)] @@ -26,21 +26,25 @@ type MapInner = indexmap::IndexMap; impl Map { /// Creates a new, empty [`Map`]. + #[must_use] pub fn new() -> Map { - Default::default() + Self::default() } /// Returns the number of elements in the map. + #[must_use] pub fn len(&self) -> usize { self.0.len() } /// Returns `true` if `self.len() == 0`, `false` otherwise. + #[must_use] pub fn is_empty(&self) -> bool { self.0.is_empty() } /// Immutably looks up an element by its `key`. + #[must_use] pub fn get(&self, key: &Value) -> Option<&Value> { self.0.get(key) } @@ -109,6 +113,7 @@ impl Index<&Value> for Map { } impl IndexMut<&Value> for Map { + #[allow(clippy::expect_used)] fn index_mut(&mut self, index: &Value) -> &mut Self::Output { self.0.get_mut(index).expect("no entry found for key") } diff --git a/src/value/mod.rs b/src/value/mod.rs index 8761fc1d..98d181b3 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -152,6 +152,7 @@ impl<'a, 'de> MapAccess<'de> for MapAccessor<'a> { } } + #[allow(clippy::panic)] fn next_value_seed(&mut self, seed: V) -> Result where V: DeserializeSeed<'de>, diff --git a/src/value/number.rs b/src/value/number.rs index 370b3abe..723ac664 100644 --- a/src/value/number.rs +++ b/src/value/number.rs @@ -81,11 +81,13 @@ macro_rules! float_ty { impl $ty { #[doc = concat!("Construct a new [`", stringify!($ty), "`].")] + #[must_use] pub fn new(v: $float) -> Self { Self(v) } #[doc = concat!("Returns the wrapped [`", stringify!($float), "`].")] + #[must_use] pub fn get(self) -> $float { self.0 } @@ -196,7 +198,9 @@ impl Number { /// assert_eq!(i.into_f64(), 5.0); /// assert_eq!(f.into_f64(), 2.0); /// ``` + #[must_use] pub fn into_f64(self) -> f64 { + #[allow(clippy::cast_precision_loss)] match self { Number::I8(v) => f64::from(v), Number::I16(v) => f64::from(v), diff --git a/src/value/raw.rs b/src/value/raw.rs index 854d59c3..5f2de136 100644 --- a/src/value/raw.rs +++ b/src/value/raw.rs @@ -20,10 +20,11 @@ pub struct RawValue { ron: str, } +#[allow(unsafe_code)] impl RawValue { fn from_borrowed_str(ron: &str) -> &Self { // Safety: RawValue is a transparent newtype around str - unsafe { std::mem::transmute::<&str, &RawValue>(ron) } + unsafe { &*(ron as *const str as *const RawValue) } } fn from_boxed_str(ron: Box) -> Box { @@ -67,6 +68,7 @@ impl fmt::Display for RawValue { impl RawValue { /// Get the inner raw RON string, which is guaranteed to contain valid RON. + #[must_use] pub fn get_ron(&self) -> &str { &self.ron } @@ -74,7 +76,7 @@ impl RawValue { /// Helper function to validate a RON string and turn it into a /// [`RawValue`]. pub fn from_ron(ron: &str) -> SpannedResult<&Self> { - let mut deserializer = crate::Deserializer::from_str_with_options(ron, Options::default())?; + let mut deserializer = crate::Deserializer::from_str(ron)?; // raw values can be used everywhere but extensions cannot if !deserializer.extensions().is_empty() { diff --git a/tests/481_number_underscores_suffixes.rs b/tests/481_number_underscores_suffixes.rs index cb2d4e52..55136191 100644 --- a/tests/481_number_underscores_suffixes.rs +++ b/tests/481_number_underscores_suffixes.rs @@ -1,6 +1,9 @@ use ron::Number; #[test] +#[allow(clippy::unusual_byte_groupings)] +#[allow(clippy::inconsistent_digit_grouping)] +#[allow(clippy::zero_prefixed_literal)] fn de_integer_underscores() { assert_eq!(ron::from_str("0b10_10___101_"), Ok(0b10_10___101__u8)); assert_eq!( @@ -94,6 +97,7 @@ fn de_integer_underscores() { } #[test] +#[allow(clippy::inconsistent_digit_grouping)] fn de_float_underscores() { assert_eq!(ron::from_str("2_18__6_"), Ok(2_18__6__f32)); assert_eq!(