aboutsummaryrefslogtreecommitdiff
path: root/gcc/rust/parse/rust-parse.cc
diff options
context:
space:
mode:
authorArthur Cohen <arthur.cohen@embecosm.com>2022-03-18 16:20:47 +0100
committerArthur Cohen <arthur.cohen@embecosm.com>2022-03-23 09:56:23 +0100
commit35ca685200830626e5abd623f65a850649beace2 (patch)
treeaed3eeb63995c6cced4bdeed0f327ceaf3d618e8 /gcc/rust/parse/rust-parse.cc
parentcc6e405912c83aee41efd3015d9157cdbe9134fe (diff)
downloadgcc-35ca685200830626e5abd623f65a850649beace2.zip
gcc-35ca685200830626e5abd623f65a850649beace2.tar.gz
gcc-35ca685200830626e5abd623f65a850649beace2.tar.bz2
macros: Add base functions to check for follow-set ambiguities
Rust does not allow for all macro fragments to be followed by any kind of tokens: We must check tokens following those fragments that might contain restrictions and make sure that they are allowed, conforming to the Macro Follow-Set Ambiguity specification Co-authored-by: philberty <philip.herron@embecosm.com> macro-frag-spec: Transform enum into a class This allows us to add methods on the fragment specifier, which are needed to make sure that follow-set ambiguities are respected tests: Add tests for forbidden follow-up tokens This also fix a test that was previously accepted but invalid: rustc also rejected it
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