Skip to content

rework #[naked] attribute parser #142493

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

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
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
11 changes: 10 additions & 1 deletion compiler/rustc_attr_data_structures/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ pub enum InstructionSetAttr {
ArmT32,
}

#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic, Default)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default, PrintAttribute)]
#[derive(Encodable, Decodable, HashStable_Generic)]
pub enum OptimizeAttr {
/// No `#[optimize(..)]` attribute
#[default]
Expand Down Expand Up @@ -201,6 +202,9 @@ pub enum AttributeKind {
span: Span,
},

/// Represents `#[cold]`.
Cold(Span),

/// Represents `#[rustc_confusables]`.
Confusables {
symbols: ThinVec<Symbol>,
Expand Down Expand Up @@ -230,6 +234,11 @@ pub enum AttributeKind {
/// Represents `#[rustc_macro_transparency]`.
MacroTransparency(Transparency),

/// Represents #[naked]
Naked(Span),

/// Represents `#[optimize(size|speed)]`
Optimize(OptimizeAttr, Span),
/// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations).
Repr(ThinVec<(ReprAttr, Span)>),

Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_attr_parsing/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ attr_parsing_missing_since =
attr_parsing_multiple_stability_levels =
multiple stability levels

attr_parsing_naked_functions_incompatible_attribute =
attribute incompatible with `#[unsafe(naked)]`
.label = the `{$attr}` attribute is incompatible with `#[unsafe(naked)]`
.naked_attribute = function marked with `#[unsafe(naked)]` here
attr_parsing_non_ident_feature =
'feature' is not an identifier

Expand Down
163 changes: 163 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
use rustc_attr_data_structures::{AttributeKind, OptimizeAttr};
use rustc_feature::{AttributeTemplate, template};
use rustc_session::parse::feature_err;
use rustc_span::{Span, sym};

use super::{AcceptMapping, AttributeOrder, AttributeParser, OnDuplicate, SingleAttributeParser};
use crate::context::{AcceptContext, FinalizeContext, Stage};
use crate::parser::ArgParser;
use crate::session_diagnostics::NakedFunctionIncompatibleAttribute;

pub(crate) struct OptimizeParser;

impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
const PATH: &[rustc_span::Symbol] = &[sym::optimize];
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))
}
}

pub(crate) struct ColdParser;

impl<S: Stage> SingleAttributeParser<S> for ColdParser {
const PATH: &[rustc_span::Symbol] = &[sym::cold];
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::Cold(cx.attr_span))
}
}

#[derive(Default)]
pub(crate) struct NakedParser {
span: Option<Span>,
}

impl<S: Stage> AttributeParser<S> for NakedParser {
const ATTRIBUTES: AcceptMapping<Self, S> =
&[(&[sym::naked], template!(Word), |this, cx, args| {
if !args.no_args() {
cx.expected_no_args(args.span().unwrap_or(cx.attr_span));
return;
}

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 Some(span) = self.span else {
return None;
};

// 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.word_is(*allowed_attr) || other_attr.starts_with(&[sym::rustfmt]) {
// effectively skips the error message being emitted below
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(),
});
}

Some(AttributeKind::Naked(span))
}
}
6 changes: 2 additions & 4 deletions compiler/rustc_attr_parsing/src/attributes/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,8 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
ArgParser::NameValue(_) => {
let suggestions =
<Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "inline");
cx.emit_lint(
AttributeLintKind::IllFormedAttributeInput { suggestions },
cx.attr_span,
);
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
return None;
}
}
Expand Down
12 changes: 3 additions & 9 deletions compiler/rustc_attr_parsing/src/attributes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
use std::marker::PhantomData;

use rustc_attr_data_structures::AttributeKind;
use rustc_attr_data_structures::lints::AttributeLintKind;
use rustc_feature::AttributeTemplate;
use rustc_span::{Span, Symbol};
use thin_vec::ThinVec;
Expand All @@ -28,6 +27,7 @@ use crate::session_diagnostics::UnusedMultiple;

pub(crate) mod allow_unstable;
pub(crate) mod cfg;
pub(crate) mod codegen_attrs;
pub(crate) mod confusables;
pub(crate) mod deprecation;
pub(crate) mod inline;
Expand Down Expand Up @@ -173,14 +173,8 @@ impl<S: Stage> OnDuplicate<S> {
unused: Span,
) {
match self {
OnDuplicate::Warn => cx.emit_lint(
AttributeLintKind::UnusedDuplicate { this: unused, other: used, warning: false },
unused,
),
OnDuplicate::WarnButFutureError => cx.emit_lint(
AttributeLintKind::UnusedDuplicate { this: unused, other: used, warning: true },
unused,
),
OnDuplicate::Warn => cx.warn_unused_duplicate(used, unused),
OnDuplicate::WarnButFutureError => cx.warn_unused_duplicate_future_error(used, unused),
OnDuplicate::Error => {
cx.emit_err(UnusedMultiple {
this: used,
Expand Down
Loading
Loading