Added unreferenced rules check

This commit is contained in:
yhirose 2021-03-02 08:14:22 -05:00
parent 9b395f2c7f
commit a248d8689e
2 changed files with 53 additions and 32 deletions

View File

@ -27,6 +27,7 @@
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <unordered_set>
#include <vector> #include <vector>
#if !defined(__cplusplus) || __cplusplus < 201703L #if !defined(__cplusplus) || __cplusplus < 201703L
@ -656,8 +657,8 @@ struct ErrorInfo {
} }
void add(const char *token, bool is_literal) { void add(const char *token, bool is_literal) {
for (const auto &x : expected_tokens) { for (const auto &[t, l] : expected_tokens) {
if (x.first == token && x.second == is_literal) { return; } if (t == token && l == is_literal) { return; }
} }
expected_tokens.push_back(std::make_pair(token, is_literal)); expected_tokens.push_back(std::make_pair(token, is_literal));
} }
@ -938,8 +939,8 @@ public:
assert(capture_scope_stack.size() >= 2); assert(capture_scope_stack.size() >= 2);
auto curr = &capture_scope_stack[capture_scope_stack_size - 1]; auto curr = &capture_scope_stack[capture_scope_stack_size - 1];
auto prev = curr - 1; auto prev = curr - 1;
for (const auto &kv : *curr) { for (const auto &[k, v] : *curr) {
(*prev)[kv.first] = kv.second; (*prev)[k] = v;
} }
} }
@ -2001,8 +2002,7 @@ struct HasEmptyElement : public Ope::Visitor {
private: private:
void set_error() { void set_error() {
is_empty = true; is_empty = true;
error_s = refs_.back().first; tie(error_s, error_name) = refs_.back();
error_name = refs_.back().second;
} }
std::list<std::pair<const char *, std::string>> &refs_; std::list<std::pair<const char *, std::string>> &refs_;
}; };
@ -2089,6 +2089,7 @@ struct ReferenceChecker : public Ope::Visitor {
std::unordered_map<std::string, const char *> error_s; std::unordered_map<std::string, const char *> error_s;
std::unordered_map<std::string, std::string> error_message; std::unordered_map<std::string, std::string> error_message;
std::unordered_set<std::string> referenced;
private: private:
const Grammar &grammar_; const Grammar &grammar_;
@ -2943,6 +2944,7 @@ inline void ReferenceChecker::visit(Reference &ope) {
error_s[ope.name_] = ope.s_; error_s[ope.name_] = ope.s_;
error_message[ope.name_] = "'" + ope.name_ + "' is not defined."; error_message[ope.name_] = "'" + ope.name_ + "' is not defined.";
} else { } else {
if (!referenced.count(ope.name_)) { referenced.insert(ope.name_); }
const auto &rule = grammar_.at(ope.name_); const auto &rule = grammar_.at(ope.name_);
if (rule.is_macro) { if (rule.is_macro) {
if (!ope.is_macro_ || ope.args_.size() != rule.params.size()) { if (!ope.is_macro_ || ope.args_.size() != rule.params.size()) {
@ -3598,8 +3600,8 @@ private:
} }
// User provided rules // User provided rules
for (const auto &x : rules) { for (auto [user_name, user_rule] : rules) {
auto name = x.first; auto name = user_name;
auto ignore = false; auto ignore = false;
if (!name.empty() && name[0] == '~') { if (!name.empty() && name[0] == '~') {
ignore = true; ignore = true;
@ -3607,7 +3609,7 @@ private:
} }
if (!name.empty()) { if (!name.empty()) {
auto &rule = grammar[name]; auto &rule = grammar[name];
rule <= x.second; rule <= user_rule;
rule.name = name; rule.name = name;
rule.ignoreSemanticValue = ignore; rule.ignoreSemanticValue = ignore;
} }
@ -3616,23 +3618,24 @@ private:
// Check duplicated definitions // Check duplicated definitions
auto ret = data.duplicates.empty(); auto ret = data.duplicates.empty();
for (const auto &x : data.duplicates) { for (const auto &[name, ptr] : data.duplicates) {
if (log) { if (log) {
const auto &name = x.first;
auto ptr = x.second;
auto line = line_info(s, ptr); auto line = line_info(s, ptr);
log(line.first, line.second, "'" + name + "' is already defined."); log(line.first, line.second, "'" + name + "' is already defined.");
} }
} }
// Set root definition
auto &start_rule = grammar[data.start];
// Check if the start rule has ignore operator // Check if the start rule has ignore operator
{ {
auto &rule = grammar[data.start]; if (start_rule.ignoreSemanticValue) {
if (rule.ignoreSemanticValue) {
if (log) { if (log) {
auto line = line_info(s, rule.s_); auto line = line_info(s, start_rule.s_);
log(line.first, line.second, log(line.first, line.second,
"Ignore operator cannot be applied to '" + rule.name + "'."); "Ignore operator cannot be applied to '" + start_rule.name +
"'.");
} }
ret = false; ret = false;
} }
@ -3641,14 +3644,18 @@ private:
if (!ret) { return nullptr; } if (!ret) { return nullptr; }
// Check missing definitions // Check missing definitions
for (auto &x : grammar) { auto referenced = std::unordered_set<std::string>{
auto &rule = x.second; WHITESPACE_DEFINITION_NAME,
WORD_DEFINITION_NAME,
RECOVER_DEFINITION_NAME,
start_rule.name,
};
for (auto &[_, rule] : grammar) {
ReferenceChecker vis(grammar, rule.params); ReferenceChecker vis(grammar, rule.params);
rule.accept(vis); rule.accept(vis);
for (const auto &y : vis.error_s) { referenced.insert(vis.referenced.begin(), vis.referenced.end());
const auto &name = y.first; for (const auto &[name, ptr] : vis.error_s) {
const auto ptr = y.second;
if (log) { if (log) {
auto line = line_info(s, ptr); auto line = line_info(s, ptr);
log(line.first, line.second, vis.error_message[name]); log(line.first, line.second, vis.error_message[name]);
@ -3657,6 +3664,17 @@ private:
} }
} }
for (auto &[name, rule] : grammar) {
if (!referenced.count(name)) {
if (log) {
auto line = line_info(s, rule.s_);
auto msg = "'" + name + "' is not referenced.";
log(line.first, line.second, msg);
}
ret = false;
}
}
if (!ret) { return nullptr; } if (!ret) { return nullptr; }
// Link references // Link references
@ -3669,10 +3687,7 @@ private:
// Check left recursion // Check left recursion
ret = true; ret = true;
for (auto &x : grammar) { for (auto &[name, rule] : grammar) {
const auto &name = x.first;
auto &rule = x.second;
DetectLeftRecursion vis(name); DetectLeftRecursion vis(name);
rule.accept(vis); rule.accept(vis);
if (vis.error_s) { if (vis.error_s) {
@ -3686,9 +3701,6 @@ private:
if (!ret) { return nullptr; } if (!ret) { return nullptr; }
// Set root definition
auto &start_rule = grammar[data.start];
// Check infinite loop // Check infinite loop
for (auto &[name, rule] : grammar) { for (auto &[name, rule] : grammar) {
DetectInfiniteLoop vis(rule.s_, name); DetectInfiniteLoop vis(rule.s_, name);
@ -3721,9 +3733,7 @@ private:
} }
// Apply instructions // Apply instructions
for (const auto &item : data.instructions) { for (const auto &[name, instruction] : data.instructions) {
const auto &name = item.first;
const auto &instruction = item.second;
auto &rule = grammar[name]; auto &rule = grammar[name];
if (instruction.type == "precedence") { if (instruction.type == "precedence") {

View File

@ -1036,12 +1036,23 @@ TEST_CASE("Macro passes an arg to another macro", "[macro]") {
A <- B(C) A <- B(C)
B(D) <- D B(D) <- D
C <- 'c' C <- 'c'
D <- 'd'
)"); )");
REQUIRE(parser.parse("c")); REQUIRE(parser.parse("c"));
} }
TEST_CASE("Unreferenced rule", "[macro]") {
parser parser(R"(
A <- B(C)
B(D) <- D
C <- 'c'
D <- 'd'
)");
bool ret = parser;
REQUIRE(ret == false);
}
TEST_CASE("Nested macro call", "[macro]") { TEST_CASE("Nested macro call", "[macro]") {
parser parser(R"( parser parser(R"(
A <- B(T) A <- B(T)