Skip to content

Commit 3c3c287

Browse files
committed
move naked checks out of check_attr.rs
1 parent 171bccd commit 3c3c287

File tree

15 files changed

+211
-191
lines changed

15 files changed

+211
-191
lines changed

compiler/rustc_attr_data_structures/src/attributes.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ pub enum AttributeKind {
237237
/// Represents [`#[may_dangle]`](https://std-dev-guide.rust-lang.org/tricky/may-dangle.html).
238238
MayDangle(Span),
239239

240-
/// Represents #[naked]
240+
/// Represents `#[naked]`
241241
Naked(Span),
242242

243243
/// Represents `#[optimize(size|speed)]`

compiler/rustc_attr_parsing/messages.ftl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ attr_parsing_missing_since =
8989
attr_parsing_multiple_stability_levels =
9090
multiple stability levels
9191
92+
attr_parsing_naked_functions_incompatible_attribute =
93+
attribute incompatible with `#[unsafe(naked)]`
94+
.label = the `{$attr}` attribute is incompatible with `#[unsafe(naked)]`
95+
.naked_attribute = function marked with `#[unsafe(naked)]` here
9296
attr_parsing_non_ident_feature =
9397
'feature' is not an identifier
9498

compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs

Lines changed: 106 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use rustc_attr_data_structures::{AttributeKind, OptimizeAttr};
22
use rustc_feature::{AttributeTemplate, template};
3-
use rustc_span::sym;
3+
use rustc_session::parse::feature_err;
4+
use rustc_span::{Span, sym};
45

5-
use super::{AttributeOrder, OnDuplicate, SingleAttributeParser};
6-
use crate::context::{AcceptContext, Stage};
6+
use super::{AcceptMapping, AttributeOrder, AttributeParser, OnDuplicate, SingleAttributeParser};
7+
use crate::context::{AcceptContext, FinalizeContext, Stage};
78
use crate::parser::ArgParser;
9+
use crate::session_diagnostics::NakedFunctionIncompatibleAttribute;
810

911
pub(crate) struct OptimizeParser;
1012

@@ -57,19 +59,109 @@ impl<S: Stage> SingleAttributeParser<S> for ColdParser {
5759
}
5860
}
5961

60-
pub(crate) struct NakedParser;
62+
#[derive(Default)]
63+
pub(crate) struct NakedParser {
64+
span: Option<Span>,
65+
}
6166

62-
impl<S: Stage> SingleAttributeParser<S> for NakedParser {
63-
const PATH: &[rustc_span::Symbol] = &[sym::naked];
64-
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
65-
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
66-
const TEMPLATE: AttributeTemplate = template!(Word);
67+
impl<S: Stage> AttributeParser<S> for NakedParser {
68+
const ATTRIBUTES: AcceptMapping<Self, S> =
69+
&[(&[sym::naked], template!(Word), |this, cx, args| {
70+
if !args.no_args() {
71+
cx.expected_no_args(args.span().unwrap_or(cx.attr_span));
72+
return;
73+
}
6774

68-
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
69-
if !args.no_args() {
70-
cx.expected_no_args(args.span().unwrap_or(cx.attr_span));
71-
return None;
75+
if let Some(earlier) = this.span {
76+
let span = cx.attr_span;
77+
cx.warn_unused_duplicate(earlier, span);
78+
} else {
79+
this.span = Some(cx.attr_span);
80+
}
81+
})];
82+
83+
fn finalize(self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
84+
// FIXME(jdonszelmann): upgrade this list to *parsed* attributes
85+
// once all of these have parsed forms. That'd make the check much nicer...
86+
//
87+
// many attributes don't make sense in combination with #[naked].
88+
// Notable attributes that are incompatible with `#[naked]` are:
89+
//
90+
// * `#[inline]`
91+
// * `#[track_caller]`
92+
// * `#[test]`, `#[ignore]`, `#[should_panic]`
93+
//
94+
// NOTE: when making changes to this list, check that `error_codes/E0736.md` remains
95+
// accurate.
96+
const ALLOW_LIST: &[rustc_span::Symbol] = &[
97+
// conditional compilation
98+
sym::cfg_trace,
99+
sym::cfg_attr_trace,
100+
// testing (allowed here so better errors can be generated in `rustc_builtin_macros::test`)
101+
sym::test,
102+
sym::ignore,
103+
sym::should_panic,
104+
sym::bench,
105+
// diagnostics
106+
sym::allow,
107+
sym::warn,
108+
sym::deny,
109+
sym::forbid,
110+
sym::deprecated,
111+
sym::must_use,
112+
// abi, linking and FFI
113+
sym::cold,
114+
sym::export_name,
115+
sym::link_section,
116+
sym::linkage,
117+
sym::no_mangle,
118+
sym::instruction_set,
119+
sym::repr,
120+
sym::rustc_std_internal_symbol,
121+
sym::align,
122+
// obviously compatible with self
123+
sym::naked,
124+
// documentation
125+
sym::doc,
126+
];
127+
128+
let span = self.span?;
129+
130+
// only if we found a naked attribute do we do the somewhat expensive check
131+
'outer: for other_attr in cx.all_attrs {
132+
for allowed_attr in ALLOW_LIST {
133+
if other_attr.segments().next().is_some_and(|i| cx.tools.contains(&i.name)) {
134+
// effectively skips the error message being emitted below
135+
// if it's a tool attribute
136+
continue 'outer;
137+
}
138+
if other_attr.word_is(*allowed_attr) {
139+
// effectively skips the error message being emitted below
140+
// if its an allowed attribute
141+
continue 'outer;
142+
}
143+
144+
if other_attr.word_is(sym::target_feature) {
145+
if !cx.features().naked_functions_target_feature() {
146+
feature_err(
147+
&cx.sess(),
148+
sym::naked_functions_target_feature,
149+
other_attr.span(),
150+
"`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions",
151+
).emit();
152+
}
153+
154+
continue 'outer;
155+
}
156+
}
157+
158+
cx.emit_err(NakedFunctionIncompatibleAttribute {
159+
span: other_attr.span(),
160+
naked_span: span,
161+
attr: other_attr.get_attribute_path().to_string(),
162+
});
72163
}
73-
Some(AttributeKind::Naked(cx.attr_span))
164+
165+
Some(AttributeKind::Naked(span))
74166
}
75167
}

compiler/rustc_attr_parsing/src/attributes/inline.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,8 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
4545
ArgParser::NameValue(_) => {
4646
let suggestions =
4747
<Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "inline");
48-
cx.emit_lint(
49-
AttributeLintKind::IllFormedAttributeInput { suggestions },
50-
cx.attr_span,
51-
);
48+
let span = cx.attr_span;
49+
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
5250
return None;
5351
}
5452
}

compiler/rustc_attr_parsing/src/attributes/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
use std::marker::PhantomData;
1818

1919
use rustc_attr_data_structures::AttributeKind;
20-
use rustc_attr_data_structures::lints::AttributeLintKind;
2120
use rustc_feature::AttributeTemplate;
2221
use rustc_span::{Span, Symbol};
2322
use thin_vec::ThinVec;

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use crate::attributes::stability::{
2727
};
2828
use crate::attributes::transparency::TransparencyParser;
2929
use crate::attributes::{AttributeParser as _, Combine, Single};
30-
use crate::parser::{ArgParser, MetaItemParser};
30+
use crate::parser::{ArgParser, MetaItemParser, PathParser};
3131
use crate::session_diagnostics::{AttributeParseError, AttributeParseErrorReason, UnknownMetaItem};
3232

3333
macro_rules! group_type {
@@ -96,6 +96,7 @@ attribute_parsers!(
9696
BodyStabilityParser,
9797
ConfusablesParser,
9898
ConstStabilityParser,
99+
NakedParser,
99100
StabilityParser,
100101
// tidy-alphabetical-end
101102

@@ -112,7 +113,6 @@ attribute_parsers!(
112113
Single<DeprecationParser>,
113114
Single<InlineParser>,
114115
Single<MayDangleParser>,
115-
Single<NakedParser>,
116116
Single<OptimizeParser>,
117117
Single<RustcForceInlineParser>,
118118
Single<TransparencyParser>,
@@ -171,7 +171,7 @@ pub struct Late;
171171
///
172172
/// Gives [`AttributeParser`]s enough information to create errors, for example.
173173
pub(crate) struct AcceptContext<'f, 'sess, S: Stage> {
174-
pub(crate) finalize_cx: FinalizeContext<'f, 'sess, S>,
174+
pub(crate) shared: SharedContext<'f, 'sess, S>,
175175
/// The span of the attribute currently being parsed
176176
pub(crate) attr_span: Span,
177177

@@ -184,7 +184,7 @@ pub(crate) struct AcceptContext<'f, 'sess, S: Stage> {
184184
pub(crate) attr_path: AttrPath,
185185
}
186186

187-
impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
187+
impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
188188
pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed {
189189
S::emit_err(&self.sess, diag)
190190
}
@@ -222,7 +222,9 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
222222
unused_span,
223223
)
224224
}
225+
}
225226

227+
impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
226228
pub(crate) fn unknown_key(
227229
&self,
228230
span: Span,
@@ -355,24 +357,24 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
355357
}
356358

357359
impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> {
358-
type Target = FinalizeContext<'f, 'sess, S>;
360+
type Target = SharedContext<'f, 'sess, S>;
359361

360362
fn deref(&self) -> &Self::Target {
361-
&self.finalize_cx
363+
&self.shared
362364
}
363365
}
364366

365367
impl<'f, 'sess, S: Stage> DerefMut for AcceptContext<'f, 'sess, S> {
366368
fn deref_mut(&mut self) -> &mut Self::Target {
367-
&mut self.finalize_cx
369+
&mut self.shared
368370
}
369371
}
370372

371373
/// Context given to every attribute parser during finalization.
372374
///
373375
/// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create
374376
/// errors, for example.
375-
pub(crate) struct FinalizeContext<'p, 'sess, S: Stage> {
377+
pub(crate) struct SharedContext<'p, 'sess, S: Stage> {
376378
/// The parse context, gives access to the session and the
377379
/// diagnostics context.
378380
pub(crate) cx: &'p mut AttributeParser<'sess, S>,
@@ -381,18 +383,48 @@ pub(crate) struct FinalizeContext<'p, 'sess, S: Stage> {
381383
/// The id ([`NodeId`] if `S` is `Early`, [`HirId`] if `S` is `Late`) of the syntactical component this attribute was applied to
382384
pub(crate) target_id: S::Id,
383385

384-
pub(crate) emit_lint: &'p mut dyn FnMut(AttributeLint<S::Id>),
386+
emit_lint: &'p mut dyn FnMut(AttributeLint<S::Id>),
387+
}
388+
389+
/// Context given to every attribute parser during finalization.
390+
///
391+
/// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create
392+
/// errors, for example.
393+
pub(crate) struct FinalizeContext<'p, 'sess, S: Stage> {
394+
pub(crate) shared: SharedContext<'p, 'sess, S>,
395+
396+
/// A list of all attribute on this syntax node.
397+
///
398+
/// Useful for compatibility checks with other attributes in [`finalize`](crate::attributes::AttributeParser::finalize)
399+
///
400+
/// Usually, you should use normal attribute parsing logic instead,
401+
/// especially when making a *denylist* of other attributes.
402+
pub(crate) all_attrs: &'p [PathParser<'p>],
385403
}
386404

387405
impl<'p, 'sess: 'p, S: Stage> Deref for FinalizeContext<'p, 'sess, S> {
406+
type Target = SharedContext<'p, 'sess, S>;
407+
408+
fn deref(&self) -> &Self::Target {
409+
&self.shared
410+
}
411+
}
412+
413+
impl<'p, 'sess: 'p, S: Stage> DerefMut for FinalizeContext<'p, 'sess, S> {
414+
fn deref_mut(&mut self) -> &mut Self::Target {
415+
&mut self.shared
416+
}
417+
}
418+
419+
impl<'p, 'sess: 'p, S: Stage> Deref for SharedContext<'p, 'sess, S> {
388420
type Target = AttributeParser<'sess, S>;
389421

390422
fn deref(&self) -> &Self::Target {
391423
self.cx
392424
}
393425
}
394426

395-
impl<'p, 'sess: 'p, S: Stage> DerefMut for FinalizeContext<'p, 'sess, S> {
427+
impl<'p, 'sess: 'p, S: Stage> DerefMut for SharedContext<'p, 'sess, S> {
396428
fn deref_mut(&mut self) -> &mut Self::Target {
397429
self.cx
398430
}
@@ -407,8 +439,7 @@ pub enum OmitDoc {
407439
/// Context created once, for example as part of the ast lowering
408440
/// context, through which all attributes can be lowered.
409441
pub struct AttributeParser<'sess, S: Stage = Late> {
410-
#[expect(dead_code)] // FIXME(jdonszelmann): needed later to verify we parsed all attributes
411-
tools: Vec<Symbol>,
442+
pub(crate) tools: Vec<Symbol>,
412443
features: Option<&'sess Features>,
413444
sess: &'sess Session,
414445
stage: PhantomData<S>,
@@ -496,6 +527,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
496527
mut emit_lint: impl FnMut(AttributeLint<S::Id>),
497528
) -> Vec<Attribute> {
498529
let mut attributes = Vec::new();
530+
let mut attr_paths = Vec::new();
499531

500532
for attr in attrs {
501533
// If we're only looking for a single attribute, skip all the ones we don't care about.
@@ -539,6 +571,8 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
539571
// }))
540572
// }
541573
ast::AttrKind::Normal(n) => {
574+
attr_paths.push(PathParser::Ast(&n.item.path));
575+
542576
let parser = MetaItemParser::from_attr(n, self.dcx());
543577
let path = parser.path();
544578
let args = parser.args();
@@ -547,7 +581,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
547581
if let Some(accepts) = S::parsers().0.get(parts.as_slice()) {
548582
for (template, accept) in accepts {
549583
let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
550-
finalize_cx: FinalizeContext {
584+
shared: SharedContext {
551585
cx: self,
552586
target_span,
553587
target_id,
@@ -591,10 +625,13 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
591625
let mut parsed_attributes = Vec::new();
592626
for f in &S::parsers().1 {
593627
if let Some(attr) = f(&mut FinalizeContext {
594-
cx: self,
595-
target_span,
596-
target_id,
597-
emit_lint: &mut emit_lint,
628+
shared: SharedContext {
629+
cx: self,
630+
target_span,
631+
target_id,
632+
emit_lint: &mut emit_lint,
633+
},
634+
all_attrs: &attr_paths,
598635
}) {
599636
parsed_attributes.push(Attribute::Parsed(attr));
600637
}

compiler/rustc_attr_parsing/src/parser.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,14 @@ impl<'a> PathParser<'a> {
8787
pub fn word_is(&self, sym: Symbol) -> bool {
8888
self.word().map(|i| i.name == sym).unwrap_or(false)
8989
}
90+
91+
/// Checks whether the first segments match the givens.
92+
///
93+
/// Unlike [`segments_is`](Self::segments_is),
94+
/// `self` may contain more segments than the number matched against.
95+
pub fn starts_with(&self, segments: &[Symbol]) -> bool {
96+
self.segments().zip(segments).all(|(a, b)| a.name == *b)
97+
}
9098
}
9199

92100
impl Display for PathParser<'_> {

compiler/rustc_attr_parsing/src/session_diagnostics.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,17 @@ pub(crate) struct UnrecognizedReprHint {
473473
pub span: Span,
474474
}
475475

476+
#[derive(Diagnostic)]
477+
#[diag(attr_parsing_naked_functions_incompatible_attribute, code = E0736)]
478+
pub(crate) struct NakedFunctionIncompatibleAttribute {
479+
#[primary_span]
480+
#[label]
481+
pub span: Span,
482+
#[label(attr_parsing_naked_attribute)]
483+
pub naked_span: Span,
484+
pub attr: String,
485+
}
486+
476487
pub(crate) enum AttributeParseErrorReason {
477488
ExpectedNoArgs,
478489
ExpectedStringLiteral { byte_string: Option<Span> },

0 commit comments

Comments
 (0)