Merge pull request #280 from alexcrichton/fix-duplicate

Fix disallowing duplicate table headers
This commit is contained in:
Eric Huss 2019-01-07 18:52:28 -08:00 committed by GitHub
commit 59d250e309
5 changed files with 66 additions and 7 deletions

View file

@ -15,6 +15,9 @@ facilitate deserializing and serializing Rust structures.
""" """
categories = ["config", "encoding", "parser-implementations"] categories = ["config", "encoding", "parser-implementations"]
[workspace]
members = ['test-suite']
[badges] [badges]
travis-ci = { repository = "alexcrichton/toml-rs" } travis-ci = { repository = "alexcrichton/toml-rs" }

View file

@ -197,6 +197,7 @@ enum ErrorKind {
/// Deserialization implementation for TOML. /// Deserialization implementation for TOML.
pub struct Deserializer<'a> { pub struct Deserializer<'a> {
require_newline_after_table: bool, require_newline_after_table: bool,
allow_duplciate_after_longer_table: bool,
input: &'a str, input: &'a str,
tokens: Tokenizer<'a>, tokens: Tokenizer<'a>,
} }
@ -335,14 +336,26 @@ impl<'de, 'b> de::MapAccess<'de> for MapVisitor<'de, 'b> {
// Test to see if we're duplicating our parent's table, and if so // Test to see if we're duplicating our parent's table, and if so
// then this is an error in the toml format // then this is an error in the toml format
if self.cur_parent != pos if self.cur_parent != pos {
&& self.tables[self.cur_parent].header == self.tables[pos].header if self.tables[self.cur_parent].header == self.tables[pos].header {
{
let at = self.tables[pos].at; let at = self.tables[pos].at;
let name = self.tables[pos].header.join("."); let name = self.tables[pos].header.join(".");
return Err(self.de.error(at, ErrorKind::DuplicateTable(name))); return Err(self.de.error(at, ErrorKind::DuplicateTable(name)));
} }
// If we're here we know we should share the same prefix, and if
// the longer table was defined first then we want to narrow
// down our parent's length if possible to ensure that we catch
// duplicate tables defined afterwards.
if !self.de.allow_duplciate_after_longer_table {
let parent_len = self.tables[self.cur_parent].header.len();
let cur_len = self.tables[pos].header.len();
if cur_len < parent_len {
self.cur_parent = pos;
}
}
}
let table = &mut self.tables[pos]; let table = &mut self.tables[pos];
// If we're not yet at the appropriate depth for this table then we // If we're not yet at the appropriate depth for this table then we
@ -965,6 +978,7 @@ impl<'a> Deserializer<'a> {
tokens: Tokenizer::new(input), tokens: Tokenizer::new(input),
input: input, input: input,
require_newline_after_table: true, require_newline_after_table: true,
allow_duplciate_after_longer_table: false,
} }
} }
@ -986,6 +1000,16 @@ impl<'a> Deserializer<'a> {
self.require_newline_after_table = require; self.require_newline_after_table = require;
} }
/// Historical versions of toml-rs accidentally allowed a duplicate table
/// header after a longer table header was previously defined. This is
/// invalid according to the TOML spec, however.
///
/// This option can be set to `true` (the default is `false`) to emulate
/// this behavior for backwards compatibility with older toml-rs versions.
pub fn set_allow_duplicate_after_longer_table(&mut self, allow: bool) {
self.allow_duplciate_after_longer_table = allow;
}
fn tables(&mut self) -> Result<Vec<Table<'a>>, Error> { fn tables(&mut self) -> Result<Vec<Table<'a>>, Error> {
let mut tables = Vec::new(); let mut tables = Vec::new();
let mut cur_table = Table { let mut cur_table = Table {

View file

@ -4,7 +4,7 @@ extern crate serde;
use serde::de::Deserialize; use serde::de::Deserialize;
#[test] #[test]
fn main() { fn newlines_after_tables() {
let s = " let s = "
[a] foo = 1 [a] foo = 1
[[b]] foo = 1 [[b]] foo = 1
@ -17,3 +17,25 @@ fn main() {
assert_eq!(value["a"]["foo"].as_integer(), Some(1)); assert_eq!(value["a"]["foo"].as_integer(), Some(1));
assert_eq!(value["b"][0]["foo"].as_integer(), Some(1)); assert_eq!(value["b"][0]["foo"].as_integer(), Some(1));
} }
#[test]
fn allow_duplicate_after_longer() {
let s = "
[dependencies.openssl-sys]
version = 1
[dependencies]
libc = 1
[dependencies]
bitflags = 1
";
assert!(s.parse::<toml::Value>().is_err());
let mut d = toml::de::Deserializer::new(s);
d.set_allow_duplicate_after_longer_table(true);
let value = toml::Value::deserialize(&mut d).unwrap();
assert_eq!(value["dependencies"]["openssl-sys"]["version"].as_integer(), Some(1));
assert_eq!(value["dependencies"]["libc"].as_integer(), Some(1));
assert_eq!(value["dependencies"]["bitflags"].as_integer(), Some(1));
}

View file

@ -96,3 +96,5 @@ test!(text_before_array_separator,
include_str!("invalid/text-before-array-separator.toml")); include_str!("invalid/text-before-array-separator.toml"));
test!(text_in_array, test!(text_in_array,
include_str!("invalid/text-in-array.toml")); include_str!("invalid/text-in-array.toml"));
test!(duplicate_table,
include_str!("invalid/duplicate-table.toml"));

View file

@ -0,0 +1,8 @@
[dependencies.openssl-sys]
version = "0.5.2"
[dependencies]
libc = "0.1"
[dependencies]
bitflags = "0.1.1"