gccrs: Collect feature gate error at parse time

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>
This commit is contained in:
Pierre-Emmanuel Patry
2026-02-06 14:21:29 +01:00
committed by Arthur Cohen
parent dcd8b112c8
commit aff1c8aade
8 changed files with 83 additions and 22 deletions

View File

@@ -27,8 +27,12 @@
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);
}

View 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;

View 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

View 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;

View 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;

View 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

View File

@@ -1,4 +1,4 @@
#![feature(no_core)]
#![no_core]
#![feature(extended_key_value_attributes)]
#![doc = concat!("AB")]

View File

@@ -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() {}