Skip to content

Rewrite inline attribute parser to use new infrastructure and improve diagnostics for all parsed attributes #138165

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jun 18, 2025
Merged
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
5 changes: 4 additions & 1 deletion compiler/rustc_attr_data_structures/src/attributes.rs
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ use thin_vec::ThinVec;

use crate::{DefaultBodyStability, PartialConstStability, PrintAttribute, RustcVersion, Stability};

#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, PrintAttribute)]
pub enum InlineAttr {
None,
Hint,
@@ -221,6 +221,9 @@ pub enum AttributeKind {
/// Represents [`#[doc]`](https://doc.rust-lang.org/stable/rustdoc/write-documentation/the-doc-attribute.html).
DocComment { style: AttrStyle, kind: CommentKind, span: Span, comment: Symbol },

/// Represents `#[inline]` and `#[rustc_force_inline]`.
Inline(InlineAttr, Span),

/// Represents `#[rustc_macro_transparency]`.
MacroTransparency(Transparency),

1 change: 1 addition & 0 deletions compiler/rustc_attr_data_structures/src/lints.rs
Original file line number Diff line number Diff line change
@@ -11,4 +11,5 @@ pub struct AttributeLint<Id> {
#[derive(Clone, Debug, HashStable_Generic)]
pub enum AttributeLintKind {
UnusedDuplicate { this: Span, other: Span, warning: bool },
IllFormedAttributeInput { suggestions: Vec<String> },
}
14 changes: 5 additions & 9 deletions compiler/rustc_attr_parsing/messages.ftl
Original file line number Diff line number Diff line change
@@ -23,8 +23,10 @@ attr_parsing_expects_feature_list =
attr_parsing_expects_features =
`{$name}` expects feature names

attr_parsing_incorrect_meta_item = expected a quoted string literal
attr_parsing_incorrect_meta_item_suggestion = consider surrounding this with quotes
attr_parsing_ill_formed_attribute_input = {$num_suggestions ->
[1] attribute must be of the form {$suggestions}
*[other] valid forms for the attribute are {$suggestions}
}

attr_parsing_incorrect_repr_format_align_one_arg =
incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses
@@ -81,9 +83,6 @@ attr_parsing_missing_note =
attr_parsing_missing_since =
missing 'since'

attr_parsing_multiple_item =
multiple '{$item}' items

attr_parsing_multiple_stability_levels =
multiple stability levels

@@ -122,10 +121,6 @@ attr_parsing_unsupported_literal_cfg_boolean =
literal in `cfg` predicate value must be a boolean
attr_parsing_unsupported_literal_cfg_string =
literal in `cfg` predicate value must be a string
attr_parsing_unsupported_literal_deprecated_kv_pair =
item in `deprecated` must be a key/value pair
attr_parsing_unsupported_literal_deprecated_string =
literal in `deprecated` value must be a string
attr_parsing_unsupported_literal_generic =
unsupported literal
attr_parsing_unsupported_literal_suggestion =
@@ -136,6 +131,7 @@ attr_parsing_unused_duplicate =
.suggestion = remove this attribute
.note = attribute also specified here
.warn = {-passes_previously_accepted}

attr_parsing_unused_multiple =
multiple `{$name}` attributes
.suggestion = remove this attribute
3 changes: 3 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::iter;

use rustc_attr_data_structures::AttributeKind;
use rustc_feature::{AttributeTemplate, template};
use rustc_span::{Span, Symbol, sym};

use super::{CombineAttributeParser, ConvertFn};
@@ -13,6 +14,7 @@ impl<S: Stage> CombineAttributeParser<S> for AllowInternalUnstableParser {
const PATH: &[Symbol] = &[sym::allow_internal_unstable];
type Item = (Symbol, Span);
const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowInternalUnstable;
const TEMPLATE: AttributeTemplate = template!(Word, List: "feat1, feat2, ...");

fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
@@ -29,6 +31,7 @@ impl<S: Stage> CombineAttributeParser<S> for AllowConstFnUnstableParser {
const PATH: &[Symbol] = &[sym::rustc_allow_const_fn_unstable];
type Item = Symbol;
const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowConstFnUnstable;
const TEMPLATE: AttributeTemplate = template!(Word, List: "feat1, feat2, ...");

fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
53 changes: 25 additions & 28 deletions compiler/rustc_attr_parsing/src/attributes/confusables.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use rustc_attr_data_structures::AttributeKind;
use rustc_feature::template;
use rustc_span::{Span, Symbol, sym};
use thin_vec::ThinVec;

@@ -13,37 +14,33 @@ pub(crate) struct ConfusablesParser {
}

impl<S: Stage> AttributeParser<S> for ConfusablesParser {
const ATTRIBUTES: AcceptMapping<Self, S> = &[(&[sym::rustc_confusables], |this, cx, args| {
let Some(list) = args.list() else {
// FIXME(jdonszelmann): error when not a list? Bring validation code here.
// NOTE: currently subsequent attributes are silently ignored using
// tcx.get_attr().
return;
};

if list.is_empty() {
cx.emit_err(session_diagnostics::EmptyConfusables { span: cx.attr_span });
}

for param in list.mixed() {
let span = param.span();

let Some(lit) = param.lit() else {
cx.emit_err(session_diagnostics::IncorrectMetaItem {
span,
suggestion: Some(session_diagnostics::IncorrectMetaItemSuggestion {
lo: span.shrink_to_lo(),
hi: span.shrink_to_hi(),
}),
});
continue;
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
&[sym::rustc_confusables],
template!(List: r#""name1", "name2", ..."#),
|this, cx, args| {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span);
return;
};

this.confusables.push(lit.symbol);
}
if list.is_empty() {
cx.emit_err(session_diagnostics::EmptyConfusables { span: cx.attr_span });
}

for param in list.mixed() {
let span = param.span();

let Some(lit) = param.lit().and_then(|i| i.value_str()) else {
cx.expected_string_literal(span, param.lit());
continue;
};

this.confusables.push(lit);
}

this.first_span.get_or_insert(cx.attr_span);
})];
this.first_span.get_or_insert(cx.attr_span);
},
)];

fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
if self.confusables.is_empty() {
111 changes: 56 additions & 55 deletions compiler/rustc_attr_parsing/src/attributes/deprecation.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use rustc_attr_data_structures::{AttributeKind, DeprecatedSince, Deprecation};
use rustc_feature::{AttributeTemplate, template};
use rustc_span::{Span, Symbol, sym};

use super::util::parse_version;
use super::{AttributeOrder, OnDuplicate, SingleAttributeParser};
use crate::context::{AcceptContext, Stage};
use crate::parser::ArgParser;
use crate::session_diagnostics;
use crate::session_diagnostics::UnsupportedLiteralReason;

pub(crate) struct DeprecationParser;

@@ -18,25 +18,18 @@ fn get<S: Stage>(
item: &Option<Symbol>,
) -> Option<Symbol> {
if item.is_some() {
cx.emit_err(session_diagnostics::MultipleItem { span: param_span, item: name.to_string() });
cx.duplicate_key(param_span, name);
return None;
}
if let Some(v) = arg.name_value() {
if let Some(value_str) = v.value_as_str() {
Some(value_str)
} else {
let lit = v.value_as_lit();
cx.emit_err(session_diagnostics::UnsupportedLiteral {
span: v.value_span,
reason: UnsupportedLiteralReason::DeprecatedString,
is_bytestr: lit.kind.is_bytestr(),
start_point_span: cx.sess().source_map().start_point(lit.span),
});
cx.expected_string_literal(v.value_span, Some(&v.value_as_lit()));
None
}
} else {
// FIXME(jdonszelmann): suggestion?
cx.emit_err(session_diagnostics::IncorrectMetaItem { span: param_span, suggestion: None });
cx.expected_name_value(param_span, Some(name));
None
}
}
@@ -45,6 +38,11 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
const PATH: &[Symbol] = &[sym::deprecated];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const TEMPLATE: AttributeTemplate = template!(
Word,
List: r#"/*opt*/ since = "version", /*opt*/ note = "reason""#,
NameValueStr: "reason"
);

fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let features = cx.features();
@@ -55,57 +53,60 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {

let is_rustc = features.staged_api();

if let Some(value) = args.name_value()
&& let Some(value_str) = value.value_as_str()
{
note = Some(value_str)
} else if let Some(list) = args.list() {
for param in list.mixed() {
let param_span = param.span();
let Some(param) = param.meta_item() else {
cx.emit_err(session_diagnostics::UnsupportedLiteral {
span: param_span,
reason: UnsupportedLiteralReason::DeprecatedKvPair,
is_bytestr: false,
start_point_span: cx.sess().source_map().start_point(param_span),
});
return None;
};
match args {
ArgParser::NoArgs => {
// ok
}
ArgParser::List(list) => {
for param in list.mixed() {
let Some(param) = param.meta_item() else {
cx.unexpected_literal(param.span());
return None;
};

let ident_name = param.path().word_sym();
let ident_name = param.path().word_sym();

match ident_name {
Some(name @ sym::since) => {
since = Some(get(cx, name, param_span, param.args(), &since)?);
}
Some(name @ sym::note) => {
note = Some(get(cx, name, param_span, param.args(), &note)?);
}
Some(name @ sym::suggestion) => {
if !features.deprecated_suggestion() {
cx.emit_err(session_diagnostics::DeprecatedItemSuggestion {
span: param_span,
is_nightly: cx.sess().is_nightly_build(),
details: (),
});
match ident_name {
Some(name @ sym::since) => {
since = Some(get(cx, name, param.span(), param.args(), &since)?);
}
Some(name @ sym::note) => {
note = Some(get(cx, name, param.span(), param.args(), &note)?);
}
Some(name @ sym::suggestion) => {
if !features.deprecated_suggestion() {
cx.emit_err(session_diagnostics::DeprecatedItemSuggestion {
span: param.span(),
is_nightly: cx.sess().is_nightly_build(),
details: (),
});
}

suggestion = Some(get(cx, name, param_span, param.args(), &suggestion)?);
}
_ => {
cx.emit_err(session_diagnostics::UnknownMetaItem {
span: param_span,
item: param.path().to_string(),
expected: if features.deprecated_suggestion() {
&["since", "note", "suggestion"]
} else {
&["since", "note"]
},
});
return None;
suggestion =
Some(get(cx, name, param.span(), param.args(), &suggestion)?);
}
_ => {
cx.unknown_key(
param.span(),
param.path().to_string(),
if features.deprecated_suggestion() {
&["since", "note", "suggestion"]
} else {
&["since", "note"]
},
);
return None;
}
}
}
}
ArgParser::NameValue(v) => {
let Some(value) = v.value_as_str() else {
cx.expected_string_literal(v.value_span, Some(v.value_as_lit()));
return None;
};
note = Some(value);
}
}

let since = if let Some(since) = since {
8 changes: 4 additions & 4 deletions compiler/rustc_attr_parsing/src/attributes/inline.rs
Original file line number Diff line number Diff line change
@@ -29,7 +29,7 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
return None;
};

match l.meta_item().and_then(|i| i.word_without_args().map(|i| i.name)) {
match l.meta_item().and_then(|i| i.path().word_sym()) {
Some(sym::always) => {
Some(AttributeKind::Inline(InlineAttr::Always, cx.attr_span))
}
@@ -63,7 +63,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcForceInlineParser {
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
const TEMPLATE: AttributeTemplate = template!(Word, List: "reason", NameValueStr: "reason");

fn convert(cx: &AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let reason = match args {
ArgParser::NoArgs => None,
ArgParser::List(list) => {
@@ -73,15 +73,15 @@ impl<S: Stage> SingleAttributeParser<S> for RustcForceInlineParser {
};

let Some(reason) = l.lit().and_then(|i| i.kind.str()) else {
cx.expected_string_literal(l.span());
cx.expected_string_literal(l.span(), l.lit());
return None;
};

Some(reason)
}
ArgParser::NameValue(v) => {
let Some(reason) = v.value_as_str() else {
cx.expected_string_literal(v.value_span);
cx.expected_string_literal(v.value_span, Some(v.value_as_lit()));
return None;
};

4 changes: 2 additions & 2 deletions compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use rustc_attr_data_structures::AttributeKind;
use rustc_feature::{AttributeTemplate, template};
use rustc_span::{Symbol, sym};

use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
@@ -9,10 +10,9 @@ pub(crate) struct AsPtrParser;

impl<S: Stage> SingleAttributeParser<S> for AsPtrParser {
const PATH: &[Symbol] = &[sym::rustc_as_ptr];

const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;

const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const TEMPLATE: AttributeTemplate = template!(Word);

fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
// FIXME: check that there's no args (this is currently checked elsewhere)
26 changes: 20 additions & 6 deletions compiler/rustc_attr_parsing/src/attributes/mod.rs
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@ 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;

@@ -29,14 +30,15 @@ pub(crate) mod allow_unstable;
pub(crate) mod cfg;
pub(crate) mod confusables;
pub(crate) mod deprecation;
pub(crate) mod inline;
pub(crate) mod lint_helpers;
pub(crate) mod repr;
pub(crate) mod stability;
pub(crate) mod transparency;
pub(crate) mod util;

type AcceptFn<T, S> = for<'sess> fn(&mut T, &mut AcceptContext<'_, 'sess, S>, &ArgParser<'_>);
type AcceptMapping<T, S> = &'static [(&'static [Symbol], AcceptFn<T, S>)];
type AcceptMapping<T, S> = &'static [(&'static [Symbol], AttributeTemplate, AcceptFn<T, S>)];

/// An [`AttributeParser`] is a type which searches for syntactic attributes.
///
@@ -88,6 +90,9 @@ pub(crate) trait SingleAttributeParser<S: Stage>: 'static {
const ATTRIBUTE_ORDER: AttributeOrder;
const ON_DUPLICATE: OnDuplicate<S>;

/// The template this attribute parser should implement. Used for diagnostics.
const TEMPLATE: AttributeTemplate;

/// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`]
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind>;
}
@@ -104,8 +109,10 @@ impl<T: SingleAttributeParser<S>, S: Stage> Default for Single<T, S> {
}

impl<T: SingleAttributeParser<S>, S: Stage> AttributeParser<S> for Single<T, S> {
const ATTRIBUTES: AcceptMapping<Self, S> =
&[(T::PATH, |group: &mut Single<T, S>, cx, args| {
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
T::PATH,
<T as SingleAttributeParser<S>>::TEMPLATE,
|group: &mut Single<T, S>, cx, args| {
if let Some(pa) = T::convert(cx, args) {
match T::ATTRIBUTE_ORDER {
// keep the first and report immediately. ignore this attribute
@@ -126,7 +133,8 @@ impl<T: SingleAttributeParser<S>, S: Stage> AttributeParser<S> for Single<T, S>

group.1 = Some((pa, cx.attr_span));
}
})];
},
)];

fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
Some(self.1?.0)
@@ -223,6 +231,9 @@ pub(crate) trait CombineAttributeParser<S: Stage>: 'static {
type Item;
const CONVERT: ConvertFn<Self::Item>;

/// The template this attribute parser should implement. Used for diagnostics.
const TEMPLATE: AttributeTemplate;

/// Converts a single syntactical attribute to a number of elements of the semantic attribute, or [`AttributeKind`]
fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
@@ -242,8 +253,11 @@ impl<T: CombineAttributeParser<S>, S: Stage> Default for Combine<T, S> {
}

impl<T: CombineAttributeParser<S>, S: Stage> AttributeParser<S> for Combine<T, S> {
const ATTRIBUTES: AcceptMapping<Self, S> =
&[(T::PATH, |group: &mut Combine<T, S>, cx, args| group.1.extend(T::extend(cx, args)))];
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
T::PATH,
<T as CombineAttributeParser<S>>::TEMPLATE,
|group: &mut Combine<T, S>, cx, args| group.1.extend(T::extend(cx, args)),
)];

fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
if self.1.is_empty() { None } else { Some(T::CONVERT(self.1)) }
4 changes: 4 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/repr.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use rustc_abi::Align;
use rustc_ast::{IntTy, LitIntType, LitKind, UintTy};
use rustc_attr_data_structures::{AttributeKind, IntType, ReprAttr};
use rustc_feature::{AttributeTemplate, template};
use rustc_span::{DUMMY_SP, Span, Symbol, sym};

use super::{CombineAttributeParser, ConvertFn};
@@ -23,6 +24,8 @@ impl<S: Stage> CombineAttributeParser<S> for ReprParser {
type Item = (ReprAttr, Span);
const PATH: &[Symbol] = &[sym::repr];
const CONVERT: ConvertFn<Self::Item> = AttributeKind::Repr;
// FIXME(jdonszelmann): never used
const TEMPLATE: AttributeTemplate = template!(List: "C");

fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
@@ -31,6 +34,7 @@ impl<S: Stage> CombineAttributeParser<S> for ReprParser {
let mut reprs = Vec::new();

let Some(list) = args.list() else {
cx.expected_list(cx.attr_span);
return reprs;
};

113 changes: 69 additions & 44 deletions compiler/rustc_attr_parsing/src/attributes/stability.rs
Original file line number Diff line number Diff line change
@@ -5,7 +5,8 @@ use rustc_attr_data_structures::{
StableSince, UnstableReason, VERSION_PLACEHOLDER,
};
use rustc_errors::ErrorGuaranteed;
use rustc_span::{Span, Symbol, sym};
use rustc_feature::{AttributeTemplate, template};
use rustc_span::{Ident, Span, Symbol, sym};

use super::util::parse_version;
use super::{AcceptMapping, AttributeOrder, AttributeParser, OnDuplicate, SingleAttributeParser};
@@ -43,26 +44,39 @@ impl StabilityParser {

impl<S: Stage> AttributeParser<S> for StabilityParser {
const ATTRIBUTES: AcceptMapping<Self, S> = &[
(&[sym::stable], |this, cx, args| {
reject_outside_std!(cx);
if !this.check_duplicate(cx)
&& let Some((feature, level)) = parse_stability(cx, args)
{
this.stability = Some((Stability { level, feature }, cx.attr_span));
}
}),
(&[sym::unstable], |this, cx, args| {
reject_outside_std!(cx);
if !this.check_duplicate(cx)
&& let Some((feature, level)) = parse_unstability(cx, args)
{
this.stability = Some((Stability { level, feature }, cx.attr_span));
}
}),
(&[sym::rustc_allowed_through_unstable_modules], |this, cx, args| {
reject_outside_std!(cx);
this.allowed_through_unstable_modules = args.name_value().and_then(|i| i.value_as_str())
}),
(
&[sym::stable],
template!(List: r#"feature = "name", since = "version""#),
|this, cx, args| {
reject_outside_std!(cx);
if !this.check_duplicate(cx)
&& let Some((feature, level)) = parse_stability(cx, args)
{
this.stability = Some((Stability { level, feature }, cx.attr_span));
}
},
),
(
&[sym::unstable],
template!(List: r#"feature = "name", reason = "...", issue = "N""#),
|this, cx, args| {
reject_outside_std!(cx);
if !this.check_duplicate(cx)
&& let Some((feature, level)) = parse_unstability(cx, args)
{
this.stability = Some((Stability { level, feature }, cx.attr_span));
}
},
),
(
&[sym::rustc_allowed_through_unstable_modules],
template!(NameValueStr: "deprecation message"),
|this, cx, args| {
reject_outside_std!(cx);
this.allowed_through_unstable_modules =
args.name_value().and_then(|i| i.value_as_str())
},
),
];

fn finalize(mut self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
@@ -96,16 +110,19 @@ pub(crate) struct BodyStabilityParser {
}

impl<S: Stage> AttributeParser<S> for BodyStabilityParser {
const ATTRIBUTES: AcceptMapping<Self, S> =
&[(&[sym::rustc_default_body_unstable], |this, cx, args| {
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
&[sym::rustc_default_body_unstable],
template!(List: r#"feature = "name", reason = "...", issue = "N""#),
|this, cx, args| {
reject_outside_std!(cx);
if this.stability.is_some() {
cx.dcx()
.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span });
} else if let Some((feature, level)) = parse_unstability(cx, args) {
this.stability = Some((DefaultBodyStability { level, feature }, cx.attr_span));
}
})];
},
)];

fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
let (stability, span) = self.stability?;
@@ -120,6 +137,7 @@ impl<S: Stage> SingleAttributeParser<S> for ConstStabilityIndirectParser {
const PATH: &[Symbol] = &[sym::rustc_const_stable_indirect];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Ignore;
const TEMPLATE: AttributeTemplate = template!(Word);

fn convert(_cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
Some(AttributeKind::ConstStabilityIndirect)
@@ -146,7 +164,7 @@ impl ConstStabilityParser {

impl<S: Stage> AttributeParser<S> for ConstStabilityParser {
const ATTRIBUTES: AcceptMapping<Self, S> = &[
(&[sym::rustc_const_stable], |this, cx, args| {
(&[sym::rustc_const_stable], template!(List: r#"feature = "name""#), |this, cx, args| {
reject_outside_std!(cx);

if !this.check_duplicate(cx)
@@ -158,7 +176,7 @@ impl<S: Stage> AttributeParser<S> for ConstStabilityParser {
));
}
}),
(&[sym::rustc_const_unstable], |this, cx, args| {
(&[sym::rustc_const_unstable], template!(List: r#"feature = "name""#), |this, cx, args| {
reject_outside_std!(cx);
if !this.check_duplicate(cx)
&& let Some((feature, level)) = parse_unstability(cx, args)
@@ -169,7 +187,7 @@ impl<S: Stage> AttributeParser<S> for ConstStabilityParser {
));
}
}),
(&[sym::rustc_promotable], |this, cx, _| {
(&[sym::rustc_promotable], template!(Word), |this, cx, _| {
reject_outside_std!(cx);
this.promotable = true;
}),
@@ -199,23 +217,18 @@ fn insert_value_into_option_or_error<S: Stage>(
cx: &AcceptContext<'_, '_, S>,
param: &MetaItemParser<'_>,
item: &mut Option<Symbol>,
name: Ident,
) -> Option<()> {
if item.is_some() {
cx.emit_err(session_diagnostics::MultipleItem {
span: param.span(),
item: param.path().to_string(),
});
cx.duplicate_key(name.span, name.name);
None
} else if let Some(v) = param.args().name_value()
&& let Some(s) = v.value_as_str()
{
*item = Some(s);
Some(())
} else {
cx.emit_err(session_diagnostics::IncorrectMetaItem {
span: param.span(),
suggestion: None,
});
cx.expected_name_value(param.span(), Some(name.name));
None
}
}
@@ -241,9 +254,14 @@ pub(crate) fn parse_stability<S: Stage>(
return None;
};

match param.path().word_sym() {
Some(sym::feature) => insert_value_into_option_or_error(cx, &param, &mut feature)?,
Some(sym::since) => insert_value_into_option_or_error(cx, &param, &mut since)?,
let word = param.path().word();
match word.map(|i| i.name) {
Some(sym::feature) => {
insert_value_into_option_or_error(cx, &param, &mut feature, word.unwrap())?
}
Some(sym::since) => {
insert_value_into_option_or_error(cx, &param, &mut since, word.unwrap())?
}
_ => {
cx.emit_err(session_diagnostics::UnknownMetaItem {
span: param_span,
@@ -310,11 +328,16 @@ pub(crate) fn parse_unstability<S: Stage>(
return None;
};

match param.path().word_sym() {
Some(sym::feature) => insert_value_into_option_or_error(cx, &param, &mut feature)?,
Some(sym::reason) => insert_value_into_option_or_error(cx, &param, &mut reason)?,
let word = param.path().word();
match word.map(|i| i.name) {
Some(sym::feature) => {
insert_value_into_option_or_error(cx, &param, &mut feature, word.unwrap())?
}
Some(sym::reason) => {
insert_value_into_option_or_error(cx, &param, &mut reason, word.unwrap())?
}
Some(sym::issue) => {
insert_value_into_option_or_error(cx, &param, &mut issue)?;
insert_value_into_option_or_error(cx, &param, &mut issue, word.unwrap())?;

// These unwraps are safe because `insert_value_into_option_or_error` ensures the meta item
// is a name/value pair string literal.
@@ -344,9 +367,11 @@ pub(crate) fn parse_unstability<S: Stage>(
is_soft = true;
}
Some(sym::implied_by) => {
insert_value_into_option_or_error(cx, &param, &mut implied_by)?
insert_value_into_option_or_error(cx, &param, &mut implied_by, word.unwrap())?
}
Some(sym::old_name) => {
insert_value_into_option_or_error(cx, &param, &mut old_name, word.unwrap())?
}
Some(sym::old_name) => insert_value_into_option_or_error(cx, &param, &mut old_name)?,
_ => {
cx.emit_err(session_diagnostics::UnknownMetaItem {
span: param.span(),
16 changes: 13 additions & 3 deletions compiler/rustc_attr_parsing/src/attributes/transparency.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use rustc_attr_data_structures::AttributeKind;
use rustc_feature::{AttributeTemplate, template};
use rustc_span::hygiene::Transparency;
use rustc_span::{Symbol, sym};

@@ -17,14 +18,23 @@ impl<S: Stage> SingleAttributeParser<S> for TransparencyParser {
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Custom(|cx, used, unused| {
cx.dcx().span_err(vec![used, unused], "multiple macro transparency attributes");
});
const TEMPLATE: AttributeTemplate =
template!(NameValueStr: "transparent|semitransparent|opaque");

fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
match args.name_value().and_then(|nv| nv.value_as_str()) {
let Some(nv) = args.name_value() else {
cx.expected_name_value(cx.attr_span, None);
return None;
};
match nv.value_as_str() {
Some(sym::transparent) => Some(Transparency::Transparent),
Some(sym::semiopaque | sym::semitransparent) => Some(Transparency::SemiOpaque),
Some(sym::opaque) => Some(Transparency::Opaque),
Some(other) => {
cx.dcx().span_err(cx.attr_span, format!("unknown macro transparency: `{other}`"));
Some(_) => {
cx.expected_specific_argument_strings(
nv.value_span,
vec!["transparent", "semitransparent", "opaque"],
);
None
}
None => None,
181 changes: 159 additions & 22 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
@@ -5,19 +5,19 @@ use std::ops::{Deref, DerefMut};
use std::sync::LazyLock;

use private::Sealed;
use rustc_ast as ast;
use rustc_ast::NodeId;
use rustc_ast::{self as ast, MetaItemLit, NodeId};
use rustc_attr_data_structures::AttributeKind;
use rustc_attr_data_structures::lints::{AttributeLint, AttributeLintKind};
use rustc_errors::{DiagCtxtHandle, Diagnostic};
use rustc_feature::Features;
use rustc_feature::{AttributeTemplate, Features};
use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, HirId};
use rustc_session::Session;
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};

use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
use crate::attributes::confusables::ConfusablesParser;
use crate::attributes::deprecation::DeprecationParser;
use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
use crate::attributes::lint_helpers::AsPtrParser;
use crate::attributes::repr::ReprParser;
use crate::attributes::stability::{
@@ -26,11 +26,12 @@ use crate::attributes::stability::{
use crate::attributes::transparency::TransparencyParser;
use crate::attributes::{AttributeParser as _, Combine, Single};
use crate::parser::{ArgParser, MetaItemParser};
use crate::session_diagnostics::{AttributeParseError, AttributeParseErrorReason, UnknownMetaItem};

macro_rules! group_type {
($stage: ty) => {
LazyLock<(
BTreeMap<&'static [Symbol], Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $stage>, &ArgParser<'a>) + Send + Sync>>,
BTreeMap<&'static [Symbol], Vec<(AttributeTemplate, Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $stage>, &ArgParser<'a>) + Send + Sync>)>>,
Vec<Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, $stage>) -> Option<AttributeKind>>>
)>
};
@@ -59,21 +60,20 @@ macro_rules! attribute_parsers {
@[$ty: ty] pub(crate) static $name: ident = [$($names: ty),* $(,)?];
) => {
pub(crate) static $name: group_type!($ty) = LazyLock::new(|| {
let mut accepts = BTreeMap::<_, Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $ty>, &ArgParser<'a>) + Send + Sync>>::new();
let mut accepts = BTreeMap::<_, Vec<(AttributeTemplate, Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $ty>, &ArgParser<'a>) + Send + Sync>)>>::new();
let mut finalizes = Vec::<Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, $ty>) -> Option<AttributeKind>>>::new();
$(
{
thread_local! {
static STATE_OBJECT: RefCell<$names> = RefCell::new(<$names>::default());
};

for (k, v) in <$names>::ATTRIBUTES {
let old = accepts.insert(*k, Box::new(|cx, args| {
for (path, template, accept_fn) in <$names>::ATTRIBUTES {
accepts.entry(*path).or_default().push((*template, Box::new(|cx, args| {
STATE_OBJECT.with_borrow_mut(|s| {
v(s, cx, args)
accept_fn(s, cx, args)
})
}));
assert!(old.is_none());
})));
}

finalizes.push(Box::new(|cx| {
@@ -106,6 +106,8 @@ attribute_parsers!(
Single<AsPtrParser>,
Single<ConstStabilityIndirectParser>,
Single<DeprecationParser>,
Single<InlineParser>,
Single<RustcForceInlineParser>,
Single<TransparencyParser>,
// tidy-alphabetical-end
];
@@ -165,17 +167,148 @@ pub(crate) struct AcceptContext<'f, 'sess, S: Stage> {
pub(crate) finalize_cx: FinalizeContext<'f, 'sess, S>,
/// The span of the attribute currently being parsed
pub(crate) attr_span: Span,

/// The expected structure of the attribute.
///
/// Used in reporting errors to give a hint to users what the attribute *should* look like.
pub(crate) template: &'f AttributeTemplate,

/// The name of the attribute we're currently accepting.
pub(crate) attr_path: AttrPath,
}

impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed {
S::emit_err(&self.sess, diag)
}

/// Emit a lint. This method is somewhat special, since lints emitted during attribute parsing
/// must be delayed until after HIR is built. This method will take care of the details of
/// that.
pub(crate) fn emit_lint(&mut self, lint: AttributeLintKind, span: Span) {
let id = self.target_id;
(self.emit_lint)(AttributeLint { id, span, kind: lint });
}

pub(crate) fn unknown_key(
&self,
span: Span,
found: String,
options: &'static [&'static str],
) -> ErrorGuaranteed {
self.emit_err(UnknownMetaItem { span, item: found, expected: options })
}

/// error that a string literal was expected.
/// You can optionally give the literal you did find (which you found not to be a string literal)
/// which can make better errors. For example, if the literal was a byte string it will suggest
/// removing the `b` prefix.
pub(crate) fn expected_string_literal(
&self,
span: Span,
actual_literal: Option<&MetaItemLit>,
) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedStringLiteral {
byte_string: actual_literal.and_then(|i| {
i.kind.is_bytestr().then(|| self.sess().source_map().start_point(i.span))
}),
},
})
}

pub(crate) fn expected_list(&self, span: Span) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedList,
})
}

/// emit an error that a `name = value` pair was expected at this span. The symbol can be given for
/// a nicer error message talking about the specific name that was found lacking a value.
pub(crate) fn expected_name_value(&self, span: Span, name: Option<Symbol>) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedNameValue(name),
})
}

/// emit an error that a `name = value` pair was found where that name was already seen.
pub(crate) fn duplicate_key(&self, span: Span, key: Symbol) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::DuplicateKey(key),
})
}

/// an error that should be emitted when a [`MetaItemOrLitParser`](crate::parser::MetaItemOrLitParser)
/// was expected *not* to be a literal, but instead a meta item.
pub(crate) fn unexpected_literal(&self, span: Span) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::UnexpectedLiteral,
})
}

pub(crate) fn expected_single_argument(&self, span: Span) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedSingleArgument,
})
}

pub(crate) fn expected_specific_argument(
&self,
span: Span,
possibilities: Vec<&'static str>,
) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedSpecificArgument {
possibilities,
strings: false,
},
})
}

pub(crate) fn expected_specific_argument_strings(
&self,
span: Span,
possibilities: Vec<&'static str>,
) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedSpecificArgument {
possibilities,
strings: true,
},
})
}
}

impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> {
@@ -374,18 +507,22 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
let args = parser.args();
let parts = path.segments().map(|i| i.name).collect::<Vec<_>>();

if let Some(accept) = S::parsers().0.get(parts.as_slice()) {
let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
finalize_cx: FinalizeContext {
cx: self,
target_span,
target_id,
emit_lint: &mut emit_lint,
},
attr_span: lower_span(attr.span),
};

accept(&mut cx, args)
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 {
cx: self,
target_span,
target_id,
emit_lint: &mut emit_lint,
},
attr_span: lower_span(attr.span),
template,
attr_path: path.get_attribute_path(),
};

accept(&mut cx, args)
}
} else {
// If we're here, we must be compiling a tool attribute... Or someone
// forgot to parse their fancy new attribute. Let's warn them in any case.
15 changes: 14 additions & 1 deletion compiler/rustc_attr_parsing/src/lints.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use rustc_attr_data_structures::lints::{AttributeLint, AttributeLintKind};
use rustc_errors::LintEmitter;
use rustc_errors::{DiagArgValue, LintEmitter};
use rustc_hir::HirId;

use crate::session_diagnostics;
@@ -15,5 +15,18 @@ pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<HirId>, lint_emi
*span,
session_diagnostics::UnusedDuplicate { this, other, warning },
),
AttributeLintKind::IllFormedAttributeInput { suggestions } => {
lint_emitter.emit_node_span_lint(
rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT,
*id,
*span,
session_diagnostics::IllFormedAttributeInput {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
),
},
);
}
}
}
166 changes: 126 additions & 40 deletions compiler/rustc_attr_parsing/src/session_diagnostics.rs
Original file line number Diff line number Diff line change
@@ -2,7 +2,11 @@ use std::num::IntErrorKind;

use rustc_ast as ast;
use rustc_errors::codes::*;
use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level};
use rustc_errors::{
Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
};
use rustc_feature::AttributeTemplate;
use rustc_hir::AttrPath;
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_span::{Span, Symbol};

@@ -12,8 +16,6 @@ pub(crate) enum UnsupportedLiteralReason {
Generic,
CfgString,
CfgBoolean,
DeprecatedString,
DeprecatedKvPair,
}

#[derive(Diagnostic)]
@@ -32,37 +34,6 @@ pub(crate) struct InvalidPredicate {
pub predicate: String,
}

#[derive(Diagnostic)]
#[diag(attr_parsing_multiple_item, code = E0538)]
pub(crate) struct MultipleItem {
#[primary_span]
pub span: Span,

pub item: String,
}

#[derive(Diagnostic)]
#[diag(attr_parsing_incorrect_meta_item, code = E0539)]
pub(crate) struct IncorrectMetaItem {
#[primary_span]
pub span: Span,

#[subdiagnostic]
pub suggestion: Option<IncorrectMetaItemSuggestion>,
}

#[derive(Subdiagnostic)]
#[multipart_suggestion(
attr_parsing_incorrect_meta_item_suggestion,
applicability = "maybe-incorrect"
)]
pub(crate) struct IncorrectMetaItemSuggestion {
#[suggestion_part(code = "\"")]
pub lo: Span,
#[suggestion_part(code = "\"")]
pub hi: Span,
}

/// Error code: E0541
pub(crate) struct UnknownMetaItem<'a> {
pub span: Span,
@@ -217,6 +188,7 @@ pub(crate) struct InvalidReprHintNoValue {
}

/// Error code: E0565
// FIXME(jdonszelmann): slowly phased out
pub(crate) struct UnsupportedLiteral {
pub span: Span,
pub reason: UnsupportedLiteralReason,
@@ -239,12 +211,6 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnsupportedLiteral {
UnsupportedLiteralReason::CfgBoolean => {
fluent::attr_parsing_unsupported_literal_cfg_boolean
}
UnsupportedLiteralReason::DeprecatedString => {
fluent::attr_parsing_unsupported_literal_deprecated_string
}
UnsupportedLiteralReason::DeprecatedKvPair => {
fluent::attr_parsing_unsupported_literal_deprecated_kv_pair
}
},
);
diag.span(self.span);
@@ -462,6 +428,14 @@ pub(crate) struct UnusedDuplicate {
pub warning: bool,
}

// FIXME(jdonszelmann): duplicated in rustc_lints, should be moved here completely.
#[derive(LintDiagnostic)]
#[diag(attr_parsing_ill_formed_attribute_input)]
pub(crate) struct IllFormedAttributeInput {
pub num_suggestions: usize,
pub suggestions: DiagArgValue,
}

#[derive(Diagnostic)]
#[diag(attr_parsing_stability_outside_std, code = E0734)]
pub(crate) struct StabilityOutsideStd {
@@ -490,3 +464,115 @@ pub(crate) struct UnrecognizedReprHint {
#[primary_span]
pub span: Span,
}

pub(crate) enum AttributeParseErrorReason {
ExpectedStringLiteral { byte_string: Option<Span> },
ExpectedSingleArgument,
ExpectedList,
UnexpectedLiteral,
ExpectedNameValue(Option<Symbol>),
DuplicateKey(Symbol),
ExpectedSpecificArgument { possibilities: Vec<&'static str>, strings: bool },
}

pub(crate) struct AttributeParseError {
pub(crate) span: Span,
pub(crate) attr_span: Span,
pub(crate) template: AttributeTemplate,
pub(crate) attribute: AttrPath,
pub(crate) reason: AttributeParseErrorReason,
}

impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
let name = self.attribute.to_string();

let mut diag = Diag::new(dcx, level, format!("malformed `{name}` attribute input"));
diag.span(self.attr_span);
diag.code(E0539);
match self.reason {
AttributeParseErrorReason::ExpectedStringLiteral { byte_string } => {
if let Some(start_point_span) = byte_string {
diag.span_suggestion(
start_point_span,
fluent::attr_parsing_unsupported_literal_suggestion,
"",
Applicability::MaybeIncorrect,
);
diag.note("expected a normal string literal, not a byte string literal");

return diag;
} else {
diag.span_label(self.span, "expected a string literal here");
}
}
AttributeParseErrorReason::ExpectedSingleArgument => {
diag.span_label(self.span, "expected a single argument here");
diag.code(E0805);
}
AttributeParseErrorReason::ExpectedList => {
diag.span_label(self.span, "expected this to be a list");
}
AttributeParseErrorReason::DuplicateKey(key) => {
diag.span_label(self.span, format!("found `{key}` used as a key more than once"));
diag.code(E0538);
}
AttributeParseErrorReason::UnexpectedLiteral => {
diag.span_label(self.span, format!("didn't expect a literal here"));
diag.code(E0565);
}
AttributeParseErrorReason::ExpectedNameValue(None) => {
diag.span_label(
self.span,
format!("expected this to be of the form `{name} = \"...\"`"),
);
}
AttributeParseErrorReason::ExpectedNameValue(Some(name)) => {
diag.span_label(
self.span,
format!("expected this to be of the form `{name} = \"...\"`"),
);
}
AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings } => {
let quote = if strings { '"' } else { '`' };
match possibilities.as_slice() {
&[] => {}
&[x] => {
diag.span_label(
self.span,
format!("the only valid argument here is {quote}{x}{quote}"),
);
}
[first, second] => {
diag.span_label(self.span, format!("valid arguments are {quote}{first}{quote} or {quote}{second}{quote}"));
}
[first @ .., second_to_last, last] => {
let mut res = String::new();
for i in first {
res.push_str(&format!("{quote}{i}{quote}, "));
}
res.push_str(&format!(
"{quote}{second_to_last}{quote} or {quote}{last}{quote}"
));

diag.span_label(self.span, format!("valid arguments are {res}"));
}
}
}
}

let suggestions = self.template.suggestions(false, &name);
diag.span_suggestions(
self.attr_span,
if suggestions.len() == 1 {
"must be of the form"
} else {
"try changing it to one of the following valid forms of the attribute"
},
suggestions,
Applicability::HasPlaceholders,
);

diag
}
}
57 changes: 11 additions & 46 deletions compiler/rustc_codegen_ssa/src/codegen_attrs.rs
Original file line number Diff line number Diff line change
@@ -4,7 +4,9 @@ use rustc_abi::ExternAbi;
use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode};
use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr};
use rustc_attr_data_structures::ReprAttr::ReprAlign;
use rustc_attr_data_structures::{AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr};
use rustc_attr_data_structures::{
AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr, find_attr,
};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
@@ -21,7 +23,6 @@ use rustc_session::parse::feature_err;
use rustc_session::{Session, lint};
use rustc_span::{Ident, Span, sym};
use rustc_target::spec::SanitizerSet;
use tracing::debug;

use crate::errors;
use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature_attr};
@@ -83,7 +84,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {

let rust_target_features = tcx.rust_target_features(LOCAL_CRATE);

let mut inline_span = None;
let mut link_ordinal_span = None;
let mut no_sanitize_span = None;
let mut mixed_export_name_no_mangle_lint_state = MixedExportNameAndNoMangleState::default();
@@ -449,48 +449,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {

mixed_export_name_no_mangle_lint_state.lint_if_mixed(tcx);

codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| {
if !attr.has_name(sym::inline) {
return ia;
}

if attr.is_word() {
return InlineAttr::Hint;
}
let Some(ref items) = attr.meta_item_list() else {
return ia;
};
inline_span = Some(attr.span());

let [item] = &items[..] else {
tcx.dcx().emit_err(errors::ExpectedOneArgument { span: attr.span() });
return InlineAttr::None;
};

if item.has_name(sym::always) {
InlineAttr::Always
} else if item.has_name(sym::never) {
InlineAttr::Never
} else {
tcx.dcx().emit_err(errors::InvalidArgument { span: items[0].span() });

InlineAttr::None
}
});
codegen_fn_attrs.inline = attrs.iter().fold(codegen_fn_attrs.inline, |ia, attr| {
if !attr.has_name(sym::rustc_force_inline) || !tcx.features().rustc_attrs() {
return ia;
}

if attr.is_word() {
InlineAttr::Force { attr_span: attr.span(), reason: None }
} else if let Some(val) = attr.value_str() {
InlineAttr::Force { attr_span: attr.span(), reason: Some(val) }
} else {
debug!("`rustc_force_inline` not checked by attribute validation");
ia
}
});
let inline_span;
(codegen_fn_attrs.inline, inline_span) = if let Some((inline_attr, span)) =
find_attr!(attrs, AttributeKind::Inline(i, span) => (*i, *span))
{
(inline_attr, Some(span))
} else {
(InlineAttr::None, None)
};

// naked function MUST NOT be inlined! This attribute is required for the rust compiler itself,
// but not for the code generation backend because at that point the naked function will just be
@@ -511,7 +477,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
return OptimizeAttr::Default;
};

inline_span = Some(attr.span());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes #137950

let [item] = &items[..] else {
tcx.dcx().emit_err(errors::ExpectedOneArgumentOptimize { span: attr.span() });
return OptimizeAttr::Default;
15 changes: 0 additions & 15 deletions compiler/rustc_codegen_ssa/src/errors.rs
Original file line number Diff line number Diff line change
@@ -208,28 +208,13 @@ pub(crate) struct OutOfRangeInteger {
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(codegen_ssa_expected_one_argument, code = E0534)]
pub(crate) struct ExpectedOneArgument {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(codegen_ssa_expected_one_argument, code = E0722)]
pub(crate) struct ExpectedOneArgumentOptimize {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(codegen_ssa_invalid_argument, code = E0535)]
#[help]
pub(crate) struct InvalidArgument {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(codegen_ssa_invalid_argument, code = E0722)]
pub(crate) struct InvalidArgumentOptimize {
8 changes: 7 additions & 1 deletion compiler/rustc_error_codes/src/error_codes/E0534.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
#### Note: this error code is no longer emitted by the compiler

This is because it was too specific to the `inline` attribute.
Similar diagnostics occur for other attributes too.
The example here will now emit `E0805`

The `inline` attribute was malformed.

Erroneous code example:

```compile_fail,E0534
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no longer emitted because it's specific to inline. I kind of want to avoid those in favor of generic attribute related error codes

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

```compile_fail,E0805
#[inline()] // error: expected one argument
pub fn something() {}
9 changes: 7 additions & 2 deletions compiler/rustc_error_codes/src/error_codes/E0535.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
An unknown argument was given to the `inline` attribute.
#### Note: this error code is no longer emitted by the compiler

This is because it was too specific to the `inline` attribute.
Similar diagnostics occur for other attributes too.
The example here will now emit `E0539`


Erroneous code example:

```compile_fail,E0535
```compile_fail,E0539
#[inline(unknown)] // error: invalid argument
pub fn something() {}
29 changes: 27 additions & 2 deletions compiler/rustc_error_codes/src/error_codes/E0539.md
Original file line number Diff line number Diff line change
@@ -24,8 +24,7 @@ struct Stable;
const fn stable_fn() {}
```

Meta items are the key-value pairs inside of an attribute.
To fix these issues you need to give required key-value pairs.
To fix the above example, you can write the following:

```
#![feature(staged_api)]
@@ -49,3 +48,29 @@ struct Stable;
#[rustc_const_stable(feature = "stable_fn", since = "1.39.0")] // ok!
const fn stable_fn() {}
```
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error code upgraded to represent the more generic issue of the structure of an attribute not matching the expected structure


Several causes of this are,
an attribute may have expected you to give a list but you gave a
`name = value` pair:

```compile_fail,E0539
// wrong, should be `#[repr(C)]`
#[repr = "C"]
struct Foo {}
```

Or a `name = value` pair, but you gave a list:

```compile_fail,E0539
// wrong, should be `note = "reason"`
#[deprecated(since = "1.0.0", note("reason"))]
struct Foo {}
```

Or it expected some specific word but you gave an unexpected one:

```compile_fail,E0539
// should be `always` or `never`
#[inline(maybe_if_you_feel_like_it)]
fn foo() {}
```
7 changes: 3 additions & 4 deletions compiler/rustc_error_codes/src/error_codes/E0565.md
Original file line number Diff line number Diff line change
@@ -9,10 +9,9 @@ struct Repr {}
fn main() {}
```

Literals in attributes are new and largely unsupported in built-in attributes.
Work to support literals where appropriate is ongoing. Try using an unquoted
name instead:

Not all attributes support literals in their input,
and in some cases they expect an identifier instead.
That would be the solution in the case of `repr`:
```
#[repr(C)] // ok!
struct Repr {}
26 changes: 26 additions & 0 deletions compiler/rustc_error_codes/src/error_codes/E0805.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
An attribute was given an invalid number of arguments

Erroneous code example:

```compile_fail,E0805
#[inline()] // error! should either have a single argument, or no parentheses
fn foo() {}
#[inline(always, never)] // error! should have only one argument, not two
fn bar() {}
```

To fix this, either give the right number of arguments the attribute needs.
In the case of inline, this could be none at all:

```
#[inline]
fn foo() {}
```

or only one:

```
#[inline(always)]
fn foo() {}
```
1 change: 1 addition & 0 deletions compiler/rustc_error_codes/src/lib.rs
Original file line number Diff line number Diff line change
@@ -547,6 +547,7 @@ E0801: 0801,
E0802: 0802,
E0803: 0803,
E0804: 0804,
E0805: 0805,
);
)
}
40 changes: 31 additions & 9 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
@@ -111,6 +111,7 @@ pub enum AttributeGate {
Ungated,
}

// FIXME(jdonszelmann): move to rustc_attr_data_structures
/// A template that the attribute input must match.
/// Only top-level shape (`#[attr]` vs `#[attr(...)]` vs `#[attr = ...]`) is considered now.
#[derive(Clone, Copy, Default)]
@@ -127,6 +128,26 @@ pub struct AttributeTemplate {
pub name_value_str: Option<&'static str>,
}

impl AttributeTemplate {
pub fn suggestions(&self, inner: bool, name: impl std::fmt::Display) -> Vec<String> {
let mut suggestions = vec![];
let inner = if inner { "!" } else { "" };
if self.word {
suggestions.push(format!("#{inner}[{name}]"));
}
if let Some(descr) = self.list {
suggestions.push(format!("#{inner}[{name}({descr})]"));
}
suggestions.extend(self.one_of.iter().map(|&word| format!("#{inner}[{name}({word})]")));
if let Some(descr) = self.name_value_str {
suggestions.push(format!("#{inner}[{name} = \"{descr}\"]"));
}
suggestions.sort();

suggestions
}
}

/// How to handle multiple duplicate attributes on the same item.
#[derive(Clone, Copy, Default)]
pub enum AttributeDuplicates {
@@ -181,20 +202,21 @@ pub enum AttributeDuplicates {
/// A convenience macro for constructing attribute templates.
/// E.g., `template!(Word, List: "description")` means that the attribute
/// supports forms `#[attr]` and `#[attr(description)]`.
#[macro_export]
macro_rules! template {
(Word) => { template!(@ true, None, &[], None) };
(List: $descr: expr) => { template!(@ false, Some($descr), &[], None) };
(OneOf: $one_of: expr) => { template!(@ false, None, $one_of, None) };
(NameValueStr: $descr: expr) => { template!(@ false, None, &[], Some($descr)) };
(Word, List: $descr: expr) => { template!(@ true, Some($descr), &[], None) };
(Word, NameValueStr: $descr: expr) => { template!(@ true, None, &[], Some($descr)) };
(Word) => { $crate::template!(@ true, None, &[], None) };
(List: $descr: expr) => { $crate::template!(@ false, Some($descr), &[], None) };
(OneOf: $one_of: expr) => { $crate::template!(@ false, None, $one_of, None) };
(NameValueStr: $descr: expr) => { $crate::template!(@ false, None, &[], Some($descr)) };
(Word, List: $descr: expr) => { $crate::template!(@ true, Some($descr), &[], None) };
(Word, NameValueStr: $descr: expr) => { $crate::template!(@ true, None, &[], Some($descr)) };
(List: $descr1: expr, NameValueStr: $descr2: expr) => {
template!(@ false, Some($descr1), &[], Some($descr2))
$crate::template!(@ false, Some($descr1), &[], Some($descr2))
};
(Word, List: $descr1: expr, NameValueStr: $descr2: expr) => {
template!(@ true, Some($descr1), &[], Some($descr2))
$crate::template!(@ true, Some($descr1), &[], Some($descr2))
};
(@ $word: expr, $list: expr, $one_of: expr, $name_value_str: expr) => { AttributeTemplate {
(@ $word: expr, $list: expr, $one_of: expr, $name_value_str: expr) => { $crate::AttributeTemplate {
word: $word, list: $list, one_of: $one_of, name_value_str: $name_value_str
} };
}
1 change: 1 addition & 0 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
@@ -2618,6 +2618,7 @@ pub(crate) struct UnusedCrateDependency {
pub local_crate: Symbol,
}

// FIXME(jdonszelmann): duplicated in rustc_attr_parsing, should be moved there completely.
#[derive(LintDiagnostic)]
#[diag(lint_ill_formed_attribute_input)]
pub(crate) struct IllFormedAttributeInput {
17 changes: 14 additions & 3 deletions compiler/rustc_parse/src/validate_attr.rs
Original file line number Diff line number Diff line change
@@ -282,11 +282,22 @@ fn emit_malformed_attribute(
name: Symbol,
template: AttributeTemplate,
) {
// attrs with new parsers are locally validated so excluded here
if matches!(
name,
sym::inline
| sym::rustc_force_inline
| sym::rustc_confusables
| sym::repr
| sym::deprecated
) {
return;
}

// Some of previously accepted forms were used in practice,
// report them as warnings for now.
let should_warn = |name| {
matches!(name, sym::doc | sym::ignore | sym::inline | sym::link | sym::test | sym::bench)
};
let should_warn =
|name| matches!(name, sym::doc | sym::ignore | sym::link | sym::test | sym::bench);

let error_msg = format!("malformed `{name}` attribute input");
let mut suggestions = vec![];
107 changes: 64 additions & 43 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ use std::collections::hash_map::Entry;

use rustc_abi::{Align, ExternAbi, Size};
use rustc_ast::{AttrStyle, LitKind, MetaItemInner, MetaItemKind, MetaItemLit, ast};
use rustc_attr_data_structures::{AttributeKind, ReprAttr, find_attr};
use rustc_attr_data_structures::{AttributeKind, InlineAttr, ReprAttr, find_attr};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{Applicability, DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey};
use rustc_feature::{AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute};
@@ -124,6 +124,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
AttributeKind::Stability { span, .. }
| AttributeKind::ConstStability { span, .. },
) => self.check_stability_promotable(*span, target),
Attribute::Parsed(AttributeKind::Inline(InlineAttr::Force { .. }, ..)) => {} // handled separately below
Attribute::Parsed(AttributeKind::Inline(kind, attr_span)) => {
self.check_inline(hir_id, *attr_span, span, kind, target)
}
Attribute::Parsed(AttributeKind::AllowInternalUnstable(syms)) => self
.check_allow_internal_unstable(
hir_id,
@@ -158,7 +162,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
[sym::diagnostic, sym::on_unimplemented, ..] => {
self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target)
}
[sym::inline, ..] => self.check_inline(hir_id, attr, span, target),
[sym::coverage, ..] => self.check_coverage(attr, span, target),
[sym::optimize, ..] => self.check_optimize(hir_id, attr, span, target),
[sym::no_sanitize, ..] => {
@@ -367,11 +370,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
self.check_rustc_force_inline(hir_id, attrs, span, target);
}

fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr_span: Span, sym: &str) {
self.tcx.emit_node_span_lint(
UNUSED_ATTRIBUTES,
hir_id,
attr.span(),
attr_span,
errors::IgnoredAttrWithMacro { sym },
);
}
@@ -431,7 +434,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}

/// Checks if an `#[inline]` is applied to a function or a closure.
fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
fn check_inline(
&self,
hir_id: HirId,
attr_span: Span,
defn_span: Span,
kind: &InlineAttr,
target: Target,
) {
match target {
Target::Fn
| Target::Closure
@@ -440,7 +450,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
self.tcx.emit_node_span_lint(
UNUSED_ATTRIBUTES,
hir_id,
attr.span(),
attr_span,
errors::IgnoredInlineAttrFnProto,
)
}
@@ -451,33 +461,30 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
Target::AssocConst => self.tcx.emit_node_span_lint(
UNUSED_ATTRIBUTES,
hir_id,
attr.span(),
attr_span,
errors::IgnoredInlineAttrConstants,
),
// FIXME(#80564): Same for fields, arms, and macro defs
Target::Field | Target::Arm | Target::MacroDef => {
self.inline_attr_str_error_with_macro_def(hir_id, attr, "inline")
self.inline_attr_str_error_with_macro_def(hir_id, attr_span, "inline")
}
_ => {
self.dcx().emit_err(errors::InlineNotFnOrClosure {
attr_span: attr.span(),
defn_span: span,
});
self.dcx().emit_err(errors::InlineNotFnOrClosure { attr_span, defn_span });
}
}

// `#[inline]` is ignored if the symbol must be codegened upstream because it's exported.
if let Some(did) = hir_id.as_owner()
&& self.tcx.def_kind(did).has_codegen_attrs()
&& !matches!(attr.meta_item_list().as_deref(), Some([item]) if item.has_name(sym::never))
&& kind != &InlineAttr::Never
{
let attrs = self.tcx.codegen_fn_attrs(did);
// Not checking naked as `#[inline]` is forbidden for naked functions anyways.
if attrs.contains_extern_indicator() {
self.tcx.emit_node_span_lint(
UNUSED_ATTRIBUTES,
hir_id,
attr.span(),
attr_span,
errors::InlineIgnoredForExported {},
);
}
@@ -676,6 +683,16 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
) => {
continue;
}
Attribute::Parsed(AttributeKind::Inline(.., span)) => {
self.dcx().emit_err(errors::NakedFunctionIncompatibleAttribute {
span: *span,
naked_span: attr.span(),
attr: sym::inline.to_string(),
});

return;
}
// FIXME(jdonszelmann): make exhaustive
_ => {}
}

@@ -787,7 +804,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
// with crates depending on them, we can't throw an error here.
Target::Field | Target::Arm | Target::MacroDef => {
for attr in attrs {
self.inline_attr_str_error_with_macro_def(hir_id, attr, "track_caller");
self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "track_caller");
}
}
_ => {
@@ -830,7 +847,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
// erroneously allowed it and some crates used it accidentally, to be compatible
// with crates depending on them, we can't throw an error here.
Target::Field | Target::Arm | Target::MacroDef => {
self.inline_attr_str_error_with_macro_def(hir_id, attr, "non_exhaustive");
self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "non_exhaustive");
}
_ => {
self.dcx().emit_err(errors::NonExhaustiveWrongLocation {
@@ -850,7 +867,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
// erroneously allowed it and some crates used it accidentally, to be compatible
// with crates depending on them, we can't throw an error here.
Target::Field | Target::Arm | Target::MacroDef => {
self.inline_attr_str_error_with_macro_def(hir_id, attr, "marker");
self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "marker");
}
_ => {
self.dcx().emit_err(errors::AttrShouldBeAppliedToTrait {
@@ -904,7 +921,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
// erroneously allowed it and some crates used it accidentally, to be compatible
// with crates depending on them, we can't throw an error here.
Target::Field | Target::Arm | Target::MacroDef => {
self.inline_attr_str_error_with_macro_def(hir_id, attr, "target_feature");
self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "target_feature");
}
_ => {
self.dcx().emit_err(errors::AttrShouldBeAppliedToFn {
@@ -1619,7 +1636,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
// erroneously allowed it and some crates used it accidentally, to be compatible
// with crates depending on them, we can't throw an error here.
Target::Field | Target::Arm | Target::MacroDef => {
self.inline_attr_str_error_with_macro_def(hir_id, attr, "cold");
self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "cold");
}
_ => {
// FIXME: #[cold] was previously allowed on non-functions and some crates used
@@ -1661,7 +1678,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
// erroneously allowed it and some crates used it accidentally, to be compatible
// with crates depending on them, we can't throw an error here.
Target::Field | Target::Arm | Target::MacroDef => {
self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_name");
self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "link_name");
}
_ => {
// FIXME: #[cold] was previously allowed on non-functions/statics and some crates
@@ -1695,7 +1712,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
// erroneously allowed it and some crates used it accidentally, to be compatible
// with crates depending on them, we can't throw an error here.
Target::Field | Target::Arm | Target::MacroDef => {
self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_link");
self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "no_link");
}
_ => {
self.dcx().emit_err(errors::NoLink { attr_span: attr.span(), span });
@@ -1717,7 +1734,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
// erroneously allowed it and some crates used it accidentally, to be compatible
// with crates depending on them, we can't throw an error here.
Target::Field | Target::Arm | Target::MacroDef => {
self.inline_attr_str_error_with_macro_def(hir_id, attr, "export_name");
self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "export_name");
}
_ => {
self.dcx().emit_err(errors::ExportName { attr_span: attr.span(), span });
@@ -1891,7 +1908,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
// erroneously allowed it and some crates used it accidentally, to be compatible
// with crates depending on them, we can't throw an error here.
Target::Field | Target::Arm | Target::MacroDef => {
self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_section");
self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "link_section");
}
_ => {
// FIXME: #[link_section] was previously allowed on non-functions/statics and some
@@ -1916,7 +1933,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
// erroneously allowed it and some crates used it accidentally, to be compatible
// with crates depending on them, we can't throw an error here.
Target::Field | Target::Arm | Target::MacroDef => {
self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_mangle");
self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "no_mangle");
}
// FIXME: #[no_mangle] was previously allowed on non-functions/statics, this should be an error
// The error should specify that the item that is wrong is specifically a *foreign* fn/static
@@ -2263,9 +2280,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
// `#[allow_internal_unstable]` attribute with just a lint, because we previously
// erroneously allowed it and some crates used it accidentally, to be compatible
// with crates depending on them, we can't throw an error here.
Target::Field | Target::Arm | Target::MacroDef => {
self.inline_attr_str_error_with_macro_def(hir_id, attr, "allow_internal_unstable")
}
Target::Field | Target::Arm | Target::MacroDef => self
.inline_attr_str_error_with_macro_def(
hir_id,
attr.span(),
"allow_internal_unstable",
),
_ => {
self.tcx
.dcx()
@@ -2638,8 +2658,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
span: Span,
target: Target,
) {
let force_inline_attr = attrs.iter().find(|attr| attr.has_name(sym::rustc_force_inline));
match (target, force_inline_attr) {
match (
target,
find_attr!(attrs, AttributeKind::Inline(InlineAttr::Force { attr_span, .. }, _) => *attr_span),
) {
(Target::Closure, None) => {
let is_coro = matches!(
self.tcx.hir_expect_expr(hir_id).kind,
@@ -2651,20 +2673,19 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
);
let parent_did = self.tcx.hir_get_parent_item(hir_id).to_def_id();
let parent_span = self.tcx.def_span(parent_did);
let parent_force_inline_attr =
self.tcx.get_attr(parent_did, sym::rustc_force_inline);
if let Some(attr) = parent_force_inline_attr
&& is_coro

if let Some(attr_span) = find_attr!(
self.tcx.get_all_attrs(parent_did),
AttributeKind::Inline(InlineAttr::Force { attr_span, .. }, _) => *attr_span
) && is_coro
{
self.dcx().emit_err(errors::RustcForceInlineCoro {
attr_span: attr.span(),
span: parent_span,
});
self.dcx()
.emit_err(errors::RustcForceInlineCoro { attr_span, span: parent_span });
}
}
(Target::Fn, _) => (),
(_, Some(attr)) => {
self.dcx().emit_err(errors::RustcForceInline { attr_span: attr.span(), span });
(_, Some(attr_span)) => {
self.dcx().emit_err(errors::RustcForceInline { attr_span, span });
}
(_, None) => (),
}
@@ -2885,10 +2906,9 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>) {
let attrs = tcx.hir_attrs(item.hir_id());

for attr in attrs {
if attr.has_name(sym::inline) {
tcx.dcx().emit_err(errors::NonExportedMacroInvalidAttrs { attr_span: attr.span() });
}
if let Some(attr_span) = find_attr!(attrs, AttributeKind::Inline(i, span) if !matches!(i, InlineAttr::Force{..}) => *span)
{
tcx.dcx().emit_err(errors::NonExportedMacroInvalidAttrs { attr_span });
}
}

@@ -2908,6 +2928,7 @@ pub(crate) fn provide(providers: &mut Providers) {
*providers = Providers { check_mod_attrs, ..*providers };
}

// FIXME(jdonszelmann): remove, check during parsing
fn check_duplicates(
tcx: TyCtxt<'_>,
attr: &Attribute,
4 changes: 2 additions & 2 deletions src/rustdoc-json-types/lib.rs
Original file line number Diff line number Diff line change
@@ -37,8 +37,8 @@ pub type FxHashMap<K, V> = HashMap<K, V>; // re-export for use in src/librustdoc
// will instead cause conflicts. See #94591 for more. (This paragraph and the "Latest feature" line
// are deliberately not in a doc comment, because they need not be in public docs.)
//
// Latest feature: rustdoc JSON: Don't apply #[repr] privacy heuristics
pub const FORMAT_VERSION: u32 = 46;
// Latest feature: Pretty printing of inline attributes changed
pub const FORMAT_VERSION: u32 = 48;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uh, we skipped 47. Oh well, not much to be done about it now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GuillaumeGomez: when you add the (tidy?) check for updating the number and comment in tandem, could you also add a check that the number is being increased by one?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I opened #142677 for that. But that's a good point. Added to my TODO list.

Copy link
Member

@aDotInTheVoid aDotInTheVoid Jun 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#138165 (comment), earlier it was thaught that someone else was going to do 46->47, to this should do 47->48. I think the takeaways are:

a) Don't try to be too clever and do a double bounce
b) #142601 should fix this. 🤞

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've published rustdoc-types = "0.48.0", with a note that 0.47.0/format_version=47 will never exist. https://github.com/rust-lang/rustdoc-types/blob/trunk/CHANGELOG.md#v0.48.0

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've published rustdoc-types = "0.48.0", with a note that 0.47.0/format_version=47 will never exist. https://github.com/rust-lang/rustdoc-types/blob/trunk/CHANGELOG.md#v0.48.0


/// The root of the emitted JSON blob.
///
25 changes: 9 additions & 16 deletions src/tools/clippy/clippy_lints/src/attrs/inline_always.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,22 @@
use super::INLINE_ALWAYS;
use super::utils::is_word;
use clippy_utils::diagnostics::span_lint;
use rustc_attr_data_structures::{find_attr, AttributeKind, InlineAttr};
use rustc_hir::Attribute;
use rustc_lint::LateContext;
use rustc_span::symbol::Symbol;
use rustc_span::{Span, sym};
use rustc_span::Span;

pub(super) fn check(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute]) {
if span.from_expansion() {
return;
}

for attr in attrs {
if let Some(values) = attr.meta_item_list() {
if values.len() != 1 || !attr.has_name(sym::inline) {
continue;
}
if is_word(&values[0], sym::always) {
span_lint(
cx,
INLINE_ALWAYS,
attr.span(),
format!("you have declared `#[inline(always)]` on `{name}`. This is usually a bad idea"),
);
}
}
if let Some(span) = find_attr!(attrs, AttributeKind::Inline(InlineAttr::Always, span) => *span) {
span_lint(
cx,
INLINE_ALWAYS,
span,
format!("you have declared `#[inline(always)]` on `{name}`. This is usually a bad idea"),
);
}
}
12 changes: 8 additions & 4 deletions src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::sugg::DiagExt;
use rustc_attr_data_structures::{find_attr, AttributeKind};
use rustc_errors::Applicability;
use rustc_hir::{TraitFn, TraitItem, TraitItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
use rustc_span::sym;

declare_clippy_lint! {
/// ### What it does
@@ -32,15 +32,19 @@ declare_lint_pass!(InlineFnWithoutBody => [INLINE_FN_WITHOUT_BODY]);
impl<'tcx> LateLintPass<'tcx> for InlineFnWithoutBody {
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
if let TraitItemKind::Fn(_, TraitFn::Required(_)) = item.kind
&& let Some(attr) = cx.tcx.hir_attrs(item.hir_id()).iter().find(|a| a.has_name(sym::inline))
&& let Some(attr_span) = find_attr!(cx
.tcx
.hir_attrs(item.hir_id()),
AttributeKind::Inline(_, span) => *span
)
{
span_lint_and_then(
cx,
INLINE_FN_WITHOUT_BODY,
attr.span(),
attr_span,
format!("use of `#[inline]` on trait method `{}` which has no body", item.ident),
|diag| {
diag.suggest_remove_item(cx, attr.span(), "remove", Applicability::MachineApplicable);
diag.suggest_remove_item(cx, attr_span, "remove", Applicability::MachineApplicable);
},
);
}
6 changes: 3 additions & 3 deletions src/tools/clippy/clippy_lints/src/missing_inline.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use clippy_utils::diagnostics::span_lint;
use rustc_attr_data_structures::{find_attr, AttributeKind};
use rustc_hir as hir;
use rustc_hir::Attribute;
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::AssocItemContainer;
use rustc_session::declare_lint_pass;
use rustc_span::{Span, sym};
use rustc_span::Span;

declare_clippy_lint! {
/// ### What it does
@@ -64,8 +65,7 @@ declare_clippy_lint! {
}

fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[Attribute], sp: Span, desc: &'static str) {
let has_inline = attrs.iter().any(|a| a.has_name(sym::inline));
if !has_inline {
if !find_attr!(attrs, AttributeKind::Inline(..)) {
span_lint(
cx,
MISSING_INLINE_IN_PUBLIC_ITEMS,
14 changes: 8 additions & 6 deletions src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
Original file line number Diff line number Diff line change
@@ -3,10 +3,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy};
use clippy_utils::{is_self, is_self_ty};
use rustc_attr_data_structures::{find_attr, AttributeKind, InlineAttr};
use rustc_data_structures::fx::FxHashSet;
use core::ops::ControlFlow;
use rustc_abi::ExternAbi;
use rustc_ast::attr;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::intravisit::FnKind;
@@ -270,11 +270,13 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue {
return;
}
let attrs = cx.tcx.hir_attrs(hir_id);
if find_attr!(attrs, AttributeKind::Inline(InlineAttr::Always, _)) {
return;
}

for a in attrs {
if let Some(meta_items) = a.meta_item_list()
&& (a.has_name(sym::proc_macro_derive)
|| (a.has_name(sym::inline) && attr::list_contains_name(&meta_items, sym::always)))
{
// FIXME(jdonszelmann): make part of the find_attr above
if a.has_name(sym::proc_macro_derive) {
return;
}
}
6 changes: 3 additions & 3 deletions tests/rustdoc-json/attrs/inline.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
//@ is "$.index[?(@.name=='just_inline')].attrs" '["#[inline]"]'
//@ is "$.index[?(@.name=='just_inline')].attrs" '["#[attr = Inline(Hint)]"]'
#[inline]
pub fn just_inline() {}

//@ is "$.index[?(@.name=='inline_always')].attrs" '["#[inline(always)]"]'
//@ is "$.index[?(@.name=='inline_always')].attrs" '["#[attr = Inline(Always)]"]'
#[inline(always)]
pub fn inline_always() {}

//@ is "$.index[?(@.name=='inline_never')].attrs" '["#[inline(never)]"]'
//@ is "$.index[?(@.name=='inline_never')].attrs" '["#[attr = Inline(Never)]"]'
#[inline(never)]
pub fn inline_never() {}
18 changes: 9 additions & 9 deletions tests/ui/attributes/multiple-invalid.stderr
Original file line number Diff line number Diff line change
@@ -1,12 +1,3 @@
error[E0518]: attribute should be applied to function or closure
--> $DIR/multiple-invalid.rs:4:1
|
LL | #[inline]
| ^^^^^^^^^
...
LL | const FOO: u8 = 0;
| ------------------ not a function or closure

error: attribute should be applied to a function definition
--> $DIR/multiple-invalid.rs:6:1
|
@@ -16,6 +7,15 @@ LL |
LL | const FOO: u8 = 0;
| ------------------ not a function definition

error[E0518]: attribute should be applied to function or closure
--> $DIR/multiple-invalid.rs:4:1
|
LL | #[inline]
| ^^^^^^^^^
...
LL | const FOO: u8 = 0;
| ------------------ not a function or closure

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error message moved

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0518`.
4 changes: 2 additions & 2 deletions tests/ui/attributes/rustc_confusables.rs
Original file line number Diff line number Diff line change
@@ -37,8 +37,8 @@ impl Bar {
fn qux() {}

#[rustc_confusables(invalid_meta_item)]
//~^ ERROR expected a quoted string literal
//~| HELP consider surrounding this with quotes
//~^ ERROR malformed `rustc_confusables` attribute input [E0539]
//~| HELP must be of the form
fn quux() {}
}

29 changes: 15 additions & 14 deletions tests/ui/attributes/rustc_confusables.stderr
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
error: malformed `rustc_confusables` attribute input
--> $DIR/rustc_confusables.rs:34:5
|
LL | #[rustc_confusables]
| ^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_confusables("name1", "name2", ...)]`

error: expected at least one confusable name
--> $DIR/rustc_confusables.rs:30:5
|
LL | #[rustc_confusables()]
| ^^^^^^^^^^^^^^^^^^^^^^

error[E0539]: expected a quoted string literal
--> $DIR/rustc_confusables.rs:39:25
|
LL | #[rustc_confusables(invalid_meta_item)]
| ^^^^^^^^^^^^^^^^^
error[E0539]: malformed `rustc_confusables` attribute input
--> $DIR/rustc_confusables.rs:34:5
|
help: consider surrounding this with quotes
LL | #[rustc_confusables]
| ^^^^^^^^^^^^^^^^^^^^
| |
| expected this to be a list
| help: must be of the form: `#[rustc_confusables("name1", "name2", ...)]`

error[E0539]: malformed `rustc_confusables` attribute input
--> $DIR/rustc_confusables.rs:39:5
|
LL | #[rustc_confusables("invalid_meta_item")]
| + +
LL | #[rustc_confusables(invalid_meta_item)]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rewording, arguably worse but consistent with other error messages with a consistent error code (as opposed to a dedicated diagnostic for this attribute only)

| ^^^^^^^^^^^^^^^^^^^^-----------------^^
| | |
| | expected a string literal here
| help: must be of the form: `#[rustc_confusables("name1", "name2", ...)]`

error: attribute should be applied to an inherent method
--> $DIR/rustc_confusables.rs:45:1
14 changes: 7 additions & 7 deletions tests/ui/deprecation/deprecation-sanity.rs
Original file line number Diff line number Diff line change
@@ -4,30 +4,30 @@ mod bogus_attribute_types_1 {
#[deprecated(since = "a", note = "a", reason)] //~ ERROR unknown meta item 'reason'
fn f1() { }

#[deprecated(since = "a", note)] //~ ERROR expected a quoted string literal
#[deprecated(since = "a", note)] //~ ERROR malformed `deprecated` attribute input [E0539]
fn f2() { }

#[deprecated(since, note = "a")] //~ ERROR expected a quoted string literal
#[deprecated(since, note = "a")] //~ ERROR malformed `deprecated` attribute input [E0539]
fn f3() { }

#[deprecated(since = "a", note(b))] //~ ERROR expected a quoted string literal
#[deprecated(since = "a", note(b))] //~ ERROR malformed `deprecated` attribute input [E0539]
fn f5() { }

#[deprecated(since(b), note = "a")] //~ ERROR expected a quoted string literal
#[deprecated(since(b), note = "a")] //~ ERROR malformed `deprecated` attribute input [E0539]
fn f6() { }

#[deprecated(note = b"test")] //~ ERROR literal in `deprecated` value must be a string
#[deprecated(note = b"test")] //~ ERROR malformed `deprecated` attribute input [E0539]
fn f7() { }

#[deprecated("test")] //~ ERROR item in `deprecated` must be a key/value pair
#[deprecated("test")] //~ ERROR malformed `deprecated` attribute input [E0565]
fn f8() { }
}

#[deprecated(since = "a", note = "b")]
#[deprecated(since = "a", note = "b")] //~ ERROR multiple `deprecated` attributes
fn multiple1() { }

#[deprecated(since = "a", since = "b", note = "c")] //~ ERROR multiple 'since' items
#[deprecated(since = "a", since = "b", note = "c")] //~ ERROR malformed `deprecated` attribute input [E0538]
fn f1() { }

struct X;
128 changes: 107 additions & 21 deletions tests/ui/deprecation/deprecation-sanity.stderr
Original file line number Diff line number Diff line change
@@ -4,43 +4,115 @@ error[E0541]: unknown meta item 'reason'
LL | #[deprecated(since = "a", note = "a", reason)]
| ^^^^^^ expected one of `since`, `note`

error[E0539]: expected a quoted string literal
--> $DIR/deprecation-sanity.rs:7:31
error[E0539]: malformed `deprecated` attribute input
--> $DIR/deprecation-sanity.rs:7:5
|
LL | #[deprecated(since = "a", note)]
| ^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^----^^
Copy link
Contributor Author

@jdonszelmann jdonszelmann Jun 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

much more detailed error messages

| |
| expected this to be of the form `note = "..."`
|
help: try changing it to one of the following valid forms of the attribute
|
LL - #[deprecated(since = "a", note)]
LL + #[deprecated = "reason"]
|
LL - #[deprecated(since = "a", note)]
LL + #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
|
LL - #[deprecated(since = "a", note)]
LL + #[deprecated]
|

error[E0539]: expected a quoted string literal
--> $DIR/deprecation-sanity.rs:10:18
error[E0539]: malformed `deprecated` attribute input
--> $DIR/deprecation-sanity.rs:10:5
|
LL | #[deprecated(since, note = "a")]
| ^^^^^
| ^^^^^^^^^^^^^-----^^^^^^^^^^^^^^
| |
| expected this to be of the form `since = "..."`
|
help: try changing it to one of the following valid forms of the attribute
|
LL - #[deprecated(since, note = "a")]
LL + #[deprecated = "reason"]
|
LL - #[deprecated(since, note = "a")]
LL + #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
|
LL - #[deprecated(since, note = "a")]
LL + #[deprecated]
|

error[E0539]: expected a quoted string literal
--> $DIR/deprecation-sanity.rs:13:31
error[E0539]: malformed `deprecated` attribute input
--> $DIR/deprecation-sanity.rs:13:5
|
LL | #[deprecated(since = "a", note(b))]
| ^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^-------^^
| |
| expected this to be of the form `note = "..."`
|
help: try changing it to one of the following valid forms of the attribute
|
LL - #[deprecated(since = "a", note(b))]
LL + #[deprecated = "reason"]
|
LL - #[deprecated(since = "a", note(b))]
LL + #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
|
LL - #[deprecated(since = "a", note(b))]
LL + #[deprecated]
|

error[E0539]: expected a quoted string literal
--> $DIR/deprecation-sanity.rs:16:18
error[E0539]: malformed `deprecated` attribute input
--> $DIR/deprecation-sanity.rs:16:5
|
LL | #[deprecated(since(b), note = "a")]
| ^^^^^^^^
| ^^^^^^^^^^^^^--------^^^^^^^^^^^^^^
| |
| expected this to be of the form `since = "..."`
|
help: try changing it to one of the following valid forms of the attribute
|
LL - #[deprecated(since(b), note = "a")]
LL + #[deprecated = "reason"]
|
LL - #[deprecated(since(b), note = "a")]
LL + #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
|
LL - #[deprecated(since(b), note = "a")]
LL + #[deprecated]
|

error[E0565]: literal in `deprecated` value must be a string
--> $DIR/deprecation-sanity.rs:19:25
error[E0539]: malformed `deprecated` attribute input
--> $DIR/deprecation-sanity.rs:19:5
|
LL | #[deprecated(note = b"test")]
| -^^^^^^
| ^^^^^^^^^^^^^^^^^^^^-^^^^^^^^
| |
| help: consider removing the prefix
|
= note: expected a normal string literal, not a byte string literal

error[E0565]: item in `deprecated` must be a key/value pair
--> $DIR/deprecation-sanity.rs:22:18
error[E0565]: malformed `deprecated` attribute input
--> $DIR/deprecation-sanity.rs:22:5
|
LL | #[deprecated("test")]
| ^^^^^^
| ^^^^^^^^^^^^^------^^
| |
| didn't expect a literal here
|
help: try changing it to one of the following valid forms of the attribute
|
LL - #[deprecated("test")]
LL + #[deprecated = "reason"]
|
LL - #[deprecated("test")]
LL + #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
|
LL - #[deprecated("test")]
LL + #[deprecated]
|

error: multiple `deprecated` attributes
--> $DIR/deprecation-sanity.rs:27:1
@@ -54,11 +126,25 @@ note: attribute also specified here
LL | #[deprecated(since = "a", note = "b")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0538]: multiple 'since' items
--> $DIR/deprecation-sanity.rs:30:27
error[E0538]: malformed `deprecated` attribute input
--> $DIR/deprecation-sanity.rs:30:1
|
LL | #[deprecated(since = "a", since = "b", note = "c")]
| ^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^^^^^^^^^^^
| |
| found `since` used as a key more than once
|
help: try changing it to one of the following valid forms of the attribute
|
LL - #[deprecated(since = "a", since = "b", note = "c")]
LL + #[deprecated = "reason"]
|
LL - #[deprecated(since = "a", since = "b", note = "c")]
LL + #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
|
LL - #[deprecated(since = "a", since = "b", note = "c")]
LL + #[deprecated]
|

error: this `#[deprecated]` annotation has no effect
--> $DIR/deprecation-sanity.rs:35:1
19 changes: 6 additions & 13 deletions tests/ui/deprecation/invalid-literal.stderr
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
error: malformed `deprecated` attribute input
error[E0539]: malformed `deprecated` attribute input
--> $DIR/invalid-literal.rs:1:1
|
LL | #[deprecated = b"test"]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
help: the following are the possible correct uses
|
LL - #[deprecated = b"test"]
LL + #[deprecated = "reason"]
|
LL - #[deprecated = b"test"]
LL + #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
|
LL - #[deprecated = b"test"]
LL + #[deprecated]
| ^^^^^^^^^^^^^^^-^^^^^^^
| |
| help: consider removing the prefix
|
= note: expected a normal string literal, not a byte string literal

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0539`.
6 changes: 0 additions & 6 deletions tests/ui/error-codes/E0534.rs

This file was deleted.

9 changes: 0 additions & 9 deletions tests/ui/error-codes/E0534.stderr

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// repr currently doesn't support literals
#[deprecated(since = b"1.29", note = "hi")] //~ ERROR E0565
#[deprecated(since = b"1.29", note = "hi")] //~ ERROR E0539
struct A { }

fn main() { }
13 changes: 13 additions & 0 deletions tests/ui/error-codes/E0539.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
error[E0539]: malformed `deprecated` attribute input
--> $DIR/E0539.rs:2:1
|
LL | #[deprecated(since = b"1.29", note = "hi")]
| ^^^^^^^^^^^^^^^^^^^^^-^^^^^^^^^^^^^^^^^^^^^
| |
| help: consider removing the prefix
|
= note: expected a normal string literal, not a byte string literal

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0539`.
6 changes: 6 additions & 0 deletions tests/ui/error-codes/E0540.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#[inline()] //~ ERROR malformed `inline` attribute input
pub fn something() {}

fn main() {
something();
}
19 changes: 19 additions & 0 deletions tests/ui/error-codes/E0540.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
error[E0805]: malformed `inline` attribute input
--> $DIR/E0540.rs:1:1
|
LL | #[inline()]
| ^^^^^^^^--^
| |
| expected a single argument here
|
help: try changing it to one of the following valid forms of the attribute
|
LL | #[inline(always|never)]
| ++++++++++++
LL - #[inline()]
LL + #[inline]
|

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0805`.
20 changes: 17 additions & 3 deletions tests/ui/error-codes/E0565-1.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
error[E0565]: item in `deprecated` must be a key/value pair
--> $DIR/E0565-1.rs:2:14
error[E0565]: malformed `deprecated` attribute input
--> $DIR/E0565-1.rs:2:1
|
LL | #[deprecated("since")]
| ^^^^^^^
| ^^^^^^^^^^^^^-------^^
| |
| didn't expect a literal here
|
help: try changing it to one of the following valid forms of the attribute
|
LL - #[deprecated("since")]
LL + #[deprecated = "reason"]
|
LL - #[deprecated("since")]
LL + #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
|
LL - #[deprecated("since")]
LL + #[deprecated]
|

error: aborting due to 1 previous error

11 changes: 0 additions & 11 deletions tests/ui/error-codes/E0565-2.stderr

This file was deleted.

Original file line number Diff line number Diff line change
@@ -8,16 +8,6 @@ LL | #![rustc_main]
= note: the `#[rustc_main]` attribute is an internal implementation detail that will never be stable
= note: the `#[rustc_main]` attribute is used internally to specify test entry point function

error: valid forms for the attribute are `#[inline]` and `#[inline(always|never)]`
--> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:46:5
|
LL | #[inline = "2100"] fn f() { }
| ^^^^^^^^^^^^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
= note: `#[deny(ill_formed_attribute_input)]` on by default

error[E0518]: attribute should be applied to function or closure
--> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:32:1
|
@@ -314,6 +304,16 @@ error[E0517]: attribute should be applied to a struct, enum, or union
LL | #[repr(Rust)] impl S { }
| ^^^^ ---------- not a struct, enum, or union

error: valid forms for the attribute are `#[inline(always|never)]` and `#[inline]`
--> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:46:5
|
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error message moved

LL | #[inline = "2100"] fn f() { }
| ^^^^^^^^^^^^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
= note: `#[deny(ill_formed_attribute_input)]` on by default

error: aborting due to 38 previous errors

Some errors have detailed explanations: E0517, E0518, E0658.
1 change: 0 additions & 1 deletion tests/ui/force-inlining/invalid.rs
Original file line number Diff line number Diff line change
@@ -9,7 +9,6 @@
// Test that invalid force inlining attributes error as expected.

#[rustc_force_inline("foo")]
//~^ ERROR malformed `rustc_force_inline` attribute input
pub fn forced1() {
}

132 changes: 67 additions & 65 deletions tests/ui/force-inlining/invalid.stderr
Original file line number Diff line number Diff line change
@@ -1,71 +1,71 @@
error: malformed `rustc_force_inline` attribute input
--> $DIR/invalid.rs:11:1
|
LL | #[rustc_force_inline("foo")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: the following are the possible correct uses
|
LL - #[rustc_force_inline("foo")]
LL + #[rustc_force_inline = "reason"]
|
LL - #[rustc_force_inline("foo")]
LL + #[rustc_force_inline]
error: allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters
--> $DIR/invalid.rs:132:11
|
LL | fn barqux(#[rustc_force_inline] _x: u32) {}
| ^^^^^^^^^^^^^^^^^^^^^

error: malformed `rustc_force_inline` attribute input
--> $DIR/invalid.rs:16:1
error[E0805]: malformed `rustc_force_inline` attribute input
--> $DIR/invalid.rs:15:1
|
LL | #[rustc_force_inline(bar, baz)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^----------^
| |
| expected a single argument here
|
help: the following are the possible correct uses
help: try changing it to one of the following valid forms of the attribute
|
LL - #[rustc_force_inline(bar, baz)]
LL + #[rustc_force_inline = "reason"]
|
LL - #[rustc_force_inline(bar, baz)]
LL + #[rustc_force_inline(reason)]
|
LL - #[rustc_force_inline(bar, baz)]
LL + #[rustc_force_inline]
|

error: malformed `rustc_force_inline` attribute input
--> $DIR/invalid.rs:21:1
error[E0539]: malformed `rustc_force_inline` attribute input
--> $DIR/invalid.rs:20:1
|
LL | #[rustc_force_inline(2)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^-^^
| |
| expected a string literal here
|
help: the following are the possible correct uses
help: try changing it to one of the following valid forms of the attribute
|
LL - #[rustc_force_inline(2)]
LL + #[rustc_force_inline = "reason"]
|
LL - #[rustc_force_inline(2)]
LL + #[rustc_force_inline(reason)]
|
LL - #[rustc_force_inline(2)]
LL + #[rustc_force_inline]
|

error: malformed `rustc_force_inline` attribute input
--> $DIR/invalid.rs:26:1
error[E0539]: malformed `rustc_force_inline` attribute input
--> $DIR/invalid.rs:25:1
|
LL | #[rustc_force_inline = 2]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^-^
| |
| expected a string literal here
|
help: the following are the possible correct uses
help: try changing it to one of the following valid forms of the attribute
|
LL - #[rustc_force_inline = 2]
LL + #[rustc_force_inline = "reason"]
|
LL - #[rustc_force_inline = 2]
LL + #[rustc_force_inline]
LL + #[rustc_force_inline(reason)]
|

error: allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters
--> $DIR/invalid.rs:133:11
LL - #[rustc_force_inline = 2]
LL + #[rustc_force_inline]
|
LL | fn barqux(#[rustc_force_inline] _x: u32) {}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a genuine regression, I think because check_attr now has to see this as a parsed attribute, let me fix that

| ^^^^^^^^^^^^^^^^^^^^^

error: attribute should be applied to a function
--> $DIR/invalid.rs:31:1
--> $DIR/invalid.rs:30:1
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -74,7 +74,7 @@ LL | extern crate std as other_std;
| ------------------------------ not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:35:1
--> $DIR/invalid.rs:34:1
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -83,7 +83,7 @@ LL | use std::collections::HashMap;
| ------------------------------ not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:39:1
--> $DIR/invalid.rs:38:1
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -92,7 +92,7 @@ LL | static _FOO: &'static str = "FOO";
| ---------------------------------- not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:43:1
--> $DIR/invalid.rs:42:1
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -101,7 +101,7 @@ LL | const _BAR: u32 = 3;
| -------------------- not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:47:1
--> $DIR/invalid.rs:46:1
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -110,7 +110,7 @@ LL | mod foo { }
| ----------- not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:51:1
--> $DIR/invalid.rs:50:1
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -125,7 +125,7 @@ LL | | }
| |_- not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:67:1
--> $DIR/invalid.rs:66:1
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -134,7 +134,7 @@ LL | type Foo = u32;
| --------------- not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:71:1
--> $DIR/invalid.rs:70:1
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -147,13 +147,13 @@ LL | | }
| |_- not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:73:10
--> $DIR/invalid.rs:72:10
|
LL | enum Bar<#[rustc_force_inline] T> {
| ^^^^^^^^^^^^^^^^^^^^^ - not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:75:5
--> $DIR/invalid.rs:74:5
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -162,7 +162,7 @@ LL | Baz(std::marker::PhantomData<T>),
| -------------------------------- not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:80:1
--> $DIR/invalid.rs:79:1
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -175,7 +175,7 @@ LL | | }
| |_- not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:83:5
--> $DIR/invalid.rs:82:5
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -184,7 +184,7 @@ LL | field: u32,
| ---------- not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:88:1
--> $DIR/invalid.rs:87:1
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -196,7 +196,7 @@ LL | | }
| |_- not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:95:1
--> $DIR/invalid.rs:94:1
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -211,7 +211,7 @@ LL | | }
| |_- not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:110:1
--> $DIR/invalid.rs:109:1
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -220,7 +220,7 @@ LL | trait FooQux = FooBaz;
| ---------------------- not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:114:1
--> $DIR/invalid.rs:113:1
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -233,7 +233,7 @@ LL | | }
| |_- not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:122:1
--> $DIR/invalid.rs:121:1
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -245,7 +245,7 @@ LL | | }
| |_- not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:129:1
--> $DIR/invalid.rs:128:1
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -254,15 +254,15 @@ LL | macro_rules! barqux { ($foo:tt) => { $foo }; }
| ---------------------------------------------- not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:133:11
--> $DIR/invalid.rs:132:11
|
LL | fn barqux(#[rustc_force_inline] _x: u32) {}
| ^^^^^^^^^^^^^^^^^^^^^--------
| |
| not a function definition

error: attribute cannot be applied to a `async`, `gen` or `async gen` function
--> $DIR/invalid.rs:137:1
--> $DIR/invalid.rs:136:1
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -271,7 +271,7 @@ LL | async fn async_foo() {}
| -------------------- `async`, `gen` or `async gen` function

error: attribute cannot be applied to a `async`, `gen` or `async gen` function
--> $DIR/invalid.rs:141:1
--> $DIR/invalid.rs:140:1
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -280,7 +280,7 @@ LL | gen fn gen_foo() {}
| ---------------- `async`, `gen` or `async gen` function

error: attribute cannot be applied to a `async`, `gen` or `async gen` function
--> $DIR/invalid.rs:145:1
--> $DIR/invalid.rs:144:1
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -289,19 +289,19 @@ LL | async gen fn async_gen_foo() {}
| ---------------------------- `async`, `gen` or `async gen` function

error: attribute should be applied to a function
--> $DIR/invalid.rs:150:14
--> $DIR/invalid.rs:149:14
|
LL | let _x = #[rustc_force_inline] || { };
| ^^^^^^^^^^^^^^^^^^^^^ ------ not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:152:14
--> $DIR/invalid.rs:151:14
|
LL | let _y = #[rustc_force_inline] 3 + 4;
| ^^^^^^^^^^^^^^^^^^^^^ - not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:154:5
--> $DIR/invalid.rs:153:5
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -310,7 +310,7 @@ LL | let _z = 3;
| ----------- not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:159:9
--> $DIR/invalid.rs:158:9
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -319,7 +319,7 @@ LL | 1 => (),
| ------- not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:98:5
--> $DIR/invalid.rs:97:5
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -328,7 +328,7 @@ LL | type Foo;
| --------- not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:101:5
--> $DIR/invalid.rs:100:5
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -337,7 +337,7 @@ LL | const Bar: i32;
| --------------- not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:105:5
--> $DIR/invalid.rs:104:5
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -346,7 +346,7 @@ LL | fn foo() {}
| ----------- not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:117:5
--> $DIR/invalid.rs:116:5
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -355,7 +355,7 @@ LL | fn foo() {}
| ----------- not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:54:5
--> $DIR/invalid.rs:53:5
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -364,7 +364,7 @@ LL | static X: &'static u32;
| ----------------------- not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:58:5
--> $DIR/invalid.rs:57:5
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -373,13 +373,15 @@ LL | type Y;
| ------- not a function definition

error: attribute should be applied to a function
--> $DIR/invalid.rs:62:5
--> $DIR/invalid.rs:61:5
|
LL | #[rustc_force_inline]
| ^^^^^^^^^^^^^^^^^^^^^
LL |
LL | fn foo();
| --------- not a function definition

error: aborting due to 38 previous errors
error: aborting due to 37 previous errors

Some errors have detailed explanations: E0539, E0805.
For more information about an error, try `rustc --explain E0539`.
4 changes: 2 additions & 2 deletions tests/ui/invalid/invalid-inline.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#![allow(dead_code)]

#[inline(please,no)] //~ ERROR expected one argument
#[inline(please,no)] //~ ERROR malformed `inline` attribute
fn a() {
}

#[inline()] //~ ERROR expected one argument
#[inline()] //~ ERROR malformed `inline` attribute
fn b() {
}

31 changes: 26 additions & 5 deletions tests/ui/invalid/invalid-inline.stderr
Original file line number Diff line number Diff line change
@@ -1,15 +1,36 @@
error[E0534]: expected one argument
error[E0805]: malformed `inline` attribute input
--> $DIR/invalid-inline.rs:3:1
|
LL | #[inline(please,no)]
| ^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^-----------^
| |
| expected a single argument here
|
help: try changing it to one of the following valid forms of the attribute
|
LL - #[inline(please,no)]
LL + #[inline(always|never)]
|
LL - #[inline(please,no)]
LL + #[inline]
|

error[E0534]: expected one argument
error[E0805]: malformed `inline` attribute input
--> $DIR/invalid-inline.rs:7:1
|
LL | #[inline()]
| ^^^^^^^^^^^
| ^^^^^^^^--^
| |
| expected a single argument here
|
help: try changing it to one of the following valid forms of the attribute
|
LL | #[inline(always|never)]
| ++++++++++++
LL - #[inline()]
LL + #[inline]
|

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0534`.
For more information about this error, try `rustc --explain E0805`.
4 changes: 2 additions & 2 deletions tests/ui/issues/issue-43988.rs
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ fn main() {

#[inline(XYZ)]
let _b = 4;
//~^^ ERROR attribute should be applied to function or closure
//~^^ ERROR malformed `inline` attribute

#[repr(nothing)]
let _x = 0;
@@ -29,7 +29,7 @@ fn main() {

#[inline(ABC)]
foo();
//~^^ ERROR attribute should be applied to function or closure
//~^^ ERROR malformed `inline` attribute

let _z = #[repr] 1;
//~^ ERROR malformed `repr` attribute
76 changes: 50 additions & 26 deletions tests/ui/issues/issue-43988.stderr
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
error: malformed `repr` attribute input
--> $DIR/issue-43988.rs:24:5
error[E0539]: malformed `inline` attribute input
--> $DIR/issue-43988.rs:10:5
|
LL | #[repr]
| ^^^^^^^ help: must be of the form: `#[repr(C)]`

error: malformed `repr` attribute input
--> $DIR/issue-43988.rs:34:14
LL | #[inline(XYZ)]
| ^^^^^^^^^---^^
| |
| valid arguments are `always` or `never`
|
help: try changing it to one of the following valid forms of the attribute
|
LL - #[inline(XYZ)]
LL + #[inline(always|never)]
|
LL - #[inline(XYZ)]
LL + #[inline]
|
LL | let _z = #[repr] 1;
| ^^^^^^^ help: must be of the form: `#[repr(C)]`

error[E0552]: unrecognized representation hint
--> $DIR/issue-43988.rs:14:12
@@ -26,6 +31,41 @@ LL | #[repr(something_not_real)]
|
= help: valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`

error[E0539]: malformed `repr` attribute input
--> $DIR/issue-43988.rs:24:5
|
LL | #[repr]
| ^^^^^^^
| |
| expected this to be a list
| help: must be of the form: `#[repr(C)]`

error[E0539]: malformed `inline` attribute input
--> $DIR/issue-43988.rs:30:5
|
LL | #[inline(ABC)]
| ^^^^^^^^^---^^
| |
| valid arguments are `always` or `never`
|
help: try changing it to one of the following valid forms of the attribute
|
LL - #[inline(ABC)]
LL + #[inline(always|never)]
|
LL - #[inline(ABC)]
LL + #[inline]
|

error[E0539]: malformed `repr` attribute input
--> $DIR/issue-43988.rs:34:14
|
LL | let _z = #[repr] 1;
| ^^^^^^^
| |
| expected this to be a list
| help: must be of the form: `#[repr(C)]`

error[E0518]: attribute should be applied to function or closure
--> $DIR/issue-43988.rs:5:5
|
@@ -34,23 +74,7 @@ LL | #[inline]
LL | let _a = 4;
| ----------- not a function or closure

error[E0518]: attribute should be applied to function or closure
--> $DIR/issue-43988.rs:10:5
|
LL | #[inline(XYZ)]
| ^^^^^^^^^^^^^^
LL | let _b = 4;
| ----------- not a function or closure

error[E0518]: attribute should be applied to function or closure
--> $DIR/issue-43988.rs:30:5
|
LL | #[inline(ABC)]
| ^^^^^^^^^^^^^^
LL | foo();
| ----- not a function or closure

error: aborting due to 7 previous errors

Some errors have detailed explanations: E0518, E0552.
Some errors have detailed explanations: E0518, E0539, E0552.
For more information about an error, try `rustc --explain E0518`.
26 changes: 13 additions & 13 deletions tests/ui/lint/unused/unused-attr-duplicate.stderr
Original file line number Diff line number Diff line change
@@ -102,19 +102,6 @@ note: attribute also specified here
LL | #[automatically_derived]
| ^^^^^^^^^^^^^^^^^^^^^^^^

error: unused attribute
--> $DIR/unused-attr-duplicate.rs:74:1
|
LL | #[inline(never)]
| ^^^^^^^^^^^^^^^^ help: remove this attribute
|
note: attribute also specified here
--> $DIR/unused-attr-duplicate.rs:73:1
|
LL | #[inline(always)]
| ^^^^^^^^^^^^^^^^^
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!

error: unused attribute
--> $DIR/unused-attr-duplicate.rs:77:1
|
@@ -289,5 +276,18 @@ note: attribute also specified here
LL | #[macro_export]
| ^^^^^^^^^^^^^^^

error: unused attribute
--> $DIR/unused-attr-duplicate.rs:74:1
|
LL | #[inline(never)]
| ^^^^^^^^^^^^^^^^ help: remove this attribute
|
note: attribute also specified here
--> $DIR/unused-attr-duplicate.rs:73:1
|
LL | #[inline(always)]
| ^^^^^^^^^^^^^^^^^
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!

error: aborting due to 23 previous errors

Loading