fix(config): Reintroduce multiple inheritance (#2271)

Pre 3.5.0, any key starting with 'inherit' was treated as an inherit
directive. This allowed for multiple inheritance even though it was
never inteded in that way.
3.5.0 removed that bug/feature by doing a strict check against 'inherit'

It seems people were relying on this behavior, so we are adding it back.
However multiple inheritance with multiple keys is also deprecated in
favor of the `inherit` key now supporting multiple space separated
sections.

This is because the config doesn't have a key order, but inheritance
does depend on the order the different section keys are copied over (if
multiple inherited sections define the same key).

Fixes #2269
This commit is contained in:
Patrick Ziegler 2020-12-05 02:48:18 +01:00 committed by GitHub
parent 3d6a7ffd4d
commit b2c515c73c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -72,34 +72,58 @@ void config::warn_deprecated(const string& section, const string& key, string re
* Look for sections set up to inherit from a base section
* and copy the missing parameters
*
* Multiple sections can be specified, separated by a space.
*
* [sub/section]
* inherit = base/section
* inherit = section1 section2
*/
void config::copy_inherited() {
for (auto&& section : m_sections) {
std::vector<string> inherit_sections;
// Collect all sections to be inherited
for (auto&& param : section.second) {
if (param.first == "inherit") {
// Get name of base section
string key_name = param.first;
if (key_name == "inherit") {
auto inherit = param.second;
if ((inherit = dereference<string>(section.first, param.first, inherit, inherit)).empty()) {
throw value_error("Invalid section \"\" defined for \"" + section.first + ".inherit\"");
inherit = dereference<string>(section.first, key_name, inherit, inherit);
std::vector<string> sections = string_util::split(std::move(inherit), ' ');
inherit_sections.insert(inherit_sections.end(), sections.begin(), sections.end());
} else if (key_name.find("inherit") == 0) {
// Legacy support for keys that just start with 'inherit'
m_log.warn(
"\"%s.%s\": Using anything other than 'inherit' for inheriting section keys is deprecated. "
"The 'inherit' key supports multiple section names separated by a space.",
section.first, key_name);
auto inherit = param.second;
inherit = dereference<string>(section.first, key_name, inherit, inherit);
if (inherit.empty() || m_sections.find(inherit) == m_sections.end()) {
throw value_error(
"Invalid section \"" + inherit + "\" defined for \"" + section.first + "." + key_name + "\"");
}
// Find and validate base section
auto base_section = m_sections.find(inherit);
if (base_section == m_sections.end()) {
throw value_error("Invalid section \"" + inherit + "\" defined for \"" + section.first + ".inherit\"");
}
inherit_sections.push_back(std::move(inherit));
}
}
m_log.trace("config: Copying missing params (sub=\"%s\", base=\"%s\")", section.first, inherit);
for (const auto& base_name : inherit_sections) {
const auto base_section = m_sections.find(base_name);
if (base_section == m_sections.end()) {
throw value_error("Invalid section \"" + base_name + "\" defined for \"" + section.first + ".inherit\"");
}
/*
* Iterate the base and copy the parameters that haven't been defined
* for the sub-section
*/
for (auto&& base_param : base_section->second) {
section.second.emplace(base_param.first, base_param.second);
}
m_log.trace("config: Inheriting keys from \"%s\" in \"%s\"", base_name, section.first);
/*
* Iterate the base and copy the parameters that haven't been defined
* yet.
*/
for (auto&& base_param : base_section->second) {
section.second.emplace(base_param.first, base_param.second);
}
}
}