From 2fcd829b1d9c70d0981411b4f4adca9124985b54 Mon Sep 17 00:00:00 2001 From: Andrzej Janik Date: Thu, 4 Jun 2015 20:23:46 +0200 Subject: Disallow table redefinitions --- src/decoder/rustc_serialize.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/decoder') diff --git a/src/decoder/rustc_serialize.rs b/src/decoder/rustc_serialize.rs index 6e8fe59..af38f9b 100644 --- a/src/decoder/rustc_serialize.rs +++ b/src/decoder/rustc_serialize.rs @@ -3,7 +3,7 @@ use std::mem; use super::{Decoder, DecodeError}; use super::DecodeErrorKind::*; -use Value; +use {Value, Table}; impl rustc_serialize::Decoder for Decoder { type Error = DecodeError; @@ -141,7 +141,7 @@ impl rustc_serialize::Decoder for Decoder { Some(Value::Table(..)) => { let ret = try!(f(self)); match self.toml { - Some(Value::Table(ref t)) if t.len() == 0 => {} + Some(Value::Table(Table(ref t, _,))) if t.len() == 0 => {} _ => return Ok(ret) } self.toml.take(); @@ -156,7 +156,7 @@ impl rustc_serialize::Decoder for Decoder { { let field = format!("{}", f_name); let toml = match self.toml { - Some(Value::Table(ref mut table)) => { + Some(Value::Table(Table(ref mut table, _))) => { table.remove(&field) .or_else(|| table.remove(&f_name.replace("_", "-"))) }, @@ -165,7 +165,7 @@ impl rustc_serialize::Decoder for Decoder { let mut d = self.sub_decoder(toml, f_name); let ret = try!(f(&mut d)); if let Some(value) = d.toml { - if let Some(Value::Table(ref mut table)) = self.toml { + if let Some(Value::Table(Table(ref mut table, _))) = self.toml { table.insert(field, value); } } @@ -260,7 +260,7 @@ impl rustc_serialize::Decoder for Decoder { where F: FnOnce(&mut Decoder, usize) -> Result { let len = match self.toml { - Some(Value::Table(ref table)) => table.len(), + Some(Value::Table(Table(ref table, _))) => table.len(), ref found => return Err(self.mismatch("table", found)), }; let ret = try!(f(self, len)); @@ -273,7 +273,7 @@ impl rustc_serialize::Decoder for Decoder { { match self.toml { Some(Value::Table(ref table)) => { - match table.iter().skip(idx).next() { + match table.0.iter().skip(idx).next() { Some((key, _)) => { let val = Value::String(format!("{}", key)); f(&mut self.sub_decoder(Some(val), &**key)) @@ -290,7 +290,7 @@ impl rustc_serialize::Decoder for Decoder { { match self.toml { Some(Value::Table(ref table)) => { - match table.iter().skip(idx).next() { + match table.0.iter().skip(idx).next() { Some((_, value)) => { // XXX: this shouldn't clone f(&mut self.sub_decoder(Some(value.clone()), "")) -- cgit v1.2.3 From 8487b63c97080296269242c31f36a557a90da0cf Mon Sep 17 00:00:00 2001 From: Andrzej Janik Date: Sat, 6 Jun 2015 18:11:48 +0200 Subject: Rework fix for table redefinition to avoid breaking AST-compatiblity --- src/decoder/rustc_serialize.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/decoder') diff --git a/src/decoder/rustc_serialize.rs b/src/decoder/rustc_serialize.rs index af38f9b..6e8fe59 100644 --- a/src/decoder/rustc_serialize.rs +++ b/src/decoder/rustc_serialize.rs @@ -3,7 +3,7 @@ use std::mem; use super::{Decoder, DecodeError}; use super::DecodeErrorKind::*; -use {Value, Table}; +use Value; impl rustc_serialize::Decoder for Decoder { type Error = DecodeError; @@ -141,7 +141,7 @@ impl rustc_serialize::Decoder for Decoder { Some(Value::Table(..)) => { let ret = try!(f(self)); match self.toml { - Some(Value::Table(Table(ref t, _,))) if t.len() == 0 => {} + Some(Value::Table(ref t)) if t.len() == 0 => {} _ => return Ok(ret) } self.toml.take(); @@ -156,7 +156,7 @@ impl rustc_serialize::Decoder for Decoder { { let field = format!("{}", f_name); let toml = match self.toml { - Some(Value::Table(Table(ref mut table, _))) => { + Some(Value::Table(ref mut table)) => { table.remove(&field) .or_else(|| table.remove(&f_name.replace("_", "-"))) }, @@ -165,7 +165,7 @@ impl rustc_serialize::Decoder for Decoder { let mut d = self.sub_decoder(toml, f_name); let ret = try!(f(&mut d)); if let Some(value) = d.toml { - if let Some(Value::Table(Table(ref mut table, _))) = self.toml { + if let Some(Value::Table(ref mut table)) = self.toml { table.insert(field, value); } } @@ -260,7 +260,7 @@ impl rustc_serialize::Decoder for Decoder { where F: FnOnce(&mut Decoder, usize) -> Result { let len = match self.toml { - Some(Value::Table(Table(ref table, _))) => table.len(), + Some(Value::Table(ref table)) => table.len(), ref found => return Err(self.mismatch("table", found)), }; let ret = try!(f(self, len)); @@ -273,7 +273,7 @@ impl rustc_serialize::Decoder for Decoder { { match self.toml { Some(Value::Table(ref table)) => { - match table.0.iter().skip(idx).next() { + match table.iter().skip(idx).next() { Some((key, _)) => { let val = Value::String(format!("{}", key)); f(&mut self.sub_decoder(Some(val), &**key)) @@ -290,7 +290,7 @@ impl rustc_serialize::Decoder for Decoder { { match self.toml { Some(Value::Table(ref table)) => { - match table.0.iter().skip(idx).next() { + match table.iter().skip(idx).next() { Some((_, value)) => { // XXX: this shouldn't clone f(&mut self.sub_decoder(Some(value.clone()), "")) -- cgit v1.2.3 From 89332806c5cab1cca39b0a676aa69aad599b3de7 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 23 Jun 2015 17:43:52 -0700 Subject: Improve the error message in failing enums --- src/decoder/rustc_serialize.rs | 75 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 9 deletions(-) (limited to 'src/decoder') diff --git a/src/decoder/rustc_serialize.rs b/src/decoder/rustc_serialize.rs index 6e8fe59..534154d 100644 --- a/src/decoder/rustc_serialize.rs +++ b/src/decoder/rustc_serialize.rs @@ -96,15 +96,31 @@ impl rustc_serialize::Decoder for Decoder { -> Result where F: FnMut(&mut Decoder, usize) -> Result { - let mut first_error = None; + // When decoding enums, this crate takes the strategy of trying to + // decode the current TOML as all of the possible variants, returning + // success on the first one that succeeds. + // + // Note that fidelity of the errors returned here is a little nebulous, + // but we try to return the error that had the relevant field as the + // longest field. This way we hopefully match an error against what was + // most likely being written down without losing too much info. + let mut first_error = None::; for i in 0..names.len() { let mut d = self.sub_decoder(self.toml.clone(), ""); match f(&mut d, i) { - Ok(t) => { self.toml = d.toml; return Ok(t) } + Ok(t) => { + self.toml = d.toml; + return Ok(t) + } Err(e) => { - if first_error.is_none() { - first_error = Some(e); + if let Some(ref first) = first_error { + let my_len = e.field.as_ref().map(|s| s.len()); + let first_len = first.field.as_ref().map(|s| s.len()); + if my_len <= first_len { + continue + } } + first_error = Some(e); } } } @@ -158,7 +174,7 @@ impl rustc_serialize::Decoder for Decoder { let toml = match self.toml { Some(Value::Table(ref mut table)) => { table.remove(&field) - .or_else(|| table.remove(&f_name.replace("_", "-"))) + .or_else(|| table.remove(&f_name.replace("_", "-"))) }, ref found => return Err(self.mismatch("table", found)), }; @@ -275,8 +291,8 @@ impl rustc_serialize::Decoder for Decoder { Some(Value::Table(ref table)) => { match table.iter().skip(idx).next() { Some((key, _)) => { - let val = Value::String(format!("{}", key)); - f(&mut self.sub_decoder(Some(val), &**key)) + let val = Value::String(key.to_string()); + f(&mut self.sub_decoder(Some(val), key)) } None => Err(self.err(ExpectedMapKey(idx))), } @@ -291,9 +307,9 @@ impl rustc_serialize::Decoder for Decoder { match self.toml { Some(Value::Table(ref table)) => { match table.iter().skip(idx).next() { - Some((_, value)) => { + Some((key, value)) => { // XXX: this shouldn't clone - f(&mut self.sub_decoder(Some(value.clone()), "")) + f(&mut self.sub_decoder(Some(value.clone()), key)) } None => Err(self.err(ExpectedMapElement(idx))), } @@ -309,3 +325,44 @@ impl rustc_serialize::Decoder for Decoder { } } } + +#[cfg(test)] +mod tests { + use rustc_serialize::Decodable; + use std::collections::HashMap; + + use {Parser, Decoder, Value}; + + #[test] + fn bad_enum_chooses_longest_error() { + #[derive(RustcDecodable)] + #[allow(dead_code)] + struct Foo { + wut: HashMap, + } + + #[derive(RustcDecodable)] + enum Bar { + Simple(String), + Detailed(Baz), + } + + #[derive(RustcDecodable, Debug)] + struct Baz { + features: Vec, + } + + let s = r#" + [wut] + a = { features = "" } + "#; + let v = Parser::new(s).parse().unwrap(); + let mut d = Decoder::new(Value::Table(v)); + let err = match Foo::decode(&mut d) { + Ok(_) => panic!("expected error"), + Err(e) => e, + }; + assert_eq!(err.field.as_ref().unwrap(), "wut.a.features"); + + } +} -- cgit v1.2.3