Skip to content

Port #[no_mangle] to new attribute parsing infrastructure #142823

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions compiler/rustc_attr_data_structures/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,9 @@ pub enum AttributeKind {
reason: Option<Symbol>,
},

/// Represents `#[no_mangle]`
NoMangle(Span),

/// Represents `#[optimize(size|speed)]`
Optimize(OptimizeAttr, Span),

Expand Down
18 changes: 18 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,21 @@ impl<S: Stage> SingleAttributeParser<S> for ColdParser {
Some(AttributeKind::Cold(cx.attr_span))
}
}

pub(crate) struct NoMangleParser;

impl<S: Stage> SingleAttributeParser<S> for NoMangleParser {
const PATH: &[rustc_span::Symbol] = &[sym::no_mangle];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const TEMPLATE: AttributeTemplate = template!(Word);

fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
if !args.no_args() {
cx.expected_no_args(args.span().unwrap_or(cx.attr_span));
return None;
};

Some(AttributeKind::NoMangle(cx.attr_span))
}
}
3 changes: 2 additions & 1 deletion compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use rustc_session::Session;
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};

use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
use crate::attributes::codegen_attrs::{ColdParser, OptimizeParser};
use crate::attributes::codegen_attrs::{ColdParser, NoMangleParser, OptimizeParser};
use crate::attributes::confusables::ConfusablesParser;
use crate::attributes::deprecation::DeprecationParser;
use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
Expand Down Expand Up @@ -114,6 +114,7 @@ attribute_parsers!(
Single<InlineParser>,
Single<MayDangleParser>,
Single<MustUseParser>,
Single<NoMangleParser>,
Single<OptimizeParser>,
Single<PubTransparentParser>,
Single<RustcForceInlineParser>,
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_codegen_ssa/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,8 @@ codegen_ssa_multiple_main_functions = entry symbol `main` declared multiple time

codegen_ssa_no_field = no field `{$name}`

codegen_ssa_no_mangle_nameless = `#[no_mangle]` cannot be used on {$definition} as it has no name

codegen_ssa_no_module_named =
no module named `{$user_path}` (mangled: {$cgu_name}). available modules: {$cgu_names}

Expand Down
48 changes: 24 additions & 24 deletions compiler/rustc_codegen_ssa/src/codegen_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use rustc_span::{Ident, Span, sym};
use rustc_target::spec::SanitizerSet;

use crate::errors;
use crate::errors::NoMangleNameless;
use crate::target_features::{
check_target_feature_trait_unsafe, check_tied_features, from_target_feature_attr,
};
Expand Down Expand Up @@ -87,7 +88,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
let mut link_ordinal_span = None;
let mut no_sanitize_span = None;
let mut mixed_export_name_no_mangle_lint_state = MixedExportNameAndNoMangleState::default();
let mut no_mangle_span = None;

for attr in attrs.iter() {
// In some cases, attribute are only valid on functions, but it's the `check_attr`
Expand Down Expand Up @@ -122,6 +122,25 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
}
AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD,
AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align),
AttributeKind::NoMangle(attr_span) => {
if tcx.opt_item_name(did.to_def_id()).is_some() {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
mixed_export_name_no_mangle_lint_state.track_no_mangle(
*attr_span,
tcx.local_def_id_to_hir_id(did),
attr,
);
} else {
tcx.dcx().emit_err(NoMangleNameless {
span: *attr_span,
definition: format!(
"{} {}",
tcx.def_descr_article(did.to_def_id()),
tcx.def_descr(did.to_def_id())
),
});
}
}
_ => {}
}
}
Expand All @@ -141,28 +160,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED
}
sym::naked => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
sym::no_mangle => {
no_mangle_span = Some(attr.span());
if tcx.opt_item_name(did.to_def_id()).is_some() {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
mixed_export_name_no_mangle_lint_state.track_no_mangle(
attr.span(),
tcx.local_def_id_to_hir_id(did),
attr,
);
} else {
tcx.dcx()
.struct_span_err(
attr.span(),
format!(
"`#[no_mangle]` cannot be used on {} {} as it has no name",
tcx.def_descr_article(did.to_def_id()),
tcx.def_descr(did.to_def_id()),
),
)
.emit();
}
}
sym::rustc_std_internal_symbol => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL
}
Expand Down Expand Up @@ -544,12 +541,15 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
&& codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
{
let no_mangle_span =
find_attr!(attrs, AttributeKind::NoMangle(no_mangle_span) => *no_mangle_span)
.unwrap_or_default();
let lang_item =
lang_items::extract(attrs).map_or(None, |(name, _span)| LangItem::from_name(name));
let mut err = tcx
.dcx()
.struct_span_err(
no_mangle_span.unwrap_or_default(),
no_mangle_span,
"`#[no_mangle]` cannot be used on internal language items",
)
.with_note("Rustc requires this item to have a specific mangled name.")
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_codegen_ssa/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1310,3 +1310,11 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for TargetFeatureDisableOrEnable<'_
diag
}
}

#[derive(Diagnostic)]
#[diag(codegen_ssa_no_mangle_nameless)]
pub(crate) struct NoMangleNameless {
#[primary_span]
pub span: Span,
pub definition: String,
}
21 changes: 12 additions & 9 deletions compiler/rustc_lint/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree};
use rustc_ast::visit::{FnCtxt, FnKind};
use rustc_ast::{self as ast, *};
use rustc_ast_pretty::pprust::expr_to_string;
use rustc_attr_data_structures::{AttributeKind, find_attr};
use rustc_errors::{Applicability, LintDiagnostic};
use rustc_feature::GateIssue;
use rustc_hir as hir;
Expand Down Expand Up @@ -954,7 +955,7 @@ declare_lint_pass!(InvalidNoMangleItems => [NO_MANGLE_CONST_ITEMS, NO_MANGLE_GEN
impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
let attrs = cx.tcx.hir_attrs(it.hir_id());
let check_no_mangle_on_generic_fn = |attr: &hir::Attribute,
let check_no_mangle_on_generic_fn = |attr_span: Span,
impl_generics: Option<&hir::Generics<'_>>,
generics: &hir::Generics<'_>,
span| {
Expand All @@ -967,7 +968,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
cx.emit_span_lint(
NO_MANGLE_GENERIC_ITEMS,
span,
BuiltinNoMangleGeneric { suggestion: attr.span() },
BuiltinNoMangleGeneric { suggestion: attr_span },
);
break;
}
Expand All @@ -976,14 +977,15 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
};
match it.kind {
hir::ItemKind::Fn { generics, .. } => {
if let Some(attr) = attr::find_by_name(attrs, sym::export_name)
.or_else(|| attr::find_by_name(attrs, sym::no_mangle))
if let Some(attr_span) = attr::find_by_name(attrs, sym::export_name)
.map(|at| at.span())
.or_else(|| find_attr!(attrs, AttributeKind::NoMangle(span) => *span))
{
check_no_mangle_on_generic_fn(attr, None, generics, it.span);
check_no_mangle_on_generic_fn(attr_span, None, generics, it.span);
}
}
hir::ItemKind::Const(..) => {
if attr::contains_name(attrs, sym::no_mangle) {
if find_attr!(attrs, AttributeKind::NoMangle(..)) {
// account for "pub const" (#45562)
let start = cx
.tcx
Expand All @@ -1008,11 +1010,12 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
for it in *items {
if let hir::AssocItemKind::Fn { .. } = it.kind {
let attrs = cx.tcx.hir_attrs(it.id.hir_id());
if let Some(attr) = attr::find_by_name(attrs, sym::export_name)
.or_else(|| attr::find_by_name(attrs, sym::no_mangle))
if let Some(attr_span) = attr::find_by_name(attrs, sym::export_name)
.map(|at| at.span())
.or_else(|| find_attr!(attrs, AttributeKind::NoMangle(span) => *span))
{
check_no_mangle_on_generic_fn(
attr,
attr_span,
Some(generics),
cx.tcx.hir_get_generics(it.id.owner_id.def_id).unwrap(),
it.span,
Expand Down
12 changes: 8 additions & 4 deletions compiler/rustc_lint/src/nonstandard_style.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use rustc_abi::ExternAbi;
use rustc_attr_data_structures::{AttributeKind, ReprAttr};
use rustc_attr_data_structures::{AttributeKind, ReprAttr, find_attr};
use rustc_attr_parsing::AttributeParser;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::FnKind;
Expand Down Expand Up @@ -396,7 +396,9 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase {
match &fk {
FnKind::Method(ident, sig, ..) => match method_context(cx, id) {
MethodLateContext::PlainImpl => {
if sig.header.abi != ExternAbi::Rust && cx.tcx.has_attr(id, sym::no_mangle) {
if sig.header.abi != ExternAbi::Rust
&& find_attr!(cx.tcx.get_all_attrs(id), AttributeKind::NoMangle(..))
{
return;
}
self.check_snake_case(cx, "method", ident);
Expand All @@ -408,7 +410,9 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase {
},
FnKind::ItemFn(ident, _, header) => {
// Skip foreign-ABI #[no_mangle] functions (Issue #31924)
if header.abi != ExternAbi::Rust && cx.tcx.has_attr(id, sym::no_mangle) {
if header.abi != ExternAbi::Rust
&& find_attr!(cx.tcx.get_all_attrs(id), AttributeKind::NoMangle(..))
{
return;
}
self.check_snake_case(cx, "function", ident);
Expand Down Expand Up @@ -514,7 +518,7 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
let attrs = cx.tcx.hir_attrs(it.hir_id());
match it.kind {
hir::ItemKind::Static(_, ident, ..)
if !ast::attr::contains_name(attrs, sym::no_mangle) =>
if !find_attr!(attrs, AttributeKind::NoMangle(..)) =>
{
NonUpperCaseGlobals::check_upper_case(cx, "static variable", &ident);
}
Expand Down
15 changes: 9 additions & 6 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
Attribute::Parsed(AttributeKind::MustUse { span, .. }) => {
self.check_must_use(hir_id, *span, target)
}
Attribute::Parsed(AttributeKind::NoMangle(attr_span)) => {
self.check_no_mangle(hir_id, *attr_span, span, target)
}
Attribute::Unparsed(attr_item) => {
style = Some(attr_item.style);
match attr.path().as_slice() {
Expand Down Expand Up @@ -261,7 +264,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
[sym::link, ..] => self.check_link(hir_id, attr, span, target),
[sym::link_name, ..] => self.check_link_name(hir_id, attr, span, target),
[sym::link_section, ..] => self.check_link_section(hir_id, attr, span, target),
[sym::no_mangle, ..] => self.check_no_mangle(hir_id, attr, span, target),
[sym::macro_use, ..] | [sym::macro_escape, ..] => {
self.check_macro_use(hir_id, attr, target)
}
Expand Down Expand Up @@ -698,6 +700,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
AttributeKind::Deprecation { .. }
| AttributeKind::Repr { .. }
| AttributeKind::Align { .. }
| AttributeKind::NoMangle(..)
| AttributeKind::Cold(..)
| AttributeKind::MustUse { .. },
) => {
Expand Down Expand Up @@ -1952,7 +1955,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}

/// Checks if `#[no_mangle]` is applied to a function or static.
fn check_no_mangle(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
fn check_no_mangle(&self, hir_id: HirId, attr_span: Span, span: Span, target: Target) {
match target {
Target::Static | Target::Fn => {}
Target::Method(..) if self.is_impl_item(hir_id) => {}
Expand All @@ -1961,7 +1964,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
// erroneously allowed it and some crates used it accidentally, to be compatible
// with crates depending on them, we can't throw an error here.
Target::Field | Target::Arm | Target::MacroDef => {
self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "no_mangle");
self.inline_attr_str_error_with_macro_def(hir_id, attr_span, "no_mangle");
}
// FIXME: #[no_mangle] was previously allowed on non-functions/statics, this should be an error
// The error should specify that the item that is wrong is specifically a *foreign* fn/static
Expand All @@ -1975,8 +1978,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
self.tcx.emit_node_span_lint(
UNUSED_ATTRIBUTES,
hir_id,
attr.span(),
errors::NoMangleForeign { span, attr_span: attr.span(), foreign_item_kind },
attr_span,
errors::NoMangleForeign { span, attr_span, foreign_item_kind },
);
}
_ => {
Expand All @@ -1985,7 +1988,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
self.tcx.emit_node_span_lint(
UNUSED_ATTRIBUTES,
hir_id,
attr.span(),
attr_span,
errors::NoMangle { span },
);
}
Expand Down
22 changes: 10 additions & 12 deletions src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -746,15 +746,17 @@ impl Item {
Some(tcx.visibility(def_id))
}

pub(crate) fn attributes_without_repr(&self, tcx: TyCtxt<'_>, is_json: bool) -> Vec<String> {
fn attributes_without_repr(&self, tcx: TyCtxt<'_>, is_json: bool) -> Vec<String> {
const ALLOWED_ATTRIBUTES: &[Symbol] =
&[sym::export_name, sym::link_section, sym::no_mangle, sym::non_exhaustive];

self.attrs
.other_attrs
.iter()
.filter_map(|attr| {
if is_json {
// NoMangle is special-cased because cargo-semver-checks uses it
if matches!(attr, hir::Attribute::Parsed(AttributeKind::NoMangle(..))) {
Some("#[no_mangle]".to_string())
} else if is_json {
match attr {
// rustdoc-json stores this in `Item::deprecation`, so we
// don't want it it `Item::attrs`.
Expand All @@ -767,26 +769,22 @@ impl Item {
s
}),
}
} else if attr.has_any_name(ALLOWED_ATTRIBUTES) {
} else {
if !attr.has_any_name(ALLOWED_ATTRIBUTES) {
return None;
}
Some(
rustc_hir_pretty::attribute_to_string(&tcx, attr)
.replace("\\\n", "")
.replace('\n', "")
.replace(" ", " "),
)
} else {
None
}
})
.collect()
}

pub(crate) fn attributes_and_repr(
&self,
tcx: TyCtxt<'_>,
cache: &Cache,
is_json: bool,
) -> Vec<String> {
pub(crate) fn attributes(&self, tcx: TyCtxt<'_>, cache: &Cache, is_json: bool) -> Vec<String> {
let mut attrs = self.attributes_without_repr(tcx, is_json);

if let Some(repr_attr) = self.repr(tcx, cache, is_json) {
Expand Down
4 changes: 2 additions & 2 deletions src/librustdoc/html/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1194,7 +1194,7 @@ fn render_assoc_item(
// a whitespace prefix and newline.
fn render_attributes_in_pre(it: &clean::Item, prefix: &str, cx: &Context<'_>) -> impl fmt::Display {
fmt::from_fn(move |f| {
for a in it.attributes_and_repr(cx.tcx(), cx.cache(), false) {
for a in it.attributes(cx.tcx(), cx.cache(), false) {
writeln!(f, "{prefix}{a}")?;
}
Ok(())
Expand All @@ -1210,7 +1210,7 @@ fn render_code_attribute(code_attr: CodeAttribute, w: &mut impl fmt::Write) {
// When an attribute is rendered inside a <code> tag, it is formatted using
// a div to produce a newline after it.
fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, cx: &Context<'_>) {
for attr in it.attributes_and_repr(cx.tcx(), cx.cache(), false) {
for attr in it.attributes(cx.tcx(), cx.cache(), false) {
render_code_attribute(CodeAttribute(attr), w);
}
}
Expand Down
Loading
Loading