fix: disallow merging dotted key and inline table

current code mistakenly allows the following TOML file.
```toml
a.b = 42       # table "a" is defined here, implicitly
a = {c = 3.14} # table "a" is overwritten here
```
But we need to allow the following (structually similar) TOML file.
```toml
a.b = 42   # table "a" is defined here, implicitly
a.c = 3.14 # table "a" is merged with {c = 3.14}
```
To distinguish those, we check whether the current table is defined as
an inline table or via dotted key. If the table we are inserting is
defined via dotted key, we accept it and merge the table. If the table
being inserted is defined as an inline table, then we report an error.
This commit is contained in:
ToruNiina 2021-12-16 01:11:47 +09:00
parent 75e297eb47
commit cc1cc27613

View File

@ -1257,6 +1257,9 @@ std::string format_dotted_keys(InputIterator first, const InputIterator last)
// forward decl for is_valid_forward_table_definition
result<std::pair<std::vector<key>, region>, std::string>
parse_table_key(location& loc);
template<typename Value>
result<std::pair<typename Value::table_type, region>, std::string>
parse_inline_table(location& loc);
// The following toml file is allowed.
// ```toml
@ -1282,16 +1285,41 @@ parse_table_key(location& loc);
// of the key. If the key region points deeper node, it would be allowed.
// Otherwise, the key points the same node. It would be rejected.
template<typename Value, typename Iterator>
bool is_valid_forward_table_definition(const Value& fwd,
bool is_valid_forward_table_definition(const Value& fwd, const Value& inserting,
Iterator key_first, Iterator key_curr, Iterator key_last)
{
// ------------------------------------------------------------------------
// check type of the value to be inserted/merged
std::string inserting_reg = "";
if(const auto ptr = detail::get_region(inserting))
{
inserting_reg = ptr->str();
}
location inserting_def("internal", std::move(inserting_reg));
if(const auto inlinetable = parse_inline_table<Value>(inserting_def))
{
// check if we are overwriting existing table.
// ```toml
// # NG
// a.b = 42
// a = {d = 3.14}
// ```
// Inserting an inline table to a existing super-table is not allowed in
// any case. If we found it, we can reject it without further checking.
return false;
}
// ------------------------------------------------------------------------
// check table defined before
std::string internal = "";
if(const auto ptr = detail::get_region(fwd))
{
internal = ptr->str();
}
location def("internal", std::move(internal));
if(const auto tabkeys = parse_table_key(def))
if(const auto tabkeys = parse_table_key(def)) // [table.key]
{
// table keys always contains all the nodes from the root.
const auto& tks = tabkeys.unwrap().first;
@ -1481,7 +1509,7 @@ insert_nested_key(typename Value::table_type& root, const Value& v,
if(tab->at(k).is_table() && v.is_table())
{
if(!is_valid_forward_table_definition(
tab->at(k), first, iter, last))
tab->at(k), v, first, iter, last))
{
throw syntax_error(format_underline(concat_to_string(
"toml::insert_value: table (\"",