diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2022-03-23 09:00:05 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-23 09:00:05 +0000 |
commit | b9720caa100efa6758a5f0d54d3764072d83be41 (patch) | |
tree | 5b1cf22e2ed1065889d29ef358bf55968beebf3c /gcc/rust/parse/rust-parse.cc | |
parent | 1d34c120c4754b921ac0520585cfef0e7a0bbb29 (diff) | |
parent | 35ca685200830626e5abd623f65a850649beace2 (diff) | |
download | gcc-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.cc | 141 |
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 |