2025-03-09 23:46:47 +01:00
|
|
|
use rustc_attr_data_structures::{AttributeKind, OptimizeAttr};
|
|
|
|
|
use rustc_feature::{AttributeTemplate, template};
|
2025-06-13 02:28:33 +02:00
|
|
|
use rustc_session::parse::feature_err;
|
2025-06-21 11:29:05 +02:00
|
|
|
use rustc_span::{Span, Symbol, sym};
|
2025-03-09 23:46:47 +01:00
|
|
|
|
2025-06-13 02:28:33 +02:00
|
|
|
use super::{AcceptMapping, AttributeOrder, AttributeParser, OnDuplicate, SingleAttributeParser};
|
|
|
|
|
use crate::context::{AcceptContext, FinalizeContext, Stage};
|
2025-03-09 23:46:47 +01:00
|
|
|
use crate::parser::ArgParser;
|
2025-06-24 22:33:44 +02:00
|
|
|
use crate::session_diagnostics::{NakedFunctionIncompatibleAttribute, NullOnExport};
|
2025-03-09 23:46:47 +01:00
|
|
|
|
|
|
|
|
pub(crate) struct OptimizeParser;
|
|
|
|
|
|
|
|
|
|
impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
|
2025-06-21 11:29:05 +02:00
|
|
|
const PATH: &[Symbol] = &[sym::optimize];
|
2025-03-09 23:46:47 +01:00
|
|
|
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
|
|
|
|
|
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
|
|
|
|
|
const TEMPLATE: AttributeTemplate = template!(List: "size|speed|none");
|
|
|
|
|
|
|
|
|
|
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
|
|
|
|
let Some(list) = args.list() else {
|
|
|
|
|
cx.expected_list(cx.attr_span);
|
|
|
|
|
return None;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let Some(single) = list.single() else {
|
|
|
|
|
cx.expected_single_argument(list.span);
|
|
|
|
|
return None;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let res = match single.meta_item().and_then(|i| i.path().word().map(|i| i.name)) {
|
|
|
|
|
Some(sym::size) => OptimizeAttr::Size,
|
|
|
|
|
Some(sym::speed) => OptimizeAttr::Speed,
|
|
|
|
|
Some(sym::none) => OptimizeAttr::DoNotOptimize,
|
|
|
|
|
_ => {
|
|
|
|
|
cx.expected_specific_argument(single.span(), vec!["size", "speed", "none"]);
|
|
|
|
|
OptimizeAttr::Default
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Some(AttributeKind::Optimize(res, cx.attr_span))
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-06-12 13:52:23 +02:00
|
|
|
|
|
|
|
|
pub(crate) struct ColdParser;
|
|
|
|
|
|
|
|
|
|
impl<S: Stage> SingleAttributeParser<S> for ColdParser {
|
2025-06-21 11:29:05 +02:00
|
|
|
const PATH: &[Symbol] = &[sym::cold];
|
2025-06-12 13:52:23 +02:00
|
|
|
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> {
|
2025-06-18 22:27:35 +03:00
|
|
|
if let Err(span) = args.no_args() {
|
|
|
|
|
cx.expected_no_args(span);
|
2025-06-12 13:52:23 +02:00
|
|
|
return None;
|
2025-06-13 01:36:52 +02:00
|
|
|
}
|
2025-06-12 13:52:23 +02:00
|
|
|
|
|
|
|
|
Some(AttributeKind::Cold(cx.attr_span))
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-06-22 12:14:38 +02:00
|
|
|
|
2025-06-24 22:33:44 +02:00
|
|
|
pub(crate) struct ExportNameParser;
|
|
|
|
|
|
|
|
|
|
impl<S: Stage> SingleAttributeParser<S> for ExportNameParser {
|
|
|
|
|
const PATH: &[rustc_span::Symbol] = &[sym::export_name];
|
|
|
|
|
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') {
|
|
|
|
|
// `#[export_name = ...]` will be converted to a null-terminated string,
|
|
|
|
|
// so it may not contain any null characters.
|
|
|
|
|
cx.emit_err(NullOnExport { span: cx.attr_span });
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
Some(AttributeKind::ExportName { name, span: cx.attr_span })
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-13 02:28:33 +02:00
|
|
|
#[derive(Default)]
|
|
|
|
|
pub(crate) struct NakedParser {
|
|
|
|
|
span: Option<Span>,
|
|
|
|
|
}
|
2025-06-13 01:36:52 +02:00
|
|
|
|
2025-06-13 02:28:33 +02:00
|
|
|
impl<S: Stage> AttributeParser<S> for NakedParser {
|
|
|
|
|
const ATTRIBUTES: AcceptMapping<Self, S> =
|
|
|
|
|
&[(&[sym::naked], template!(Word), |this, cx, args| {
|
2025-06-18 22:27:35 +03:00
|
|
|
if let Err(span) = args.no_args() {
|
|
|
|
|
cx.expected_no_args(span);
|
2025-06-13 02:28:33 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2025-06-13 01:36:52 +02:00
|
|
|
|
2025-06-13 02:28:33 +02:00
|
|
|
if let Some(earlier) = this.span {
|
|
|
|
|
let span = cx.attr_span;
|
|
|
|
|
cx.warn_unused_duplicate(earlier, span);
|
|
|
|
|
} else {
|
|
|
|
|
this.span = Some(cx.attr_span);
|
|
|
|
|
}
|
|
|
|
|
})];
|
|
|
|
|
|
|
|
|
|
fn finalize(self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
|
|
|
|
// FIXME(jdonszelmann): upgrade this list to *parsed* attributes
|
|
|
|
|
// once all of these have parsed forms. That'd make the check much nicer...
|
|
|
|
|
//
|
|
|
|
|
// many attributes don't make sense in combination with #[naked].
|
|
|
|
|
// Notable attributes that are incompatible with `#[naked]` are:
|
|
|
|
|
//
|
|
|
|
|
// * `#[inline]`
|
|
|
|
|
// * `#[track_caller]`
|
|
|
|
|
// * `#[test]`, `#[ignore]`, `#[should_panic]`
|
|
|
|
|
//
|
|
|
|
|
// NOTE: when making changes to this list, check that `error_codes/E0736.md` remains
|
|
|
|
|
// accurate.
|
|
|
|
|
const ALLOW_LIST: &[rustc_span::Symbol] = &[
|
|
|
|
|
// conditional compilation
|
|
|
|
|
sym::cfg_trace,
|
|
|
|
|
sym::cfg_attr_trace,
|
|
|
|
|
// testing (allowed here so better errors can be generated in `rustc_builtin_macros::test`)
|
|
|
|
|
sym::test,
|
|
|
|
|
sym::ignore,
|
|
|
|
|
sym::should_panic,
|
|
|
|
|
sym::bench,
|
|
|
|
|
// diagnostics
|
|
|
|
|
sym::allow,
|
|
|
|
|
sym::warn,
|
|
|
|
|
sym::deny,
|
|
|
|
|
sym::forbid,
|
|
|
|
|
sym::deprecated,
|
|
|
|
|
sym::must_use,
|
|
|
|
|
// abi, linking and FFI
|
|
|
|
|
sym::cold,
|
|
|
|
|
sym::export_name,
|
|
|
|
|
sym::link_section,
|
|
|
|
|
sym::linkage,
|
|
|
|
|
sym::no_mangle,
|
|
|
|
|
sym::instruction_set,
|
|
|
|
|
sym::repr,
|
|
|
|
|
sym::rustc_std_internal_symbol,
|
|
|
|
|
sym::align,
|
|
|
|
|
// obviously compatible with self
|
|
|
|
|
sym::naked,
|
|
|
|
|
// documentation
|
|
|
|
|
sym::doc,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
let span = self.span?;
|
|
|
|
|
|
|
|
|
|
// only if we found a naked attribute do we do the somewhat expensive check
|
|
|
|
|
'outer: for other_attr in cx.all_attrs {
|
|
|
|
|
for allowed_attr in ALLOW_LIST {
|
|
|
|
|
if other_attr.segments().next().is_some_and(|i| cx.tools.contains(&i.name)) {
|
|
|
|
|
// effectively skips the error message being emitted below
|
|
|
|
|
// if it's a tool attribute
|
|
|
|
|
continue 'outer;
|
|
|
|
|
}
|
|
|
|
|
if other_attr.word_is(*allowed_attr) {
|
|
|
|
|
// effectively skips the error message being emitted below
|
|
|
|
|
// if its an allowed attribute
|
|
|
|
|
continue 'outer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if other_attr.word_is(sym::target_feature) {
|
|
|
|
|
if !cx.features().naked_functions_target_feature() {
|
|
|
|
|
feature_err(
|
|
|
|
|
&cx.sess(),
|
|
|
|
|
sym::naked_functions_target_feature,
|
|
|
|
|
other_attr.span(),
|
|
|
|
|
"`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions",
|
|
|
|
|
).emit();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
continue 'outer;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cx.emit_err(NakedFunctionIncompatibleAttribute {
|
|
|
|
|
span: other_attr.span(),
|
|
|
|
|
naked_span: span,
|
|
|
|
|
attr: other_attr.get_attribute_path().to_string(),
|
|
|
|
|
});
|
2025-06-13 01:36:52 +02:00
|
|
|
}
|
2025-06-13 02:28:33 +02:00
|
|
|
|
|
|
|
|
Some(AttributeKind::Naked(span))
|
2025-06-13 01:36:52 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-21 11:29:05 +02:00
|
|
|
pub(crate) struct TrackCallerParser;
|
|
|
|
|
|
|
|
|
|
impl<S: Stage> SingleAttributeParser<S> for TrackCallerParser {
|
|
|
|
|
const PATH: &[Symbol] = &[sym::track_caller];
|
|
|
|
|
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 let Err(span) = args.no_args() {
|
|
|
|
|
cx.expected_no_args(span);
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Some(AttributeKind::TrackCaller(cx.attr_span))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-22 12:14:38 +02:00
|
|
|
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> {
|
2025-06-18 22:27:35 +03:00
|
|
|
if let Err(span) = args.no_args() {
|
|
|
|
|
cx.expected_no_args(span);
|
2025-06-22 12:14:38 +02:00
|
|
|
return None;
|
2025-06-18 22:27:35 +03:00
|
|
|
}
|
2025-06-22 12:14:38 +02:00
|
|
|
|
|
|
|
|
Some(AttributeKind::NoMangle(cx.attr_span))
|
|
|
|
|
}
|
|
|
|
|
}
|