diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs index b4a5c9da155c9..5dd46bf0922de 100644 --- a/compiler/rustc_attr_parsing/src/attributes/inline.rs +++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs @@ -3,125 +3,156 @@ use rustc_feature::AttributeStability; use rustc_lint_defs::builtin::{ILL_FORMED_ATTRIBUTE_INPUT, UNUSED_ATTRIBUTES}; use super::prelude::*; -use crate::diagnostics::{InlineForceInlineConflict, InlineIgnoredForExported}; +use crate::diagnostics::{InlineForceInlineConflict, InlineIgnoredForExported, UnusedMultiple}; -pub(crate) struct InlineParser; +#[derive(Default)] +pub(crate) struct InlineParser(Option<(InlineAttr, Span)>); -impl SingleAttributeParser for InlineParser { - const PATH: &[Symbol] = &[sym::inline]; - const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; - const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ - Allow(Target::Fn), - Allow(Target::Method(MethodKind::Inherent)), - Allow(Target::Method(MethodKind::Trait { body: true })), - Allow(Target::Method(MethodKind::TraitImpl)), - Allow(Target::Closure), - Allow(Target::Delegation { mac: false }), - Warn(Target::Method(MethodKind::Trait { body: false })), - Warn(Target::ForeignFn), - Warn(Target::Field), - Warn(Target::MacroDef), - Warn(Target::Arm), - Warn(Target::AssocConst(AssocCtxt::Impl { of_trait: false })), - Warn(Target::AssocConst(AssocCtxt::Trait)), - Warn(Target::AssocConst(AssocCtxt::Impl { of_trait: true })), - Warn(Target::MacroCall), - ]); - const TEMPLATE: AttributeTemplate = template!( - Word, - List: &["always", "never"], - "https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute" - ); - const STABILITY: AttributeStability = AttributeStability::Stable; +impl AttributeParser for InlineParser { + const ATTRIBUTES: AcceptMapping = &[ + ( + &[sym::inline], + template!( + Word, + List: &["always", "never"], + "https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute" + ), + AttributeStability::Stable, + |this, cx, args| { + cx.check_target( + "", + &AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), + Allow(Target::Closure), + Allow(Target::Delegation { mac: false }), + Warn(Target::Method(MethodKind::Trait { body: false })), + Warn(Target::ForeignFn), + Warn(Target::Field), + Warn(Target::MacroDef), + Warn(Target::Arm), + Warn(Target::AssocConst(AssocCtxt::Impl { of_trait: false })), + Warn(Target::AssocConst(AssocCtxt::Trait)), + Warn(Target::AssocConst(AssocCtxt::Impl { of_trait: true })), + Warn(Target::MacroCall), + ]), + ); - fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - match args { - ArgParser::NoArgs => Some(AttributeKind::Inline(InlineAttr::Hint, cx.attr_span)), - ArgParser::List(list) => { - let l = cx.expect_single(list)?; + let inline_span = cx.attr_span; + let attr = match args { + ArgParser::NoArgs => InlineAttr::Hint, + ArgParser::List(list) => { + let Some(l) = cx.expect_single(list) else { return }; - match l.meta_item_no_args().and_then(|i| i.path().word_sym()) { - Some(sym::always) => { - Some(AttributeKind::Inline(InlineAttr::Always, cx.attr_span)) + match l.meta_item_no_args().and_then(|i| i.path().word_sym()) { + Some(sym::always) => InlineAttr::Always, + Some(sym::never) => InlineAttr::Never, + _ => { + cx.adcx().expected_specific_argument( + l.span(), + &[sym::always, sym::never], + ); + return; + } + } } - Some(sym::never) => { - Some(AttributeKind::Inline(InlineAttr::Never, cx.attr_span)) + ArgParser::NameValue(_) => { + cx.adcx().warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT); + return; } - _ => { - cx.adcx().expected_specific_argument(l.span(), &[sym::always, sym::never]); - None + }; + + match this.0 { + None => this.0 = Some((attr, inline_span)), + Some((InlineAttr::Force { .. }, force_inline_span)) => { + cx.emit_err(InlineForceInlineConflict { inline_span, force_inline_span }); + } + Some((_, first_inline_span)) => { + cx.warn_unused_duplicate_future_error(first_inline_span, inline_span); } } - } - ArgParser::NameValue(_) => { - cx.adcx().warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT); - None - } - } - } + }, + ), + ( + &[sym::rustc_force_inline], + template!(Word, List: &["reason"], NameValueStr: "reason"), + unstable!( + rustc_attrs, + "the `rustc_force_inline` attribute forces a free function to be inlined" + ), + |this, cx, args| { + cx.check_target( + "", + &AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + ]), + ); + let force_inline_span = cx.attr_span; + let reason = match args { + ArgParser::NoArgs => None, + ArgParser::List(list) => { + let Some(l) = cx.expect_single(list) else { return }; - fn finalize_check(cx: &mut FinalizeCheckContext<'_, '_>, attr_span: Span) { - let exported = cx.parsed_attrs.iter().any(|attr| { - let Attribute::Parsed(kind) = attr else { return false }; - kind.is_extern_indicator() - }); - if matches!( - cx.target, - Target::Fn - | Target::Closure - | Target::Method( - MethodKind::Trait { body: true } | MethodKind::TraitImpl | MethodKind::Inherent, - ) - ) && !find_attr!(cx.parsed_attrs, Inline(InlineAttr::Never, _)) - && exported - { - cx.emit_lint(UNUSED_ATTRIBUTES, InlineIgnoredForExported, attr_span); - } - } -} - -pub(crate) struct RustcForceInlineParser; + let Some(reason) = cx.expect_string_literal(l) else { return }; -impl SingleAttributeParser for RustcForceInlineParser { - const PATH: &[Symbol] = &[sym::rustc_force_inline]; - const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ - Allow(Target::Fn), - Allow(Target::Method(MethodKind::Inherent)), - ]); - const STABILITY: AttributeStability = unstable!( - rustc_attrs, - "the `rustc_force_inline` attribute forces a free function to be inlined" - ); - const TEMPLATE: AttributeTemplate = template!(Word, List: &["reason"], NameValueStr: "reason"); + Some(reason) + } + ArgParser::NameValue(v) => cx.expect_string_literal(v), + }; - fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { - let reason = match args { - ArgParser::NoArgs => None, - ArgParser::List(list) => { - let l = cx.expect_single(list)?; + let attr = InlineAttr::Force { attr_span: cx.attr_span, reason }; - let reason = cx.expect_string_literal(l)?; + match this.0 { + None => this.0 = Some((attr, force_inline_span)), + Some((InlineAttr::Force { .. }, first_force_inline_span)) => { + cx.emit_err(UnusedMultiple { + this: force_inline_span, + other: first_force_inline_span, + name: sym::rustc_force_inline, + }); + } + Some((_, inline_span)) => { + cx.emit_err(InlineForceInlineConflict { inline_span, force_inline_span }); + } + } + }, + ), + ]; - Some(reason) - } - ArgParser::NameValue(v) => cx.expect_string_literal(v), - }; + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::ManuallyChecked; - Some(AttributeKind::Inline( - InlineAttr::Force { attr_span: cx.attr_span, reason }, - cx.attr_span, - )) + fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option { + self.0.map(|(a, s)| AttributeKind::Inline(a, s)) } - fn finalize_check(cx: &mut FinalizeCheckContext<'_, '_>, attr_span: Span) { - let Some(inline_span) = find_attr!(cx.parsed_attrs, Inline(attr, span) if !matches!(attr, InlineAttr::Force { .. }) => span) - else { - return; - }; - - cx.emit_err(InlineForceInlineConflict { - inline_span: *inline_span, - force_inline_span: attr_span, - }); + fn deferred_finalize_check( + &self, + ) -> Option<(fn(&mut FinalizeCheckContext<'_, '_>, Span), Span)> { + Some(( + |cx: &mut FinalizeCheckContext<'_, '_>, span| { + let exported = cx.parsed_attrs.iter().any(|attr| { + let Attribute::Parsed(kind) = attr else { return false }; + kind.is_extern_indicator() + }); + if matches!( + cx.target, + Target::Fn + | Target::Closure + | Target::Method( + MethodKind::Trait { body: true } + | MethodKind::TraitImpl + | MethodKind::Inherent, + ) + ) && !find_attr!(cx.parsed_attrs, Inline(InlineAttr::Never, _)) + && exported + { + cx.emit_lint(UNUSED_ATTRIBUTES, InlineIgnoredForExported, span); + } + }, + self.0?.1, + )) } } diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index f5e1868364355..38d1019a4553e 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -167,6 +167,7 @@ attribute_parsers!( ConfusablesParser, ConstStabilityParser, DocParser, + InlineParser, LintParser, MacroUseParser, NakedParser, @@ -213,7 +214,6 @@ attribute_parsers!( Single, Single, Single, - Single, Single, Single, Single, @@ -243,7 +243,6 @@ attribute_parsers!( Single, Single, Single, - Single, Single, Single, Single, diff --git a/tests/ui/attributes/attribute-on-wrong-item-inline-repr.stderr b/tests/ui/attributes/attribute-on-wrong-item-inline-repr.stderr index f3326cb7769b9..7c046990b993d 100644 --- a/tests/ui/attributes/attribute-on-wrong-item-inline-repr.stderr +++ b/tests/ui/attributes/attribute-on-wrong-item-inline-repr.stderr @@ -7,6 +7,15 @@ LL | let _a = 4; | = help: the `inline` attribute can only be applied to functions +error: the `inline` attribute cannot be used on statements + --> $DIR/attribute-on-wrong-item-inline-repr.rs:12:7 + | +LL | #[inline(XYZ)] + | ^^^^^^ +LL | let _b = 4; + | + = help: the `inline` attribute can only be applied to functions + error[E0539]: malformed `inline` attribute input --> $DIR/attribute-on-wrong-item-inline-repr.rs:12:7 | @@ -28,15 +37,6 @@ LL - #[inline(XYZ)] LL + #[inline(never)] | -error: the `inline` attribute cannot be used on statements - --> $DIR/attribute-on-wrong-item-inline-repr.rs:12:7 - | -LL | #[inline(XYZ)] - | ^^^^^^ -LL | let _b = 4; - | - = help: the `inline` attribute can only be applied to functions - error[E0539]: malformed `repr` attribute input --> $DIR/attribute-on-wrong-item-inline-repr.rs:17:7 | @@ -65,6 +65,15 @@ LL | #[repr] | = note: for more information, visit +error: the `inline` attribute cannot be used on expressions + --> $DIR/attribute-on-wrong-item-inline-repr.rs:33:7 + | +LL | #[inline(ABC)] + | ^^^^^^ +LL | foo(); + | + = help: the `inline` attribute can only be applied to functions + error[E0539]: malformed `inline` attribute input --> $DIR/attribute-on-wrong-item-inline-repr.rs:33:7 | @@ -86,15 +95,6 @@ LL - #[inline(ABC)] LL + #[inline(never)] | -error: the `inline` attribute cannot be used on expressions - --> $DIR/attribute-on-wrong-item-inline-repr.rs:33:7 - | -LL | #[inline(ABC)] - | ^^^^^^ -LL | foo(); - | - = help: the `inline` attribute can only be applied to functions - error[E0539]: malformed `repr` attribute input --> $DIR/attribute-on-wrong-item-inline-repr.rs:38:16 |