Auto merge of #138165 - jdonszelmann:inline, r=oli-obk

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

r? `@oli-obk`

This PR:
- creates a new parser for inline attributes
- creates consistent error messages and error codes between attribute parsers; inline and others
- as such changes a few error messages for other attributes to be (in my eyes) much more consistent
- tests ast-lowering lints introduced by rust-lang/rust#138164 since this is now useful for the first time
- Coalesce some useless error codes

Builds on top of rust-lang/rust#138164

Closes rust-lang/rust#137950
This commit is contained in:
bors 2025-06-18 06:25:21 +00:00
commit 1bb335244c
66 changed files with 1221 additions and 679 deletions

View file

@ -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),

View file

@ -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> },
}

View file

@ -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

View file

@ -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>,

View file

@ -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 });
}
this.first_span.get_or_insert(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);
},
)];
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
if self.confusables.is_empty() {

View file

@ -1,4 +1,5 @@
use rustc_attr_data_structures::{AttributeKind, DeprecatedSince, Deprecation};
use rustc_feature::{AttributeTemplate, template};
use rustc_span::{Span, Symbol, sym};
use super::util::parse_version;
@ -6,7 +7,6 @@ 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;
};
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: (),
});
}
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"]
},
});
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();
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.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 {

View file

@ -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,7 +73,7 @@ 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;
};
@ -81,7 +81,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcForceInlineParser {
}
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;
};

View file

@ -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)

View file

@ -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,6 +30,7 @@ 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;
@ -36,7 +38,7 @@ 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)) }

View file

@ -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;
};

View file

@ -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,8 +110,10 @@ 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()
@ -105,7 +121,8 @@ impl<S: Stage> AttributeParser<S> for BodyStabilityParser {
} 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,12 +217,10 @@ 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()
@ -212,10 +228,7 @@ fn insert_value_into_option_or_error<S: Stage>(
*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(),

View file

@ -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,

View file

@ -5,12 +5,11 @@ 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};
@ -18,6 +17,7 @@ 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,7 +60,7 @@ 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();
$(
{
@ -67,13 +68,12 @@ macro_rules! attribute_parsers {
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,6 +167,14 @@ 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> {
@ -172,10 +182,133 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
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),
};
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)
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.

View file

@ -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(),
),
},
);
}
}
}

View file

@ -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
}
}

View file

@ -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());
let [item] = &items[..] else {
tcx.dcx().emit_err(errors::ExpectedOneArgumentOptimize { span: attr.span() });
return OptimizeAttr::Default;

View file

@ -208,13 +208,6 @@ 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 {
@ -222,14 +215,6 @@ pub(crate) struct ExpectedOneArgumentOptimize {
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 {

View file

@ -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
```compile_fail,E0805
#[inline()] // error: expected one argument
pub fn something() {}

View file

@ -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() {}

View file

@ -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() {}
```
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() {}
```

View file

@ -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 {}

View file

@ -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() {}
```

View file

@ -547,6 +547,7 @@ E0801: 0801,
E0802: 0802,
E0803: 0803,
E0804: 0804,
E0805: 0805,
);
)
}

View file

@ -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
} };
}

View file

@ -2626,6 +2626,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 {

View file

@ -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![];

View file

@ -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,25 +461,22 @@ 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.
@ -477,7 +484,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
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,

View file

@ -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;
/// The root of the emitted JSON blob.
///

View file

@ -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"),
);
}
}

View file

@ -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);
},
);
}

View file

@ -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,

View file

@ -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;
}
}

View file

@ -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() {}

View file

@ -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
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0518`.

View file

@ -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() {}
}

View file

@ -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
error[E0539]: malformed `rustc_confusables` attribute input
--> $DIR/rustc_confusables.rs:34:5
|
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)]
| ^^^^^^^^^^^^^^^^^
|
help: consider surrounding this with quotes
|
LL | #[rustc_confusables("invalid_meta_item")]
| + +
| ^^^^^^^^^^^^^^^^^^^^-----------------^^
| | |
| | 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

View file

@ -4,22 +4,22 @@ 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() { }
}
@ -27,7 +27,7 @@ mod bogus_attribute_types_1 {
#[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;

View file

@ -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)]
| ^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^----^^
| |
| 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

View file

@ -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`.

View file

@ -1,6 +0,0 @@
#[inline()] //~ ERROR E0534
pub fn something() {}
fn main() {
something();
}

View file

@ -1,9 +0,0 @@
error[E0534]: expected one argument
--> $DIR/E0534.rs:1:1
|
LL | #[inline()]
| ^^^^^^^^^^^
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0534`.

View file

@ -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() { }

View file

@ -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`.

View file

@ -0,0 +1,6 @@
#[inline()] //~ ERROR malformed `inline` attribute input
pub fn something() {}
fn main() {
something();
}

View file

@ -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`.

View file

@ -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

View file

@ -1,11 +0,0 @@
error[E0565]: literal in `deprecated` value must be a string
--> $DIR/E0565-2.rs:2:22
|
LL | #[deprecated(since = b"1.29", note = "hi")]
| -^^^^^^
| |
| help: consider removing the prefix
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0565`.

View file

@ -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
|
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.

View file

@ -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() {
}

View file

@ -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: malformed `rustc_force_inline` attribute input
--> $DIR/invalid.rs:16:1
|
LL | #[rustc_force_inline(bar, baz)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: the following are the possible correct uses
|
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
|
LL | #[rustc_force_inline(2)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
help: the following are the possible correct uses
|
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
|
LL | #[rustc_force_inline = 2]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: the following are the possible correct uses
|
LL - #[rustc_force_inline = 2]
LL + #[rustc_force_inline = "reason"]
|
LL - #[rustc_force_inline = 2]
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:133:11
--> $DIR/invalid.rs:132:11
|
LL | fn barqux(#[rustc_force_inline] _x: u32) {}
| ^^^^^^^^^^^^^^^^^^^^^
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: 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[E0539]: malformed `rustc_force_inline` attribute input
--> $DIR/invalid.rs:20:1
|
LL | #[rustc_force_inline(2)]
| ^^^^^^^^^^^^^^^^^^^^^-^^
| |
| expected a string literal here
|
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[E0539]: malformed `rustc_force_inline` attribute input
--> $DIR/invalid.rs:25:1
|
LL | #[rustc_force_inline = 2]
| ^^^^^^^^^^^^^^^^^^^^^^^-^
| |
| expected a string literal here
|
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: 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,7 +254,7 @@ 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) {}
| ^^^^^^^^^^^^^^^^^^^^^--------
@ -262,7 +262,7 @@ 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,7 +373,7 @@ 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]
| ^^^^^^^^^^^^^^^^^^^^^
@ -381,5 +381,7 @@ 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`.

View file

@ -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() {
}

View file

@ -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`.

View file

@ -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

View file

@ -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`.

View file

@ -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

View file

@ -8,7 +8,7 @@ extern crate std;
// issue#97006
macro_rules! m { ($attr_path: path) => { #[$attr_path] fn f() {} } }
#[inline]
#[attr = Inline(Hint)]
fn f() { }
fn main() { }

View file

@ -17,15 +17,6 @@ LL | #[ignore()]
= 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>
error: valid forms for the attribute are `#[inline]` and `#[inline(always|never)]`
--> $DIR/malformed-regressions.rs:5:1
|
LL | #[inline = ""]
| ^^^^^^^^^^^^^^
|
= 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>
error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated")]`
--> $DIR/malformed-regressions.rs:7:1
|
@ -44,5 +35,14 @@ LL | #[link = ""]
= 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>
error: valid forms for the attribute are `#[inline(always|never)]` and `#[inline]`
--> $DIR/malformed-regressions.rs:5:1
|
LL | #[inline = ""]
| ^^^^^^^^^^^^^^
|
= 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>
error: aborting due to 5 previous errors

View file

@ -1,20 +1,30 @@
error: malformed `repr` attribute input
error[E0539]: malformed `repr` attribute input
--> $DIR/repr.rs:1:1
|
LL | #[repr]
| ^^^^^^^ help: must be of the form: `#[repr(C)]`
| ^^^^^^^
| |
| expected this to be a list
| help: must be of the form: `#[repr(C)]`
error: malformed `repr` attribute input
error[E0539]: malformed `repr` attribute input
--> $DIR/repr.rs:4:1
|
LL | #[repr = "B"]
| ^^^^^^^^^^^^^ help: must be of the form: `#[repr(C)]`
| ^^^^^^^^^^^^^
| |
| expected this to be a list
| help: must be of the form: `#[repr(C)]`
error: malformed `repr` attribute input
error[E0539]: malformed `repr` attribute input
--> $DIR/repr.rs:7:1
|
LL | #[repr = "C"]
| ^^^^^^^^^^^^^ help: must be of the form: `#[repr(C)]`
| ^^^^^^^^^^^^^
| |
| expected this to be a list
| help: must be of the form: `#[repr(C)]`
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0539`.

View file

@ -1,6 +0,0 @@
#[inline(unknown)] //~ ERROR E0535
pub fn something() {}
fn main() {
something();
}

View file

@ -1,11 +0,0 @@
error[E0535]: invalid argument
--> $DIR/E0535.rs:1:10
|
LL | #[inline(unknown)]
| ^^^^^^^
|
= help: valid inline arguments are `always` and `never`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0535`.

6
tests/ui/span/E0539.rs Normal file
View file

@ -0,0 +1,6 @@
#[inline(unknown)] //~ ERROR malformed `inline` attribute
pub fn something() {}
fn main() {
something();
}

View file

@ -0,0 +1,20 @@
error[E0539]: malformed `inline` attribute input
--> $DIR/E0539.rs:1:1
|
LL | #[inline(unknown)]
| ^^^^^^^^^-------^^
| |
| valid arguments are `always` or `never`
|
help: try changing it to one of the following valid forms of the attribute
|
LL - #[inline(unknown)]
LL + #[inline(always|never)]
|
LL - #[inline(unknown)]
LL + #[inline]
|
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0539`.

View file

@ -4,7 +4,7 @@
#![stable(feature = "stable_test_feature", since = "1.0.0")]
#[stable(feature = "a", feature = "b", since = "1.0.0")] //~ ERROR multiple 'feature' items
#[stable(feature = "a", feature = "b", since = "1.0.0")] //~ ERROR malformed `stable` attribute input [E0538]
fn f1() { }
#[stable(feature = "a", sinse = "1.0.0")] //~ ERROR unknown meta item 'sinse'

View file

@ -1,8 +1,11 @@
error[E0538]: multiple 'feature' items
--> $DIR/stability-attribute-sanity-2.rs:7:25
error[E0538]: malformed `stable` attribute input
--> $DIR/stability-attribute-sanity-2.rs:7:1
|
LL | #[stable(feature = "a", feature = "b", since = "1.0.0")]
| ^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^-------^^^^^^^^^^^^^^^^^^^^^^^^^
| | |
| | found `feature` used as a key more than once
| help: must be of the form: `#[stable(feature = "name", since = "version")]`
error[E0541]: unknown meta item 'sinse'
--> $DIR/stability-attribute-sanity-2.rs:10:25

View file

@ -8,16 +8,16 @@ mod bogus_attribute_types_1 {
#[stable(feature = "a", since = "4.4.4", reason)] //~ ERROR unknown meta item 'reason' [E0541]
fn f1() { }
#[stable(feature = "a", since)] //~ ERROR expected a quoted string literal [E0539]
#[stable(feature = "a", since)] //~ ERROR malformed `stable` attribute input [E0539]
fn f2() { }
#[stable(feature, since = "3.3.3")] //~ ERROR expected a quoted string literal [E0539]
#[stable(feature, since = "3.3.3")] //~ ERROR malformed `stable` attribute input [E0539]
fn f3() { }
#[stable(feature = "a", since(b))] //~ ERROR expected a quoted string literal [E0539]
#[stable(feature = "a", since(b))] //~ ERROR malformed `stable` attribute input [E0539]
fn f5() { }
#[stable(feature(b), since = "3.3.3")] //~ ERROR expected a quoted string literal [E0539]
#[stable(feature(b), since = "3.3.3")] //~ ERROR malformed `stable` attribute input [E0539]
fn f6() { }
}

View file

@ -4,29 +4,41 @@ error[E0541]: unknown meta item 'reason'
LL | #[stable(feature = "a", since = "4.4.4", reason)]
| ^^^^^^ expected one of `feature`, `since`
error[E0539]: expected a quoted string literal
--> $DIR/stability-attribute-sanity.rs:11:29
error[E0539]: malformed `stable` attribute input
--> $DIR/stability-attribute-sanity.rs:11:5
|
LL | #[stable(feature = "a", since)]
| ^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^-----^^
| | |
| | expected this to be of the form `since = "..."`
| help: must be of the form: `#[stable(feature = "name", since = "version")]`
error[E0539]: expected a quoted string literal
--> $DIR/stability-attribute-sanity.rs:14:14
error[E0539]: malformed `stable` attribute input
--> $DIR/stability-attribute-sanity.rs:14:5
|
LL | #[stable(feature, since = "3.3.3")]
| ^^^^^^^
| ^^^^^^^^^-------^^^^^^^^^^^^^^^^^^^
| | |
| | expected this to be of the form `feature = "..."`
| help: must be of the form: `#[stable(feature = "name", since = "version")]`
error[E0539]: expected a quoted string literal
--> $DIR/stability-attribute-sanity.rs:17:29
error[E0539]: malformed `stable` attribute input
--> $DIR/stability-attribute-sanity.rs:17:5
|
LL | #[stable(feature = "a", since(b))]
| ^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^--------^^
| | |
| | expected this to be of the form `since = "..."`
| help: must be of the form: `#[stable(feature = "name", since = "version")]`
error[E0539]: expected a quoted string literal
--> $DIR/stability-attribute-sanity.rs:20:14
error[E0539]: malformed `stable` attribute input
--> $DIR/stability-attribute-sanity.rs:20:5
|
LL | #[stable(feature(b), since = "3.3.3")]
| ^^^^^^^^^^
| ^^^^^^^^^----------^^^^^^^^^^^^^^^^^^^
| | |
| | expected this to be of the form `feature = "..."`
| help: must be of the form: `#[stable(feature = "name", since = "version")]`
error[E0546]: missing 'feature'
--> $DIR/stability-attribute-sanity.rs:25:5