Repository: toml-rs/toml (language: rust)

Issue:
Not the greatest error message on bad dotted-key insertion
This is a regression compared to toml-rs found by cargo's tests

For the following document
```toml
[http]
ssl-version = 'tlsv1.1'
ssl-version.min = 'tlsv1.2'
ssl-version.max = 'tlsv1.3'
```

```
---- config::config_get_ssl_version_both_forms_configured stdout ----                                                                              
thread 'config::config_get_ssl_version_both_forms_configured' panicked at '                                                                        
                                                                                                                                                   
error: output did not match:                                                                                                                       
1   1     could not load Cargo configuration                                                                                                       
2   2                                                                                                                                              
3   3     Caused by:                                                                                                                               
4   4       could not parse TOML configuration in `[..]/.cargo/config`                                                                             
5   5                                                                                                                                              
6   6     Caused by:                                                                                                                               
7   7       could not parse input as TOML                                                                                                          
8   8                                                                                                                                              
9   9     Caused by:                                                                                                                               
10       -  dotted key attempted to extend non-table type at line 2 column 15                                                                      
    10   +  TOML parse error at line 3, column 1                                                                                                   
    11   +  |                                                                                                                                      
    12   +3 | ssl-version.min = 'tlsv1.2'                                                                                                          
    13   +  | ^                                                                                                                                    
    14   +Duplicate key `ssl-version` in `[]` table                                                                                                
    15   +                                                                                                                                         
```


Discussion / hints:

Fix patch:
diff --git a/src/parser/document.rs b/src/parser/document.rs
index 4b3b1843..f6e14279 100644
--- a/src/parser/document.rs
+++ b/src/parser/document.rs
@@ -159,7 +159,7 @@ impl TomlParser {
         if mixed_table_types {
             return Err(CustomError::DuplicateKey {
                 key: kv.key.get().into(),
-                table: "<unknown>".into(), // TODO: get actual table name
+                table: None,
             });
         }
 
@@ -170,7 +170,7 @@ impl TomlParser {
         if duplicate_key {
             return Err(CustomError::DuplicateKey {
                 key: key.as_str().into(),
-                table: "<unknown>".into(), // TODO: get actual table name
+                table: Some(self.current_table_path.clone()),
             });
         }
 
diff --git a/src/parser/errors.rs b/src/parser/errors.rs
index 7ec9786f..295d3c5b 100644
--- a/src/parser/errors.rs
+++ b/src/parser/errors.rs
@@ -1,6 +1,8 @@
+use crate::Key;
 use combine::easy::Errors as ParseError;
 use combine::stream::easy::Error;
 use combine::stream::position::SourcePosition;
+use itertools::Itertools;
 use std::error::Error as StdError;
 use std::fmt::{Display, Formatter, Result};
 
@@ -233,7 +235,14 @@ mod test_translate_position {
 
 #[derive(Debug, Clone)]
 pub(crate) enum CustomError {
-    DuplicateKey { key: String, table: String },
+    DuplicateKey {
+        key: String,
+        table: Option<Vec<Key>>,
+    },
+    DottedKeyExtendWrongType {
+        key: Vec<Key>,
+        actual: &'static str,
+    },
     InvalidHexEscape(u32),
     UnparsedLine,
     OutOfRange,
@@ -247,11 +256,28 @@ impl StdError for CustomError {
 
 impl Display for CustomError {
     fn fmt(&self, f: &mut Formatter<'_>) -> Result {
-        match *self {
-            CustomError::DuplicateKey { ref key, ref table } => {
-                writeln!(f, "Duplicate key `{}` in `{}` table", key, table)
+        match self {
+            CustomError::DuplicateKey { key, table } => {
+                if let Some(table) = table {
+                    if table.is_empty() {
+                        writeln!(f, "Duplicate key `{}` in document root", key)
+                    } else {
+                        let path = table.iter().join(".");
+                        writeln!(f, "Duplicate key `{}` in table `{}`", key, path)
+                    }
+                } else {
+                    writeln!(f, "Duplicate key `{}`", key)
+                }
             }
-            CustomError::InvalidHexEscape(ref h) => {
+            CustomError::DottedKeyExtendWrongType { key, actual } => {
+                let path = key.iter().join(".");
+                writeln!(
+                    f,
+                    "Dotted key `{}` attempted to extend non-table type (`{}`)",
+                    path, actual
+                )
+            }
+            CustomError::InvalidHexEscape(h) => {
                 writeln!(f, "Invalid hex escape code: {:x} ", h)
             }
             CustomError::UnparsedLine => writeln!(f, "Could not parse the line"),
diff --git a/src/parser/inline_table.rs b/src/parser/inline_table.rs
index bc797c12..7cc9d43a 100644
--- a/src/parser/inline_table.rs
+++ b/src/parser/inline_table.rs
@@ -1,7 +1,7 @@
 use crate::key::Key;
 use crate::parser::errors::CustomError;
 use crate::parser::key::key;
-use crate::parser::table::duplicate_key;
+use crate::parser::table::extend_wrong_type;
 use crate::parser::trivia::ws;
 use crate::parser::value::value;
 use crate::table::TableKeyValue;
@@ -35,7 +35,7 @@ fn table_from_pairs(
         if duplicate_key {
             return Err(CustomError::DuplicateKey {
                 key: key.as_str().into(),
-                table: "inline".into(),
+                table: None,
             });
         }
     }
@@ -57,8 +57,8 @@ fn descend_path<'a>(
             Value::InlineTable(ref mut sweet_child_of_mine) => {
                 table = sweet_child_of_mine;
             }
-            _ => {
-                return Err(duplicate_key(path, i));
+            ref v => {
+                return Err(extend_wrong_type(path, i, v.type_name()));
             }
         }
     }
diff --git a/src/parser/table.rs b/src/parser/table.rs
index b05edfb6..6c17df75 100644
--- a/src/parser/table.rs
+++ b/src/parser/table.rs
@@ -9,7 +9,6 @@ use combine::parser::byte::byte;
 use combine::parser::range::range;
 use combine::stream::RangeStream;
 use combine::*;
-use itertools::Itertools;
 use std::cell::RefCell;
 use std::mem;
 // https://github.com/rust-lang/rust/issues/41358
@@ -71,13 +70,17 @@ parser! {
 
 pub(crate) fn duplicate_key(path: &[Key], i: usize) -> CustomError {
     assert!(i < path.len());
-    let header = path[..i]
-        .iter()
-        .map(|k| k.to_repr().as_ref().as_raw().to_owned())
-        .join(".");
     CustomError::DuplicateKey {
         key: path[i].to_repr().as_ref().as_raw().into(),
-        table: format!("[{}]", header),
+        table: Some(path[..i].to_vec()),
+    }
+}
+
+pub(crate) fn extend_wrong_type(path: &[Key], i: usize, actual: &'static str) -> CustomError {
+    assert!(i < path.len());
+    CustomError::DottedKeyExtendWrongType {
+        key: path[..=i].to_vec(),
+        actual,
     }
 }
 
@@ -96,8 +99,8 @@ impl TomlParser {
                 Item::Table(new_table)
             });
             match *entry {
-                Item::Value(..) => {
-                    return Err(duplicate_key(path, i));
+                Item::Value(ref v) => {
+                    return Err(extend_wrong_type(path, i, v.type_name()));
                 }
                 Item::ArrayOfTables(ref mut array) => {
                     debug_assert!(!array.is_empty());


Test patch:
diff --git a/tests/fixtures/invalid/duplicate-key-dotted-into-std.stderr b/tests/fixtures/invalid/duplicate-key-dotted-into-std.stderr
index a3382be6..16e126ed 100644
--- a/tests/fixtures/invalid/duplicate-key-dotted-into-std.stderr
+++ b/tests/fixtures/invalid/duplicate-key-dotted-into-std.stderr
@@ -2,5 +2,5 @@ TOML parse error at line 5, column 1
   |
 5 | apple.color = "red"
   | ^
-Duplicate key `color` in `<unknown>` table
+Duplicate key `color`
 
diff --git a/tests/fixtures/invalid/duplicate-key-std-into-dotted.stderr b/tests/fixtures/invalid/duplicate-key-std-into-dotted.stderr
index 87679a75..210e442e 100644
--- a/tests/fixtures/invalid/duplicate-key-std-into-dotted.stderr
+++ b/tests/fixtures/invalid/duplicate-key-std-into-dotted.stderr
@@ -2,5 +2,5 @@ TOML parse error at line 6, column 1
   |
 6 | size = "small"
   | ^
-Duplicate key `size` in `<unknown>` table
+Duplicate key `size`
 
diff --git a/tests/fixtures/invalid/duplicate-key-table.stderr b/tests/fixtures/invalid/duplicate-key-table.stderr
index c0824110..a1c49025 100644
--- a/tests/fixtures/invalid/duplicate-key-table.stderr
+++ b/tests/fixtures/invalid/duplicate-key-table.stderr
@@ -2,6 +2,6 @@ TOML parse error at line 4, column 1
   |
 4 | [fruit.type]
   | ^
-Duplicate key `type` in `[fruit]` table
+Duplicate key `type` in table `fruit`
 
 While parsing a Table Header
diff --git a/tests/fixtures/invalid/duplicate-keys-cargo.stderr b/tests/fixtures/invalid/duplicate-keys-cargo.stderr
new file mode 100644
index 00000000..be31ca3e
--- /dev/null
+++ b/tests/fixtures/invalid/duplicate-keys-cargo.stderr
@@ -0,0 +1,6 @@
+TOML parse error at line 7, column 1
+  |
+7 | categories = ["algorithms"]
+  | ^
+Duplicate key `categories` in table `project`
+
diff --git a/tests/fixtures/invalid/duplicate-keys-cargo.toml b/tests/fixtures/invalid/duplicate-keys-cargo.toml
new file mode 100644
index 00000000..26240e02
--- /dev/null
+++ b/tests/fixtures/invalid/duplicate-keys-cargo.toml
@@ -0,0 +1,11 @@
+[project]
+
+name = "dep1"
+version = "0.5.0"
+authors = ["carlhuda@example.com"]
+categories = ["algorithms"]
+categories = ["algorithms"]
+
+[lib]
+
+name = "dep1"
diff --git a/tests/fixtures/invalid/duplicate-keys-dotted.stderr b/tests/fixtures/invalid/duplicate-keys-dotted.stderr
new file mode 100644
index 00000000..c6d08ac6
--- /dev/null
+++ b/tests/fixtures/invalid/duplicate-keys-dotted.stderr
@@ -0,0 +1,6 @@
+TOML parse error at line 3, column 1
+  |
+3 | ssl-version.min = 'tlsv1.2'
+  | ^
+Dotted key `ssl-version` attempted to extend non-table type (`string`)
+
diff --git a/tests/fixtures/invalid/duplicate-keys-dotted.toml b/tests/fixtures/invalid/duplicate-keys-dotted.toml
new file mode 100644
index 00000000..199f36e7
--- /dev/null
+++ b/tests/fixtures/invalid/duplicate-keys-dotted.toml
@@ -0,0 +1,4 @@
+[http]
+ssl-version = 'tlsv1.1'
+ssl-version.min = 'tlsv1.2'
+ssl-version.max = 'tlsv1.3'
diff --git a/tests/fixtures/invalid/duplicate-keys.stderr b/tests/fixtures/invalid/duplicate-keys.stderr
index 6ef41bd8..586f016a 100644
--- a/tests/fixtures/invalid/duplicate-keys.stderr
+++ b/tests/fixtures/invalid/duplicate-keys.stderr
@@ -2,5 +2,5 @@ TOML parse error at line 2, column 1
   |
 2 | dupe = true
   | ^
-Duplicate key `dupe` in `<unknown>` table
+Duplicate key `dupe` in document root
 
diff --git a/tests/fixtures/invalid/duplicate-tables.stderr b/tests/fixtures/invalid/duplicate-tables.stderr
index 86b8f1ed..6ff97fa3 100644
--- a/tests/fixtures/invalid/duplicate-tables.stderr
+++ b/tests/fixtures/invalid/duplicate-tables.stderr
@@ -2,6 +2,6 @@ TOML parse error at line 2, column 1
   |
 2 | [a]
   | ^
-Duplicate key `a` in `[]` table
+Duplicate key `a` in document root
 
 While parsing a Table Header
diff --git a/tests/fixtures/invalid/table-array-implicit.stderr b/tests/fixtures/invalid/table-array-implicit.stderr
index 14815c68..d771f5c3 100644
--- a/tests/fixtures/invalid/table-array-implicit.stderr
+++ b/tests/fixtures/invalid/table-array-implicit.stderr
@@ -2,6 +2,6 @@ TOML parse error at line 13, column 1
    |
 13 | [[albums]]
    | ^
-Duplicate key `albums` in `[]` table
+Duplicate key `albums` in document root
 
 While parsing a Table Header