move naked checks out of check_attr.rs
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -57,20 +59,110 @@ impl<S: Stage> SingleAttributeParser<S> for ColdParser {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct NakedParser;
|
||||
#[derive(Default)]
|
||||
pub(crate) struct NakedParser {
|
||||
span: Option<Span>,
|
||||
}
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for NakedParser {
|
||||
const PATH: &[rustc_span::Symbol] = &[sym::naked];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const TEMPLATE: AttributeTemplate = template!(Word);
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
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(),
|
||||
});
|
||||
}
|
||||
Some(AttributeKind::Naked(cx.attr_span))
|
||||
|
||||
Some(AttributeKind::Naked(span))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -28,7 +28,7 @@ use crate::attributes::stability::{
|
||||
};
|
||||
use crate::attributes::transparency::TransparencyParser;
|
||||
use crate::attributes::{AttributeParser as _, Combine, Single};
|
||||
use crate::parser::{ArgParser, MetaItemParser};
|
||||
use crate::parser::{ArgParser, MetaItemParser, PathParser};
|
||||
use crate::session_diagnostics::{AttributeParseError, AttributeParseErrorReason, UnknownMetaItem};
|
||||
|
||||
macro_rules! group_type {
|
||||
@@ -97,6 +97,7 @@ attribute_parsers!(
|
||||
BodyStabilityParser,
|
||||
ConfusablesParser,
|
||||
ConstStabilityParser,
|
||||
NakedParser,
|
||||
StabilityParser,
|
||||
// tidy-alphabetical-end
|
||||
|
||||
@@ -114,7 +115,6 @@ attribute_parsers!(
|
||||
Single<InlineParser>,
|
||||
Single<MayDangleParser>,
|
||||
Single<MustUseParser>,
|
||||
Single<NakedParser>,
|
||||
Single<NoMangleParser>,
|
||||
Single<OptimizeParser>,
|
||||
Single<PubTransparentParser>,
|
||||
@@ -175,7 +175,7 @@ pub struct Late;
|
||||
///
|
||||
/// Gives [`AttributeParser`]s enough information to create errors, for example.
|
||||
pub(crate) struct AcceptContext<'f, 'sess, S: Stage> {
|
||||
pub(crate) finalize_cx: FinalizeContext<'f, 'sess, S>,
|
||||
pub(crate) shared: SharedContext<'f, 'sess, S>,
|
||||
/// The span of the attribute currently being parsed
|
||||
pub(crate) attr_span: Span,
|
||||
|
||||
@@ -188,7 +188,7 @@ pub(crate) struct AcceptContext<'f, 'sess, S: Stage> {
|
||||
pub(crate) attr_path: AttrPath,
|
||||
}
|
||||
|
||||
impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
||||
impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
|
||||
pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed {
|
||||
S::emit_err(&self.sess, diag)
|
||||
}
|
||||
@@ -226,7 +226,9 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
||||
unused_span,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
||||
pub(crate) fn unknown_key(
|
||||
&self,
|
||||
span: Span,
|
||||
@@ -359,16 +361,16 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
||||
}
|
||||
|
||||
impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> {
|
||||
type Target = FinalizeContext<'f, 'sess, S>;
|
||||
type Target = SharedContext<'f, 'sess, S>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.finalize_cx
|
||||
&self.shared
|
||||
}
|
||||
}
|
||||
|
||||
impl<'f, 'sess, S: Stage> DerefMut for AcceptContext<'f, 'sess, S> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.finalize_cx
|
||||
&mut self.shared
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,7 +378,7 @@ impl<'f, 'sess, S: Stage> DerefMut for AcceptContext<'f, 'sess, S> {
|
||||
///
|
||||
/// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create
|
||||
/// errors, for example.
|
||||
pub(crate) struct FinalizeContext<'p, 'sess, S: Stage> {
|
||||
pub(crate) struct SharedContext<'p, 'sess, S: Stage> {
|
||||
/// The parse context, gives access to the session and the
|
||||
/// diagnostics context.
|
||||
pub(crate) cx: &'p mut AttributeParser<'sess, S>,
|
||||
@@ -385,10 +387,40 @@ pub(crate) struct FinalizeContext<'p, 'sess, S: Stage> {
|
||||
/// The id ([`NodeId`] if `S` is `Early`, [`HirId`] if `S` is `Late`) of the syntactical component this attribute was applied to
|
||||
pub(crate) target_id: S::Id,
|
||||
|
||||
pub(crate) emit_lint: &'p mut dyn FnMut(AttributeLint<S::Id>),
|
||||
emit_lint: &'p mut dyn FnMut(AttributeLint<S::Id>),
|
||||
}
|
||||
|
||||
/// Context given to every attribute parser during finalization.
|
||||
///
|
||||
/// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create
|
||||
/// errors, for example.
|
||||
pub(crate) struct FinalizeContext<'p, 'sess, S: Stage> {
|
||||
pub(crate) shared: SharedContext<'p, 'sess, S>,
|
||||
|
||||
/// A list of all attribute on this syntax node.
|
||||
///
|
||||
/// Useful for compatibility checks with other attributes in [`finalize`](crate::attributes::AttributeParser::finalize)
|
||||
///
|
||||
/// Usually, you should use normal attribute parsing logic instead,
|
||||
/// especially when making a *denylist* of other attributes.
|
||||
pub(crate) all_attrs: &'p [PathParser<'p>],
|
||||
}
|
||||
|
||||
impl<'p, 'sess: 'p, S: Stage> Deref for FinalizeContext<'p, 'sess, S> {
|
||||
type Target = SharedContext<'p, 'sess, S>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.shared
|
||||
}
|
||||
}
|
||||
|
||||
impl<'p, 'sess: 'p, S: Stage> DerefMut for FinalizeContext<'p, 'sess, S> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.shared
|
||||
}
|
||||
}
|
||||
|
||||
impl<'p, 'sess: 'p, S: Stage> Deref for SharedContext<'p, 'sess, S> {
|
||||
type Target = AttributeParser<'sess, S>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
@@ -396,7 +428,7 @@ impl<'p, 'sess: 'p, S: Stage> Deref for FinalizeContext<'p, 'sess, S> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'p, 'sess: 'p, S: Stage> DerefMut for FinalizeContext<'p, 'sess, S> {
|
||||
impl<'p, 'sess: 'p, S: Stage> DerefMut for SharedContext<'p, 'sess, S> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.cx
|
||||
}
|
||||
@@ -411,8 +443,7 @@ pub enum OmitDoc {
|
||||
/// Context created once, for example as part of the ast lowering
|
||||
/// context, through which all attributes can be lowered.
|
||||
pub struct AttributeParser<'sess, S: Stage = Late> {
|
||||
#[expect(dead_code)] // FIXME(jdonszelmann): needed later to verify we parsed all attributes
|
||||
tools: Vec<Symbol>,
|
||||
pub(crate) tools: Vec<Symbol>,
|
||||
features: Option<&'sess Features>,
|
||||
sess: &'sess Session,
|
||||
stage: PhantomData<S>,
|
||||
@@ -500,6 +531,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
|
||||
mut emit_lint: impl FnMut(AttributeLint<S::Id>),
|
||||
) -> Vec<Attribute> {
|
||||
let mut attributes = Vec::new();
|
||||
let mut attr_paths = Vec::new();
|
||||
|
||||
for attr in attrs {
|
||||
// If we're only looking for a single attribute, skip all the ones we don't care about.
|
||||
@@ -543,6 +575,8 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
|
||||
// }))
|
||||
// }
|
||||
ast::AttrKind::Normal(n) => {
|
||||
attr_paths.push(PathParser::Ast(&n.item.path));
|
||||
|
||||
let parser = MetaItemParser::from_attr(n, self.dcx());
|
||||
let path = parser.path();
|
||||
let args = parser.args();
|
||||
@@ -551,7 +585,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
|
||||
if let Some(accepts) = S::parsers().0.get(parts.as_slice()) {
|
||||
for (template, accept) in accepts {
|
||||
let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
|
||||
finalize_cx: FinalizeContext {
|
||||
shared: SharedContext {
|
||||
cx: self,
|
||||
target_span,
|
||||
target_id,
|
||||
@@ -595,10 +629,13 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
|
||||
let mut parsed_attributes = Vec::new();
|
||||
for f in &S::parsers().1 {
|
||||
if let Some(attr) = f(&mut FinalizeContext {
|
||||
cx: self,
|
||||
target_span,
|
||||
target_id,
|
||||
emit_lint: &mut emit_lint,
|
||||
shared: SharedContext {
|
||||
cx: self,
|
||||
target_span,
|
||||
target_id,
|
||||
emit_lint: &mut emit_lint,
|
||||
},
|
||||
all_attrs: &attr_paths,
|
||||
}) {
|
||||
parsed_attributes.push(Attribute::Parsed(attr));
|
||||
}
|
||||
|
||||
@@ -87,6 +87,14 @@ impl<'a> PathParser<'a> {
|
||||
pub fn word_is(&self, sym: Symbol) -> bool {
|
||||
self.word().map(|i| i.name == sym).unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Checks whether the first segments match the givens.
|
||||
///
|
||||
/// Unlike [`segments_is`](Self::segments_is),
|
||||
/// `self` may contain more segments than the number matched against.
|
||||
pub fn starts_with(&self, segments: &[Symbol]) -> bool {
|
||||
segments.len() < self.len() && self.segments().zip(segments).all(|(a, b)| a.name == *b)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PathParser<'_> {
|
||||
|
||||
@@ -482,6 +482,17 @@ pub(crate) struct UnrecognizedReprHint {
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_naked_functions_incompatible_attribute, code = E0736)]
|
||||
pub(crate) struct NakedFunctionIncompatibleAttribute {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
#[label(attr_parsing_naked_attribute)]
|
||||
pub naked_span: Span,
|
||||
pub attr: String,
|
||||
}
|
||||
|
||||
pub(crate) enum AttributeParseErrorReason {
|
||||
ExpectedNoArgs,
|
||||
ExpectedStringLiteral { byte_string: Option<Span> },
|
||||
|
||||
Reference in New Issue
Block a user