]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
gccrs: Collect feature gate error at parse time
authorPierre-Emmanuel Patry <pierre-emmanuel.patry@embecosm.com>
Fri, 6 Feb 2026 13:21:29 +0000 (14:21 +0100)
committerArthur Cohen <arthur.cohen@embecosm.com>
Fri, 20 Mar 2026 17:10:37 +0000 (18:10 +0100)
Some nightly features change the parser's behavior, it may accepts syntax
that should be rejected when the feature is missing. But the complete
list of enabled features can only be found once the parsing is complete.
We should therefore not emit any error at parse time and instead collect
a potential error and emit it later during the feature gating step.

gcc/rust/ChangeLog:

* checks/errors/feature/rust-feature-gate.cc (FeatureGate::check):
Check all parse time errors.
* checks/errors/feature/rust-feature-gate.h: Update function prototype
with parse time errors.
* parse/rust-parse-impl-attribute.hxx: Collect potential gating error
with non literal attribute values. Remove error emission.
* parse/rust-parse.h: Add a function to gather potential feature gating
errors as well as a getter for collected errors.
* rust-session-manager.cc (Session::compile_crate): Retrieve potential
feature gating errors and check them later during the feature gating
step.
* util/rust-attributes.cc (check_export_name_attribute): Change
attribute checking error emission to prevent errors with macro inputs.

gcc/testsuite/ChangeLog:

* rust/compile/doc_macro.rs: Enable feature to use a macro within an
attribute input.
* rust/compile/parse_time_feature_gate.rs: New test.

Signed-off-by: Pierre-Emmanuel Patry <pierre-emmanuel.patry@embecosm.com>
gcc/rust/checks/errors/feature/rust-feature-gate.cc
gcc/rust/checks/errors/feature/rust-feature-gate.h
gcc/rust/parse/rust-parse-impl-attribute.hxx
gcc/rust/parse/rust-parse.h
gcc/rust/rust-session-manager.cc
gcc/rust/util/rust-attributes.cc
gcc/testsuite/rust/compile/doc_macro.rs
gcc/testsuite/rust/compile/parse_time_feature_gate.rs [new file with mode: 0644]

index 515b753a43bd7ddb0af4b4ffb3d5a453356a718f..2c0ee08e4dd0983a7beda4eef6663b38b3b1c064 100644 (file)
 namespace Rust {
 
 void
-FeatureGate::check (AST::Crate &crate)
+FeatureGate::check (
+  AST::Crate &crate,
+  std::vector<std::pair<Feature::Name, Error>> &parsing_feature_gate_errors)
 {
+  for (auto &pair : parsing_feature_gate_errors)
+    gate (pair.first, pair.second.locus, pair.second.message);
   visit (crate);
 }
 
index 120808339f0ee2b3b64e8a7e3e897dc11448a97e..33cb2758ae89371e114dc82bdfd159deb4c39936 100644 (file)
@@ -32,7 +32,9 @@ public:
 
   using AST::DefaultASTVisitor::visit;
 
-  void check (AST::Crate &crate);
+  void check (
+    AST::Crate &crate,
+    std::vector<std::pair<Feature::Name, Error>> &parsing_feature_gate_errors);
   void visit (AST::Crate &crate) override;
 
   void visit (AST::LifetimeParam &lifetime_param) override;
index 85fab52c99772c182eb518ebec5fa8966c7b2970..5c7bd99bf3eac734ee9e933cbb02e7fe432eb469 100644 (file)
@@ -293,6 +293,16 @@ Parser<ManagedTokenSource>::parse_attr_input ()
 
        t = lexer.peek_token ();
 
+       /* Ensure token is a "literal expression" (literally only a literal
+        * token of any type) */
+       if (!t->is_literal ())
+         {
+           Error error (
+             t->get_locus (),
+             "arbitrary expressions in key-value attributes are unstable");
+           collect_potential_gating_error (
+             Feature::Name::EXTENDED_KEY_VALUE_ATTRIBUTES, std::move (error));
+         }
        // attempt to parse macro
        // TODO: macros may/may not be allowed in attributes
        // this is needed for "#[doc = include_str!(...)]"
@@ -308,20 +318,6 @@ Parser<ManagedTokenSource>::parse_attr_input ()
              new AST::AttrInputMacro (std::move (invoke)));
          }
 
-       /* Ensure token is a "literal expression" (literally only a literal
-        * token of any type) */
-       if (!t->is_literal ())
-         {
-           Error error (
-             t->get_locus (),
-             "unknown token %qs in attribute body - literal expected",
-             t->get_token_description ());
-           add_error (std::move (error));
-
-           skip_after_end_attribute ();
-           return Parse::Error::AttrInput::make_malformed ();
-         }
-
        AST::Literal::LitType lit_type = AST::Literal::STRING;
        // Crappy mapping of token type to literal type
        switch (t->get_id ())
@@ -345,9 +341,14 @@ Parser<ManagedTokenSource>::parse_attr_input ()
            lit_type = AST::Literal::RAW_STRING;
            break;
          case STRING_LITERAL:
-         default:
            lit_type = AST::Literal::STRING;
            break; // TODO: raw string? don't eliminate it from lexer?
+         default:
+           rust_sorry_at (t->get_locus (),
+                          "Unsupported attribute input, only literals and "
+                          "macros are supported for now");
+           skip_after_end_attribute ();
+           return Parse::Error::AttrInput::make_malformed ();
          }
 
        // create actual LiteralExpr
index 7d632391c7a3f652807683a82fc17d920f5b371a..07d08333b7f1bce6f317266fb6344b3c0877e1e8 100644 (file)
@@ -24,6 +24,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "rust-diagnostics.h"
 #include "rust-parse-error.h"
 #include "rust-parse-utils.h"
+#include "rust-feature.h"
 
 #include "expected.h"
 
@@ -852,6 +853,17 @@ private:
 
   void add_error (Error error) { error_table.push_back (std::move (error)); }
 
+  // We don't know the crate's valid feature set since we may not have parsed
+  // all feature declaration attributes yet, some features are not available and
+  // we can't decide at parse time whether we should reject the syntax.
+  //
+  // To fix this we collect the feature gating errors now and will emit the
+  // errors later.
+  void collect_potential_gating_error (Feature::Name feature, Error error)
+  {
+    gating_errors.emplace_back (feature, error);
+  }
+
 public:
   // Construct parser with specified "managed" token source.
   Parser (ManagedTokenSource &tokenSource) : lexer (tokenSource) {}
@@ -874,6 +886,12 @@ public:
   // Get a reference to the list of errors encountered
   std::vector<Error> &get_errors () { return error_table; }
 
+  std::vector<std::pair<Feature::Name, Error>> &
+  get_potential_feature_gate_errors ()
+  {
+    return gating_errors;
+  }
+
   const ManagedTokenSource &get_token_source () const { return lexer; }
 
   const_TokenPtr peek_current_token () { return lexer.peek_token (0); }
@@ -884,6 +902,8 @@ private:
   ManagedTokenSource &lexer;
   // The error list.
   std::vector<Error> error_table;
+
+  std::vector<std::pair<Feature::Name, Error>> gating_errors;
   // The names of inline modules while parsing.
   std::vector<std::string> inline_module_stack;
 
index cf3237ccd71162016d66d8fd6748d95d92c9e159..a87f00780477297e662797b36b0169b6549d081a 100644 (file)
@@ -601,6 +601,7 @@ Session::compile_crate (const char *filename)
 
   // generate crate from parser
   std::unique_ptr<AST::Crate> ast_crate = parser.parse_crate ();
+  auto &feature_gate_errors = parser.get_potential_feature_gate_errors ();
 
   // handle crate name
   handle_crate_name (filename, *ast_crate.get ());
@@ -712,7 +713,7 @@ Session::compile_crate (const char *filename)
   if (last_step == CompileOptions::CompileStep::FeatureGating)
     return;
 
-  FeatureGate (parsed_crate_features).check (parsed_crate);
+  FeatureGate (parsed_crate_features).check (parsed_crate, feature_gate_errors);
 
   if (last_step == CompileOptions::CompileStep::NameResolution)
     return;
index af6f5fdac081cff64a841e59c2913d3ac0692d78..b3d60791432be7f71a67fdda37fc6af1d0e90167 100644 (file)
@@ -498,11 +498,38 @@ check_export_name_attribute (const AST::Attribute &attribute)
       return;
     }
 
-  if (!Attributes::extract_string_literal (attribute))
+  // We don't support the whole extended_key_value_attributes feature, we only
+  // support a subset for macros. We need to emit an error message when the
+  // attribute does not contain a macro or a string literal.
+  if (attribute.has_attr_input ())
     {
-      rust_error_at (attribute.get_locus (),
-                    "attribute must be a string literal");
+      auto &attr_input = attribute.get_attr_input ();
+      switch (attr_input.get_attr_input_type ())
+       {
+       case AST::AttrInput::AttrInputType::LITERAL:
+         {
+           auto &literal_expr
+             = static_cast<AST::AttrInputLiteral &> (attr_input)
+                 .get_literal ();
+           auto lit_type = literal_expr.get_lit_type ();
+           switch (lit_type)
+             {
+             case AST::Literal::LitType::STRING:
+             case AST::Literal::LitType::RAW_STRING:
+             case AST::Literal::LitType::BYTE_STRING:
+               return;
+             default:
+               break;
+             }
+           break;
+         }
+       case AST::AttrInput::AttrInputType::MACRO:
+         return;
+       default:
+         break;
+       }
     }
+  rust_error_at (attribute.get_locus (), "attribute must be a string literal");
 }
 
 static void
index f71bd7ca04c94465ee0590003139a1ced46eb5ec..179455ddf951e89052b304c1418561e043f30dc3 100644 (file)
@@ -1,4 +1,4 @@
 #![feature(no_core)]
 #![no_core]
-
+#![feature(extended_key_value_attributes)]
 #![doc = concat!("AB")]
diff --git a/gcc/testsuite/rust/compile/parse_time_feature_gate.rs b/gcc/testsuite/rust/compile/parse_time_feature_gate.rs
new file mode 100644 (file)
index 0000000..238b2cf
--- /dev/null
@@ -0,0 +1,6 @@
+#![feature(no_core)]
+#![no_core]
+
+// { dg-error "arbitrary expressions in key-value attributes are unstable" "" { target *-*-* } .+1 }
+#[export_name = concat!(stringify!(non), stringify!(literal))]
+pub extern "C" fn attribute_test_function() {}