]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
gccrs: Avoid invalid metavariable substitutions
authorOwen Avery <powerboat9.gamer@gmail.com>
Sun, 28 May 2023 17:33:52 +0000 (13:33 -0400)
committerArthur Cohen <arthur.cohen@embecosm.com>
Tue, 16 Jan 2024 17:37:22 +0000 (18:37 +0100)
gcc/rust/ChangeLog:

* expand/rust-macro-substitute-ctx.cc
(SubstituteCtx::substitute_metavar): Allow substitution failure.
(SubstituteCtx::substitute_token):
Handle substitution failure, include dollar sign in count of tokens to substitute.
(SubstituteCtx::substitute_tokens):
Include dollar sign in count of tokens to substitute.
* expand/rust-macro-substitute-ctx.h
(SubstituteCtx::substitute_metavar):
Adjust signature and document new substitution failure handling.

gcc/testsuite/ChangeLog:

* rust/compile/issue-2225.rs: New test.

Signed-off-by: Owen Avery <powerboat9.gamer@gmail.com>
gcc/rust/expand/rust-macro-substitute-ctx.cc
gcc/rust/expand/rust-macro-substitute-ctx.h
gcc/testsuite/rust/compile/issue-2225.rs [new file with mode: 0644]

index 85c9d7e01764df22ffeb66751f6b0a48fc6fe025..84df2c2623e699615cd038be65c9d047994ab424 100644 (file)
@@ -2,17 +2,18 @@
 
 namespace Rust {
 
-std::vector<std::unique_ptr<AST::Token>>
-SubstituteCtx::substitute_metavar (std::unique_ptr<AST::Token> &metavar)
+bool
+SubstituteCtx::substitute_metavar (
+  std::unique_ptr<AST::Token> &metavar,
+  std::vector<std::unique_ptr<AST::Token>> &expanded)
 {
   auto metavar_name = metavar->get_str ();
 
-  std::vector<std::unique_ptr<AST::Token>> expanded;
   auto it = fragments.find (metavar_name);
   if (it == fragments.end ())
     {
-      // Return a copy of the original token
-      expanded.push_back (metavar->clone_token ());
+      // fail to substitute
+      return false;
     }
   else
     {
@@ -28,7 +29,7 @@ SubstituteCtx::substitute_metavar (std::unique_ptr<AST::Token> &metavar)
            metavar->get_locus (),
            "you probably forgot the repetition operator: %<%s%s%s%>", "$(",
            metavar->as_string ().c_str (), ")*");
-         return expanded;
+         return true;
        }
 
       // We only care about the vector when expanding repetitions.
@@ -42,7 +43,7 @@ SubstituteCtx::substitute_metavar (std::unique_ptr<AST::Token> &metavar)
        }
     }
 
-  return expanded;
+  return true;
 }
 
 bool
@@ -209,14 +210,17 @@ SubstituteCtx::substitute_token (size_t token_idx)
       if (token_id_is_keyword (token->get_id ()))
        {
        case IDENTIFIER:
+         std::vector<std::unique_ptr<AST::Token>> expanded;
+
          rust_debug ("expanding metavar: %s", token->get_str ().c_str ());
-         return {substitute_metavar (token), 1};
+
+         if (substitute_metavar (token, expanded))
+           return {std::move (expanded), 2};
        }
-      rust_error_at (token->get_locus (),
-                    "unexpected token in macro transcribe: expected "
-                    "%<(%> or identifier after %<$%>, got %<%s%>",
-                    get_token_description (token->get_id ()));
-      break;
+
+      // don't substitute, dollar sign is alone/metavar is unknown
+      return {std::vector<std::unique_ptr<AST::Token>> (), 0};
+
       case LEFT_PAREN: {
        // We need to parse up until the closing delimiter and expand this
        // fragment->n times.
@@ -285,17 +289,11 @@ SubstituteCtx::substitute_token (size_t token_idx)
 
        return {substitute_repetition (pattern_start, pattern_end,
                                       std::move (separator_token)),
-               pattern_end - pattern_start + to_skip};
+               pattern_end - pattern_start + to_skip + 1};
       }
-      // TODO: We need to check if the $ was alone. In that case, do
-      // not error out: Simply act as if there was an empty identifier
-      // with no associated fragment and paste the dollar sign in the
-      // transcription. Unsure how to do that since we always have at
-      // least the closing curly brace after an empty $...
     }
 
-  // FIXME: gcc_unreachable() error case?
-  return {std::vector<std::unique_ptr<AST::Token>> (), 0};
+  gcc_unreachable ();
 }
 
 std::vector<std::unique_ptr<AST::Token>>
@@ -304,7 +302,7 @@ SubstituteCtx::substitute_tokens ()
   std::vector<std::unique_ptr<AST::Token>> replaced_tokens;
   rust_debug ("expanding tokens");
 
-  for (size_t i = 0; i < macro.size (); i++)
+  for (size_t i = 0; i < macro.size ();)
     {
       auto &tok = macro.at (i);
       if (tok->get_id () == DOLLAR_SIGN)
@@ -315,6 +313,12 @@ SubstituteCtx::substitute_tokens ()
          auto expanded = std::move (p.first);
          auto tok_to_skip = p.second;
 
+         if (!tok_to_skip)
+           {
+             replaced_tokens.emplace_back (tok->clone_token ());
+             tok_to_skip++;
+           }
+
          i += tok_to_skip;
 
          for (auto &token : expanded)
@@ -323,6 +327,7 @@ SubstituteCtx::substitute_tokens ()
       else
        {
          replaced_tokens.emplace_back (tok->clone_token ());
+         i++;
        }
     }
 
index 86dddb2a7fd00391476fe56804348b7fa46a0dc0..dd49bb1ddca1cf1458e80ca28e6f675c38f36ac7 100644 (file)
@@ -49,12 +49,13 @@ public:
    * i.e. replacing $var with the associated fragment.
    *
    * @param metavar Metavariable to try and replace
+   * @param expanded Reference to a vector upon which expanded tokens will be
+   * pushed
    *
-   * @return A token containing the associated fragment expanded into tokens if
-   * any, or the cloned token if no fragment was associated
+   * @return True iff the substitution succeeded
    */
-  std::vector<std::unique_ptr<AST::Token>>
-  substitute_metavar (std::unique_ptr<AST::Token> &metavar);
+  bool substitute_metavar (std::unique_ptr<AST::Token> &metavar,
+                          std::vector<std::unique_ptr<AST::Token>> &expanded);
 
   /**
    * Substitute a macro repetition by its given fragments
diff --git a/gcc/testsuite/rust/compile/issue-2225.rs b/gcc/testsuite/rust/compile/issue-2225.rs
new file mode 100644 (file)
index 0000000..53757c1
--- /dev/null
@@ -0,0 +1,14 @@
+macro_rules! foo {
+    ($_:tt) => {};
+    () => {foo!($);};
+}
+
+macro_rules! bar {
+    () => {let $_a = 12;} // { dg-error "unrecognised token" }
+}
+
+pub fn main() -> i32 {
+    foo!();
+    bar!();
+    0
+}