Merge pull request #353 from fmoor/feature/trailing_comma

change error type for trailing commas, fixes #352
This commit is contained in:
David Tolnay 2017-09-09 13:15:54 -07:00 committed by GitHub
commit adade3344a
3 changed files with 32 additions and 7 deletions

View File

@ -105,6 +105,7 @@ impl<'de, R: Read<'de>> Deserializer<R> {
/// only has trailing whitespace. /// only has trailing whitespace.
pub fn end(&mut self) -> Result<()> { pub fn end(&mut self) -> Result<()> {
match try!(self.parse_whitespace()) { match try!(self.parse_whitespace()) {
Some(b',') => Err(self.peek_error(ErrorCode::TrailingComma)),
Some(_) => Err(self.peek_error(ErrorCode::TrailingCharacters)), Some(_) => Err(self.peek_error(ErrorCode::TrailingCharacters)),
None => Ok(()), None => Ok(()),
} }
@ -517,6 +518,13 @@ impl<'de, R: Read<'de>> Deserializer<R> {
self.eat_char(); self.eat_char();
Ok(()) Ok(())
} }
Some(b',') => {
self.eat_char();
match self.parse_whitespace() {
Ok(Some(b']')) => Err(self.peek_error(ErrorCode::TrailingComma)),
_ => Err(self.peek_error(ErrorCode::TrailingCharacters)),
}
}
Some(_) => Err(self.peek_error(ErrorCode::TrailingCharacters)), Some(_) => Err(self.peek_error(ErrorCode::TrailingCharacters)),
None => Err(self.peek_error(ErrorCode::EofWhileParsingList)), None => Err(self.peek_error(ErrorCode::EofWhileParsingList)),
} }
@ -528,6 +536,7 @@ impl<'de, R: Read<'de>> Deserializer<R> {
self.eat_char(); self.eat_char();
Ok(()) Ok(())
} }
Some(b',') => Err(self.peek_error(ErrorCode::TrailingComma)),
Some(_) => Err(self.peek_error(ErrorCode::TrailingCharacters)), Some(_) => Err(self.peek_error(ErrorCode::TrailingCharacters)),
None => Err(self.peek_error(ErrorCode::EofWhileParsingObject)), None => Err(self.peek_error(ErrorCode::EofWhileParsingObject)),
} }
@ -994,16 +1003,18 @@ impl<'de, 'a, R: Read<'de> + 'a> de::SeqAccess<'de> for SeqAccess<'a, R> {
where where
T: de::DeserializeSeed<'de>, T: de::DeserializeSeed<'de>,
{ {
match try!(self.de.parse_whitespace()) { let peek = match try!(self.de.parse_whitespace()) {
Some(b']') => { Some(b']') => {
return Ok(None); return Ok(None);
} }
Some(b',') if !self.first => { Some(b',') if !self.first => {
self.de.eat_char(); self.de.eat_char();
try!(self.de.parse_whitespace())
} }
Some(_) => { Some(b) => {
if self.first { if self.first {
self.first = false; self.first = false;
Some(b)
} else { } else {
return Err(self.de.peek_error(ErrorCode::ExpectedListCommaOrEnd)); return Err(self.de.peek_error(ErrorCode::ExpectedListCommaOrEnd));
} }
@ -1011,10 +1022,13 @@ impl<'de, 'a, R: Read<'de> + 'a> de::SeqAccess<'de> for SeqAccess<'a, R> {
None => { None => {
return Err(self.de.peek_error(ErrorCode::EofWhileParsingList)); return Err(self.de.peek_error(ErrorCode::EofWhileParsingList));
} }
} };
let value = try!(seed.deserialize(&mut *self.de)); match peek {
Ok(Some(value)) Some(b']') => Err(self.de.peek_error(ErrorCode::TrailingComma)),
Some(_) => Ok(Some(try!(seed.deserialize(&mut *self.de)))),
None => Err(self.de.peek_error(ErrorCode::EofWhileParsingValue)),
}
} }
} }
@ -1062,6 +1076,7 @@ impl<'de, 'a, R: Read<'de> + 'a> de::MapAccess<'de> for MapAccess<'a, R> {
match peek { match peek {
Some(b'"') => seed.deserialize(MapKey { de: &mut *self.de }).map(Some), Some(b'"') => seed.deserialize(MapKey { de: &mut *self.de }).map(Some),
Some(b'}') => Err(self.de.peek_error(ErrorCode::TrailingComma)),
Some(_) => Err(self.de.peek_error(ErrorCode::KeyMustBeAString)), Some(_) => Err(self.de.peek_error(ErrorCode::KeyMustBeAString)),
None => Err(self.de.peek_error(ErrorCode::EofWhileParsingValue)), None => Err(self.de.peek_error(ErrorCode::EofWhileParsingValue)),
} }

View File

@ -75,6 +75,7 @@ impl Error {
ErrorCode::InvalidUnicodeCodePoint | ErrorCode::InvalidUnicodeCodePoint |
ErrorCode::KeyMustBeAString | ErrorCode::KeyMustBeAString |
ErrorCode::LoneLeadingSurrogateInHexEscape | ErrorCode::LoneLeadingSurrogateInHexEscape |
ErrorCode::TrailingComma |
ErrorCode::TrailingCharacters | ErrorCode::TrailingCharacters |
ErrorCode::UnexpectedEndOfHexEscape | ErrorCode::UnexpectedEndOfHexEscape |
ErrorCode::RecursionLimitExceeded => Category::Syntax, ErrorCode::RecursionLimitExceeded => Category::Syntax,
@ -244,6 +245,9 @@ pub enum ErrorCode {
/// Lone leading surrogate in hex escape. /// Lone leading surrogate in hex escape.
LoneLeadingSurrogateInHexEscape, LoneLeadingSurrogateInHexEscape,
/// JSON has a comma after the last value in an array or map.
TrailingComma,
/// JSON has non-whitespace trailing characters after the value. /// JSON has non-whitespace trailing characters after the value.
TrailingCharacters, TrailingCharacters,
@ -321,6 +325,7 @@ impl Display for ErrorCode {
ErrorCode::LoneLeadingSurrogateInHexEscape => { ErrorCode::LoneLeadingSurrogateInHexEscape => {
f.write_str("lone leading surrogate in hex escape") f.write_str("lone leading surrogate in hex escape")
} }
ErrorCode::TrailingComma => f.write_str("trailing comma"),
ErrorCode::TrailingCharacters => f.write_str("trailing characters"), ErrorCode::TrailingCharacters => f.write_str("trailing characters"),
ErrorCode::UnexpectedEndOfHexEscape => f.write_str("unexpected end of hex escape"), ErrorCode::UnexpectedEndOfHexEscape => f.write_str("unexpected end of hex escape"),
ErrorCode::RecursionLimitExceeded => f.write_str("recursion limit exceeded"), ErrorCode::RecursionLimitExceeded => f.write_str("recursion limit exceeded"),

View File

@ -894,7 +894,7 @@ fn test_parse_list() {
("[ ", "EOF while parsing a list at line 1 column 2"), ("[ ", "EOF while parsing a list at line 1 column 2"),
("[1", "EOF while parsing a list at line 1 column 2"), ("[1", "EOF while parsing a list at line 1 column 2"),
("[1,", "EOF while parsing a value at line 1 column 3"), ("[1,", "EOF while parsing a value at line 1 column 3"),
("[1,]", "expected value at line 1 column 4"), ("[1,]", "trailing comma at line 1 column 4"),
("[1 2]", "expected `,` or `]` at line 1 column 4"), ("[1 2]", "expected `,` or `]` at line 1 column 4"),
("[]a", "trailing characters at line 1 column 3"), ("[]a", "trailing characters at line 1 column 3"),
], ],
@ -1111,9 +1111,14 @@ fn test_parse_enum_errors() {
"invalid type: map, expected tuple variant Animal::Frog at line 1 column 10"), "invalid type: map, expected tuple variant Animal::Frog at line 1 column 10"),
("{\"Cat\":[]}", "invalid length 0, expected tuple of 2 elements at line 1 column 9"), ("{\"Cat\":[]}", "invalid length 0, expected tuple of 2 elements at line 1 column 9"),
("{\"Cat\":[0]}", "invalid length 1, expected tuple of 2 elements at line 1 column 10"), ("{\"Cat\":[0]}", "invalid length 1, expected tuple of 2 elements at line 1 column 10"),
("{\"Cat\":[0, \"\", 2]}", "trailing characters at line 1 column 14"), ("{\"Cat\":[0, \"\", 2]}", "trailing characters at line 1 column 16"),
("{\"Cat\":{\"age\": 5, \"name\": \"Kate\", \"foo\":\"bar\"}", ("{\"Cat\":{\"age\": 5, \"name\": \"Kate\", \"foo\":\"bar\"}",
"unknown field `foo`, expected `age` or `name` at line 1 column 39"), "unknown field `foo`, expected `age` or `name` at line 1 column 39"),
// JSON does not allow trailing commas in data structures
("{\"Cat\":[0, \"Kate\",]}", "trailing comma at line 1 column 19"),
("{\"Cat\":{\"age\": 2, \"name\": \"Kate\",}}",
"trailing comma at line 1 column 34"),
], ],
); );
} }