From 767b76db3b15e7b3200834772c11f9c1c43bc4ed Mon Sep 17 00:00:00 2001 From: Taha Dostifam Date: Fri, 24 Jul 2026 19:06:23 +0330 Subject: [PATCH] feat: replaced SingleAttributeParser of Inline and RustcInlineParser with AttributeParser --- .../src/attributes/inline.rs | 255 ++++++++++++------ compiler/rustc_attr_parsing/src/context.rs | 3 +- .../src/session_diagnostics.rs | 10 + tests/ui/attributes/args-checked.stderr | 8 +- ...attribute-on-wrong-item-inline-repr.stderr | 32 +-- 5 files changed, 203 insertions(+), 105 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs index d3b632fae99d9..f071d4c22c735 100644 --- a/compiler/rustc_attr_parsing/src/attributes/inline.rs +++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs @@ -1,97 +1,186 @@ -// FIXME(jdonszelmann): merge these two parsers and error when both attributes are present here. -// note: need to model better how duplicate attr errors work when not using -// SingleAttributeParser which is what we have two of here. - use rustc_feature::AttributeStability; use rustc_hir::attrs::{AttributeKind, InlineAttr}; use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT; use super::prelude::*; +use crate::session_diagnostics::InlineForceInlineConflict; -pub(crate) struct InlineParser; - -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), - 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; - - 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)?; - - match l.meta_item_no_args().and_then(|i| i.path().word_sym()) { - Some(sym::always) => { - Some(AttributeKind::Inline(InlineAttr::Always, cx.attr_span)) - } - Some(sym::never) => { - Some(AttributeKind::Inline(InlineAttr::Never, cx.attr_span)) +#[derive(Default)] +pub(crate) struct InlineParser { + pub(crate) rustc_force_inline: Option, + pub(crate) inline: Option, +} + +pub(crate) struct InlineState { + attr: InlineAttr, + span: Span, +} + +pub(crate) struct ForceInlineState { + reason: Option, + span: Span, +} + +impl AttributeParser for InlineParser { + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::ManuallyChecked; + const ATTRIBUTES: AcceptMapping = &[ + ( + &[sym::inline], + template!( + Word, + List: &["always", "never"], + "https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute" + ), + AttributeStability::Stable, + |group: &mut Self, cx, args| { + let span = cx.attr_span; + + let attr_args = match args { + ArgParser::NoArgs => "".to_string(), + ArgParser::NameValue(_) => "".to_string(), + + ArgParser::List(list) => list + .as_single() + .and_then(|item| { + item.meta_item().and_then(|item| match item.path().word_sym() { + Some(sym::always) => Some("(always)".to_string()), + Some(sym::never) => Some("(never)".to_string()), + _ => None, + }) + }) + .unwrap_or_else(|| "".to_string()), + }; + + let parse_attr = |cx: &mut AcceptContext<'_, '_>| match args { + ArgParser::NoArgs => Some(InlineAttr::Hint), + + ArgParser::List(list) => { + let Some(item) = list.as_single().and_then(|e| e.meta_item()) else { + cx.adcx().expected_single_argument(list.span, list.len()); + return None; + }; + + if item.args().as_no_args().is_err() { + cx.adcx().expected_no_args(item.span()); + return None; + } + + let Some(word) = item.path().word_sym() else { + cx.adcx().warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT); + return None; + }; + + match word { + sym::always => Some(InlineAttr::Always), + sym::never => Some(InlineAttr::Never), + _ => { + cx.adcx().expected_specific_argument( + item.path().span(), + &[sym::always, sym::never], + ); + return None; + } + } } - _ => { - cx.adcx().expected_specific_argument(l.span(), &[sym::always, sym::never]); + + ArgParser::NameValue(_) => { + cx.adcx().warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT); return None; } + }; + + cx.check_target( + &attr_args, + &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), + Warn(Target::MacroCall), + ]), + ); + + if let Some(prev) = &group.inline { + cx.warn_unused_duplicate_future_error(prev.span, span); + return; + } + + if let Some(attr) = parse_attr(cx) { + group.inline = Some(InlineState { attr, span }); + } + }, + ), + ( + &[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" + ), + |group: &mut Self, cx, args| { + let span = cx.attr_span; + + let (reason, attr_args) = match args { + ArgParser::NoArgs => (None, "".to_string()), + ArgParser::List(list) => { + let single = cx.expect_single(list); + let reason = single.and_then(|item| cx.expect_string_literal(item)); + let attr_args = + reason.map_or_else(|| "(...)".to_string(), |sym| format!("({})", sym)); + (reason, attr_args) + } + ArgParser::NameValue(nv) => { + let reason = cx.expect_string_literal(nv); + let attr_args = if let Some(val) = reason { + format!(" = \"{}\"", val) + } else { + " = \"...\"".to_string() + }; + (reason, attr_args) + } + }; + + cx.check_target( + &attr_args, + &AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + ]), + ); + + if let Some(prev) = &group.rustc_force_inline { + cx.warn_unused_duplicate(prev.span, span); + return; } - } - ArgParser::NameValue(_) => { - cx.adcx().warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT); - return None; - } - } - } -} -pub(crate) struct RustcForceInlineParser; - -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"); - - 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 reason = cx.expect_string_literal(l)?; - - Some(reason) - } - ArgParser::NameValue(v) => cx.expect_string_literal(v), - }; + group.rustc_force_inline = Some(ForceInlineState { reason, span }); + }, + ), + ]; - Some(AttributeKind::Inline( - InlineAttr::Force { attr_span: cx.attr_span, reason }, - cx.attr_span, - )) + fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option { + match (self.inline, self.rustc_force_inline) { + (Some(inline), None) => Some(AttributeKind::Inline(inline.attr, inline.span)), + (None, Some(force_inline)) => Some(AttributeKind::Inline( + InlineAttr::Force { attr_span: force_inline.span, reason: force_inline.reason }, + force_inline.span, + )), + (Some(inline), Some(force_inline)) => { + cx.emit_err(InlineForceInlineConflict { + inline_span: inline.span, + force_inline_span: force_inline.span, + }); + None + } + (None, None) => None, + } } } diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 8ca8dee86d562..3fe66500188a1 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -165,6 +165,7 @@ attribute_parsers!( ConfusablesParser, ConstStabilityParser, DocParser, + InlineParser, MacroUseParser, NakedParser, OnConstParser, @@ -210,7 +211,6 @@ attribute_parsers!( Single, Single, Single, - Single, Single, Single, Single, @@ -239,7 +239,6 @@ attribute_parsers!( Single, Single, Single, - Single, Single, Single, Single, diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index fb984912b1936..d0a9750c9d92d 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -13,6 +13,16 @@ use rustc_target::spec::TargetTuple; use crate::AttributeTemplate; use crate::context::Suggestion; +#[derive(Diagnostic)] +#[diag("`#[inline]` and `#[rustc_force_inline]` cannot be used together")] +pub(crate) struct InlineForceInlineConflict { + #[primary_span] + pub inline_span: Span, + + #[primary_span] + pub force_inline_span: Span, +} + #[derive(Diagnostic)] #[diag("`#[ffi_const]` function cannot be `#[ffi_pure]`", code = E0757)] pub(crate) struct BothFfiConstAndPure { diff --git a/tests/ui/attributes/args-checked.stderr b/tests/ui/attributes/args-checked.stderr index fd6b0f43f88d2..7d269b6641bc1 100644 --- a/tests/ui/attributes/args-checked.stderr +++ b/tests/ui/attributes/args-checked.stderr @@ -26,13 +26,13 @@ LL - #![test_runner(x(x,y,z))] LL + #![test_runner(path)] | -error[E0539]: malformed `inline` attribute input +error[E0565]: malformed `inline` attribute input --> $DIR/args-checked.rs:12:3 | LL | #[inline(always = 5)] | ^^^^^^^----------^ | | - | valid arguments are `always` or `never` + | didn't expect any arguments here | = note: for more information, visit help: try changing it to one of the following valid forms of the attribute @@ -47,13 +47,13 @@ LL - #[inline(always = 5)] LL + #[inline] | -error[E0539]: malformed `inline` attribute input +error[E0565]: malformed `inline` attribute input --> $DIR/args-checked.rs:14:3 | LL | #[inline(always(x, y, z))] | ^^^^^^^---------------^ | | - | valid arguments are `always` or `never` + | didn't expect any arguments here | = note: for more information, visit help: try changing it to one of the following valid forms of the attribute 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 9200d1978312f..264b7faaf12ef 100644 --- a/tests/ui/attributes/attribute-on-wrong-item-inline-repr.stderr +++ b/tests/ui/attributes/attribute-on-wrong-item-inline-repr.stderr @@ -6,6 +6,14 @@ LL | #[inline] | = 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)] + | ^^^^^^ + | + = 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 | @@ -27,14 +35,6 @@ LL - #[inline(XYZ)] LL + #[inline] | -error: the `inline` attribute cannot be used on statements - --> $DIR/attribute-on-wrong-item-inline-repr.rs:12:7 - | -LL | #[inline(XYZ)] - | ^^^^^^ - | - = 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 | @@ -63,6 +63,14 @@ 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)] + | ^^^^^^ + | + = 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 | @@ -84,14 +92,6 @@ LL - #[inline(ABC)] LL + #[inline] | -error: the `inline` attribute cannot be used on expressions - --> $DIR/attribute-on-wrong-item-inline-repr.rs:33:7 - | -LL | #[inline(ABC)] - | ^^^^^^ - | - = 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 |