Add a new check to the WML schema self-validator

This warns about tag definitions that can never match any tag (or for which one element of the comma-separated list can never match) because there is an earlier-defined tag definition that would match it instead.

The new check also found some errors in the schema, which have been fixed.
This commit is contained in:
Celtic Minstrel 2020-10-24 01:12:39 -04:00 committed by Celtic Minstrel
parent 23859c6d78
commit 6d7f8e8ce6
4 changed files with 53 additions and 9 deletions

View file

@ -94,12 +94,7 @@
name="while"
max=infinite
super="$conditional_wml"
{ACTION_TAG "do" min,max=0,infinite}
[tag]
name="do"
max=infinite
super="$action_wml"
[/tag]
{ACTION_TAG "do" max=infinite}
[/tag]
[tag]
name="for"
@ -261,7 +256,7 @@
{REQUIRED_KEY types string_list}
[/tag]
[tag]
name="allow_extra recruit,disallow_extra_recruit,set_recruit"
name="allow_extra_recruit,disallow_extra_recruit,set_extra_recruit"
max=infinite
super="$filter_unit"
{INSERT_TAG}

View file

@ -92,7 +92,6 @@
[tag]
name="intersection"
min=1
{LINK_TAG "wml_schema/type"}
[tag]
name="type"
max=infinite

View file

@ -128,6 +128,18 @@ static void wrong_path_error(const std::string& file,
print_output(ss.str(), flag_exception);
}
static void duplicate_tag_error(const std::string& file,
int line,
const std::string& tag,
const std::string& key,
const std::string& value,
bool flag_exception)
{
std::ostringstream ss;
ss << "Duplicate or fully-overlapping tag definition '" << value << "' in key '" << key << "=' in tag [" << tag << "]\n" << at(file, line) << "\n";
print_output(ss.str(), flag_exception);
}
static void wrong_type_error(const std::string & file, int line,
const std::string & tag,
const std::string & key,
@ -446,10 +458,43 @@ bool schema_self_validator::tag_path_exists(const config& cfg, const reference&
return false;
}
bool schema_self_validator::tag_matches(const std::string& pattern, const std::string& tag)
{
for(const std::string& pat : utils::split(pattern)) {
if(utils::wildcard_string_match(tag, pat)) return true;
}
return false;
}
void schema_self_validator::validate(const config& cfg, const std::string& name, int start_line, const std::string& file)
{
if(type_nesting_ == 1 && name == "type") {
defined_types_.insert(cfg["name"]);
} else if(name == "tag") {
bool first = true;
std::vector<std::string> tag_names;
for(auto current : cfg.all_children_range()) {
if(current.key != "tag" && current.key != "link") continue;
std::string tag_name = current.cfg["name"];
if(current.key == "link") {
tag_name.erase(0, tag_name.find_last_of('/') + 1);
}
if(first) {
tag_names.push_back(tag_name);
first = false;
continue;
}
auto split = utils::split(tag_name);
for(const std::string& pattern : tag_names) {
for(const std::string& tag : split) {
if(tag_matches(pattern, tag)) {
queue_message(current.cfg, DUPLICATE_TAG, file, start_line, 0, current.key, "name", tag_name);
continue;
}
}
}
tag_names.push_back(tag_name);
}
} else if(name == "wml_schema") {
using namespace std::placeholders;
std::vector<reference> missing_types = referenced_types_, missing_tags = referenced_tag_paths_;
@ -551,6 +596,9 @@ void schema_self_validator::print(message_info& el)
case WRONG_PATH:
wrong_path_error(el.file, el.line, el.tag, el.key, el.value, create_exceptions_);
break;
case DUPLICATE_TAG:
duplicate_tag_error(el.file, el.line, el.tag, el.key, el.value, create_exceptions_);
break;
}
}

View file

@ -182,8 +182,10 @@ private:
std::multimap<std::string, std::string> derivations_;
int type_nesting_, condition_nesting_;
bool tag_path_exists(const config& cfg, const reference& ref);
static bool tag_matches(const std::string& pattern, const std::string& tag);
void print(message_info& message) override;
enum { WRONG_TYPE = NEXT_ERROR, WRONG_PATH, NEXT_ERROR };
enum { WRONG_TYPE = NEXT_ERROR, WRONG_PATH, DUPLICATE_TAG, NEXT_ERROR };
};
} // namespace schema_validation{