Introduce new-style attribute parsers for several attributes
note: compiler compiles but librustdoc and clippy don't
This commit is contained in:
parent
dbd3b7928e
commit
7e0f5b5016
50 changed files with 1519 additions and 1342 deletions
|
@ -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(),
|
||||
name: symbol.to_ident_string(),
|
||||
});
|
||||
None
|
||||
})
|
||||
})
|
||||
.flatten();
|
||||
) -> impl IntoIterator<Item = Symbol> {
|
||||
let mut res = Vec::new();
|
||||
|
||||
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(),
|
||||
let Some(list) = args.list() else {
|
||||
cx.emit_err(session_diagnostics::ExpectsFeatureList {
|
||||
span: cx.attr_span,
|
||||
name: symbol.to_ident_string(),
|
||||
});
|
||||
return res;
|
||||
};
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()?;
|
||||
|
||||
let mut candidates = Vec::new();
|
||||
|
||||
for meta in metas {
|
||||
let MetaItemInner::Lit(meta_lit) = meta else {
|
||||
return None;
|
||||
};
|
||||
candidates.push(meta_lit.symbol);
|
||||
}
|
||||
|
||||
Some(candidates)
|
||||
#[derive(Default)]
|
||||
pub(crate) struct ConfusablesParser {
|
||||
confusables: ThinVec<Symbol>,
|
||||
first_span: Option<Span>,
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
if list.is_empty() {
|
||||
cx.emit_err(session_diagnostics::EmptyConfusables { span: cx.attr_span });
|
||||
}
|
||||
|
||||
for param in list.mixed() {
|
||||
let span = param.span();
|
||||
|
||||
let Some(lit) = param.lit() else {
|
||||
cx.emit_err(session_diagnostics::IncorrectMetaItem {
|
||||
span,
|
||||
suggestion: Some(session_diagnostics::IncorrectMetaItemSuggestion {
|
||||
lo: span.shrink_to_lo(),
|
||||
hi: span.shrink_to_hi(),
|
||||
}),
|
||||
});
|
||||
continue;
|
||||
};
|
||||
|
||||
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(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 });
|
||||
return None;
|
||||
};
|
||||
|
||||
let (ident, arg) = param.word_or_empty();
|
||||
|
||||
match ident.name {
|
||||
sym::since => {
|
||||
since = Some(get(cx, ident, param_span, arg, &since)?);
|
||||
}
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
for meta in &list {
|
||||
match meta {
|
||||
MetaItemInner::MetaItem(mi) => match mi.name_or_empty() {
|
||||
sym::since => {
|
||||
if !get(mi, &mut since) {
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
sym::note => {
|
||||
if !get(mi, &mut note) {
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
sym::suggestion => {
|
||||
if !features.deprecated_suggestion() {
|
||||
sess.dcx().emit_err(
|
||||
session_diagnostics::DeprecatedItemSuggestion {
|
||||
span: mi.span,
|
||||
is_nightly: sess.is_nightly_build(),
|
||||
details: (),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if !get(mi, &mut suggestion) {
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
sess.dcx().emit_err(session_diagnostics::UnknownMetaItem {
|
||||
span: meta.span(),
|
||||
item: pprust::path_to_string(&mi.path),
|
||||
expected: if features.deprecated_suggestion() {
|
||||
&["since", "note", "suggestion"]
|
||||
} else {
|
||||
&["since", "note"]
|
||||
},
|
||||
sym::note => {
|
||||
note = Some(get(cx, ident, param_span, arg, ¬e)?);
|
||||
}
|
||||
sym::suggestion => {
|
||||
if !features.deprecated_suggestion() {
|
||||
cx.emit_err(session_diagnostics::DeprecatedItemSuggestion {
|
||||
span: param_span,
|
||||
is_nightly: cx.sess().is_nightly_build(),
|
||||
details: (),
|
||||
});
|
||||
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),
|
||||
|
||||
suggestion = Some(get(cx, ident, param_span, arg, &suggestion)?);
|
||||
}
|
||||
_ => {
|
||||
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;
|
||||
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
|
||||
}
|
||||
|
|
|
@ -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>)];
|
||||
|
||||
|
|
|
@ -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),
|
||||
};
|
||||
|
||||
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
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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
|
||||
}))
|
||||
}
|
||||
sym::unstable => {
|
||||
if stab.is_some() {
|
||||
sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
|
||||
span: attr.span(),
|
||||
});
|
||||
break;
|
||||
}
|
||||
#[derive(Default)]
|
||||
pub(crate) struct StabilityParser {
|
||||
allowed_through_unstable_modules: Option<Symbol>,
|
||||
stability: Option<(Stability, Span)>,
|
||||
}
|
||||
|
||||
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()));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(allowed_through_unstable_modules) = allowed_through_unstable_modules {
|
||||
match &mut stab {
|
||||
Some((
|
||||
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,
|
||||
});
|
||||
}),
|
||||
];
|
||||
|
||||
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 });
|
||||
)) = self.stability
|
||||
{
|
||||
*allowed_through_unstable_modules = Some(atum);
|
||||
} else {
|
||||
cx.dcx().emit_err(session_diagnostics::RustcAllowedUnstablePairing {
|
||||
span: cx.target_span,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stab
|
||||
}
|
||||
let (stability, span) = self.stability?;
|
||||
|
||||
/// 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;
|
||||
} 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.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const_stab
|
||||
}
|
||||
|
||||
/// 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, ¶m, &mut feature)?,
|
||||
sym::since => insert_value_into_option_or_error(cx, ¶m, &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, ¶m, &mut feature)?,
|
||||
sym::reason => insert_value_into_option_or_error(cx, ¶m, &mut reason)?,
|
||||
sym::issue => {
|
||||
insert_or_error(sess, mi, &mut issue)?;
|
||||
insert_value_into_option_or_error(cx, ¶m, &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, ¶m, &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(_)) => {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
},
|
||||
attr.span(),
|
||||
));
|
||||
}
|
||||
}
|
||||
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");
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
];
|
||||
);
|
||||
|
||||
|
|
|
@ -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)
|
||||
}};
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue