rust-lang/rust · #164648

parse inline and rustc_force_inline with same parser

mejrs · merged Oct 28, 20263 files · 154 + / 124 −
compiler/rustc_attr_parsing/src/attributes/inline.rs135 + / 104 −
@@ -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<Self> = &[+        (+            &[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<AttributeKind> {-        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<AttributeKind> {-        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<AttributeKind> {+        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,+        ))     } }
compiler/rustc_attr_parsing/src/context.rs1 + / 2 −
@@ -167,6 +167,7 @@ attribute_parsers!(         ConfusablesParser,         ConstStabilityParser,         DocParser,+        InlineParser,         LintParser,         MacroUseParser,         NakedParser,@@ -213,7 +214,6 @@ attribute_parsers!(         Single<DoNotRecommendParser>,         Single<ExportNameParser>,         Single<IgnoreParser>,-        Single<InlineParser>,         Single<InstructionSetParser>,         Single<InstrumentFnParser>,         Single<LangParser>,@@ -243,7 +243,6 @@ attribute_parsers!(         Single<RustcDummyParser>,         Single<RustcDumpDefPathParser>,         Single<RustcDumpSymbolNameParser>,-        Single<RustcForceInlineParser>,         Single<RustcIfThisChangedParser>,         Single<RustcLegacyConstGenericsParser>,         Single<RustcLintOptDenyFieldAccessParser>,
tests/ui/attributes/attribute-on-wrong-item-inline-repr.stderr18 + / 18 −
@@ -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 <https://doc.rust-lang.org/reference/type-layout.html#representations> +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    |