Introduce new-style attribute parsers for several attributes

note: compiler compiles but librustdoc and clippy don't
This commit is contained in:
Jana Dönszelmann 2025-02-09 22:49:33 +01:00
parent dbd3b7928e
commit 7e0f5b5016
No known key found for this signature in database
50 changed files with 1519 additions and 1342 deletions

View file

@ -3341,6 +3341,7 @@ dependencies = [
"rustc_expand",
"rustc_feature",
"rustc_fluent_macro",
"rustc_hir",
"rustc_index",
"rustc_lexer",
"rustc_lint_defs",
@ -3597,6 +3598,7 @@ dependencies = [
"rustc_abi",
"rustc_ast",
"rustc_ast_pretty",
"rustc_attr_data_structures",
"rustc_data_structures",
"rustc_error_codes",
"rustc_error_messages",
@ -3631,6 +3633,7 @@ dependencies = [
"rustc_errors",
"rustc_feature",
"rustc_fluent_macro",
"rustc_hir",
"rustc_lexer",
"rustc_lint_defs",
"rustc_macros",
@ -4319,6 +4322,7 @@ version = "0.0.0"
dependencies = [
"bitflags",
"rustc_abi",
"rustc_ast",
"rustc_data_structures",
"rustc_hir",
"rustc_middle",
@ -4415,6 +4419,7 @@ dependencies = [
"punycode",
"rustc-demangle",
"rustc_abi",
"rustc_ast",
"rustc_data_structures",
"rustc_errors",
"rustc_hashes",

View file

@ -874,11 +874,21 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
debug_assert_eq!(id.owner, self.current_hir_id_owner);
let ret = self.arena.alloc_from_iter(lowered_attrs);
debug_assert!(!ret.is_empty());
// this is possible if an item contained syntactical attribute,
// but none of them parse succesfully or all of them were ignored
// for not being built-in attributes at all. They could be remaining
// unexpanded attributes used as markers in proc-macro derives for example.
// This will have emitted some diagnostics for the misparse, but will then
// not emit the attribute making the list empty.
if ret.is_empty() {
&[]
} else {
self.attrs.insert(id.local_id, ret);
ret
}
}
}
fn lower_attrs_vec(&self, attrs: &[Attribute], target_span: Span) -> Vec<hir::Attribute> {
self.attribute_parser.parse_attribute_list(attrs, target_span, OmitDoc::Lower)

View file

@ -207,8 +207,6 @@ ast_passes_precise_capturing_duplicated = duplicate `use<...>` precise capturing
ast_passes_precise_capturing_not_allowed_here = `use<...>` precise capturing syntax not allowed in {$loc}
ast_passes_stability_outside_std = stability attributes may not be used outside of the standard library
ast_passes_static_without_body =
free static item without body
.suggestion = provide a definition for the static

View file

@ -732,13 +732,6 @@ pub(crate) struct AssociatedSuggestion2 {
pub potential_assoc: Ident,
}
#[derive(Diagnostic)]
#[diag(ast_passes_stability_outside_std, code = E0734)]
pub(crate) struct StabilityOutsideStd {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(ast_passes_feature_on_non_nightly, code = E0554)]
pub(crate) struct FeatureOnNonNightly {

View file

@ -178,18 +178,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
);
}
}
// Emit errors for non-staged-api crates.
if !self.features.staged_api() {
if attr.has_name(sym::unstable)
|| attr.has_name(sym::stable)
|| attr.has_name(sym::rustc_const_unstable)
|| attr.has_name(sym::rustc_const_stable)
|| attr.has_name(sym::rustc_default_body_unstable)
{
self.sess.dcx().emit_err(errors::StabilityOutsideStd { span: attr.span });
}
}
}
fn visit_item(&mut self, i: &'a ast::Item) {

View file

@ -2,9 +2,11 @@ use rustc_abi::Align;
use rustc_ast::token::CommentKind;
use rustc_ast::{self as ast, AttrStyle};
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
use rustc_span::hygiene::Transparency;
use rustc_span::{Span, Symbol};
use thin_vec::ThinVec;
use crate::RustcVersion;
use crate::{DefaultBodyStability, PartialConstStability, RustcVersion, Stability};
#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
pub enum InlineAttr {
@ -72,6 +74,8 @@ pub enum ReprAttr {
ReprSimd,
ReprTransparent,
ReprAlign(Align),
// this one is just so we can emit a lint for it
ReprEmpty,
}
pub use ReprAttr::*;
@ -150,10 +154,44 @@ impl Deprecation {
/// happen.
///
/// For more docs, look in [`rustc_attr`](https://doc.rust-lang.org/stable/nightly-rustc/rustc_attr/index.html)
// FIXME(jdonszelmann): rename to AttributeKind once hir::AttributeKind is dissolved
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable)]
pub enum AttributeKind {
// tidy-alphabetical-start
DocComment { style: AttrStyle, kind: CommentKind, span: Span, comment: Symbol },
AllowConstFnUnstable(ThinVec<Symbol>),
AllowInternalUnstable(ThinVec<(Symbol, Span)>),
BodyStability {
stability: DefaultBodyStability,
/// Span of the `#[rustc_default_body_unstable(...)]` attribute
span: Span,
},
Confusables {
symbols: ThinVec<Symbol>,
// FIXME(jdonszelmann): remove when target validation code is moved
first_span: Span,
},
ConstStability {
stability: PartialConstStability,
/// Span of the `#[rustc_const_stable(...)]` or `#[rustc_const_unstable(...)]` attribute
span: Span,
},
ConstStabilityIndirect,
Deprecation {
deprecation: Deprecation,
span: Span,
},
Diagnostic(DiagnosticAttribute),
DocComment {
style: AttrStyle,
kind: CommentKind,
span: Span,
comment: Symbol,
},
MacroTransparency(Transparency),
Repr(ThinVec<(ReprAttr, Span)>),
Stability {
stability: Stability,
/// Span of the `#[stable(...)]` or `#[unstable(...)]` attribute
span: Span,
},
// tidy-alphabetical-end
}

View file

@ -6,6 +6,8 @@ attr_parsing_deprecated_item_suggestion =
.help = add `#![feature(deprecated_suggestion)]` to the crate root
.note = see #94785 for more details
attr_parsing_empty_confusables =
expected at least one confusable name
attr_parsing_expected_one_cfg_pattern =
expected 1 cfg-pattern
@ -21,8 +23,8 @@ attr_parsing_expects_feature_list =
attr_parsing_expects_features =
`{$name}` expects feature names
attr_parsing_incorrect_meta_item =
incorrect meta item
attr_parsing_incorrect_meta_item = expected a quoted string literal
attr_parsing_incorrect_meta_item_suggestion = consider surrounding this with quotes
attr_parsing_incorrect_repr_format_align_one_arg =
incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses
@ -90,18 +92,18 @@ attr_parsing_non_ident_feature =
attr_parsing_repr_ident =
meta item in `repr` must be an identifier
attr_parsing_rustc_allowed_unstable_pairing =
`rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute
attr_parsing_rustc_const_stable_indirect_pairing =
`const_stable_indirect` attribute does not make sense on `rustc_const_stable` function, its behavior is already implied
attr_parsing_rustc_promotable_pairing =
`rustc_promotable` attribute must be paired with either a `rustc_const_unstable` or a `rustc_const_stable` attribute
attr_parsing_soft_no_args =
`soft` should not have any arguments
attr_parsing_stability_outside_std = stability attributes may not be used outside of the standard library
attr_parsing_unknown_meta_item =
unknown meta item '{$item}'
.label = expected one of {$expected}
@ -128,3 +130,8 @@ attr_parsing_unsupported_literal_generic =
unsupported literal
attr_parsing_unsupported_literal_suggestion =
consider removing the prefix
attr_parsing_unused_multiple =
multiple `{$name}` attributes
.suggestion = remove this attribute
.note = attribute also specified here

View file

@ -1,49 +1,67 @@
use rustc_ast::attr::{AttributeExt, filter_by_name};
use rustc_session::Session;
use rustc_span::{Symbol, sym};
use std::iter;
use rustc_attr_data_structures::AttributeKind;
use rustc_span::{Span, Symbol, sym};
use super::{CombineAttributeParser, ConvertFn};
use crate::context::AcceptContext;
use crate::parser::ArgParser;
use crate::session_diagnostics;
pub fn allow_internal_unstable(
sess: &Session,
attrs: &[impl AttributeExt],
) -> impl Iterator<Item = Symbol> {
allow_unstable(sess, attrs, sym::allow_internal_unstable)
pub(crate) struct AllowInternalUnstableParser;
impl CombineAttributeParser for AllowInternalUnstableParser {
const PATH: &'static [rustc_span::Symbol] = &[sym::allow_internal_unstable];
type Item = (Symbol, Span);
const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowInternalUnstable;
fn extend<'a>(
cx: &'a AcceptContext<'a>,
args: &'a ArgParser<'a>,
) -> impl IntoIterator<Item = Self::Item> + 'a {
parse_unstable(cx, args, Self::PATH[0]).into_iter().zip(iter::repeat(cx.attr_span))
}
}
pub fn rustc_allow_const_fn_unstable(
sess: &Session,
attrs: &[impl AttributeExt],
) -> impl Iterator<Item = Symbol> {
allow_unstable(sess, attrs, sym::rustc_allow_const_fn_unstable)
pub(crate) struct AllowConstFnUnstableParser;
impl CombineAttributeParser for AllowConstFnUnstableParser {
const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_allow_const_fn_unstable];
type Item = Symbol;
const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowConstFnUnstable;
fn extend<'a>(
cx: &'a AcceptContext<'a>,
args: &'a ArgParser<'a>,
) -> impl IntoIterator<Item = Self::Item> + 'a {
parse_unstable(cx, args, Self::PATH[0])
}
}
fn allow_unstable(
sess: &Session,
attrs: &[impl AttributeExt],
fn parse_unstable<'a>(
cx: &AcceptContext<'_>,
args: &'a ArgParser<'a>,
symbol: Symbol,
) -> impl Iterator<Item = Symbol> {
let attrs = filter_by_name(attrs, symbol);
let list = attrs
.filter_map(move |attr| {
attr.meta_item_list().or_else(|| {
sess.dcx().emit_err(session_diagnostics::ExpectsFeatureList {
span: attr.span(),
) -> impl IntoIterator<Item = Symbol> {
let mut res = Vec::new();
let Some(list) = args.list() else {
cx.emit_err(session_diagnostics::ExpectsFeatureList {
span: cx.attr_span,
name: symbol.to_ident_string(),
});
None
})
})
.flatten();
return res;
};
list.into_iter().filter_map(move |it| {
let name = it.ident().map(|ident| ident.name);
if name.is_none() {
sess.dcx().emit_err(session_diagnostics::ExpectsFeatures {
span: it.span(),
for param in list.mixed() {
let param_span = param.span();
if let Some(ident) = param.meta_item().and_then(|i| i.word_without_args()) {
res.push(ident.name);
} else {
cx.emit_err(session_diagnostics::ExpectsFeatures {
span: param_span,
name: symbol.to_ident_string(),
});
}
name
})
}
res
}

View file

@ -1,6 +1,4 @@
//! Parsing and validation of builtin attributes
use rustc_ast::{self as ast, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId};
use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId};
use rustc_ast_pretty::pprust;
use rustc_attr_data_structures::RustcVersion;
use rustc_feature::{Features, GatedCfg, find_gated_cfg};
@ -9,10 +7,11 @@ use rustc_session::config::ExpectedValues;
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
use rustc_session::parse::feature_err;
use rustc_span::{Span, Symbol, kw, sym};
use rustc_span::symbol::kw;
use rustc_span::{Span, Symbol, sym};
use crate::util::UnsupportedLiteralReason;
use crate::{fluent_generated, parse_version, session_diagnostics};
use crate::session_diagnostics::{self, UnsupportedLiteralReason};
use crate::{fluent_generated, parse_version};
#[derive(Clone, Debug)]
pub struct Condition {
@ -25,7 +24,7 @@ pub struct Condition {
/// Tests if a cfg-pattern matches the cfg set
pub fn cfg_matches(
cfg: &ast::MetaItemInner,
cfg: &MetaItemInner,
sess: &Session,
lint_node_id: NodeId,
features: Option<&Features>,
@ -80,7 +79,7 @@ fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Fea
/// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
/// evaluate individual items.
pub fn eval_condition(
cfg: &ast::MetaItemInner,
cfg: &MetaItemInner,
sess: &Session,
features: Option<&Features>,
eval: &mut impl FnMut(Condition) -> bool,
@ -88,8 +87,8 @@ pub fn eval_condition(
let dcx = sess.dcx();
let cfg = match cfg {
ast::MetaItemInner::MetaItem(meta_item) => meta_item,
ast::MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => {
MetaItemInner::MetaItem(meta_item) => meta_item,
MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => {
if let Some(features) = features {
// we can't use `try_gate_cfg` as symbols don't differentiate between `r#true`
// and `true`, and we want to keep the former working without feature gate
@ -118,7 +117,7 @@ pub fn eval_condition(
};
match &cfg.kind {
ast::MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => {
MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => {
try_gate_cfg(sym::version, cfg.span, sess, features);
let (min_version, span) = match &mis[..] {
[MetaItemInner::Lit(MetaItemLit { kind: LitKind::Str(sym, ..), span, .. })] => {
@ -150,7 +149,7 @@ pub fn eval_condition(
RustcVersion::CURRENT >= min_version
}
}
ast::MetaItemKind::List(mis) => {
MetaItemKind::List(mis) => {
for mi in mis.iter() {
if mi.meta_item_or_bool().is_none() {
dcx.emit_err(session_diagnostics::UnsupportedLiteral {
@ -209,12 +208,7 @@ pub fn eval_condition(
seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name));
}
res & eval_condition(
&ast::MetaItemInner::MetaItem(mi),
sess,
features,
eval,
)
res & eval_condition(&MetaItemInner::MetaItem(mi), sess, features, eval)
})
}
_ => {
@ -226,7 +220,7 @@ pub fn eval_condition(
}
}
}
ast::MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => {
MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => {
dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span });
true
}
@ -239,7 +233,7 @@ pub fn eval_condition(
});
true
}
ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => {
MetaItemKind::Word | MetaItemKind::NameValue(..) => {
let ident = cfg.ident().expect("multi-segment cfg predicate");
eval(Condition {
name: ident.name,

View file

@ -1,21 +1,58 @@
//! Parsing and validation of builtin attributes
use rustc_attr_data_structures::AttributeKind;
use rustc_span::{Span, Symbol, sym};
use thin_vec::ThinVec;
use rustc_ast::MetaItemInner;
use rustc_ast::attr::AttributeExt;
use rustc_span::Symbol;
use super::{AcceptMapping, AttributeParser};
use crate::context::FinalizeContext;
use crate::session_diagnostics;
/// Read the content of a `rustc_confusables` attribute, and return the list of candidate names.
pub fn parse_confusables(attr: &impl AttributeExt) -> Option<Vec<Symbol>> {
let metas = attr.meta_item_list()?;
#[derive(Default)]
pub(crate) struct ConfusablesParser {
confusables: ThinVec<Symbol>,
first_span: Option<Span>,
}
let mut candidates = Vec::new();
for meta in metas {
let MetaItemInner::Lit(meta_lit) = meta else {
return None;
impl AttributeParser for ConfusablesParser {
const ATTRIBUTES: AcceptMapping<Self> = &[(&[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;
};
candidates.push(meta_lit.symbol);
if list.is_empty() {
cx.emit_err(session_diagnostics::EmptyConfusables { span: cx.attr_span });
}
Some(candidates)
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;
};
this.confusables.push(lit.symbol);
}
this.first_span.get_or_insert(cx.attr_span);
})];
fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
if self.confusables.is_empty() {
return None;
}
Some(AttributeKind::Confusables {
symbols: self.confusables,
first_span: self.first_span.unwrap(),
})
}
}

View file

@ -1,121 +1,122 @@
//! Parsing and validation of builtin attributes
use rustc_ast::attr::AttributeExt;
use rustc_ast::{MetaItem, MetaItemInner};
use rustc_ast_pretty::pprust;
use rustc_attr_data_structures::{DeprecatedSince, Deprecation};
use rustc_feature::Features;
use rustc_session::Session;
use rustc_attr_data_structures::{AttributeKind, DeprecatedSince, Deprecation};
use rustc_span::symbol::Ident;
use rustc_span::{Span, Symbol, sym};
use super::util::UnsupportedLiteralReason;
use crate::{parse_version, session_diagnostics};
use super::SingleAttributeParser;
use super::util::parse_version;
use crate::context::AcceptContext;
use crate::parser::ArgParser;
use crate::session_diagnostics;
use crate::session_diagnostics::UnsupportedLiteralReason;
/// Finds the deprecation attribute. `None` if none exists.
pub fn find_deprecation(
sess: &Session,
features: &Features,
attrs: &[impl AttributeExt],
) -> Option<(Deprecation, Span)> {
let mut depr: Option<(Deprecation, Span)> = None;
let is_rustc = features.staged_api();
pub(crate) struct DeprecationParser;
'outer: for attr in attrs {
if !attr.has_name(sym::deprecated) {
continue;
fn get(
cx: &AcceptContext<'_>,
ident: Ident,
param_span: Span,
arg: &ArgParser<'_>,
item: &Option<Symbol>,
) -> Option<Symbol> {
if item.is_some() {
cx.emit_err(session_diagnostics::MultipleItem {
span: param_span,
item: ident.to_string(),
});
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),
});
None
}
} else {
// FIXME(jdonszelmann): suggestion?
cx.emit_err(session_diagnostics::IncorrectMetaItem { span: param_span, suggestion: None });
None
}
}
impl SingleAttributeParser for DeprecationParser {
const PATH: &'static [rustc_span::Symbol] = &[sym::deprecated];
fn on_duplicate(cx: &AcceptContext<'_>, first_span: rustc_span::Span) {
// FIXME(jdonszelmann): merge with errors from check_attrs.rs
cx.emit_err(session_diagnostics::UnusedMultiple {
this: cx.attr_span,
other: first_span,
name: sym::deprecated,
});
}
fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let features = cx.features();
let mut since = None;
let mut note = None;
let mut suggestion = None;
if attr.is_doc_comment() {
continue;
} else if attr.is_word() {
} else if let Some(value) = attr.value_str() {
note = Some(value)
} else if let Some(list) = attr.meta_item_list() {
let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
if item.is_some() {
sess.dcx().emit_err(session_diagnostics::MultipleItem {
span: meta.span,
item: pprust::path_to_string(&meta.path),
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 false;
}
if let Some(v) = meta.value_str() {
*item = Some(v);
true
} else {
if let Some(lit) = meta.name_value_literal() {
sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral {
span: lit.span,
reason: UnsupportedLiteralReason::DeprecatedString,
is_bytestr: lit.kind.is_bytestr(),
start_point_span: sess.source_map().start_point(lit.span),
});
} else {
sess.dcx()
.emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span });
}
false
}
return None;
};
for meta in &list {
match meta {
MetaItemInner::MetaItem(mi) => match mi.name_or_empty() {
let (ident, arg) = param.word_or_empty();
match ident.name {
sym::since => {
if !get(mi, &mut since) {
continue 'outer;
}
since = Some(get(cx, ident, param_span, arg, &since)?);
}
sym::note => {
if !get(mi, &mut note) {
continue 'outer;
}
note = Some(get(cx, ident, param_span, arg, &note)?);
}
sym::suggestion => {
if !features.deprecated_suggestion() {
sess.dcx().emit_err(
session_diagnostics::DeprecatedItemSuggestion {
span: mi.span,
is_nightly: sess.is_nightly_build(),
cx.emit_err(session_diagnostics::DeprecatedItemSuggestion {
span: param_span,
is_nightly: cx.sess().is_nightly_build(),
details: (),
},
);
});
}
if !get(mi, &mut suggestion) {
continue 'outer;
}
suggestion = Some(get(cx, ident, param_span, arg, &suggestion)?);
}
_ => {
sess.dcx().emit_err(session_diagnostics::UnknownMetaItem {
span: meta.span(),
item: pprust::path_to_string(&mi.path),
cx.emit_err(session_diagnostics::UnknownMetaItem {
span: param_span,
item: ident.to_string(),
expected: if features.deprecated_suggestion() {
&["since", "note", "suggestion"]
} else {
&["since", "note"]
},
});
continue 'outer;
}
},
MetaItemInner::Lit(lit) => {
sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral {
span: lit.span,
reason: UnsupportedLiteralReason::DeprecatedKvPair,
is_bytestr: false,
start_point_span: sess.source_map().start_point(lit.span),
});
continue 'outer;
return None;
}
}
}
} else {
continue;
}
let since = if let Some(since) = since {
@ -126,23 +127,24 @@ pub fn find_deprecation(
} else if let Some(version) = parse_version(since) {
DeprecatedSince::RustcVersion(version)
} else {
sess.dcx().emit_err(session_diagnostics::InvalidSince { span: attr.span() });
cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span });
DeprecatedSince::Err
}
} else if is_rustc {
sess.dcx().emit_err(session_diagnostics::MissingSince { span: attr.span() });
cx.emit_err(session_diagnostics::MissingSince { span: cx.attr_span });
DeprecatedSince::Err
} else {
DeprecatedSince::Unspecified
};
if is_rustc && note.is_none() {
sess.dcx().emit_err(session_diagnostics::MissingNote { span: attr.span() });
continue;
cx.emit_err(session_diagnostics::MissingNote { span: cx.attr_span });
return None;
}
depr = Some((Deprecation { since, note, suggestion }, attr.span()));
Some(AttributeKind::Deprecation {
deprecation: Deprecation { since, note, suggestion },
span: cx.attr_span,
})
}
depr
}

View file

@ -32,14 +32,6 @@ pub(crate) mod stability;
pub(crate) mod transparency;
pub(crate) mod util;
pub use allow_unstable::*;
pub use cfg::*;
pub use confusables::*;
pub use deprecation::*;
pub use repr::*;
pub use stability::*;
pub use transparency::*;
type AcceptFn<T> = fn(&mut T, &AcceptContext<'_>, &ArgParser<'_>);
type AcceptMapping<T> = &'static [(&'static [rustc_span::Symbol], AcceptFn<T>)];

View file

@ -1,15 +1,13 @@
//! Parsing and validation of builtin attributes
use rustc_abi::Align;
use rustc_ast::attr::AttributeExt;
use rustc_ast::{self as ast, MetaItemKind};
use rustc_attr_data_structures::IntType;
use rustc_attr_data_structures::ReprAttr::*;
use rustc_session::Session;
use rustc_span::{Symbol, sym};
use rustc_ast::{IntTy, LitIntType, LitKind, UintTy};
use rustc_attr_data_structures::{AttributeKind, IntType, ReprAttr};
use rustc_span::{Span, Symbol, sym};
use crate::ReprAttr;
use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
use super::{CombineAttributeParser, ConvertFn};
use crate::context::AcceptContext;
use crate::parser::{ArgParser, MetaItemListParser, MetaItemParser};
use crate::session_diagnostics;
use crate::session_diagnostics::IncorrectReprFormatGenericCause;
/// Parse #[repr(...)] forms.
///
@ -18,185 +16,216 @@ use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
/// the same discriminant size that the corresponding C enum would or C
/// structure layout, `packed` to remove padding, and `transparent` to delegate representation
/// concerns to the only non-ZST field.
pub fn find_repr_attrs(sess: &Session, attr: &impl AttributeExt) -> Vec<ReprAttr> {
if attr.has_name(sym::repr) { parse_repr_attr(sess, attr) } else { Vec::new() }
// FIXME(jdonszelmann): is a vec the right representation here even? isn't it just a struct?
pub(crate) struct ReprParser;
impl CombineAttributeParser for ReprParser {
type Item = (ReprAttr, Span);
const PATH: &'static [rustc_span::Symbol] = &[sym::repr];
const CONVERT: ConvertFn<Self::Item> = AttributeKind::Repr;
fn extend<'a>(
cx: &'a AcceptContext<'a>,
args: &'a ArgParser<'a>,
) -> impl IntoIterator<Item = Self::Item> + 'a {
let mut reprs = Vec::new();
let Some(list) = args.list() else {
return reprs;
};
if list.is_empty() {
// this is so validation can emit a lint
reprs.push((ReprAttr::ReprEmpty, cx.attr_span));
}
for param in list.mixed() {
if let Some(_) = param.lit() {
cx.emit_err(session_diagnostics::ReprIdent { span: cx.attr_span });
continue;
}
reprs.extend(
param.meta_item().and_then(|mi| parse_repr(cx, &mi)).map(|r| (r, param.span())),
);
}
reprs
}
}
pub fn parse_repr_attr(sess: &Session, attr: &impl AttributeExt) -> Vec<ReprAttr> {
assert!(attr.has_name(sym::repr), "expected `#[repr(..)]`, found: {attr:?}");
let mut acc = Vec::new();
let dcx = sess.dcx();
if let Some(items) = attr.meta_item_list() {
for item in items {
let mut recognised = false;
if item.is_word() {
let hint = match item.name_or_empty() {
sym::Rust => Some(ReprRust),
sym::C => Some(ReprC),
sym::packed => Some(ReprPacked(Align::ONE)),
sym::simd => Some(ReprSimd),
sym::transparent => Some(ReprTransparent),
sym::align => {
sess.dcx().emit_err(session_diagnostics::InvalidReprAlignNeedArg {
span: item.span(),
});
recognised = true;
None
}
name => int_type_of_word(name).map(ReprInt),
macro_rules! int_pat {
() => {
sym::i8
| sym::u8
| sym::i16
| sym::u16
| sym::i32
| sym::u32
| sym::i64
| sym::u64
| sym::i128
| sym::u128
| sym::isize
| sym::usize
};
if let Some(h) = hint {
recognised = true;
acc.push(h);
}
} else if let Some((name, value)) = item.singleton_lit_list() {
let mut literal_error = None;
let mut err_span = item.span();
if name == sym::align {
recognised = true;
match parse_alignment(&value.kind) {
Ok(literal) => acc.push(ReprAlign(literal)),
Err(message) => {
err_span = value.span;
literal_error = Some(message)
}
};
} else if name == sym::packed {
recognised = true;
match parse_alignment(&value.kind) {
Ok(literal) => acc.push(ReprPacked(literal)),
Err(message) => {
err_span = value.span;
literal_error = Some(message)
}
};
} else if matches!(name, sym::Rust | sym::C | sym::simd | sym::transparent)
|| int_type_of_word(name).is_some()
{
recognised = true;
sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen {
span: item.span(),
name: name.to_ident_string(),
});
}
if let Some(literal_error) = literal_error {
sess.dcx().emit_err(session_diagnostics::InvalidReprGeneric {
span: err_span,
repr_arg: name.to_ident_string(),
error_part: literal_error,
});
}
} else if let Some(meta_item) = item.meta_item() {
match &meta_item.kind {
MetaItemKind::NameValue(value) => {
if meta_item.has_name(sym::align) || meta_item.has_name(sym::packed) {
let name = meta_item.name_or_empty().to_ident_string();
recognised = true;
sess.dcx().emit_err(session_diagnostics::IncorrectReprFormatGeneric {
span: item.span(),
repr_arg: &name,
cause: IncorrectReprFormatGenericCause::from_lit_kind(
item.span(),
&value.kind,
&name,
),
});
} else if matches!(
meta_item.name_or_empty(),
sym::Rust | sym::C | sym::simd | sym::transparent
) || int_type_of_word(meta_item.name_or_empty()).is_some()
{
recognised = true;
sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoValue {
span: meta_item.span,
name: meta_item.name_or_empty().to_ident_string(),
});
}
}
MetaItemKind::List(nested_items) => {
if meta_item.has_name(sym::align) {
recognised = true;
if let [nested_item] = nested_items.as_slice() {
sess.dcx().emit_err(
session_diagnostics::IncorrectReprFormatExpectInteger {
span: nested_item.span(),
},
);
} else {
sess.dcx().emit_err(
session_diagnostics::IncorrectReprFormatAlignOneArg {
span: meta_item.span,
},
);
}
} else if meta_item.has_name(sym::packed) {
recognised = true;
if let [nested_item] = nested_items.as_slice() {
sess.dcx().emit_err(
session_diagnostics::IncorrectReprFormatPackedExpectInteger {
span: nested_item.span(),
},
);
} else {
sess.dcx().emit_err(
session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg {
span: meta_item.span,
},
);
}
} else if matches!(
meta_item.name_or_empty(),
sym::Rust | sym::C | sym::simd | sym::transparent
) || int_type_of_word(meta_item.name_or_empty()).is_some()
{
recognised = true;
sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen {
span: meta_item.span,
name: meta_item.name_or_empty().to_ident_string(),
});
}
}
_ => (),
}
}
if !recognised {
// Not a word we recognize. This will be caught and reported by
// the `check_mod_attrs` pass, but this pass doesn't always run
// (e.g. if we only pretty-print the source), so we have to gate
// the `span_delayed_bug` call as follows:
if sess.opts.pretty.is_none_or(|pp| pp.needs_analysis()) {
dcx.span_delayed_bug(item.span(), "unrecognized representation hint");
}
}
}
}
acc
}
fn int_type_of_word(s: Symbol) -> Option<IntType> {
use rustc_attr_data_structures::IntType::*;
use IntType::*;
match s {
sym::i8 => Some(SignedInt(ast::IntTy::I8)),
sym::u8 => Some(UnsignedInt(ast::UintTy::U8)),
sym::i16 => Some(SignedInt(ast::IntTy::I16)),
sym::u16 => Some(UnsignedInt(ast::UintTy::U16)),
sym::i32 => Some(SignedInt(ast::IntTy::I32)),
sym::u32 => Some(UnsignedInt(ast::UintTy::U32)),
sym::i64 => Some(SignedInt(ast::IntTy::I64)),
sym::u64 => Some(UnsignedInt(ast::UintTy::U64)),
sym::i128 => Some(SignedInt(ast::IntTy::I128)),
sym::u128 => Some(UnsignedInt(ast::UintTy::U128)),
sym::isize => Some(SignedInt(ast::IntTy::Isize)),
sym::usize => Some(UnsignedInt(ast::UintTy::Usize)),
sym::i8 => Some(SignedInt(IntTy::I8)),
sym::u8 => Some(UnsignedInt(UintTy::U8)),
sym::i16 => Some(SignedInt(IntTy::I16)),
sym::u16 => Some(UnsignedInt(UintTy::U16)),
sym::i32 => Some(SignedInt(IntTy::I32)),
sym::u32 => Some(UnsignedInt(UintTy::U32)),
sym::i64 => Some(SignedInt(IntTy::I64)),
sym::u64 => Some(UnsignedInt(UintTy::U64)),
sym::i128 => Some(SignedInt(IntTy::I128)),
sym::u128 => Some(UnsignedInt(UintTy::U128)),
sym::isize => Some(SignedInt(IntTy::Isize)),
sym::usize => Some(UnsignedInt(UintTy::Usize)),
_ => None,
}
}
pub fn parse_alignment(node: &ast::LitKind) -> Result<Align, &'static str> {
if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node {
fn parse_repr(cx: &AcceptContext<'_>, param: &MetaItemParser<'_>) -> Option<ReprAttr> {
use ReprAttr::*;
// FIXME(jdonszelmann): invert the parsing here to match on the word first and then the
// structure.
let (ident, args) = param.word_or_empty();
match (ident.name, args) {
(sym::align, ArgParser::NoArgs) => {
cx.emit_err(session_diagnostics::InvalidReprAlignNeedArg { span: ident.span });
None
}
(sym::align, ArgParser::List(l)) => parse_repr_align(cx, l, param.span(), AlignKind::Align),
(sym::packed, ArgParser::NoArgs) => Some(ReprPacked(Align::ONE)),
(sym::packed, ArgParser::List(l)) => {
parse_repr_align(cx, l, param.span(), AlignKind::Packed)
}
(sym::align | sym::packed, ArgParser::NameValue(l)) => {
cx.emit_err(session_diagnostics::IncorrectReprFormatGeneric {
span: param.span(),
// FIXME(jdonszelmann) can just be a string in the diag type
repr_arg: &ident.to_string(),
cause: IncorrectReprFormatGenericCause::from_lit_kind(
param.span(),
&l.value_as_lit().kind,
ident.name.as_str(),
),
});
None
}
(sym::Rust, ArgParser::NoArgs) => Some(ReprRust),
(sym::C, ArgParser::NoArgs) => Some(ReprC),
(sym::simd, ArgParser::NoArgs) => Some(ReprSimd),
(sym::transparent, ArgParser::NoArgs) => Some(ReprTransparent),
(i @ int_pat!(), ArgParser::NoArgs) => {
// int_pat!() should make sure it always parses
Some(ReprInt(int_type_of_word(i).unwrap()))
}
(
sym::Rust | sym::C | sym::simd | sym::transparent | int_pat!(),
ArgParser::NameValue(_),
) => {
cx.emit_err(session_diagnostics::InvalidReprHintNoValue {
span: param.span(),
name: ident.to_string(),
});
None
}
(sym::Rust | sym::C | sym::simd | sym::transparent | int_pat!(), ArgParser::List(_)) => {
cx.emit_err(session_diagnostics::InvalidReprHintNoParen {
span: param.span(),
name: ident.to_string(),
});
None
}
_ => {
cx.emit_err(session_diagnostics::UnrecognizedReprHint { span: param.span() });
None
}
}
}
enum AlignKind {
Packed,
Align,
}
fn parse_repr_align(
cx: &AcceptContext<'_>,
list: &MetaItemListParser<'_>,
param_span: Span,
align_kind: AlignKind,
) -> Option<ReprAttr> {
use AlignKind::*;
let Some(align) = list.single() else {
match align_kind {
Packed => {
cx.emit_err(session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg {
span: param_span,
});
}
Align => {
cx.dcx().emit_err(session_diagnostics::IncorrectReprFormatAlignOneArg {
span: param_span,
});
}
}
return None;
};
let Some(lit) = align.lit() else {
match align_kind {
Packed => {
cx.emit_err(session_diagnostics::IncorrectReprFormatPackedExpectInteger {
span: align.span(),
});
}
Align => {
cx.emit_err(session_diagnostics::IncorrectReprFormatExpectInteger {
span: align.span(),
});
}
}
return None;
};
match parse_alignment(&lit.kind) {
Ok(literal) => Some(match align_kind {
AlignKind::Packed => ReprAttr::ReprPacked(literal),
AlignKind::Align => ReprAttr::ReprAlign(literal),
}),
Err(message) => {
cx.emit_err(session_diagnostics::InvalidReprGeneric {
span: lit.span,
repr_arg: match align_kind {
Packed => "packed".to_string(),
Align => "align".to_string(),
},
error_part: message,
});
None
}
}
}
fn parse_alignment(node: &LitKind) -> Result<Align, &'static str> {
if let LitKind::Int(literal, LitIntType::Unsuffixed) = node {
// `Align::from_bytes` accepts 0 as an input, check is_power_of_two() first
if literal.get().is_power_of_two() {
// Only possible error is larger than 2^29

View file

@ -1,266 +1,258 @@
//! Parsing and validation of builtin attributes
use std::num::NonZero;
use rustc_ast::MetaItem;
use rustc_ast::attr::AttributeExt;
use rustc_ast_pretty::pprust;
use rustc_attr_data_structures::{
ConstStability, DefaultBodyStability, Stability, StabilityLevel, StableSince, UnstableReason,
VERSION_PLACEHOLDER,
AttributeKind, DefaultBodyStability, PartialConstStability, Stability, StabilityLevel,
StableSince, UnstableReason, VERSION_PLACEHOLDER,
};
use rustc_errors::ErrorGuaranteed;
use rustc_session::Session;
use rustc_span::{Span, Symbol, kw, sym};
use crate::attributes::util::UnsupportedLiteralReason;
use crate::{parse_version, session_diagnostics};
use super::util::parse_version;
use super::{AcceptMapping, AttributeParser, SingleAttributeParser};
use crate::context::{AcceptContext, FinalizeContext};
use crate::parser::{ArgParser, MetaItemParser};
use crate::session_diagnostics::{self, UnsupportedLiteralReason};
/// Collects stability info from `stable`/`unstable`/`rustc_allowed_through_unstable_modules`
/// attributes in `attrs`. Returns `None` if no stability attributes are found.
pub fn find_stability(
sess: &Session,
attrs: &[impl AttributeExt],
item_sp: Span,
) -> Option<(Stability, Span)> {
let mut stab: Option<(Stability, Span)> = None;
let mut allowed_through_unstable_modules = None;
for attr in attrs {
match attr.name_or_empty() {
sym::rustc_allowed_through_unstable_modules => {
// The value is mandatory, but avoid ICEs in case such code reaches this function.
allowed_through_unstable_modules = Some(attr.value_str().unwrap_or_else(|| {
sess.dcx().span_delayed_bug(
item_sp,
"`#[rustc_allowed_through_unstable_modules]` without deprecation message",
);
kw::Empty
}))
macro_rules! reject_outside_std {
($cx: ident) => {
// Emit errors for non-staged-api crates.
if !$cx.features().staged_api() {
$cx.emit_err(session_diagnostics::StabilityOutsideStd { span: $cx.attr_span });
return;
}
sym::unstable => {
if stab.is_some() {
sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
span: attr.span(),
};
}
#[derive(Default)]
pub(crate) struct StabilityParser {
allowed_through_unstable_modules: Option<Symbol>,
stability: Option<(Stability, Span)>,
}
impl StabilityParser {
/// Checks, and emits an error when a stability (or unstability) was already set, which would be a duplicate.
fn check_duplicate(&self, cx: &AcceptContext<'_>) -> bool {
if let Some((_, _)) = self.stability {
cx.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span });
true
} else {
false
}
}
}
impl AttributeParser for StabilityParser {
const ATTRIBUTES: AcceptMapping<Self> = &[
(&[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 =
Some(match args.name_value().and_then(|i| i.value_as_str()) {
Some(msg) => msg,
None => kw::Empty,
});
break;
}
}),
];
if let Some((feature, level)) = parse_unstability(sess, attr) {
stab = Some((Stability { level, feature }, attr.span()));
}
}
sym::stable => {
if stab.is_some() {
sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
span: attr.span(),
});
break;
}
if let Some((feature, level)) = parse_stability(sess, attr) {
stab = Some((Stability { level, feature }, attr.span()));
}
}
_ => {}
}
}
if let Some(allowed_through_unstable_modules) = allowed_through_unstable_modules {
match &mut stab {
Some((
fn finalize(mut self, cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
if let Some(atum) = self.allowed_through_unstable_modules {
if let Some((
Stability {
level: StabilityLevel::Stable { allowed_through_unstable_modules: in_stab, .. },
level: StabilityLevel::Stable { ref mut allowed_through_unstable_modules, .. },
..
},
_,
)) => *in_stab = Some(allowed_through_unstable_modules),
_ => {
sess.dcx()
.emit_err(session_diagnostics::RustcAllowedUnstablePairing { span: item_sp });
}
}
}
stab
}
/// Collects stability info from `rustc_const_stable`/`rustc_const_unstable`/`rustc_promotable`
/// attributes in `attrs`. Returns `None` if no stability attributes are found.
pub fn find_const_stability(
sess: &Session,
attrs: &[impl AttributeExt],
item_sp: Span,
) -> Option<(ConstStability, Span)> {
let mut const_stab: Option<(ConstStability, Span)> = None;
let mut promotable = false;
let mut const_stable_indirect = false;
for attr in attrs {
match attr.name_or_empty() {
sym::rustc_promotable => promotable = true,
sym::rustc_const_stable_indirect => const_stable_indirect = true,
sym::rustc_const_unstable => {
if const_stab.is_some() {
sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
span: attr.span(),
});
break;
}
if let Some((feature, level)) = parse_unstability(sess, attr) {
const_stab = Some((
ConstStability {
level,
feature,
const_stable_indirect: false,
promotable: false,
},
attr.span(),
));
}
}
sym::rustc_const_stable => {
if const_stab.is_some() {
sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
span: attr.span(),
});
break;
}
if let Some((feature, level)) = parse_stability(sess, attr) {
const_stab = Some((
ConstStability {
level,
feature,
const_stable_indirect: false,
promotable: false,
},
attr.span(),
));
}
}
_ => {}
}
}
// Merge promotable and const_stable_indirect into stability info
if promotable {
match &mut const_stab {
Some((stab, _)) => stab.promotable = promotable,
_ => {
_ = sess
.dcx()
.emit_err(session_diagnostics::RustcPromotablePairing { span: item_sp })
}
}
}
if const_stable_indirect {
match &mut const_stab {
Some((stab, _)) => {
if stab.is_const_unstable() {
stab.const_stable_indirect = true;
)) = self.stability
{
*allowed_through_unstable_modules = Some(atum);
} else {
_ = sess.dcx().emit_err(session_diagnostics::RustcConstStableIndirectPairing {
span: item_sp,
})
}
}
_ => {
// This function has no const stability attribute, but has `const_stable_indirect`.
// We ignore that; unmarked functions are subject to recursive const stability
// checks by default so we do carry out the user's intent.
}
cx.dcx().emit_err(session_diagnostics::RustcAllowedUnstablePairing {
span: cx.target_span,
});
}
}
const_stab
}
let (stability, span) = self.stability?;
/// Calculates the const stability for a const function in a `-Zforce-unstable-if-unmarked` crate
/// without the `staged_api` feature.
pub fn unmarked_crate_const_stab(
_sess: &Session,
attrs: &[impl AttributeExt],
regular_stab: Stability,
) -> ConstStability {
assert!(regular_stab.level.is_unstable());
// The only attribute that matters here is `rustc_const_stable_indirect`.
// We enforce recursive const stability rules for those functions.
let const_stable_indirect =
attrs.iter().any(|a| a.name_or_empty() == sym::rustc_const_stable_indirect);
ConstStability {
feature: regular_stab.feature,
const_stable_indirect,
promotable: false,
level: regular_stab.level,
Some(AttributeKind::Stability { stability, span })
}
}
/// Collects stability info from `rustc_default_body_unstable` attributes in `attrs`.
/// Returns `None` if no stability attributes are found.
pub fn find_body_stability(
sess: &Session,
attrs: &[impl AttributeExt],
) -> Option<(DefaultBodyStability, Span)> {
let mut body_stab: Option<(DefaultBodyStability, Span)> = None;
for attr in attrs {
if attr.has_name(sym::rustc_default_body_unstable) {
if body_stab.is_some() {
sess.dcx()
.emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span() });
break;
}
if let Some((feature, level)) = parse_unstability(sess, attr) {
body_stab = Some((DefaultBodyStability { level, feature }, attr.span()));
}
}
}
body_stab
// FIXME(jdonszelmann) change to Single
#[derive(Default)]
pub(crate) struct BodyStabilityParser {
stability: Option<(DefaultBodyStability, Span)>,
}
fn insert_or_error(sess: &Session, meta: &MetaItem, item: &mut Option<Symbol>) -> Option<()> {
impl AttributeParser for BodyStabilityParser {
const ATTRIBUTES: AcceptMapping<Self> =
&[(&[sym::rustc_default_body_unstable], |this, cx, args| {
reject_outside_std!(cx);
if this.stability.is_some() {
cx.dcx()
.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span });
} else if let Some((feature, level)) = parse_unstability(cx, args) {
this.stability = Some((DefaultBodyStability { level, feature }, cx.attr_span));
}
})];
fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
let (stability, span) = self.stability?;
Some(AttributeKind::BodyStability { stability, span })
}
}
pub(crate) struct ConstStabilityIndirectParser;
// FIXME(jdonszelmann): single word attribute group when we have these
impl SingleAttributeParser for ConstStabilityIndirectParser {
const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_const_stable_indirect];
// ignore
fn on_duplicate(_cx: &AcceptContext<'_>, _first_span: Span) {}
fn convert(_cx: &AcceptContext<'_>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
Some(AttributeKind::ConstStabilityIndirect)
}
}
#[derive(Default)]
pub(crate) struct ConstStabilityParser {
promotable: bool,
stability: Option<(PartialConstStability, Span)>,
}
impl ConstStabilityParser {
/// Checks, and emits an error when a stability (or unstability) was already set, which would be a duplicate.
fn check_duplicate(&self, cx: &AcceptContext<'_>) -> bool {
if let Some((_, _)) = self.stability {
cx.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span });
true
} else {
false
}
}
}
impl AttributeParser for ConstStabilityParser {
const ATTRIBUTES: AcceptMapping<Self> = &[
(&[sym::rustc_const_stable], |this, cx, args| {
reject_outside_std!(cx);
if !this.check_duplicate(cx)
&& let Some((feature, level)) = parse_stability(cx, args)
{
this.stability = Some((
PartialConstStability { level, feature, promotable: false },
cx.attr_span,
));
}
}),
(&[sym::rustc_const_unstable], |this, cx, args| {
reject_outside_std!(cx);
if !this.check_duplicate(cx)
&& let Some((feature, level)) = parse_unstability(cx, args)
{
this.stability = Some((
PartialConstStability { level, feature, promotable: false },
cx.attr_span,
));
}
}),
(&[sym::rustc_promotable], |this, cx, _| {
reject_outside_std!(cx);
this.promotable = true;
}),
];
fn finalize(mut self, cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
if self.promotable {
if let Some((ref mut stab, _)) = self.stability {
stab.promotable = true;
} else {
cx.dcx()
.emit_err(session_diagnostics::RustcPromotablePairing { span: cx.target_span });
}
}
let (stability, span) = self.stability?;
Some(AttributeKind::ConstStability { stability, span })
}
}
/// Tries to insert the value of a `key = value` meta item into an option.
///
/// Emits an error when either the option was already Some, or the arguments weren't of form
/// `name = value`
fn insert_value_into_option_or_error(
cx: &AcceptContext<'_>,
param: &MetaItemParser<'_>,
item: &mut Option<Symbol>,
) -> Option<()> {
if item.is_some() {
sess.dcx().emit_err(session_diagnostics::MultipleItem {
span: meta.span,
item: pprust::path_to_string(&meta.path),
cx.emit_err(session_diagnostics::MultipleItem {
span: param.span(),
item: param.path_without_args().to_string(),
});
None
} else if let Some(v) = meta.value_str() {
*item = Some(v);
} else if let Some(v) = param.args().name_value()
&& let Some(s) = v.value_as_str()
{
*item = Some(s);
Some(())
} else {
sess.dcx().emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span });
cx.emit_err(session_diagnostics::IncorrectMetaItem {
span: param.span(),
suggestion: None,
});
None
}
}
/// Read the content of a `stable`/`rustc_const_stable` attribute, and return the feature name and
/// its stability information.
fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol, StabilityLevel)> {
let metas = attr.meta_item_list()?;
pub(crate) fn parse_stability(
cx: &AcceptContext<'_>,
args: &ArgParser<'_>,
) -> Option<(Symbol, StabilityLevel)> {
let mut feature = None;
let mut since = None;
for meta in metas {
let Some(mi) = meta.meta_item() else {
sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral {
span: meta.span(),
for param in args.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::Generic,
is_bytestr: false,
start_point_span: sess.source_map().start_point(meta.span()),
start_point_span: cx.sess().source_map().start_point(param_span),
});
return None;
};
match mi.name_or_empty() {
sym::feature => insert_or_error(sess, mi, &mut feature)?,
sym::since => insert_or_error(sess, mi, &mut since)?,
match param.word_or_empty_without_args().name {
sym::feature => insert_value_into_option_or_error(cx, &param, &mut feature)?,
sym::since => insert_value_into_option_or_error(cx, &param, &mut since)?,
_ => {
sess.dcx().emit_err(session_diagnostics::UnknownMetaItem {
span: meta.span(),
item: pprust::path_to_string(&mi.path),
cx.emit_err(session_diagnostics::UnknownMetaItem {
span: param_span,
item: param.path_without_args().to_string(),
expected: &["feature", "since"],
});
return None;
@ -271,9 +263,9 @@ fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol,
let feature = match feature {
Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature),
Some(_bad_feature) => {
Err(sess.dcx().emit_err(session_diagnostics::NonIdentFeature { span: attr.span() }))
Err(cx.emit_err(session_diagnostics::NonIdentFeature { span: cx.attr_span }))
}
None => Err(sess.dcx().emit_err(session_diagnostics::MissingFeature { span: attr.span() })),
None => Err(cx.emit_err(session_diagnostics::MissingFeature { span: cx.attr_span })),
};
let since = if let Some(since) = since {
@ -282,11 +274,11 @@ fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol,
} else if let Some(version) = parse_version(since) {
StableSince::Version(version)
} else {
sess.dcx().emit_err(session_diagnostics::InvalidSince { span: attr.span() });
cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span });
StableSince::Err
}
} else {
sess.dcx().emit_err(session_diagnostics::MissingSince { span: attr.span() });
cx.emit_err(session_diagnostics::MissingSince { span: cx.attr_span });
StableSince::Err
};
@ -299,46 +291,48 @@ fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol,
}
}
/// Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable`
// Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable`
/// attribute, and return the feature name and its stability information.
fn parse_unstability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol, StabilityLevel)> {
let metas = attr.meta_item_list()?;
pub(crate) fn parse_unstability(
cx: &AcceptContext<'_>,
args: &ArgParser<'_>,
) -> Option<(Symbol, StabilityLevel)> {
let mut feature = None;
let mut reason = None;
let mut issue = None;
let mut issue_num = None;
let mut is_soft = false;
let mut implied_by = None;
for meta in metas {
let Some(mi) = meta.meta_item() else {
sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral {
span: meta.span(),
for param in args.list()?.mixed() {
let Some(param) = param.meta_item() else {
cx.emit_err(session_diagnostics::UnsupportedLiteral {
span: param.span(),
reason: UnsupportedLiteralReason::Generic,
is_bytestr: false,
start_point_span: sess.source_map().start_point(meta.span()),
start_point_span: cx.sess().source_map().start_point(param.span()),
});
return None;
};
match mi.name_or_empty() {
sym::feature => insert_or_error(sess, mi, &mut feature)?,
sym::reason => insert_or_error(sess, mi, &mut reason)?,
let (word, args) = param.word_or_empty();
match word.name {
sym::feature => insert_value_into_option_or_error(cx, &param, &mut feature)?,
sym::reason => insert_value_into_option_or_error(cx, &param, &mut reason)?,
sym::issue => {
insert_or_error(sess, mi, &mut issue)?;
insert_value_into_option_or_error(cx, &param, &mut issue)?;
// These unwraps are safe because `insert_or_error` ensures the meta item
// These unwraps are safe because `insert_value_into_option_or_error` ensures the meta item
// is a name/value pair string literal.
issue_num = match issue.unwrap().as_str() {
"none" => None,
issue => match issue.parse::<NonZero<u32>>() {
issue_str => match issue_str.parse::<NonZero<u32>>() {
Ok(num) => Some(num),
Err(err) => {
sess.dcx().emit_err(
cx.emit_err(
session_diagnostics::InvalidIssueString {
span: mi.span,
span: param.span(),
cause: session_diagnostics::InvalidIssueStringCause::from_int_error_kind(
mi.name_value_literal_span().unwrap(),
args.name_value().unwrap().value_span,
err.kind(),
),
},
@ -349,16 +343,16 @@ fn parse_unstability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol
};
}
sym::soft => {
if !mi.is_word() {
sess.dcx().emit_err(session_diagnostics::SoftNoArgs { span: mi.span });
if !args.no_args() {
cx.emit_err(session_diagnostics::SoftNoArgs { span: param.span() });
}
is_soft = true;
}
sym::implied_by => insert_or_error(sess, mi, &mut implied_by)?,
sym::implied_by => insert_value_into_option_or_error(cx, &param, &mut implied_by)?,
_ => {
sess.dcx().emit_err(session_diagnostics::UnknownMetaItem {
span: meta.span(),
item: pprust::path_to_string(&mi.path),
cx.emit_err(session_diagnostics::UnknownMetaItem {
span: param.span(),
item: param.path_without_args().to_string(),
expected: &["feature", "reason", "issue", "soft", "implied_by"],
});
return None;
@ -369,14 +363,13 @@ fn parse_unstability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol
let feature = match feature {
Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature),
Some(_bad_feature) => {
Err(sess.dcx().emit_err(session_diagnostics::NonIdentFeature { span: attr.span() }))
Err(cx.emit_err(session_diagnostics::NonIdentFeature { span: cx.attr_span }))
}
None => Err(sess.dcx().emit_err(session_diagnostics::MissingFeature { span: attr.span() })),
None => Err(cx.emit_err(session_diagnostics::MissingFeature { span: cx.attr_span })),
};
let issue = issue.ok_or_else(|| {
sess.dcx().emit_err(session_diagnostics::MissingIssue { span: attr.span() })
});
let issue =
issue.ok_or_else(|| cx.emit_err(session_diagnostics::MissingIssue { span: cx.attr_span }));
match (feature, issue) {
(Ok(feature), Ok(_)) => {

View file

@ -1,36 +1,33 @@
use rustc_ast::attr::AttributeExt;
use rustc_attr_data_structures::TransparencyError;
use rustc_attr_data_structures::AttributeKind;
use rustc_span::hygiene::Transparency;
use rustc_span::sym;
pub fn find_transparency(
attrs: &[impl AttributeExt],
macro_rules: bool,
) -> (Transparency, Option<TransparencyError>) {
let mut transparency = None;
let mut error = None;
for attr in attrs {
if attr.has_name(sym::rustc_macro_transparency) {
if let Some((_, old_span)) = transparency {
error = Some(TransparencyError::MultipleTransparencyAttrs(old_span, attr.span()));
break;
} else if let Some(value) = attr.value_str() {
transparency = Some((
match value {
sym::transparent => Transparency::Transparent,
sym::semitransparent => Transparency::SemiTransparent,
sym::opaque => Transparency::Opaque,
_ => {
error =
Some(TransparencyError::UnknownTransparency(value, attr.span()));
continue;
use super::{AcceptContext, SingleAttributeParser};
use crate::parser::ArgParser;
pub(crate) struct TransparencyParser;
// FIXME(jdonszelmann): make these proper diagnostics
#[allow(rustc::untranslatable_diagnostic)]
#[allow(rustc::diagnostic_outside_of_impl)]
impl SingleAttributeParser for TransparencyParser {
const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_macro_transparency];
fn on_duplicate(cx: &crate::context::AcceptContext<'_>, first_span: rustc_span::Span) {
cx.dcx().span_err(vec![first_span, cx.attr_span], "multiple macro transparency attributes");
}
},
attr.span(),
));
fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind> {
match args.name_value().and_then(|nv| nv.value_as_str()) {
Some(sym::transparent) => Some(Transparency::Transparent),
Some(sym::semitransparent) => Some(Transparency::SemiTransparent),
Some(sym::opaque) => Some(Transparency::Opaque),
Some(other) => {
cx.dcx().span_err(cx.attr_span, format!("unknown macro transparency: `{other}`"));
None
}
None => None,
}
.map(AttributeKind::MacroTransparency)
}
let fallback = if macro_rules { Transparency::SemiTransparent } else { Transparency::Opaque };
(transparency.map_or(fallback, |t| t.0), error)
}

View file

@ -3,22 +3,6 @@ use rustc_attr_data_structures::RustcVersion;
use rustc_feature::is_builtin_attr_name;
use rustc_span::{Symbol, sym};
pub(crate) enum UnsupportedLiteralReason {
Generic,
CfgString,
CfgBoolean,
DeprecatedString,
DeprecatedKvPair,
}
pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool {
attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
}
pub fn find_crate_name(attrs: &[impl AttributeExt]) -> Option<Symbol> {
first_attr_value_str_by_name(attrs, sym::crate_name)
}
/// Parse a rustc version number written inside string literal in an attribute,
/// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are
/// not accepted in this position, unlike when parsing CFG_RELEASE.
@ -34,3 +18,11 @@ pub fn parse_version(s: Symbol) -> Option<RustcVersion> {
let patch = digits.next().unwrap_or("0").parse().ok()?;
Some(RustcVersion { major, minor, patch })
}
pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool {
attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
}
pub fn find_crate_name(attrs: &[impl AttributeExt]) -> Option<Symbol> {
first_attr_value_str_by_name(attrs, sym::crate_name)
}

View file

@ -1,3 +1,4 @@
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::ops::Deref;
use std::sync::LazyLock;
@ -11,7 +12,15 @@ use rustc_session::Session;
use rustc_span::symbol::kw;
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
use crate::attributes::AttributeParser as _;
use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
use crate::attributes::confusables::ConfusablesParser;
use crate::attributes::deprecation::DeprecationParser;
use crate::attributes::repr::ReprParser;
use crate::attributes::stability::{
BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser,
};
use crate::attributes::transparency::TransparencyParser;
use crate::attributes::{AttributeParser as _, Combine, Single};
use crate::parser::{ArgParser, MetaItemParser};
macro_rules! attribute_groups {
@ -52,6 +61,24 @@ macro_rules! attribute_groups {
attribute_groups!(
pub(crate) static ATTRIBUTE_MAPPING = [
// tidy-alphabetical-start
BodyStabilityParser,
ConfusablesParser,
ConstStabilityParser,
StabilityParser,
// tidy-alphabetical-end
// tidy-alphabetical-start
Combine<AllowConstFnUnstableParser>,
Combine<AllowInternalUnstableParser>,
Combine<ReprParser>,
// tidy-alphabetical-end
// tidy-alphabetical-start
Single<ConstStabilityIndirectParser>,
Single<DeprecationParser>,
Single<TransparencyParser>,
// tidy-alphabetical-end
];
);

View file

@ -83,14 +83,59 @@
#![warn(unreachable_pub)]
// tidy-alphabetical-end
#[macro_use]
mod attributes;
mod context;
pub mod parser;
mod session_diagnostics;
pub use attributes::*;
pub use attributes::cfg::*;
pub use attributes::util::{find_crate_name, is_builtin_attr, parse_version};
pub use context::{AttributeParser, OmitDoc};
pub use rustc_attr_data_structures::*;
pub use util::{find_crate_name, is_builtin_attr, parse_version};
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
/// Finds attributes in sequences of attributes by pattern matching.
///
/// A little like `matches` but for attributes.
///
/// ```rust,ignore (illustrative)
/// // finds the repr attribute
/// if let Some(r) = find_attr!(attrs, AttributeKind::Repr(r) => r) {
///
/// }
///
/// // checks if one has matched
/// if find_attr!(attrs, AttributeKind::Repr(_)) {
///
/// }
/// ```
///
/// Often this requires you to first end up with a list of attributes.
/// A common way to get those is through `tcx.get_all_attrs(did)`
#[macro_export]
macro_rules! find_attr {
($attributes_list: expr, $pattern: pat $(if $guard: expr)?) => {{
$crate::find_attr!($attributes_list, $pattern $(if $guard)? => ()).is_some()
}};
($attributes_list: expr, $pattern: pat $(if $guard: expr)? => $e: expr) => {{
fn check_attribute_iterator<'a>(_: &'_ impl IntoIterator<Item = &'a rustc_hir::Attribute>) {}
check_attribute_iterator(&$attributes_list);
let find_attribute = |iter| {
for i in $attributes_list {
match i {
rustc_hir::Attribute::Parsed($pattern) $(if $guard)? => {
return Some($e);
}
_ => {}
}
}
None
};
find_attribute($attributes_list)
}};
}

View file

@ -6,9 +6,16 @@ use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuar
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_span::{Span, Symbol};
use crate::attributes::util::UnsupportedLiteralReason;
use crate::fluent_generated as fluent;
pub(crate) enum UnsupportedLiteralReason {
Generic,
CfgString,
CfgBoolean,
DeprecatedString,
DeprecatedKvPair,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_expected_one_cfg_pattern, code = E0536)]
pub(crate) struct ExpectedOneCfgPattern {
@ -39,6 +46,21 @@ pub(crate) struct MultipleItem {
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
@ -337,13 +359,6 @@ pub(crate) struct RustcPromotablePairing {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_rustc_const_stable_indirect_pairing)]
pub(crate) struct RustcConstStableIndirectPairing {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_rustc_allowed_unstable_pairing, code = E0789)]
pub(crate) struct RustcAllowedUnstablePairing {
@ -423,3 +438,44 @@ pub(crate) struct UnknownVersionLiteral {
#[primary_span]
pub span: Span,
}
// FIXME(jdonszelmann) duplicated from `rustc_passes`, remove once `check_attr` is integrated.
#[derive(Diagnostic)]
#[diag(attr_parsing_unused_multiple)]
pub(crate) struct UnusedMultiple {
#[primary_span]
#[suggestion(code = "", applicability = "machine-applicable")]
pub this: Span,
#[note]
pub other: Span,
pub name: Symbol,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_stability_outside_std, code = E0734)]
pub(crate) struct StabilityOutsideStd {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_empty_confusables)]
pub(crate) struct EmptyConfusables {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_repr_ident, code = E0565)]
pub(crate) struct ReprIdent {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_unrecognized_repr_hint, code = E0552)]
#[help]
pub(crate) struct UnrecognizedReprHint {
#[primary_span]
pub span: Span,
}

View file

@ -20,6 +20,7 @@ rustc_errors = { path = "../rustc_errors" }
rustc_expand = { path = "../rustc_expand" }
rustc_feature = { path = "../rustc_feature" }
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_hir = { path = "../rustc_hir" }
rustc_index = { path = "../rustc_index" }
rustc_lexer = { path = "../rustc_lexer" }
rustc_lint_defs = { path = "../rustc_lint_defs" }

View file

@ -185,8 +185,9 @@ use rustc_ast::{
self as ast, AnonConst, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind,
Generics, Mutability, PatKind, VariantData,
};
use rustc_attr_parsing as attr;
use rustc_attr_parsing::{AttributeKind, AttributeParser, ReprPacked};
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_hir::Attribute;
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
use thin_vec::{ThinVec, thin_vec};
use ty::{Bounds, Path, Ref, Self_, Ty};
@ -480,14 +481,10 @@ impl<'a> TraitDef<'a> {
) {
match item {
Annotatable::Item(item) => {
let is_packed = item.attrs.iter().any(|attr| {
for r in attr::find_repr_attrs(cx.sess, attr) {
if let attr::ReprPacked(_) = r {
return true;
}
}
false
});
let is_packed = matches!(
AttributeParser::parse_limited(cx.sess, &item.attrs, sym::repr, item.span, true),
Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.iter().any(|(x, _)| matches!(x, ReprPacked(..)))
);
let newitem = match &item.kind {
ast::ItemKind::Struct(struct_def, generics) => self.expand_struct_def(

View file

@ -5,7 +5,9 @@ use std::time::{Duration, Instant};
use itertools::Itertools;
use rustc_abi::FIRST_VARIANT;
use rustc_ast as ast;
use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, AllocatorKind, global_fn_name};
use rustc_attr_parsing::OptimizeAttr;
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry};
use rustc_data_structures::sync::par_map;
@ -29,7 +31,6 @@ use rustc_span::{DUMMY_SP, Symbol, sym};
use rustc_trait_selection::infer::{BoundRegionConversionTime, TyCtxtInferExt};
use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt};
use tracing::{debug, info};
use {rustc_ast as ast, rustc_attr_parsing as attr};
use crate::assert_module_sources::CguReuse;
use crate::back::link::are_upstream_rust_objects_already_included;
@ -1061,7 +1062,7 @@ pub(crate) fn provide(providers: &mut Providers) {
let any_for_speed = defids.items().any(|id| {
let CodegenFnAttrs { optimize, .. } = tcx.codegen_fn_attrs(*id);
matches!(optimize, attr::OptimizeAttr::Speed)
matches!(optimize, OptimizeAttr::Speed)
});
if any_for_speed {

View file

@ -5,7 +5,8 @@ use rustc_ast::expand::autodiff_attrs::{
AutoDiffAttrs, DiffActivity, DiffMode, valid_input_activity, valid_ret_activity,
};
use rustc_ast::{MetaItem, MetaItemInner, attr};
use rustc_attr_parsing::{InlineAttr, InstructionSetAttr, OptimizeAttr};
use rustc_attr_parsing::ReprAttr::ReprAlign;
use rustc_attr_parsing::{AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::codes::*;
use rustc_errors::{DiagMessage, SubdiagMessage, struct_span_code_err};
@ -112,6 +113,18 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
}
};
if let hir::Attribute::Parsed(p) = attr {
match p {
AttributeKind::Repr(reprs) => {
codegen_fn_attrs.alignment = reprs
.iter()
.find_map(|(r, _)| if let ReprAlign(x) = r { Some(*x) } else { None });
}
_ => {}
}
}
let Some(Ident { name, .. }) = attr.ident() else {
continue;
};
@ -426,27 +439,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
}
})
}
sym::repr => {
codegen_fn_attrs.alignment = if let Some(items) = attr.meta_item_list()
&& let [item] = items.as_slice()
&& let Some((sym::align, literal)) = item.singleton_lit_list()
{
rustc_attr_parsing::parse_alignment(&literal.kind)
.inspect_err(|msg| {
struct_span_code_err!(
tcx.dcx(),
literal.span,
E0589,
"invalid `repr(align)` attribute: {}",
msg
)
.emit();
})
.ok()
} else {
None
};
}
sym::patchable_function_entry => {
codegen_fn_attrs.patchable_function_entry = attr.meta_item_list().and_then(|l| {
let mut prefix = None;
@ -831,7 +823,7 @@ impl<'a> MixedExportNameAndNoMangleState<'a> {
export_name: Some(export_name),
no_mangle: Some(no_mangle),
hir_id: Some(hir_id),
no_mangle_attr: Some(no_mangle_attr),
no_mangle_attr: Some(_),
} = self
{
tcx.emit_node_span_lint(
@ -840,7 +832,7 @@ impl<'a> MixedExportNameAndNoMangleState<'a> {
no_mangle,
errors::MixedExportNameAndNoMangle {
no_mangle,
no_mangle_attr: rustc_hir_pretty::attribute_to_string(&tcx, no_mangle_attr),
no_mangle_attr: "#[unsafe(no_mangle)]".to_string(),
export_name,
removal_span: no_mangle,
},

View file

@ -4,12 +4,13 @@
//! has interior mutability or needs to be dropped, as well as the visitor that emits errors when
//! it finds operations that are invalid in a certain context.
use rustc_attr_parsing::{AttributeKind, find_attr};
use rustc_errors::DiagCtxtHandle;
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::ty::{self, PolyFnSig, TyCtxt};
use rustc_middle::{bug, mir};
use rustc_span::Symbol;
use {rustc_attr_parsing as attr, rustc_hir as hir};
pub use self::qualifs::Qualif;
@ -81,7 +82,8 @@ pub fn rustc_allow_const_fn_unstable(
feature_gate: Symbol,
) -> bool {
let attrs = tcx.hir().attrs(tcx.local_def_id_to_hir_id(def_id));
attr::rustc_allow_const_fn_unstable(tcx.sess, attrs).any(|name| name == feature_gate)
find_attr!(attrs, AttributeKind::AllowConstFnUnstable(syms) if syms.contains(&feature_gate))
}
/// Returns `true` if the given `def_id` (trait or function) is "safe to expose on stable".

View file

@ -10,6 +10,7 @@ derive_setters = "0.1.6"
rustc_abi = { path = "../rustc_abi" }
rustc_ast = { path = "../rustc_ast" }
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
rustc_attr_data_structures = { path = "../rustc_attr_data_structures" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_error_codes = { path = "../rustc_error_codes" }
rustc_error_messages = { path = "../rustc_error_messages" }

View file

@ -8,6 +8,7 @@ use std::process::ExitStatus;
use rustc_abi::TargetDataLayoutErrors;
use rustc_ast::util::parser::ExprPrecedence;
use rustc_ast_pretty::pprust;
use rustc_attr_data_structures::RustcVersion;
use rustc_macros::Subdiagnostic;
use rustc_span::edition::Edition;
use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol};
@ -96,6 +97,12 @@ into_diag_arg_using_display!(
rustc_abi::ExternAbi,
);
impl IntoDiagArg for RustcVersion {
fn into_diag_arg(self) -> DiagArgValue {
DiagArgValue::Str(Cow::Owned(self.to_string()))
}
}
impl<I: rustc_type_ir::Interner> IntoDiagArg for rustc_type_ir::TraitRef<I> {
fn into_diag_arg(self) -> DiagArgValue {
self.to_string().into_diag_arg()

View file

@ -17,6 +17,7 @@ rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_feature = { path = "../rustc_feature" }
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_hir = { path = "../rustc_hir" }
rustc_lexer = { path = "../rustc_lexer" }
rustc_lint_defs = { path = "../rustc_lint_defs" }
rustc_macros = { path = "../rustc_macros" }

View file

@ -11,11 +11,12 @@ use rustc_ast::token::Nonterminal;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::visit::{AssocCtxt, Visitor};
use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind};
use rustc_attr_parsing::{self as attr, Deprecation, Stability};
use rustc_attr_parsing::{AttributeKind, Deprecation, Stability, find_attr};
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::sync;
use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult};
use rustc_feature::Features;
use rustc_hir as hir;
use rustc_lint_defs::{BufferedEarlyLint, RegisteredTools};
use rustc_parse::MACRO_ARGUMENTS;
use rustc_parse::parser::Parser;
@ -838,19 +839,23 @@ impl SyntaxExtension {
/// and other properties converted from attributes.
pub fn new(
sess: &Session,
features: &Features,
kind: SyntaxExtensionKind,
span: Span,
helper_attrs: Vec<Symbol>,
edition: Edition,
name: Symbol,
attrs: &[impl AttributeExt],
attrs: &[hir::Attribute],
is_local: bool,
) -> SyntaxExtension {
let allow_internal_unstable =
rustc_attr_parsing::allow_internal_unstable(sess, attrs).collect::<Vec<Symbol>>();
find_attr!(attrs, AttributeKind::AllowInternalUnstable(i) => i)
.map(|i| i.as_slice())
.unwrap_or_default();
// FIXME(jdonszelman): allow_internal_unsafe isn't yet new-style
// let allow_internal_unsafe = find_attr!(attrs, AttributeKind::AllowInternalUnsafe);
let allow_internal_unsafe =
ast::attr::find_by_name(attrs, sym::allow_internal_unsafe).is_some();
let allow_internal_unsafe = ast::attr::contains_name(attrs, sym::allow_internal_unsafe);
let local_inner_macros = ast::attr::find_by_name(attrs, sym::macro_export)
.and_then(|macro_export| macro_export.meta_item_list())
.is_some_and(|l| ast::attr::list_contains_name(&l, sym::local_inner_macros));
@ -867,16 +872,17 @@ impl SyntaxExtension {
)
})
.unwrap_or_else(|| (None, helper_attrs));
let stability = attr::find_stability(sess, attrs, span);
let const_stability = attr::find_const_stability(sess, attrs, span);
let body_stability = attr::find_body_stability(sess, attrs);
if let Some((_, sp)) = const_stability {
let stability = find_attr!(attrs, AttributeKind::Stability{stability, ..} => *stability);
// FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem
if let Some(sp) = find_attr!(attrs, AttributeKind::ConstStability{span, ..} => *span) {
sess.dcx().emit_err(errors::MacroConstStability {
span: sp,
head_span: sess.source_map().guess_head_span(span),
});
}
if let Some((_, sp)) = body_stability {
if let Some(sp) = find_attr!(attrs, AttributeKind::BodyStability{span, ..} => *span) {
sess.dcx().emit_err(errors::MacroBodyStability {
span: sp,
head_span: sess.source_map().guess_head_span(span),
@ -887,9 +893,10 @@ impl SyntaxExtension {
kind,
span,
allow_internal_unstable: (!allow_internal_unstable.is_empty())
.then(|| allow_internal_unstable.into()),
stability: stability.map(|(s, _)| s),
deprecation: attr::find_deprecation(sess, features, attrs).map(|(d, _)| d),
// FIXME(jdonszelmann): avoid the into_iter/collect?
.then(|| allow_internal_unstable.iter().map(|i| i.0).collect::<Vec<_>>().into()),
stability,
deprecation: find_attr!(attrs, AttributeKind::Deprecation{deprecation, ..} => *deprecation),
helper_attrs,
edition,
builtin_name,

View file

@ -3,17 +3,17 @@ use std::collections::hash_map::Entry;
use std::{mem, slice};
use ast::token::IdentIsRaw;
use rustc_ast::attr::AttributeExt;
use rustc_ast::token::NtPatKind::*;
use rustc_ast::token::TokenKind::*;
use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind};
use rustc_ast::tokenstream::{DelimSpan, TokenStream};
use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId};
use rustc_ast_pretty::pprust;
use rustc_attr_parsing::{self as attr, TransparencyError};
use rustc_attr_parsing::{AttributeKind, find_attr};
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_errors::{Applicability, ErrorGuaranteed};
use rustc_feature::Features;
use rustc_hir as hir;
use rustc_lint_defs::BuiltinLintDiag;
use rustc_lint_defs::builtin::{
RUST_2021_INCOMPATIBLE_OR_PATTERNS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
@ -371,7 +371,7 @@ pub fn compile_declarative_macro(
features: &Features,
macro_def: &ast::MacroDef,
ident: Ident,
attrs: &[impl AttributeExt],
attrs: &[hir::Attribute],
span: Span,
node_id: NodeId,
edition: Edition,
@ -379,7 +379,6 @@ pub fn compile_declarative_macro(
let mk_syn_ext = |expander| {
SyntaxExtension::new(
sess,
features,
SyntaxExtensionKind::LegacyBang(expander),
span,
Vec::new(),
@ -391,7 +390,6 @@ pub fn compile_declarative_macro(
};
let dummy_syn_ext = |guar| (mk_syn_ext(Box::new(DummyExpander(guar))), Vec::new());
let dcx = sess.dcx();
let lhs_nm = Ident::new(sym::lhs, span);
let rhs_nm = Ident::new(sym::rhs, span);
let tt_spec = Some(NonterminalKind::TT);
@ -542,16 +540,8 @@ pub fn compile_declarative_macro(
check_emission(macro_check::check_meta_variables(&sess.psess, node_id, span, &lhses, &rhses));
let (transparency, transparency_error) = attr::find_transparency(attrs, macro_rules);
match transparency_error {
Some(TransparencyError::UnknownTransparency(value, span)) => {
dcx.span_err(span, format!("unknown macro transparency: `{value}`"));
}
Some(TransparencyError::MultipleTransparencyAttrs(old_span, new_span)) => {
dcx.span_err(vec![old_span, new_span], "multiple macro transparency attributes");
}
None => {}
}
let transparency = find_attr!(attrs, AttributeKind::MacroTransparency(x) => *x)
.unwrap_or(Transparency::fallback(macro_rules));
if let Some(guar) = guar {
// To avoid warning noise, only consider the rules of this

View file

@ -1151,6 +1151,9 @@ impl AttributeExt for Attribute {
fn span(&self) -> Span {
match &self {
Attribute::Unparsed(u) => u.span,
// FIXME: should not be needed anymore when all attrs are parsed
Attribute::Parsed(AttributeKind::Deprecation { span, .. }) => *span,
Attribute::Parsed(AttributeKind::DocComment { span, .. }) => *span,
a => panic!("can't get the span of an arbitrary parsed attribute: {a:?}"),
}
}
@ -1193,6 +1196,7 @@ impl AttributeExt for Attribute {
fn style(&self) -> AttrStyle {
match &self {
Attribute::Unparsed(u) => u.style,
Attribute::Parsed(AttributeKind::DocComment { style, .. }) => *style,
_ => panic!(),
}
}

View file

@ -2,6 +2,8 @@ use std::cell::LazyCell;
use std::ops::ControlFlow;
use rustc_abi::FieldIdx;
use rustc_attr_parsing::AttributeKind;
use rustc_attr_parsing::ReprAttr::ReprPacked;
use rustc_data_structures::unord::{UnordMap, UnordSet};
use rustc_errors::MultiSpan;
use rustc_errors::codes::*;
@ -1203,11 +1205,13 @@ fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) {
pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) {
let repr = def.repr();
if repr.packed() {
for attr in tcx.get_attrs(def.did(), sym::repr) {
for r in attr::parse_repr_attr(tcx.sess, attr) {
if let attr::ReprPacked(pack) = r
if let Some(reprs) =
attr::find_attr!(tcx.get_all_attrs(def.did()), AttributeKind::Repr(r) => r)
{
for (r, _) in reprs {
if let ReprPacked(pack) = r
&& let Some(repr_pack) = repr.pack
&& pack != repr_pack
&& pack != &repr_pack
{
struct_span_code_err!(
tcx.dcx(),
@ -1419,16 +1423,19 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) {
def.destructor(tcx); // force the destructor to be evaluated
if def.variants().is_empty() {
if let Some(attr) = tcx.get_attrs(def_id, sym::repr).next() {
attr::find_attr!(
tcx.get_all_attrs(def_id),
AttributeKind::Repr(rs) => {
struct_span_code_err!(
tcx.dcx(),
attr.span(),
rs.first().unwrap().1,
E0084,
"unsupported representation for zero-variant enum"
)
.with_span_label(tcx.def_span(def_id), "zero-variant enum")
.emit();
}
);
}
let repr_type_ty = def.repr().discr_type().to_ty(tcx);

View file

@ -9,7 +9,7 @@ use std::path::PathBuf;
use hir::Expr;
use rustc_ast::ast::Mutability;
use rustc_attr_parsing::parse_confusables;
use rustc_attr_parsing::{AttributeKind, find_attr};
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::sorted_map::SortedMap;
use rustc_data_structures::unord::UnordSet;
@ -1884,9 +1884,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
for inherent_method in
self.tcx.associated_items(inherent_impl_did).in_definition_order()
{
if let Some(attr) =
self.tcx.get_attr(inherent_method.def_id, sym::rustc_confusables)
&& let Some(candidates) = parse_confusables(attr)
if let Some(candidates) = find_attr!(self.tcx.get_all_attrs(inherent_method.def_id), AttributeKind::Confusables{symbols, ..} => symbols)
&& candidates.contains(&item_name.name)
&& let ty::AssocKind::Fn = inherent_method.kind
{

View file

@ -39,6 +39,7 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) {
LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => {
// We are an `eval_always` query, so looking at the attribute's `AttrId` is ok.
let attr_id = tcx.hir().attrs(hir_id)[attr_index as usize].id();
(attr_id, lint_index)
}
_ => panic!("fulfilled expectations must have a lint index"),

View file

@ -1,4 +1,5 @@
use rustc_abi::ExternAbi;
use rustc_attr_parsing::{AttributeKind, AttributeParser, ReprAttr};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::FnKind;
use rustc_hir::{AttrArgs, AttrItem, Attribute, GenericParamKind, PatExprKind, PatKind};
@ -7,7 +8,7 @@ use rustc_session::config::CrateType;
use rustc_session::{declare_lint, declare_lint_pass};
use rustc_span::def_id::LocalDefId;
use rustc_span::{BytePos, Ident, Span, sym};
use {rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir};
use {rustc_ast as ast, rustc_hir as hir};
use crate::lints::{
NonCamelCaseType, NonCamelCaseTypeSub, NonSnakeCaseDiag, NonSnakeCaseDiagSub,
@ -161,10 +162,10 @@ impl NonCamelCaseTypes {
impl EarlyLintPass for NonCamelCaseTypes {
fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) {
let has_repr_c = it
.attrs
.iter()
.any(|attr| attr::find_repr_attrs(cx.sess(), attr).contains(&attr::ReprC));
let has_repr_c = matches!(
AttributeParser::parse_limited(cx.sess(), &it.attrs, sym::repr, it.span, true),
Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.iter().any(|(r, _)| r == &ReprAttr::ReprC)
);
if has_repr_c {
return;

View file

@ -251,19 +251,23 @@ impl Level {
/// Converts an `Attribute` to a level.
pub fn from_attr(attr: &impl AttributeExt) -> Option<Self> {
Self::from_symbol(attr.name_or_empty(), Some(attr.id()))
Self::from_symbol(attr.name_or_empty(), || Some(attr.id()))
}
/// Converts a `Symbol` to a level.
pub fn from_symbol(s: Symbol, id: Option<AttrId>) -> Option<Self> {
match (s, id) {
(sym::allow, _) => Some(Level::Allow),
(sym::expect, Some(attr_id)) => {
pub fn from_symbol(s: Symbol, id: impl FnOnce() -> Option<AttrId>) -> Option<Self> {
match s {
sym::allow => Some(Level::Allow),
sym::expect => {
if let Some(attr_id) = id() {
Some(Level::Expect(LintExpectationId::Unstable { attr_id, lint_index: None }))
} else {
None
}
(sym::warn, _) => Some(Level::Warn),
(sym::deny, _) => Some(Level::Deny),
(sym::forbid, _) => Some(Level::Forbid),
}
sym::warn => Some(Level::Warn),
sym::deny => Some(Level::Deny),
sym::forbid => Some(Level::Forbid),
_ => None,
}
}

View file

@ -1071,7 +1071,6 @@ impl<'a> CrateMetadataRef<'a> {
let attrs: Vec<_> = self.get_item_attrs(id, sess).collect();
SyntaxExtension::new(
sess,
tcx.features(),
kind,
self.get_span(id, sess),
helper_attrs,

View file

@ -27,6 +27,7 @@ pub use intrinsic::IntrinsicDef;
use rustc_abi::{Align, FieldIdx, Integer, IntegerType, ReprFlags, ReprOptions, VariantIdx};
use rustc_ast::expand::StrippedCfgItem;
use rustc_ast::node_id::NodeMap;
use rustc_attr_parsing::AttributeKind;
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
use rustc_data_structures::intern::Interned;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
@ -1495,9 +1496,10 @@ impl<'tcx> TyCtxt<'tcx> {
field_shuffle_seed ^= user_seed;
}
for attr in self.get_attrs(did, sym::repr) {
for r in attr::parse_repr_attr(self.sess, attr) {
flags.insert(match r {
if let Some(reprs) = attr::find_attr!(self.get_all_attrs(did), AttributeKind::Repr(r) => r)
{
for (r, _) in reprs {
flags.insert(match *r {
attr::ReprRust => ReprFlags::empty(),
attr::ReprC => ReprFlags::IS_C,
attr::ReprPacked(pack) => {
@ -1535,6 +1537,10 @@ impl<'tcx> TyCtxt<'tcx> {
max_align = max_align.max(Some(align));
ReprFlags::empty()
}
attr::ReprEmpty => {
/* skip these, they're just for diagnostics */
ReprFlags::empty()
}
});
}
}
@ -1756,14 +1762,22 @@ impl<'tcx> TyCtxt<'tcx> {
self,
did: impl Into<DefId>,
attr: Symbol,
) -> impl Iterator<Item = &'tcx hir::Attribute> {
self.get_all_attrs(did).filter(move |a: &&hir::Attribute| a.has_name(attr))
}
/// Gets all attributes.
///
/// To see if an item has a specific attribute, you should use [`rustc_attr_parsing::find_attr!`] so you can use matching.
pub fn get_all_attrs(
self,
did: impl Into<DefId>,
) -> impl Iterator<Item = &'tcx hir::Attribute> {
let did: DefId = did.into();
let filter_fn = move |a: &&hir::Attribute| a.has_name(attr);
if let Some(did) = did.as_local() {
self.hir().attrs(self.local_def_id_to_hir_id(did)).iter().filter(filter_fn)
self.hir().attrs(self.local_def_id_to_hir_id(did)).iter()
} else {
debug_assert!(rustc_feature::encode_cross_crate(attr));
self.attrs_for_def(did).iter().filter(filter_fn)
self.attrs_for_def(did).iter()
}
}

View file

@ -311,9 +311,6 @@ passes_duplicate_lang_item_crate_depends =
.first_definition_path = first definition in `{$orig_crate_name}` loaded from {$orig_path}
.second_definition_path = second definition in `{$crate_name}` loaded from {$path}
passes_empty_confusables =
expected at least one confusable name
passes_export_name =
attribute should be applied to a free function, impl method or static
.label = not a free function, impl method or static
@ -365,9 +362,6 @@ passes_incorrect_do_not_recommend_args =
passes_incorrect_do_not_recommend_location =
`#[diagnostic::do_not_recommend]` can only be placed on trait implementations
passes_incorrect_meta_item = expected a quoted string literal
passes_incorrect_meta_item_suggestion = consider surrounding this with quotes
passes_incorrect_target =
`{$name}` lang item must be applied to a {$kind} with {$at_least ->
[true] at least {$num}
@ -641,13 +635,12 @@ passes_repr_align_greater_than_target_max =
passes_repr_conflicting =
conflicting representation hints
passes_repr_ident =
meta item in `repr` must be an identifier
passes_rustc_allow_const_fn_unstable =
attribute should be applied to `const fn`
.label = not a `const fn`
passes_rustc_const_stable_indirect_pairing =
`const_stable_indirect` attribute does not make sense on `rustc_const_stable` function, its behavior is already implied
passes_rustc_dirty_clean =
attribute requires -Z query-dep-graph to be enabled
@ -774,10 +767,6 @@ passes_unreachable_due_to_uninhabited = unreachable {$descr}
passes_unrecognized_field =
unrecognized field name `{$name}`
passes_unrecognized_repr_hint =
unrecognized representation hint
.help = valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`
passes_unstable_attr_for_already_stable_feature =
can't mark as unstable using an already stable feature
.label = this feature is already stable

View file

@ -1,3 +1,4 @@
// FIXME(jdonszelmann): should become rustc_attr_validation
//! This module implements some validity checks for attributes.
//! In particular it verifies that `#[inline]` and `#[repr]` attributes are
//! attached to items that actually support them and if there are
@ -7,8 +8,9 @@
use std::cell::Cell;
use std::collections::hash_map::Entry;
use rustc_abi::{ExternAbi, Size};
use rustc_abi::{Align, ExternAbi, Size};
use rustc_ast::{AttrStyle, LitKind, MetaItemInner, MetaItemKind, MetaItemLit, ast};
use rustc_attr_parsing::{AttributeKind, 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};
@ -113,6 +115,23 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
let mut seen = FxHashMap::default();
let attrs = self.tcx.hir().attrs(hir_id);
for attr in attrs {
match attr {
Attribute::Parsed(AttributeKind::Confusables { first_span, .. }) => {
self.check_confusables(*first_span, target);
}
Attribute::Parsed(
AttributeKind::Stability { span, .. }
| AttributeKind::ConstStability { span, .. },
) => self.check_stability_promotable(*span, target),
Attribute::Parsed(AttributeKind::AllowInternalUnstable(syms)) => self
.check_allow_internal_unstable(
hir_id,
syms.first().unwrap().1,
span,
target,
attrs,
),
_ => {
match attr.path().as_slice() {
[sym::diagnostic, sym::do_not_recommend, ..] => {
self.check_do_not_recommend(attr.span(), hir_id, target, attr, item)
@ -123,7 +142,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
[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, ..] => self.check_no_sanitize(attr, span, target),
[sym::no_sanitize, ..] => {
self.check_no_sanitize(attr, span, target)
}
[sym::non_exhaustive, ..] => self.check_non_exhaustive(hir_id, attr, span, target, item),
[sym::marker, ..] => self.check_marker(hir_id, attr, span, target),
[sym::target_feature, ..] => {
@ -146,9 +167,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| [sym::rustc_layout_scalar_valid_range_end, ..] => {
self.check_rustc_layout_scalar_valid_range(attr, span, target)
}
[sym::allow_internal_unstable, ..] => {
self.check_allow_internal_unstable(hir_id, attr, span, target, attrs)
}
[sym::debugger_visualizer, ..] => self.check_debugger_visualizer(attr, target),
[sym::rustc_allow_const_fn_unstable, ..] => {
self.check_rustc_allow_const_fn_unstable(hir_id, attr, span, target)
@ -201,14 +219,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
[sym::ffi_pure, ..] => self.check_ffi_pure(attr.span(), attrs, target),
[sym::ffi_const, ..] => self.check_ffi_const(attr.span(), target),
[sym::rustc_const_unstable, ..]
| [sym::rustc_const_stable, ..]
| [sym::unstable, ..]
| [sym::stable, ..]
| [sym::rustc_allowed_through_unstable_modules, ..]
| [sym::rustc_promotable, ..] => self.check_stability_promotable(attr, target),
[sym::link_ordinal, ..] => self.check_link_ordinal(attr, span, target),
[sym::rustc_confusables, ..] => self.check_confusables(attr, target),
[sym::cold, ..] => self.check_cold(hir_id, attr, span, target),
[sym::link, ..] => self.check_link(hir_id, attr, span, target),
[sym::link_name, ..] => self.check_link_name(hir_id, attr, span, target),
@ -298,6 +309,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
[] => unreachable!(),
}
}
}
let builtin = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name));
@ -343,11 +356,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
);
}
fn inline_attr_str_error_without_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
fn inline_attr_str_error_without_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::IgnoredAttr { sym },
);
}
@ -568,6 +581,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
sym::warn,
sym::deny,
sym::forbid,
// FIXME(jdonszelmann): not used, because already a new-style attr (ugh)
sym::deprecated,
sym::must_use,
// abi, linking and FFI
@ -595,6 +609,17 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
continue;
}
// FIXME(jdonszelmann): once naked uses new-style parsing,
// this check can be part of the parser and be removed here
match other_attr {
Attribute::Parsed(
AttributeKind::Deprecation { .. } | AttributeKind::Repr { .. },
) => {
continue;
}
_ => {}
}
if !ALLOW_LIST.iter().any(|name| other_attr.has_name(*name)) {
self.dcx().emit_err(errors::NakedFunctionIncompatibleAttribute {
span: other_attr.span(),
@ -1858,12 +1883,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
// #[repr(foo)]
// #[repr(bar, align(8))]
// ```
let hints: Vec<_> = attrs
.iter()
.filter(|attr| attr.has_name(sym::repr))
.filter_map(|attr| attr.meta_item_list())
.flatten()
.collect();
let reprs = find_attr!(attrs, AttributeKind::Repr(r) => r.as_slice()).unwrap_or(&[]);
let mut int_reprs = 0;
let mut is_explicit_rust = false;
@ -1871,16 +1891,114 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
let mut is_simd = false;
let mut is_transparent = false;
for (repr, repr_span) in reprs {
match repr {
ReprAttr::ReprRust => {
is_explicit_rust = true;
match target {
Target::Struct | Target::Union | Target::Enum => continue,
_ => {
self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
hint_span: *repr_span,
span,
});
}
}
}
ReprAttr::ReprC => {
is_c = true;
match target {
Target::Struct | Target::Union | Target::Enum => continue,
_ => {
self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
hint_span: *repr_span,
span,
});
}
}
}
ReprAttr::ReprAlign(align) => {
match target {
Target::Struct | Target::Union | Target::Enum => {}
Target::Fn | Target::Method(_) => {
if !self.tcx.features().fn_align() {
feature_err(
&self.tcx.sess,
sym::fn_align,
*repr_span,
fluent::passes_repr_align_function,
)
.emit();
}
}
_ => {
self.dcx().emit_err(
errors::AttrApplication::StructEnumFunctionMethodUnion {
hint_span: *repr_span,
span,
},
);
}
}
self.check_align_value(*align, *repr_span);
}
ReprAttr::ReprPacked(_) => {
if target != Target::Struct && target != Target::Union {
self.dcx().emit_err(errors::AttrApplication::StructUnion {
hint_span: *repr_span,
span,
});
} else {
continue;
}
}
ReprAttr::ReprSimd => {
is_simd = true;
if target != Target::Struct {
self.dcx().emit_err(errors::AttrApplication::Struct {
hint_span: *repr_span,
span,
});
} else {
continue;
}
}
ReprAttr::ReprTransparent => {
is_transparent = true;
match target {
Target::Struct | Target::Union | Target::Enum => continue,
_ => {
self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
hint_span: *repr_span,
span,
});
}
}
}
ReprAttr::ReprInt(_) => {
int_reprs += 1;
if target != Target::Enum {
self.dcx().emit_err(errors::AttrApplication::Enum {
hint_span: *repr_span,
span,
});
} else {
continue;
}
}
// FIXME(jdonszelmann): move the diagnostic for unused repr attrs here, I think
// it's a better place for it.
ReprAttr::ReprEmpty => {
// catch `repr()` with no arguments, applied to an item (i.e. not `#![repr()]`)
if hints.is_empty() && item.is_some() {
for attr in attrs.iter().filter(|attr| attr.has_name(sym::repr)) {
if item.is_some() {
match target {
Target::Struct | Target::Union | Target::Enum => {}
Target::Fn | Target::Method(_) => {
feature_err(
&self.tcx.sess,
sym::fn_align,
attr.span(),
*repr_span,
fluent::passes_repr_align_function,
)
.emit();
@ -1888,7 +2006,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
_ => {
self.dcx().emit_err(
errors::AttrApplication::StructEnumFunctionMethodUnion {
hint_span: attr.span(),
hint_span: *repr_span,
span,
},
);
@ -1898,132 +2016,15 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
return;
}
for hint in &hints {
if !hint.is_meta_item() {
self.dcx().emit_err(errors::ReprIdent { span: hint.span() });
continue;
}
match hint.name_or_empty() {
sym::Rust => {
is_explicit_rust = true;
match target {
Target::Struct | Target::Union | Target::Enum => continue,
_ => {
self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
hint_span: hint.span(),
span,
});
}
}
}
sym::C => {
is_c = true;
match target {
Target::Struct | Target::Union | Target::Enum => continue,
_ => {
self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
hint_span: hint.span(),
span,
});
}
}
}
sym::align => {
match target {
Target::Struct | Target::Union | Target::Enum => {}
Target::Fn | Target::Method(_) => {
if !self.tcx.features().fn_align() {
feature_err(
&self.tcx.sess,
sym::fn_align,
hint.span(),
fluent::passes_repr_align_function,
)
.emit();
}
}
_ => {
self.dcx().emit_err(
errors::AttrApplication::StructEnumFunctionMethodUnion {
hint_span: hint.span(),
span,
},
);
}
}
self.check_align_value(hint);
}
sym::packed => {
if target != Target::Struct && target != Target::Union {
self.dcx().emit_err(errors::AttrApplication::StructUnion {
hint_span: hint.span(),
span,
});
} else {
continue;
}
}
sym::simd => {
is_simd = true;
if target != Target::Struct {
self.dcx().emit_err(errors::AttrApplication::Struct {
hint_span: hint.span(),
span,
});
} else {
continue;
}
}
sym::transparent => {
is_transparent = true;
match target {
Target::Struct | Target::Union | Target::Enum => continue,
_ => {
self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
hint_span: hint.span(),
span,
});
}
}
}
sym::i8
| sym::u8
| sym::i16
| sym::u16
| sym::i32
| sym::u32
| sym::i64
| sym::u64
| sym::i128
| sym::u128
| sym::isize
| sym::usize => {
int_reprs += 1;
if target != Target::Enum {
self.dcx().emit_err(errors::AttrApplication::Enum {
hint_span: hint.span(),
span,
});
} else {
continue;
}
}
_ => {
self.dcx().emit_err(errors::UnrecognizedReprHint { span: hint.span() });
continue;
}
};
}
// Just point at all repr hints if there are any incompatibilities.
// This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
let hint_spans = hints.iter().map(|hint| hint.span());
let hint_spans = reprs.iter().map(|(_, span)| *span);
// Error on repr(transparent, <anything else>).
if is_transparent && hints.len() > 1 {
if is_transparent && reprs.len() > 1 {
let hint_spans = hint_spans.clone().collect();
self.dcx().emit_err(errors::TransparentIncompatible {
hint_spans,
@ -2052,19 +2053,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
}
fn check_align_value(&self, item: &MetaItemInner) {
match item.singleton_lit_list() {
Some((
_,
MetaItemLit {
kind: ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed), ..
},
)) => {
let val = literal.get() as u64;
if val > 2_u64.pow(29) {
fn check_align_value(&self, align: Align, span: Span) {
if align.bytes() > 2_u64.pow(29) {
// for values greater than 2^29, a different error will be emitted, make sure that happens
self.dcx().span_delayed_bug(
item.span(),
span,
"alignment greater than 2^29 should be errored on elsewhere",
);
} else {
@ -2072,21 +2065,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
// alignment greater than 2^29 not supported
// alignment is too large for the current target
let max =
Size::from_bits(self.tcx.sess.target.pointer_width).signed_int_max() as u64;
if val > max {
self.dcx().emit_err(errors::InvalidReprAlignForTarget {
span: item.span(),
size: max,
});
}
}
}
// if the attribute is malformed, singleton_lit_list may not be of the expected type or may be None
// but an error will have already been emitted, so this code should just skip such attributes
Some((_, _)) | None => {
self.dcx().span_delayed_bug(item.span(), "malformed repr(align(N))");
let max = Size::from_bits(self.tcx.sess.target.pointer_width).signed_int_max() as u64;
if align.bytes() > max {
self.dcx().emit_err(errors::InvalidReprAlignForTarget { span, size: max });
}
}
}
@ -2134,41 +2115,44 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
/// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
/// (Allows proc_macro functions)
// FIXME(jdonszelmann): if possible, move to attr parsing
fn check_allow_internal_unstable(
&self,
hir_id: HirId,
attr: &Attribute,
attr_span: Span,
span: Span,
target: Target,
attrs: &[Attribute],
) {
debug!("Checking target: {:?}", target);
match target {
Target::Fn => {
for attr in attrs {
if attr.is_proc_macro_attr() {
debug!("Is proc macro attr");
// return on proc macros
return;
}
}
debug!("Is not proc macro attr");
// continue out of the match
}
Target::MacroDef => {}
// return on decl macros
Target::MacroDef => return,
// FIXME(#80564): We permit struct fields and match arms to have an
// `#[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 => self.inline_attr_str_error_without_macro_def(
Target::Field | Target::Arm => {
self.inline_attr_str_error_without_macro_def(
hir_id,
attr,
attr_span,
"allow_internal_unstable",
),
_ => {
self.tcx
.dcx()
.emit_err(errors::AllowInternalUnstable { attr_span: attr.span(), span });
);
return;
}
// otherwise continue out of the match
_ => {}
}
self.tcx.dcx().emit_err(errors::AllowInternalUnstable { attr_span, span });
}
/// Checks if the items on the `#[debugger_visualizer]` attribute are valid.
@ -2223,10 +2207,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
}
fn check_stability_promotable(&self, attr: &Attribute, target: Target) {
fn check_stability_promotable(&self, span: Span, target: Target) {
match target {
Target::Expression => {
self.dcx().emit_err(errors::StabilityPromotable { attr_span: attr.span() });
self.dcx().emit_err(errors::StabilityPromotable { attr_span: span });
}
_ => {}
}
@ -2241,36 +2225,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
}
fn check_confusables(&self, attr: &Attribute, target: Target) {
match target {
Target::Method(MethodKind::Inherent) => {
let Some(metas) = attr.meta_item_list() else {
return;
};
let mut candidates = Vec::new();
for meta in metas {
let MetaItemInner::Lit(meta_lit) = meta else {
self.dcx().emit_err(errors::IncorrectMetaItem {
span: meta.span(),
suggestion: errors::IncorrectMetaItemSuggestion {
lo: meta.span().shrink_to_lo(),
hi: meta.span().shrink_to_hi(),
},
});
return;
};
candidates.push(meta_lit.symbol);
}
if candidates.is_empty() {
self.dcx().emit_err(errors::EmptyConfusables { span: attr.span() });
}
}
_ => {
self.dcx().emit_err(errors::Confusables { attr_span: attr.span() });
}
fn check_confusables(&self, span: Span, target: Target) {
if !matches!(target, Target::Method(MethodKind::Inherent)) {
self.dcx().emit_err(errors::Confusables { attr_span: span });
}
}
@ -2346,8 +2303,32 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute) {
// FIXME(jdonszelmann): deduplicate these checks after more attrs are parsed. This is very
// ugly now but can 100% be removed later.
if let Attribute::Parsed(p) = attr {
match p {
AttributeKind::Repr(reprs) => {
for (r, span) in reprs {
if let ReprAttr::ReprEmpty = r {
self.tcx.emit_node_span_lint(
UNUSED_ATTRIBUTES,
hir_id,
*span,
errors::Unused {
attr_span: *span,
note: errors::UnusedNote::EmptyList { name: sym::repr },
},
);
}
}
return;
}
_ => {}
}
}
// Warn on useless empty attributes.
let note = if matches!(
let note = if (matches!(
attr.name_or_empty(),
sym::macro_use
| sym::allow
@ -2356,9 +2337,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| sym::deny
| sym::forbid
| sym::feature
| sym::repr
| sym::target_feature
) && attr.meta_item_list().is_some_and(|list| list.is_empty())
) && attr.meta_item_list().is_some_and(|list| list.is_empty()))
{
errors::UnusedNote::EmptyList { name: attr.name_or_empty() }
} else if matches!(
@ -2552,12 +2532,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
fn check_rustc_pub_transparent(&self, attr_span: Span, span: Span, attrs: &[Attribute]) {
if !attrs
.iter()
.filter(|attr| attr.has_name(sym::repr))
.filter_map(|attr| attr.meta_item_list())
.flatten()
.any(|nmi| nmi.has_name(sym::transparent))
if !find_attr!(attrs, AttributeKind::Repr(r) => r.iter().any(|(r, _)| r == &ReprAttr::ReprTransparent))
.unwrap_or(false)
{
self.dcx().emit_err(errors::RustcPubTransparent { span, attr_span });
}
@ -2734,7 +2710,6 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
// resolution for the attribute macro error.
const ATTRS_TO_CHECK: &[Symbol] = &[
sym::macro_export,
sym::repr,
sym::path,
sym::automatically_derived,
sym::rustc_main,
@ -2746,11 +2721,18 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
];
for attr in attrs {
// This function should only be called with crate attributes
// which are inner attributes always but lets check to make sure
if attr.style() == AttrStyle::Inner {
for attr_to_check in ATTRS_TO_CHECK {
if attr.has_name(*attr_to_check) {
// FIXME(jdonszelmann): all attrs should be combined here cleaning this up some day.
let (span, name) = if let Some(a) =
ATTRS_TO_CHECK.iter().find(|attr_to_check| attr.has_name(**attr_to_check))
{
(attr.span(), *a)
} else if let Attribute::Parsed(AttributeKind::Repr(r)) = attr {
(r.first().unwrap().1, sym::repr)
} else {
continue;
};
let item = tcx
.hir_free_items()
.map(|id| tcx.hir_item(id))
@ -2760,19 +2742,15 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
kind: item.kind.descr(),
});
let err = tcx.dcx().create_err(errors::InvalidAttrAtCrateLevel {
span: attr.span(),
span,
sugg_span: tcx
.sess
.source_map()
.span_to_snippet(attr.span())
.span_to_snippet(span)
.ok()
.filter(|src| src.starts_with("#!["))
.map(|_| {
attr.span()
.with_lo(attr.span().lo() + BytePos(1))
.with_hi(attr.span().lo() + BytePos(2))
}),
name: *attr_to_check,
.map(|_| span.with_lo(span.lo() + BytePos(1)).with_hi(span.lo() + BytePos(2))),
name,
item,
});
@ -2786,9 +2764,6 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
err.emit();
}
}
}
}
}
}
fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>) {

View file

@ -582,13 +582,6 @@ pub(crate) struct NoMangle {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(passes_repr_ident, code = E0565)]
pub(crate) struct ReprIdent {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(passes_repr_conflicting, code = E0566)]
pub(crate) struct ReprConflicting {
@ -736,31 +729,6 @@ pub(crate) struct Linkage {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(passes_empty_confusables)]
pub(crate) struct EmptyConfusables {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(passes_incorrect_meta_item, code = E0539)]
pub(crate) struct IncorrectMetaItem {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub suggestion: IncorrectMetaItemSuggestion,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(passes_incorrect_meta_item_suggestion, applicability = "maybe-incorrect")]
pub(crate) struct IncorrectMetaItemSuggestion {
#[suggestion_part(code = "\"")]
pub lo: Span,
#[suggestion_part(code = "\"")]
pub hi: Span,
}
#[derive(Diagnostic)]
#[diag(passes_stability_promotable)]
pub(crate) struct StabilityPromotable {
@ -1475,14 +1443,6 @@ pub(crate) struct ObjectLifetimeErr {
pub repr: String,
}
#[derive(Diagnostic)]
#[diag(passes_unrecognized_repr_hint, code = E0552)]
#[help]
pub(crate) struct UnrecognizedReprHint {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
pub(crate) enum AttrApplication {
#[diag(passes_attr_application_enum, code = E0517)]
@ -1902,3 +1862,11 @@ pub(crate) struct NoSanitize<'a> {
pub accepted_kind: &'a str,
pub attr_str: &'a str,
}
// FIXME(jdonszelmann): move back to rustc_attr
#[derive(Diagnostic)]
#[diag(passes_rustc_const_stable_indirect_pairing)]
pub(crate) struct RustcConstStableIndirectPairing {
#[primary_span]
pub span: Span,
}

View file

@ -4,7 +4,7 @@
//! but are not declared in one single location (unlike lang features), which means we need to
//! collect them instead.
use rustc_attr_parsing::VERSION_PLACEHOLDER;
use rustc_attr_parsing::{AttributeKind, StabilityLevel, StableSince};
use rustc_hir::Attribute;
use rustc_hir::intravisit::Visitor;
use rustc_middle::hir::nested_filter;
@ -26,66 +26,29 @@ impl<'tcx> LibFeatureCollector<'tcx> {
}
fn extract(&self, attr: &Attribute) -> Option<(Symbol, FeatureStability, Span)> {
let stab_attrs = [
sym::stable,
sym::unstable,
sym::rustc_const_stable,
sym::rustc_const_unstable,
sym::rustc_default_body_unstable,
];
let (feature, level, span) = match attr {
Attribute::Parsed(AttributeKind::Stability { stability, span }) => {
(stability.feature, stability.level, *span)
}
Attribute::Parsed(AttributeKind::ConstStability { stability, span }) => {
(stability.feature, stability.level, *span)
}
Attribute::Parsed(AttributeKind::BodyStability { stability, span }) => {
(stability.feature, stability.level, *span)
}
_ => return None,
};
// Find a stability attribute: one of #[stable(…)], #[unstable(…)],
// #[rustc_const_stable(…)], #[rustc_const_unstable(…)] or #[rustc_default_body_unstable].
if let Some(stab_attr) = stab_attrs.iter().find(|stab_attr| attr.has_name(**stab_attr)) {
if let Some(metas) = attr.meta_item_list() {
let mut feature = None;
let mut since = None;
for meta in metas {
if let Some(mi) = meta.meta_item() {
// Find the `feature = ".."` meta-item.
match (mi.name_or_empty(), mi.value_str()) {
(sym::feature, val) => feature = val,
(sym::since, val) => since = val,
_ => {}
}
}
}
let feature_stability = match level {
StabilityLevel::Unstable { .. } => FeatureStability::Unstable,
StabilityLevel::Stable { since, .. } => FeatureStability::AcceptedSince(match since {
StableSince::Version(v) => Symbol::intern(&v.to_string()),
StableSince::Current => sym::env_CFG_RELEASE,
StableSince::Err => return None,
}),
};
if let Some(s) = since
&& s.as_str() == VERSION_PLACEHOLDER
{
since = Some(sym::env_CFG_RELEASE);
}
if let Some(feature) = feature {
// This additional check for stability is to make sure we
// don't emit additional, irrelevant errors for malformed
// attributes.
let is_unstable = matches!(
*stab_attr,
sym::unstable
| sym::rustc_const_unstable
| sym::rustc_default_body_unstable
);
if is_unstable {
return Some((feature, FeatureStability::Unstable, attr.span()));
}
if let Some(since) = since {
return Some((
feature,
FeatureStability::AcceptedSince(since),
attr.span(),
));
}
}
// We need to iterate over the other attributes, because
// `rustc_const_unstable` is not mutually exclusive with
// the other stability attributes, so we can't just `break`
// here.
}
}
None
Some((feature, feature_stability, span))
}
fn collect_feature(&mut self, feature: Symbol, stability: FeatureStability, span: Span) {

View file

@ -6,8 +6,8 @@ use std::num::NonZero;
use rustc_ast_lowering::stability::extern_abi_stability;
use rustc_attr_parsing::{
self as attr, ConstStability, DeprecatedSince, Stability, StabilityLevel, StableSince,
UnstableReason, VERSION_PLACEHOLDER,
self as attr, AttributeKind, ConstStability, DeprecatedSince, PartialConstStability, Stability,
StabilityLevel, StableSince, UnstableReason, VERSION_PLACEHOLDER, find_attr,
};
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet};
@ -121,7 +121,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
let attrs = self.tcx.hir().attrs(self.tcx.local_def_id_to_hir_id(def_id));
debug!("annotate(id = {:?}, attrs = {:?})", def_id, attrs);
let depr = attr::find_deprecation(self.tcx.sess, self.tcx.features(), attrs);
let depr = attr::find_attr!(attrs, AttributeKind::Deprecation{deprecation, span} => (*deprecation, *span));
let const_stability_indirect = find_attr!(attrs, AttributeKind::ConstStabilityIndirect);
let mut is_deprecated = false;
if let Some((depr, span)) = &depr {
is_deprecated = true;
@ -154,9 +156,10 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
if inherit_deprecation.yes() && stab.is_unstable() {
self.index.stab_map.insert(def_id, stab);
if fn_sig.is_some_and(|s| s.header.is_const()) {
let const_stab =
attr::unmarked_crate_const_stab(self.tcx.sess, attrs, stab);
self.index.const_stab_map.insert(def_id, const_stab);
self.index.const_stab_map.insert(
def_id,
ConstStability::unmarked(const_stability_indirect, stab),
);
}
}
}
@ -171,9 +174,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
}
// # Regular and body stability
let stab = attr::find_stability(self.tcx.sess, attrs, item_sp);
let body_stab = attr::find_body_stability(self.tcx.sess, attrs);
let stab = attr::find_attr!(attrs, AttributeKind::Stability { stability, span } => (*stability, *span));
let body_stab =
attr::find_attr!(attrs, AttributeKind::BodyStability { stability, .. } => *stability);
if let Some((depr, span)) = &depr
&& depr.is_since_rustc_version()
@ -182,7 +185,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
self.tcx.dcx().emit_err(errors::DeprecatedAttribute { span: *span });
}
if let Some((body_stab, _span)) = body_stab {
if let Some(body_stab) = body_stab {
// FIXME: check that this item can have body stability
self.index.default_body_stab_map.insert(def_id, body_stab);
@ -260,10 +263,10 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
// # Const stability
let const_stab = attr::find_const_stability(self.tcx.sess, attrs, item_sp);
let const_stab = attr::find_attr!(attrs, AttributeKind::ConstStability { stability, span } => (*stability, *span));
// If the current node is a function with const stability attributes (directly given or
// implied), check if the function/method is const.
// implied), check if the function/method is const or the parent impl block is const.
if let Some(fn_sig) = fn_sig
&& !fn_sig.header.is_const()
&& const_stab.is_some()
@ -285,7 +288,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
// Stable *language* features shouldn't be used as unstable library features.
// (Not doing this for stable library features is checked by tidy.)
if let Some((
ConstStability { level: StabilityLevel::Unstable { .. }, feature, .. },
PartialConstStability { level: StabilityLevel::Unstable { .. }, feature, .. },
const_span,
)) = const_stab
{
@ -297,9 +300,17 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
}
}
if let Some((stab, span)) = &const_stab
&& stab.is_const_stable()
&& const_stability_indirect
{
self.tcx.dcx().emit_err(errors::RustcConstStableIndirectPairing { span: *span });
}
// After checking the immediate attributes, get rid of the span and compute implied
// const stability: inherit feature gate from regular stability.
let mut const_stab = const_stab.map(|(stab, _span)| stab);
let mut const_stab = const_stab
.map(|(stab, _span)| ConstStability::from_partial(stab, const_stability_indirect));
// If this is a const fn but not annotated with stability markers, see if we can inherit regular stability.
if fn_sig.is_some_and(|s| s.header.is_const()) && const_stab.is_none() &&
@ -785,8 +796,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
let features = self.tcx.features();
if features.staged_api() {
let attrs = self.tcx.hir().attrs(item.hir_id());
let stab = attr::find_stability(self.tcx.sess, attrs, item.span);
let const_stab = attr::find_const_stability(self.tcx.sess, attrs, item.span);
let stab = attr::find_attr!(attrs, AttributeKind::Stability{stability, span} => (*stability, *span));
// FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem
let const_stab = attr::find_attr!(attrs, AttributeKind::ConstStability{stability, ..} => *stability);
// If this impl block has an #[unstable] attribute, give an
// error if all involved types and traits are stable, because
@ -817,7 +830,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
// needs to have an error emitted.
if features.const_trait_impl()
&& self.tcx.is_const_trait_impl(item.owner_id.to_def_id())
&& const_stab.is_some_and(|(stab, _)| stab.is_const_stable())
&& const_stab.is_some_and(|stab| stab.is_const_stable())
{
self.tcx.dcx().emit_err(errors::TraitImplConstStable { span: item.span });
}

View file

@ -22,6 +22,7 @@ use errors::{
};
use rustc_ast::MacroDef;
use rustc_ast::visit::{VisitorResult, try_visit};
use rustc_attr_parsing::AttributeKind;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::intern::Interned;
use rustc_errors::{MultiSpan, listify};
@ -493,7 +494,11 @@ impl<'tcx> EmbargoVisitor<'tcx> {
// Non-opaque macros cannot make other items more accessible than they already are.
let hir_id = self.tcx.local_def_id_to_hir_id(local_def_id);
let attrs = self.tcx.hir().attrs(hir_id);
if attr::find_transparency(attrs, md.macro_rules).0 != Transparency::Opaque {
if attr::find_attr!(attrs, AttributeKind::MacroTransparency(x) => *x)
.unwrap_or(Transparency::fallback(md.macro_rules))
!= Transparency::Opaque
{
return;
}

View file

@ -3,6 +3,7 @@ use std::mem;
use rustc_ast::visit::FnKind;
use rustc_ast::*;
use rustc_ast_pretty::pprust;
use rustc_attr_parsing::{AttributeParser, OmitDoc};
use rustc_expand::expand::AstFragment;
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, CtorOf, DefKind};
@ -132,8 +133,19 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
ItemKind::Fn(..) | ItemKind::Delegation(..) => DefKind::Fn,
ItemKind::MacroDef(def) => {
let edition = i.span.edition();
// FIXME(jdonszelmann) make one of these in the resolver?
// FIXME(jdonszelmann) don't care about tools here maybe? Just parse what we can.
// Does that prevents errors from happening? maybe
let parser = AttributeParser::new(
&self.resolver.tcx.sess,
self.resolver.tcx.features(),
Vec::new(),
);
let attrs = parser.parse_attribute_list(&i.attrs, i.span, OmitDoc::Skip);
let macro_data =
self.resolver.compile_macro(def, i.ident, &i.attrs, i.span, i.id, edition);
self.resolver.compile_macro(def, i.ident, &attrs, i.span, i.id, edition);
let macro_kind = macro_data.ext.macro_kind();
opt_macro_data = Some(macro_data);
DefKind::Macro(macro_kind)

View file

@ -5,7 +5,6 @@ use std::cell::Cell;
use std::mem;
use std::sync::Arc;
use rustc_ast::attr::AttributeExt;
use rustc_ast::expand::StrippedCfgItem;
use rustc_ast::{self as ast, Crate, NodeId, attr};
use rustc_ast_pretty::pprust;
@ -1112,7 +1111,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
&mut self,
macro_def: &ast::MacroDef,
ident: Ident,
attrs: &[impl AttributeExt],
attrs: &[rustc_hir::Attribute],
span: Span,
node_id: NodeId,
edition: Edition,

View file

@ -8,6 +8,7 @@ bitflags = "2.5.0"
tracing = "0.1"
twox-hash = "1.6.3"
rustc_abi = { path = "../rustc_abi" }
rustc_ast = { path = "../rustc_ast" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_hir = { path = "../rustc_hir" }
rustc_middle = { path = "../rustc_middle" }

View file

@ -175,6 +175,12 @@ pub enum Transparency {
Opaque,
}
impl Transparency {
pub fn fallback(macro_rules: bool) -> Self {
if macro_rules { Transparency::SemiTransparent } else { Transparency::Opaque }
}
}
impl LocalExpnId {
/// The ID of the theoretical expansion that generates freshly parsed, unexpanded AST.
pub const ROOT: LocalExpnId = LocalExpnId::ZERO;

View file

@ -9,6 +9,7 @@ punycode = "0.4.0"
rustc-demangle = "0.1.21"
rustc_abi = { path = "../rustc_abi" }
rustc_ast = { path = "../rustc_ast" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_hashes = { path = "../rustc_hashes" }

View file

@ -4,6 +4,6 @@
fn requires_alignment() {}
trait MyTrait {
#[repr(align)] //~ ERROR `repr(align)` attributes on functions are unstable
#[repr(align)] //~ ERROR invalid `repr(align)` attribute: `align` needs an argument
fn myfun();
}

View file

@ -1,3 +1,9 @@
error[E0589]: invalid `repr(align)` attribute: `align` needs an argument
--> $DIR/feature-gate-fn_align.rs:7:12
|
LL | #[repr(align)]
| ^^^^^ help: supply an argument here: `align(...)`
error[E0658]: `repr(align)` attributes on functions are unstable
--> $DIR/feature-gate-fn_align.rs:3:8
|
@ -8,16 +14,7 @@ LL | #[repr(align(16))]
= help: add `#![feature(fn_align)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: `repr(align)` attributes on functions are unstable
--> $DIR/feature-gate-fn_align.rs:7:12
|
LL | #[repr(align)]
| ^^^^^
|
= note: see issue #82232 <https://github.com/rust-lang/rust/issues/82232> for more information
= help: add `#![feature(fn_align)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0658`.
Some errors have detailed explanations: E0589, E0658.
For more information about an error, try `rustc --explain E0589`.