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 5 commits into
base: master
Choose a base branch
from
Draft
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
7 changes: 7 additions & 0 deletions compiler/rustc_attr_data_structures/src/attributes.rs
Original file line number Diff line number Diff line change
@@ -202,6 +202,9 @@ pub enum AttributeKind {
span: Span,
},

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

/// Represents `#[rustc_confusables]`.
Confusables {
symbols: ThinVec<Symbol>,
@@ -230,6 +233,10 @@ 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).
4 changes: 4 additions & 0 deletions compiler/rustc_attr_parsing/messages.ftl
Original file line number Diff line number Diff line change
@@ -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

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

use super::{AttributeOrder, OnDuplicate, SingleAttributeParser};
use crate::context::{AcceptContext, Stage};
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;

@@ -38,3 +40,124 @@ impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
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
@@ -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;
}
}
11 changes: 2 additions & 9 deletions compiler/rustc_attr_parsing/src/attributes/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
@@ -174,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,
Loading