diff options
Diffstat (limited to 'gcc/rust/parse')
-rw-r--r-- | gcc/rust/parse/rust-parse-impl.h | 13 | ||||
-rw-r--r-- | gcc/rust/parse/rust-parse.cc | 141 | ||||
-rw-r--r-- | gcc/rust/parse/rust-parse.h | 12 |
3 files changed, 164 insertions, 2 deletions
diff --git a/gcc/rust/parse/rust-parse-impl.h b/gcc/rust/parse/rust-parse-impl.h index 644e789..1d1b624 100644 --- a/gcc/rust/parse/rust-parse-impl.h +++ b/gcc/rust/parse/rust-parse-impl.h @@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see #include "rust-diagnostics.h" #include "util/rust-make-unique.h" +#include <algorithm> namespace Rust { // Left binding powers of operations. @@ -1767,6 +1768,13 @@ Parser<ManagedTokenSource>::parse_macro_matcher () return AST::MacroMatcher::create_error (t->get_locus ()); } + if (matches.size () > 0) + { + auto &last_match = matches.back (); + if (!is_match_compatible (*last_match, *match)) + return AST::MacroMatcher::create_error (match->get_match_locus ()); + } + matches.push_back (std::move (match)); // DEBUG @@ -1955,8 +1963,9 @@ Parser<ManagedTokenSource>::parse_macro_match_fragment () if (t == nullptr) return nullptr; - AST::MacroFragSpec frag = AST::get_frag_spec_from_str (t->get_str ()); - if (frag == AST::INVALID) + AST::MacroFragSpec frag + = AST::MacroFragSpec::get_frag_spec_from_str (t->get_str ()); + if (frag.is_error ()) { Error error (t->get_locus (), "invalid fragment specifier %qs in fragment macro match", 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 diff --git a/gcc/rust/parse/rust-parse.h b/gcc/rust/parse/rust-parse.h index 9a31fb6..c86d194 100644 --- a/gcc/rust/parse/rust-parse.h +++ b/gcc/rust/parse/rust-parse.h @@ -702,6 +702,18 @@ private: std::string extract_module_path (const AST::AttrVec &inner_attrs, const AST::AttrVec &outer_attrs, const std::string &name); + +/** + * Check if a MacroMatch is allowed to follow the last parsed MacroMatch. + * + * @param last_match Last matcher parsed before the current match + * @param match Current matcher to check + * + * @return true if the follow-up is valid, false otherwise + */ +bool +is_match_compatible (AST::MacroMatch &last_match, + AST::MacroMatch ¤t_match); } // namespace Rust // as now template, include implementations of all methods |