Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
255 changes: 172 additions & 83 deletions compiler/rustc_attr_parsing/src/attributes/inline.rs
Original file line number Diff line number Diff line change
@@ -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<AttributeKind> {
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<ForceInlineState>,
pub(crate) inline: Option<InlineState>,
}

pub(crate) struct InlineState {
attr: InlineAttr,
span: Span,
}

pub(crate) struct ForceInlineState {
reason: Option<Symbol>,
span: Span,
}

impl AttributeParser for InlineParser {
const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::ManuallyChecked;
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,
|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<AttributeKind> {
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<AttributeKind> {
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,
}
}
}
3 changes: 1 addition & 2 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ attribute_parsers!(
ConfusablesParser,
ConstStabilityParser,
DocParser,
InlineParser,
MacroUseParser,
NakedParser,
OnConstParser,
Expand Down Expand Up @@ -210,7 +211,6 @@ attribute_parsers!(
Single<DoNotRecommendParser>,
Single<ExportNameParser>,
Single<IgnoreParser>,
Single<InlineParser>,
Single<InstructionSetParser>,
Single<InstrumentFnParser>,
Single<LangParser>,
Expand Down Expand Up @@ -239,7 +239,6 @@ attribute_parsers!(
Single<RustcDummyParser>,
Single<RustcDumpDefPathParser>,
Single<RustcDumpSymbolNameParser>,
Single<RustcForceInlineParser>,
Single<RustcIfThisChangedParser>,
Single<RustcLegacyConstGenericsParser>,
Single<RustcLintOptDenyFieldAccessParser>,
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_attr_parsing/src/session_diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
8 changes: 4 additions & 4 deletions tests/ui/attributes/args-checked.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute>
help: try changing it to one of the following valid forms of the attribute
Expand All @@ -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 <https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute>
help: try changing it to one of the following valid forms of the attribute
Expand Down
Loading
Loading