aboutsummaryrefslogtreecommitdiff
path: root/gcc/rust/parse/rust-parse.cc
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2022-03-23 09:00:05 +0000
committerGitHub <noreply@github.com>2022-03-23 09:00:05 +0000
commitb9720caa100efa6758a5f0d54d3764072d83be41 (patch)
tree5b1cf22e2ed1065889d29ef358bf55968beebf3c /gcc/rust/parse/rust-parse.cc
parent1d34c120c4754b921ac0520585cfef0e7a0bbb29 (diff)
parent35ca685200830626e5abd623f65a850649beace2 (diff)
downloadgcc-b9720caa100efa6758a5f0d54d3764072d83be41.zip
gcc-b9720caa100efa6758a5f0d54d3764072d83be41.tar.gz
gcc-b9720caa100efa6758a5f0d54d3764072d83be41.tar.bz2
Merge #1044
1044: Restrict follow-up tokens on `expr` and `stmt` r=CohenArthur a=CohenArthur This adds a base for respecting the [Macro Follow-Set Ambiguity specification](https://doc.rust-lang.org/reference/macro-ambiguity.html). If the design is validated, adding more restrictions on other fragment specifiers should not be difficult Addresses #947 Co-authored-by: Arthur Cohen <arthur.cohen@embecosm.com>
Diffstat (limited to 'gcc/rust/parse/rust-parse.cc')
-rw-r--r--gcc/rust/parse/rust-parse.cc141
1 files changed, 141 insertions, 0 deletions
diff --git a/gcc/rust/parse/rust-parse.cc b/gcc/rust/parse/rust-parse.cc
index f995e4b..16ed4a0 100644
--- a/gcc/rust/parse/rust-parse.cc
+++ b/gcc/rust/parse/rust-parse.cc
@@ -92,4 +92,145 @@ extract_module_path (const AST::AttrVec &inner_attrs,
return path;
}
+
+static bool
+peculiar_fragment_match_compatible (AST::MacroMatchFragment &last_match,
+ AST::MacroMatch &match)
+{
+ static std::unordered_map<AST::MacroFragSpec::Kind, std::vector<TokenId>>
+ follow_set = {
+ {AST::MacroFragSpec::EXPR, {MATCH_ARROW, COMMA, SEMICOLON}},
+ {AST::MacroFragSpec::STMT, {MATCH_ARROW, COMMA, SEMICOLON}},
+ };
+
+ Location error_locus = match.get_match_locus ();
+
+ // There are two behaviors to handle here: If the follow-up match is a token,
+ // we want to check if it is allowed.
+ // If it is a fragment, repetition or matcher then we know that it will be
+ // an error.
+ // For repetitions and matchers we want to extract a proper location to report
+ // the error.
+ switch (match.get_macro_match_type ())
+ {
+ case AST::MacroMatch::Tok: {
+ auto tok = static_cast<AST::Token *> (&match);
+ auto &allowed_toks
+ = follow_set[last_match.get_frag_spec ().get_kind ()];
+ auto is_valid = std::find (allowed_toks.begin (), allowed_toks.end (),
+ tok->get_id ())
+ != allowed_toks.end ();
+ if (!is_valid)
+ // FIXME: Add hint about allowed fragments
+ rust_error_at (tok->get_match_locus (),
+ "token %<%s%> is not allowed after %<%s%> fragment",
+ tok->get_str ().c_str (),
+ last_match.get_frag_spec ().as_string ().c_str ());
+ return is_valid;
+ }
+ break;
+ case AST::MacroMatch::Repetition: {
+ auto repetition = static_cast<AST::MacroMatchRepetition *> (&match);
+ auto &matches = repetition->get_matches ();
+ if (!matches.empty ())
+ error_locus = matches.front ()->get_match_locus ();
+ break;
+ }
+ case AST::MacroMatch::Matcher: {
+ auto matcher = static_cast<AST::MacroMatcher *> (&match);
+ auto &matches = matcher->get_matches ();
+ if (!matches.empty ())
+ error_locus = matches.front ()->get_match_locus ();
+ break;
+ }
+ default:
+ break;
+ }
+
+ rust_error_at (error_locus, "fragment not allowed after %<%s%> fragment",
+ last_match.get_frag_spec ().as_string ().c_str ());
+
+ return false;
+}
+
+/**
+ * Avoid UB by calling .front() and .back() on empty containers...
+ */
+
+template <typename T>
+static T *
+get_back_ptr (std::vector<std::unique_ptr<T>> &values)
+{
+ if (values.empty ())
+ return nullptr;
+
+ return values.back ().get ();
+}
+
+template <typename T>
+static T *
+get_front_ptr (std::vector<std::unique_ptr<T>> &values)
+{
+ if (values.empty ())
+ return nullptr;
+
+ return values.front ().get ();
+}
+
+bool
+is_match_compatible (AST::MacroMatch &last_match, AST::MacroMatch &match)
+{
+ AST::MacroMatch *new_last = nullptr;
+
+ // We want to "extract" the concerning matches. In cases such as matchers and
+ // repetitions, we actually store multiple matchers, but are only concerned
+ // about the follow-set ambiguities of certain elements.
+ // There are some cases where we can short-circuit the algorithm: There will
+ // never be restrictions on token literals, or on certain fragments which do
+ // not have a set of follow-restrictions.
+
+ switch (last_match.get_macro_match_type ())
+ {
+ // This is our main stop condition: When we are finally looking at the
+ // last match (or its actual last component), and it is a fragment, it
+ // may contain some follow up restrictions.
+ case AST::MacroMatch::Fragment: {
+ auto fragment = static_cast<AST::MacroMatchFragment *> (&last_match);
+ if (fragment->get_frag_spec ().has_follow_set_restrictions ())
+ return peculiar_fragment_match_compatible (*fragment, match);
+ else
+ return true;
+ }
+ case AST::MacroMatch::Repetition: {
+ // A repetition on the left hand side means we want to make sure the
+ // last match of the repetition is compatible with the new match
+ auto repetition
+ = static_cast<AST::MacroMatchRepetition *> (&last_match);
+ new_last = get_back_ptr (repetition->get_matches ());
+ // If there are no matches in the matcher, then it can be followed by
+ // anything
+ if (!new_last)
+ return true;
+ break;
+ }
+ case AST::MacroMatch::Matcher: {
+ // Likewise for another matcher
+ auto matcher = static_cast<AST::MacroMatcher *> (&last_match);
+ new_last = get_back_ptr (matcher->get_matches ());
+ // If there are no matches in the matcher, then it can be followed by
+ // anything
+ if (!new_last)
+ return true;
+ break;
+ }
+ case AST::MacroMatch::Tok:
+ return true;
+ }
+
+ rust_assert (new_last);
+
+ // We check recursively until we find a terminating condition
+ // FIXME: Does expansion depth/limit matter here?
+ return is_match_compatible (*new_last, match);
+}
} // namespace Rust