Skip to content

Port #[link_section] to the new attribute parsing infrastructure #143196

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 30, 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 @@ -256,6 +256,9 @@ pub enum AttributeKind {
/// Represents `#[link_name]`.
LinkName { name: Symbol, span: Span },

/// Represents [`#[link_section]`](https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute)
LinkSection { name: Symbol, span: Span },

/// Represents `#[loop_match]`.
LoopMatch(Span),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ impl AttributeKind {
DocComment { .. } => Yes,
ExportName { .. } => Yes,
Inline(..) => No,
LinkSection { .. } => No,
MacroTransparency(..) => Yes,
Repr(..) => No,
Stability { .. } => Yes,
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_attr_parsing/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ attr_parsing_non_ident_feature =

attr_parsing_null_on_export = `export_name` may not contain null characters

attr_parsing_null_on_link_section = `link_section` may not contain null characters

attr_parsing_repr_ident =
meta item in `repr` must be an identifier

Expand Down
31 changes: 30 additions & 1 deletion compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use rustc_attr_data_structures::AttributeKind;
use rustc_attr_data_structures::AttributeKind::LinkName;
use rustc_attr_data_structures::AttributeKind::{LinkName, LinkSection};
use rustc_feature::{AttributeTemplate, template};
use rustc_span::{Symbol, sym};

use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
use crate::context::{AcceptContext, Stage};
use crate::parser::ArgParser;
use crate::session_diagnostics::NullOnLinkSection;

pub(crate) struct LinkNameParser;

Expand All @@ -28,3 +29,31 @@ impl<S: Stage> SingleAttributeParser<S> for LinkNameParser {
Some(LinkName { name, span: cx.attr_span })
}
}

pub(crate) struct LinkSectionParser;

impl<S: Stage> SingleAttributeParser<S> for LinkSectionParser {
const PATH: &[Symbol] = &[sym::link_section];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");

fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let Some(nv) = args.name_value() else {
cx.expected_name_value(cx.attr_span, None);
return None;
};
let Some(name) = nv.value_as_str() else {
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
return None;
};
if name.as_str().contains('\0') {
// `#[link_section = ...]` will be converted to a null-terminated string,
// so it may not contain any null characters.
cx.emit_err(NullOnLinkSection { span: cx.attr_span });
return None;
}

Some(LinkSection { name, span: 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 @@ -22,7 +22,7 @@ use crate::attributes::codegen_attrs::{
use crate::attributes::confusables::ConfusablesParser;
use crate::attributes::deprecation::DeprecationParser;
use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
use crate::attributes::link_attrs::LinkNameParser;
use crate::attributes::link_attrs::{LinkNameParser, LinkSectionParser};
use crate::attributes::lint_helpers::{AsPtrParser, PubTransparentParser};
use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser};
use crate::attributes::must_use::MustUseParser;
Expand Down Expand Up @@ -123,6 +123,7 @@ attribute_parsers!(
Single<ExportNameParser>,
Single<InlineParser>,
Single<LinkNameParser>,
Single<LinkSectionParser>,
Single<LoopMatchParser>,
Single<MayDangleParser>,
Single<MustUseParser>,
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_attr_parsing/src/session_diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,13 @@ pub(crate) struct NullOnExport {
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(attr_parsing_null_on_link_section, code = E0648)]
pub(crate) struct NullOnLinkSection {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(attr_parsing_stability_outside_std, code = E0734)]
pub(crate) struct StabilityOutsideStd {
Expand Down
13 changes: 3 additions & 10 deletions compiler/rustc_codegen_ssa/src/codegen_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align),
AttributeKind::LinkName { name, .. } => codegen_fn_attrs.link_name = Some(*name),
AttributeKind::LinkSection { name, .. } => {
codegen_fn_attrs.link_section = Some(*name)
}
AttributeKind::NoMangle(attr_span) => {
if tcx.opt_item_name(did.to_def_id()).is_some() {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
Expand Down Expand Up @@ -253,16 +256,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
}
}
}
sym::link_section => {
if let Some(val) = attr.value_str() {
if val.as_str().bytes().any(|b| b == 0) {
let msg = format!("illegal null byte in link_section value: `{val}`");
tcx.dcx().span_err(attr.span(), msg);
} else {
codegen_fn_attrs.link_section = Some(val);
}
}
}
sym::link_ordinal => {
link_ordinal_span = Some(attr.span());
if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {
Expand Down
10 changes: 6 additions & 4 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
Attribute::Parsed(AttributeKind::Align { align, span: repr_span }) => {
self.check_align(span, target, *align, *repr_span)
}
Attribute::Parsed(AttributeKind::LinkSection { span: attr_span, .. }) => {
self.check_link_section(hir_id, *attr_span, span, target)
}
Attribute::Parsed(AttributeKind::Naked(attr_span)) => {
self.check_naked(hir_id, *attr_span, span, target)
}
Expand Down Expand Up @@ -286,7 +289,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
[sym::ffi_const, ..] => self.check_ffi_const(attr.span(), target),
[sym::link_ordinal, ..] => self.check_link_ordinal(attr, span, target),
[sym::link, ..] => self.check_link(hir_id, attr, span, target),
[sym::link_section, ..] => self.check_link_section(hir_id, attr, span, target),
[sym::macro_use, ..] | [sym::macro_escape, ..] => {
self.check_macro_use(hir_id, attr, target)
}
Expand Down Expand Up @@ -1831,23 +1833,23 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}

/// Checks if `#[link_section]` is applied to a function or static.
fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
fn check_link_section(&self, hir_id: HirId, attr_span: Span, span: Span, target: Target) {
match target {
Target::Static | Target::Fn | Target::Method(..) => {}
// FIXME(#80564): We permit struct fields, match arms and macro defs to have an
// `#[link_section]` attribute with just a lint, because we previously
// 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(), "link_section");
self.inline_attr_str_error_with_macro_def(hir_id, attr_span, "link_section");
}
_ => {
// FIXME: #[link_section] was previously allowed on non-functions/statics and some
// crates used this, so only emit a warning.
self.tcx.emit_node_span_lint(
UNUSED_ATTRIBUTES,
hir_id,
attr.span(),
attr_span,
errors::LinkSection { span },
);
}
Expand Down
5 changes: 4 additions & 1 deletion src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -753,9 +753,12 @@ impl Item {
.other_attrs
.iter()
.filter_map(|attr| {
if let hir::Attribute::Parsed(AttributeKind::LinkSection { name, .. }) = attr {
Some(format!("#[link_section = \"{name}\"]"))
}
// NoMangle is special cased, as it appears in HTML output, and we want to show it in source form, not HIR printing.
// It is also used by cargo-semver-checks.
if let hir::Attribute::Parsed(AttributeKind::NoMangle(..)) = attr {
else if let hir::Attribute::Parsed(AttributeKind::NoMangle(..)) = attr {
Some("#[no_mangle]".to_string())
} else if let hir::Attribute::Parsed(AttributeKind::ExportName { name, .. }) = attr
{
Expand Down
6 changes: 6 additions & 0 deletions tests/rustdoc-json/attrs/link_section_2021.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//@ edition: 2021
#![no_std]

//@ is "$.index[?(@.name=='example')].attrs" '["#[link_section = \".text\"]"]'
#[link_section = ".text"]
pub extern "C" fn example() {}
9 changes: 9 additions & 0 deletions tests/rustdoc-json/attrs/link_section_2024.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//@ edition: 2024
#![no_std]

// Since the 2024 edition the link_section attribute must use the unsafe qualification.
// However, the unsafe qualification is not shown by rustdoc.

//@ is "$.index[?(@.name=='example')].attrs" '["#[link_section = \".text\"]"]'
#[unsafe(link_section = ".text")]
pub extern "C" fn example() {}
Original file line number Diff line number Diff line change
Expand Up @@ -387,14 +387,6 @@ LL | #![link()]
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!

warning: attribute should be applied to a function or static
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:69:1
|
LL | #![link_section = "1800"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ not a function or static
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!

warning: attribute should be applied to a function definition
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:62:1
|
Expand All @@ -411,6 +403,14 @@ LL | #![link_name = "1900"]
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!

warning: attribute should be applied to a function or static
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:69:1
|
LL | #![link_section = "1800"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ not a function or static
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!

warning: `#[must_use]` has no effect when applied to a module
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:72:1
|
Expand Down
6 changes: 6 additions & 0 deletions tests/ui/lint/unused/unused-attr-duplicate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,10 @@ pub fn no_mangle_test() {}
#[used] //~ ERROR unused attribute
static FOO: u32 = 0;

#[link_section = ".text"]
//~^ ERROR unused attribute
//~| WARN this was previously accepted
#[link_section = ".bss"]
pub extern "C" fn example() {}

fn main() {}
15 changes: 14 additions & 1 deletion tests/ui/lint/unused/unused-attr-duplicate.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -289,5 +289,18 @@ note: attribute also specified here
LL | #[used]
| ^^^^^^^

error: aborting due to 23 previous errors
error: unused attribute
--> $DIR/unused-attr-duplicate.rs:105:1
|
LL | #[link_section = ".text"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute
|
note: attribute also specified here
--> $DIR/unused-attr-duplicate.rs:108:1
|
LL | #[link_section = ".bss"]
| ^^^^^^^^^^^^^^^^^^^^^^^^
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!

error: aborting due to 24 previous errors

Loading