Auto merge of #135726 - jdonszelmann:attr-parsing, r=oli-obk
New attribute parsing infrastructure Another step in the plan outlined in https://github.com/rust-lang/rust/issues/131229 introduces infrastructure for structured parsers for attributes, as well as converting a couple of complex attributes to have such structured parsers. This PR may prove too large to review. I left some of my own comments to guide it a little. Some general notes: - The first commit is basically standalone. It just preps some mostly unrelated sources for the rest of the PR to work. It might not have enormous merit on its own, but not negative merit either. Could be merged alone, but also doesn't make the review a whole lot easier. (but it's only +274 -209) - The second commit is the one that introduces new infrastructure. It's the important one to review. - The 3rd commit uses the new infrastructure showing how some of the more complex attributes can be parsed using it. Theoretically can be split up, though the parsers in this commit are the ones that really test the new infrastructure and show that it all works. - The 4th commit fixes up rustdoc and clippy. In the previous 2 they didn't compile yet while the compiler does. Separated them out to separate concerns and make the rest more palatable. - The 5th commit blesses some test outputs. Sometimes that's just because a diagnostic happens slightly earlier than before, which I'd say is acceptable. Sometimes a diagnostic is now only emitted once where it would've been twice before (yay! fixed some bugs). One test I actually moved from crashes to fixed, because it simply doesn't crash anymore. That's why this PR Closes #132391. I think most choices I made here are generally reasonable, but let me know if you disagree anywhere. - The 6th commit adds a derive to pretty print attributes - The 7th removes smir apis for attributes, for the time being. The api will at some point be replaced by one based on `rustc_ast_data_structures::AttributeKind` In general, a lot of the additions here are comments. I've found it very important to document new things in the 2nd commit well so other people can start using it. Closes #132391 Closes #136717
This commit is contained in:
commit
7d8c6e781d
167 changed files with 3963 additions and 2292 deletions
19
Cargo.lock
19
Cargo.lock
|
@ -3207,6 +3207,7 @@ dependencies = [
|
|||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_ast_pretty",
|
||||
"rustc_attr_parsing",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_feature",
|
||||
|
@ -3215,6 +3216,7 @@ dependencies = [
|
|||
"rustc_index",
|
||||
"rustc_macros",
|
||||
"rustc_middle",
|
||||
"rustc_parse",
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
"rustc_target",
|
||||
|
@ -3263,14 +3265,10 @@ dependencies = [
|
|||
"rustc_ast",
|
||||
"rustc_ast_pretty",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_feature",
|
||||
"rustc_fluent_macro",
|
||||
"rustc_lexer",
|
||||
"rustc_macros",
|
||||
"rustc_serialize",
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
"thin-vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3285,11 +3283,13 @@ dependencies = [
|
|||
"rustc_errors",
|
||||
"rustc_feature",
|
||||
"rustc_fluent_macro",
|
||||
"rustc_hir",
|
||||
"rustc_lexer",
|
||||
"rustc_macros",
|
||||
"rustc_serialize",
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
"thin-vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3342,6 +3342,7 @@ dependencies = [
|
|||
"rustc_expand",
|
||||
"rustc_feature",
|
||||
"rustc_fluent_macro",
|
||||
"rustc_hir",
|
||||
"rustc_index",
|
||||
"rustc_lexer",
|
||||
"rustc_lint_defs",
|
||||
|
@ -3598,6 +3599,7 @@ dependencies = [
|
|||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_ast_pretty",
|
||||
"rustc_attr_data_structures",
|
||||
"rustc_data_structures",
|
||||
"rustc_error_codes",
|
||||
"rustc_error_messages",
|
||||
|
@ -3632,6 +3634,7 @@ dependencies = [
|
|||
"rustc_errors",
|
||||
"rustc_feature",
|
||||
"rustc_fluent_macro",
|
||||
"rustc_hir",
|
||||
"rustc_lexer",
|
||||
"rustc_lint_defs",
|
||||
"rustc_macros",
|
||||
|
@ -3690,6 +3693,7 @@ dependencies = [
|
|||
"rustc_abi",
|
||||
"rustc_arena",
|
||||
"rustc_ast",
|
||||
"rustc_attr_data_structures",
|
||||
"rustc_data_structures",
|
||||
"rustc_hashes",
|
||||
"rustc_index",
|
||||
|
@ -3737,6 +3741,7 @@ dependencies = [
|
|||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_ast_pretty",
|
||||
"rustc_attr_parsing",
|
||||
"rustc_hir",
|
||||
"rustc_span",
|
||||
]
|
||||
|
@ -4244,6 +4249,7 @@ name = "rustc_query_impl"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"measureme",
|
||||
"rustc_attr_data_structures",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_hashes",
|
||||
|
@ -4266,6 +4272,7 @@ dependencies = [
|
|||
"rustc-rayon-core",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_attr_data_structures",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_feature",
|
||||
|
@ -4316,6 +4323,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"bitflags",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_data_structures",
|
||||
"rustc_hir",
|
||||
"rustc_middle",
|
||||
|
@ -4412,6 +4420,7 @@ dependencies = [
|
|||
"punycode",
|
||||
"rustc-demangle",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_hashes",
|
||||
|
|
|
@ -11,6 +11,7 @@ doctest = false
|
|||
rustc_abi = { path = "../rustc_abi" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
|
||||
rustc_attr_parsing = { path = "../rustc_attr_parsing" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
rustc_feature = { path = "../rustc_feature" }
|
||||
|
@ -19,6 +20,7 @@ rustc_hir = { path = "../rustc_hir" }
|
|||
rustc_index = { path = "../rustc_index" }
|
||||
rustc_macros = { path = "../rustc_macros" }
|
||||
rustc_middle = { path = "../rustc_middle" }
|
||||
rustc_parse = { path = "../rustc_parse" }
|
||||
rustc_session = { path = "../rustc_session" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_target = { path = "../rustc_target" }
|
||||
|
|
|
@ -108,7 +108,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
};
|
||||
let span = self.lower_span(l.span);
|
||||
let source = hir::LocalSource::Normal;
|
||||
self.lower_attrs(hir_id, &l.attrs);
|
||||
self.lower_attrs(hir_id, &l.attrs, l.span);
|
||||
self.arena.alloc(hir::LetStmt { hir_id, ty, pat, init, els, span, source })
|
||||
}
|
||||
|
||||
|
|
|
@ -77,9 +77,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
self.attrs.insert(
|
||||
ex.hir_id.local_id,
|
||||
&*self.arena.alloc_from_iter(
|
||||
e.attrs
|
||||
.iter()
|
||||
.map(|a| self.lower_attr(a))
|
||||
self.lower_attrs_vec(&e.attrs, e.span)
|
||||
.into_iter()
|
||||
.chain(old_attrs.iter().cloned()),
|
||||
),
|
||||
);
|
||||
|
@ -98,7 +97,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
}
|
||||
|
||||
let expr_hir_id = self.lower_node_id(e.id);
|
||||
self.lower_attrs(expr_hir_id, &e.attrs);
|
||||
self.lower_attrs(expr_hir_id, &e.attrs, e.span);
|
||||
|
||||
let kind = match &e.kind {
|
||||
ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)),
|
||||
|
@ -670,7 +669,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let guard = arm.guard.as_ref().map(|cond| self.lower_expr(cond));
|
||||
let hir_id = self.next_id();
|
||||
let span = self.lower_span(arm.span);
|
||||
self.lower_attrs(hir_id, &arm.attrs);
|
||||
self.lower_attrs(hir_id, &arm.attrs, arm.span);
|
||||
let is_never_pattern = pat.is_never_pattern();
|
||||
let body = if let Some(body) = &arm.body
|
||||
&& !is_never_pattern
|
||||
|
@ -839,6 +838,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
style: AttrStyle::Outer,
|
||||
span: unstable_span,
|
||||
}],
|
||||
span,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1673,7 +1673,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
|
||||
fn lower_expr_field(&mut self, f: &ExprField) -> hir::ExprField<'hir> {
|
||||
let hir_id = self.lower_node_id(f.id);
|
||||
self.lower_attrs(hir_id, &f.attrs);
|
||||
self.lower_attrs(hir_id, &f.attrs, f.span);
|
||||
hir::ExprField {
|
||||
hir_id,
|
||||
ident: self.lower_ident(f.ident),
|
||||
|
@ -1936,7 +1936,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
//
|
||||
// Also, add the attributes to the outer returned expr node.
|
||||
let expr = self.expr_drop_temps_mut(for_span, match_expr);
|
||||
self.lower_attrs(expr.hir_id, &e.attrs);
|
||||
self.lower_attrs(expr.hir_id, &e.attrs, e.span);
|
||||
expr
|
||||
}
|
||||
|
||||
|
@ -1993,7 +1993,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let val_ident = Ident::with_dummy_span(sym::val);
|
||||
let (val_pat, val_pat_nid) = self.pat_ident(span, val_ident);
|
||||
let val_expr = self.expr_ident(span, val_ident, val_pat_nid);
|
||||
self.lower_attrs(val_expr.hir_id, &attrs);
|
||||
self.lower_attrs(val_expr.hir_id, &attrs, span);
|
||||
let continue_pat = self.pat_cf_continue(unstable_span, val_pat);
|
||||
self.arm(continue_pat, val_expr)
|
||||
};
|
||||
|
@ -2024,7 +2024,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let ret_expr = self.checked_return(Some(from_residual_expr));
|
||||
self.arena.alloc(self.expr(try_span, ret_expr))
|
||||
};
|
||||
self.lower_attrs(ret_expr.hir_id, &attrs);
|
||||
self.lower_attrs(ret_expr.hir_id, &attrs, ret_expr.span);
|
||||
|
||||
let break_pat = self.pat_cf_break(try_span, residual_local);
|
||||
self.arm(break_pat, ret_expr)
|
||||
|
|
|
@ -11,7 +11,7 @@ use rustc_index::{IndexSlice, IndexVec};
|
|||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
|
||||
use rustc_span::edit_distance::find_best_match_for_name;
|
||||
use rustc_span::{DesugaringKind, Ident, Span, Symbol, kw, sym};
|
||||
use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym};
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use thin_vec::ThinVec;
|
||||
use tracing::instrument;
|
||||
|
@ -93,7 +93,8 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
|
|||
debug_assert_eq!(self.resolver.node_id_to_def_id[&CRATE_NODE_ID], CRATE_DEF_ID);
|
||||
self.with_lctx(CRATE_NODE_ID, |lctx| {
|
||||
let module = lctx.lower_mod(&c.items, &c.spans);
|
||||
lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs);
|
||||
// FIXME(jdonszelman): is dummy span ever a problem here?
|
||||
lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs, DUMMY_SP);
|
||||
hir::OwnerNode::Crate(module)
|
||||
})
|
||||
}
|
||||
|
@ -157,7 +158,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let mut ident = i.ident;
|
||||
let vis_span = self.lower_span(i.vis.span);
|
||||
let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
|
||||
let attrs = self.lower_attrs(hir_id, &i.attrs);
|
||||
let attrs = self.lower_attrs(hir_id, &i.attrs, i.span);
|
||||
let kind = self.lower_item_kind(i.span, i.id, hir_id, &mut ident, attrs, vis_span, &i.kind);
|
||||
let item = hir::Item {
|
||||
owner_id: hir_id.expect_owner(),
|
||||
|
@ -620,7 +621,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
fn lower_foreign_item(&mut self, i: &ForeignItem) -> &'hir hir::ForeignItem<'hir> {
|
||||
let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
|
||||
let owner_id = hir_id.expect_owner();
|
||||
let attrs = self.lower_attrs(hir_id, &i.attrs);
|
||||
let attrs = self.lower_attrs(hir_id, &i.attrs, i.span);
|
||||
let item = hir::ForeignItem {
|
||||
owner_id,
|
||||
ident: self.lower_ident(i.ident),
|
||||
|
@ -678,7 +679,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
|
||||
fn lower_variant(&mut self, v: &Variant) -> hir::Variant<'hir> {
|
||||
let hir_id = self.lower_node_id(v.id);
|
||||
self.lower_attrs(hir_id, &v.attrs);
|
||||
self.lower_attrs(hir_id, &v.attrs, v.span);
|
||||
hir::Variant {
|
||||
hir_id,
|
||||
def_id: self.local_def_id(v.id),
|
||||
|
@ -740,7 +741,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
) -> hir::FieldDef<'hir> {
|
||||
let ty = self.lower_ty(&f.ty, ImplTraitContext::Disallowed(ImplTraitPosition::FieldTy));
|
||||
let hir_id = self.lower_node_id(f.id);
|
||||
self.lower_attrs(hir_id, &f.attrs);
|
||||
self.lower_attrs(hir_id, &f.attrs, f.span);
|
||||
hir::FieldDef {
|
||||
span: self.lower_span(f.span),
|
||||
hir_id,
|
||||
|
@ -759,7 +760,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
|
||||
fn lower_trait_item(&mut self, i: &AssocItem) -> &'hir hir::TraitItem<'hir> {
|
||||
let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
|
||||
let attrs = self.lower_attrs(hir_id, &i.attrs);
|
||||
let attrs = self.lower_attrs(hir_id, &i.attrs, i.span);
|
||||
let trait_item_def_id = hir_id.expect_owner();
|
||||
|
||||
let (generics, kind, has_default) = match &i.kind {
|
||||
|
@ -895,7 +896,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let has_value = true;
|
||||
let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value);
|
||||
let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
|
||||
let attrs = self.lower_attrs(hir_id, &i.attrs);
|
||||
let attrs = self.lower_attrs(hir_id, &i.attrs, i.span);
|
||||
|
||||
let (generics, kind) = match &i.kind {
|
||||
AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => self.lower_generics(
|
||||
|
@ -1056,7 +1057,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
|
||||
fn lower_param(&mut self, param: &Param) -> hir::Param<'hir> {
|
||||
let hir_id = self.lower_node_id(param.id);
|
||||
self.lower_attrs(hir_id, ¶m.attrs);
|
||||
self.lower_attrs(hir_id, ¶m.attrs, param.span);
|
||||
hir::Param {
|
||||
hir_id,
|
||||
pat: self.lower_pat(¶m.pat),
|
||||
|
|
|
@ -45,6 +45,7 @@ use std::sync::Arc;
|
|||
|
||||
use rustc_ast::node_id::NodeMap;
|
||||
use rustc_ast::{self as ast, *};
|
||||
use rustc_attr_parsing::{AttributeParser, OmitDoc};
|
||||
use rustc_data_structures::fingerprint::Fingerprint;
|
||||
use rustc_data_structures::sorted_map::SortedMap;
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
|
@ -60,7 +61,8 @@ use rustc_macros::extension;
|
|||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
|
||||
use rustc_session::parse::{add_feature_diagnostics, feature_err};
|
||||
use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym};
|
||||
use rustc_span::symbol::{Ident, Symbol, kw, sym};
|
||||
use rustc_span::{DUMMY_SP, DesugaringKind, Span};
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use thin_vec::ThinVec;
|
||||
use tracing::{debug, instrument, trace};
|
||||
|
@ -137,10 +139,13 @@ struct LoweringContext<'a, 'hir> {
|
|||
allow_async_iterator: Arc<[Symbol]>,
|
||||
allow_for_await: Arc<[Symbol]>,
|
||||
allow_async_fn_traits: Arc<[Symbol]>,
|
||||
|
||||
attribute_parser: AttributeParser<'hir>,
|
||||
}
|
||||
|
||||
impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
fn new(tcx: TyCtxt<'hir>, resolver: &'a mut ResolverAstLowering) -> Self {
|
||||
let registered_tools = tcx.registered_tools(()).iter().map(|x| x.name).collect();
|
||||
Self {
|
||||
// Pseudo-globals.
|
||||
tcx,
|
||||
|
@ -181,6 +186,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
// FIXME(gen_blocks): how does `closure_track_caller`/`async_fn_track_caller`
|
||||
// interact with `gen`/`async gen` blocks
|
||||
allow_async_iterator: [sym::gen_future, sym::async_iterator].into(),
|
||||
|
||||
attribute_parser: AttributeParser::new(tcx.sess, tcx.features(), registered_tools),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -216,7 +223,6 @@ impl ResolverAstLowering {
|
|||
None
|
||||
}
|
||||
|
||||
/// Obtains resolution for a `NodeId` with a single resolution.
|
||||
fn get_partial_res(&self, id: NodeId) -> Option<PartialRes> {
|
||||
self.partial_res_map.get(&id).copied()
|
||||
}
|
||||
|
@ -855,45 +861,38 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
ret
|
||||
}
|
||||
|
||||
fn lower_attrs(&mut self, id: HirId, attrs: &[Attribute]) -> &'hir [hir::Attribute] {
|
||||
fn lower_attrs(
|
||||
&mut self,
|
||||
id: HirId,
|
||||
attrs: &[Attribute],
|
||||
target_span: Span,
|
||||
) -> &'hir [hir::Attribute] {
|
||||
if attrs.is_empty() {
|
||||
&[]
|
||||
} else {
|
||||
let lowered_attrs = self.lower_attrs_vec(attrs, self.lower_span(target_span));
|
||||
|
||||
debug_assert_eq!(id.owner, self.current_hir_id_owner);
|
||||
let ret = self.arena.alloc_from_iter(attrs.iter().map(|a| self.lower_attr(a)));
|
||||
debug_assert!(!ret.is_empty());
|
||||
self.attrs.insert(id.local_id, ret);
|
||||
ret
|
||||
let ret = self.arena.alloc_from_iter(lowered_attrs);
|
||||
|
||||
// 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_attr(&self, attr: &Attribute) -> hir::Attribute {
|
||||
// Note that we explicitly do not walk the path. Since we don't really
|
||||
// lower attributes (we use the AST version) there is nowhere to keep
|
||||
// the `HirId`s. We don't actually need HIR version of attributes anyway.
|
||||
// Tokens are also not needed after macro expansion and parsing.
|
||||
let kind = match attr.kind {
|
||||
AttrKind::Normal(ref normal) => hir::AttrKind::Normal(Box::new(hir::AttrItem {
|
||||
unsafety: self.lower_safety(normal.item.unsafety, hir::Safety::Safe),
|
||||
path: hir::AttrPath {
|
||||
segments: normal
|
||||
.item
|
||||
.path
|
||||
.segments
|
||||
.iter()
|
||||
.map(|i| i.ident)
|
||||
.collect::<Vec<_>>()
|
||||
.into_boxed_slice(),
|
||||
span: normal.item.path.span,
|
||||
},
|
||||
args: self.lower_attr_args(&normal.item.args),
|
||||
})),
|
||||
AttrKind::DocComment(comment_kind, data) => {
|
||||
hir::AttrKind::DocComment(comment_kind, data)
|
||||
}
|
||||
};
|
||||
|
||||
hir::Attribute { kind, id: attr.id, style: attr.style, span: self.lower_span(attr.span) }
|
||||
fn lower_attrs_vec(&self, attrs: &[Attribute], target_span: Span) -> Vec<hir::Attribute> {
|
||||
self.attribute_parser
|
||||
.parse_attribute_list(attrs, target_span, OmitDoc::Lower, |s| self.lower_span(s))
|
||||
}
|
||||
|
||||
fn alias_attrs(&mut self, id: HirId, target_id: HirId) {
|
||||
|
@ -905,34 +904,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
}
|
||||
}
|
||||
|
||||
fn lower_attr_args(&self, args: &AttrArgs) -> hir::AttrArgs {
|
||||
match args {
|
||||
AttrArgs::Empty => hir::AttrArgs::Empty,
|
||||
AttrArgs::Delimited(args) => hir::AttrArgs::Delimited(self.lower_delim_args(args)),
|
||||
// This is an inert key-value attribute - it will never be visible to macros
|
||||
// after it gets lowered to HIR. Therefore, we can extract literals to handle
|
||||
// nonterminals in `#[doc]` (e.g. `#[doc = $e]`).
|
||||
&AttrArgs::Eq { eq_span, ref expr } => {
|
||||
// In valid code the value always ends up as a single literal. Otherwise, a dummy
|
||||
// literal suffices because the error is handled elsewhere.
|
||||
let lit = if let ExprKind::Lit(token_lit) = expr.kind
|
||||
&& let Ok(lit) = MetaItemLit::from_token_lit(token_lit, expr.span)
|
||||
{
|
||||
lit
|
||||
} else {
|
||||
let guar = self.dcx().has_errors().unwrap();
|
||||
MetaItemLit {
|
||||
symbol: kw::Empty,
|
||||
suffix: None,
|
||||
kind: LitKind::Err(guar),
|
||||
span: DUMMY_SP,
|
||||
}
|
||||
};
|
||||
hir::AttrArgs::Eq { eq_span, expr: lit }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_delim_args(&self, args: &DelimArgs) -> DelimArgs {
|
||||
DelimArgs { dspan: args.dspan, delim: args.delim, tokens: args.tokens.flattened() }
|
||||
}
|
||||
|
@ -1845,7 +1816,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
let (name, kind) = self.lower_generic_param_kind(param, source);
|
||||
|
||||
let hir_id = self.lower_node_id(param.id);
|
||||
self.lower_attrs(hir_id, ¶m.attrs);
|
||||
self.lower_attrs(hir_id, ¶m.attrs, param.span());
|
||||
hir::GenericParam {
|
||||
hir_id,
|
||||
def_id: self.local_def_id(param.id),
|
||||
|
|
|
@ -93,7 +93,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
|
||||
let fs = self.arena.alloc_from_iter(fields.iter().map(|f| {
|
||||
let hir_id = self.lower_node_id(f.id);
|
||||
self.lower_attrs(hir_id, &f.attrs);
|
||||
self.lower_attrs(hir_id, &f.attrs, f.span);
|
||||
|
||||
hir::PatField {
|
||||
hir_id,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -5,16 +5,12 @@ edition = "2024"
|
|||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
rustc_abi = { path = "../rustc_abi" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
|
||||
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_lexer = { path = "../rustc_lexer" }
|
||||
rustc_macros = { path = "../rustc_macros" }
|
||||
rustc_serialize = { path = "../rustc_serialize" }
|
||||
rustc_session = { path = "../rustc_session" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_abi = {path = "../rustc_abi"}
|
||||
rustc_ast = {path = "../rustc_ast"}
|
||||
rustc_ast_pretty = {path = "../rustc_ast_pretty"}
|
||||
rustc_data_structures = {path = "../rustc_data_structures"}
|
||||
rustc_macros = {path = "../rustc_macros"}
|
||||
rustc_serialize = {path = "../rustc_serialize"}
|
||||
rustc_span = {path = "../rustc_span"}
|
||||
thin-vec = "0.2.12"
|
||||
# tidy-alphabetical-end
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
use rustc_abi::Align;
|
||||
use rustc_ast as ast;
|
||||
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
|
||||
use rustc_ast::token::CommentKind;
|
||||
use rustc_ast::{self as ast, AttrStyle};
|
||||
use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute};
|
||||
use rustc_span::hygiene::Transparency;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use crate::RustcVersion;
|
||||
use crate::{DefaultBodyStability, PartialConstStability, PrintAttribute, RustcVersion, Stability};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
|
||||
pub enum InlineAttr {
|
||||
|
@ -54,7 +57,7 @@ impl OptimizeAttr {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Encodable, Decodable)]
|
||||
#[derive(Clone, Debug, Encodable, Decodable, HashStable_Generic, PrintAttribute)]
|
||||
pub enum DiagnosticAttribute {
|
||||
// tidy-alphabetical-start
|
||||
DoNotRecommend,
|
||||
|
@ -62,7 +65,7 @@ pub enum DiagnosticAttribute {
|
|||
// tidy-alphabetical-end
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Encodable, Decodable, Copy, Clone)]
|
||||
#[derive(PartialEq, Debug, Encodable, Decodable, Copy, Clone, HashStable_Generic, PrintAttribute)]
|
||||
pub enum ReprAttr {
|
||||
ReprInt(IntType),
|
||||
ReprRust,
|
||||
|
@ -71,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::*;
|
||||
|
||||
|
@ -80,13 +85,13 @@ pub enum TransparencyError {
|
|||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
|
||||
#[derive(Encodable, Decodable)]
|
||||
#[derive(Encodable, Decodable, HashStable_Generic, PrintAttribute)]
|
||||
pub enum IntType {
|
||||
SignedInt(ast::IntTy),
|
||||
UnsignedInt(ast::UintTy),
|
||||
}
|
||||
|
||||
#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)]
|
||||
#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic, PrintAttribute)]
|
||||
pub struct Deprecation {
|
||||
pub since: DeprecatedSince,
|
||||
/// The note to issue a reason.
|
||||
|
@ -98,7 +103,7 @@ pub struct Deprecation {
|
|||
}
|
||||
|
||||
/// Release in which an API is deprecated.
|
||||
#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)]
|
||||
#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic, PrintAttribute)]
|
||||
pub enum DeprecatedSince {
|
||||
RustcVersion(RustcVersion),
|
||||
/// Deprecated in the future ("to be determined").
|
||||
|
@ -132,3 +137,61 @@ impl Deprecation {
|
|||
matches!(self.since, DeprecatedSince::RustcVersion(_))
|
||||
}
|
||||
}
|
||||
|
||||
/// Attributes represent parsed, *built in*, inert attributes. That means,
|
||||
/// attributes that are not actually ever expanded.
|
||||
/// For more information on this, see the module docs on the rustc_attr_parsing crate.
|
||||
/// They're instead used as markers, to guide the compilation process in various way in most every stage of the compiler.
|
||||
/// These are kept around after the AST, into the HIR and further on.
|
||||
///
|
||||
/// The word parsed could be a little misleading here, because the parser already parses
|
||||
/// attributes early on. However, the result, an [`ast::Attribute`]
|
||||
/// is only parsed at a high level, still containing a token stream in many cases. That is
|
||||
/// because the structure of the contents varies from attribute to attribute.
|
||||
/// With a parsed attribute I mean that each attribute is processed individually into a
|
||||
/// final structure, which on-site (the place where the attribute is useful for, think the
|
||||
/// the place where `must_use` is checked) little to no extra parsing or validating needs to
|
||||
/// happen.
|
||||
///
|
||||
/// For more docs, look in [`rustc_attr`](https://doc.rust-lang.org/stable/nightly-rustc/rustc_attr/index.html)
|
||||
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
|
||||
pub enum AttributeKind {
|
||||
// tidy-alphabetical-start
|
||||
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
|
||||
}
|
||||
|
|
|
@ -10,7 +10,142 @@ mod attributes;
|
|||
mod stability;
|
||||
mod version;
|
||||
|
||||
use std::num::NonZero;
|
||||
|
||||
pub use attributes::*;
|
||||
pub(crate) use rustc_session::HashStableContext;
|
||||
use rustc_abi::Align;
|
||||
use rustc_ast::token::CommentKind;
|
||||
use rustc_ast::{AttrStyle, IntTy, UintTy};
|
||||
use rustc_ast_pretty::pp::Printer;
|
||||
use rustc_span::hygiene::Transparency;
|
||||
use rustc_span::{Span, Symbol};
|
||||
pub use stability::*;
|
||||
use thin_vec::ThinVec;
|
||||
pub use version::*;
|
||||
|
||||
/// Requirements for a `StableHashingContext` to be used in this crate.
|
||||
/// This is a hack to allow using the `HashStable_Generic` derive macro
|
||||
/// instead of implementing everything in `rustc_middle`.
|
||||
pub trait HashStableContext: rustc_ast::HashStableContext + rustc_abi::HashStableContext {}
|
||||
|
||||
/// This trait is used to print attributes in `rustc_hir_pretty`.
|
||||
///
|
||||
/// For structs and enums it can be derived using [`rustc_macros::PrintAttribute`].
|
||||
/// The output will look a lot like a `Debug` implementation, but fields of several types
|
||||
/// like [`Span`]s and empty tuples, are gracefully skipped so they don't clutter the
|
||||
/// representation much.
|
||||
pub trait PrintAttribute {
|
||||
fn print_something(&self) -> bool;
|
||||
fn print_attribute(&self, p: &mut Printer);
|
||||
}
|
||||
|
||||
impl<T: PrintAttribute> PrintAttribute for &T {
|
||||
fn print_something(&self) -> bool {
|
||||
T::print_something(self)
|
||||
}
|
||||
|
||||
fn print_attribute(&self, p: &mut Printer) {
|
||||
T::print_attribute(self, p)
|
||||
}
|
||||
}
|
||||
impl<T: PrintAttribute> PrintAttribute for Option<T> {
|
||||
fn print_something(&self) -> bool {
|
||||
self.as_ref().is_some_and(|x| x.print_something())
|
||||
}
|
||||
fn print_attribute(&self, p: &mut Printer) {
|
||||
if let Some(i) = self {
|
||||
T::print_attribute(i, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T: PrintAttribute> PrintAttribute for ThinVec<T> {
|
||||
fn print_something(&self) -> bool {
|
||||
self.is_empty() || self[0].print_something()
|
||||
}
|
||||
fn print_attribute(&self, p: &mut Printer) {
|
||||
let mut last_printed = false;
|
||||
p.word("[");
|
||||
for i in self {
|
||||
if last_printed {
|
||||
p.word_space(",");
|
||||
}
|
||||
i.print_attribute(p);
|
||||
last_printed = i.print_something();
|
||||
}
|
||||
p.word("]");
|
||||
}
|
||||
}
|
||||
macro_rules! print_skip {
|
||||
($($t: ty),* $(,)?) => {$(
|
||||
impl PrintAttribute for $t {
|
||||
fn print_something(&self) -> bool { false }
|
||||
fn print_attribute(&self, _: &mut Printer) { }
|
||||
})*
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! print_disp {
|
||||
($($t: ty),* $(,)?) => {$(
|
||||
impl PrintAttribute for $t {
|
||||
fn print_something(&self) -> bool { true }
|
||||
fn print_attribute(&self, p: &mut Printer) {
|
||||
p.word(format!("{}", self));
|
||||
}
|
||||
}
|
||||
)*};
|
||||
}
|
||||
macro_rules! print_debug {
|
||||
($($t: ty),* $(,)?) => {$(
|
||||
impl PrintAttribute for $t {
|
||||
fn print_something(&self) -> bool { true }
|
||||
fn print_attribute(&self, p: &mut Printer) {
|
||||
p.word(format!("{:?}", self));
|
||||
}
|
||||
}
|
||||
)*};
|
||||
}
|
||||
|
||||
macro_rules! print_tup {
|
||||
(num_print_something $($ts: ident)*) => { 0 $(+ $ts.print_something() as usize)* };
|
||||
() => {};
|
||||
($t: ident $($ts: ident)*) => {
|
||||
#[allow(non_snake_case, unused)]
|
||||
impl<$t: PrintAttribute, $($ts: PrintAttribute),*> PrintAttribute for ($t, $($ts),*) {
|
||||
fn print_something(&self) -> bool {
|
||||
let ($t, $($ts),*) = self;
|
||||
print_tup!(num_print_something $t $($ts)*) != 0
|
||||
}
|
||||
|
||||
fn print_attribute(&self, p: &mut Printer) {
|
||||
let ($t, $($ts),*) = self;
|
||||
let parens = print_tup!(num_print_something $t $($ts)*) > 1;
|
||||
if parens {
|
||||
p.word("(");
|
||||
}
|
||||
|
||||
let mut printed_anything = $t.print_something();
|
||||
|
||||
$t.print_attribute(p);
|
||||
|
||||
$(
|
||||
if printed_anything && $ts.print_something() {
|
||||
p.word_space(",");
|
||||
printed_anything = true;
|
||||
}
|
||||
$ts.print_attribute(p);
|
||||
)*
|
||||
|
||||
if parens {
|
||||
p.word(")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print_tup!($($ts)*);
|
||||
};
|
||||
}
|
||||
|
||||
print_tup!(A B C D E F G H);
|
||||
print_skip!(Span, ());
|
||||
print_disp!(Symbol, u16, bool, NonZero<u32>);
|
||||
print_debug!(UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency);
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use std::num::NonZero;
|
||||
|
||||
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
|
||||
use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute};
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
||||
use crate::RustcVersion;
|
||||
use crate::{PrintAttribute, RustcVersion};
|
||||
|
||||
/// The version placeholder that recently stabilized features contain inside the
|
||||
/// `since` field of the `#[stable]` attribute.
|
||||
|
@ -21,7 +21,7 @@ pub const VERSION_PLACEHOLDER: &str = concat!("CURRENT_RUSTC_VERSIO", "N");
|
|||
/// - `#[stable]`
|
||||
/// - `#[unstable]`
|
||||
#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[derive(HashStable_Generic)]
|
||||
#[derive(HashStable_Generic, PrintAttribute)]
|
||||
pub struct Stability {
|
||||
pub level: StabilityLevel,
|
||||
pub feature: Symbol,
|
||||
|
@ -43,7 +43,7 @@ impl Stability {
|
|||
|
||||
/// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes.
|
||||
#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[derive(HashStable_Generic)]
|
||||
#[derive(HashStable_Generic, PrintAttribute)]
|
||||
pub struct ConstStability {
|
||||
pub level: StabilityLevel,
|
||||
pub feature: Symbol,
|
||||
|
@ -83,7 +83,7 @@ impl ConstStability {
|
|||
/// Excludes `const_stable_indirect`. This is necessary because when `-Zforce-unstable-if-unmarked`
|
||||
/// is set, we need to encode standalone `#[rustc_const_stable_indirect]` attributes
|
||||
#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[derive(HashStable_Generic)]
|
||||
#[derive(HashStable_Generic, PrintAttribute)]
|
||||
pub struct PartialConstStability {
|
||||
pub level: StabilityLevel,
|
||||
pub feature: Symbol,
|
||||
|
@ -103,7 +103,7 @@ impl PartialConstStability {
|
|||
|
||||
/// The available stability levels.
|
||||
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
|
||||
#[derive(HashStable_Generic)]
|
||||
#[derive(HashStable_Generic, PrintAttribute)]
|
||||
pub enum StabilityLevel {
|
||||
/// `#[unstable]`
|
||||
Unstable {
|
||||
|
@ -145,7 +145,7 @@ pub enum StabilityLevel {
|
|||
|
||||
/// Rust release in which a feature is stabilized.
|
||||
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, PartialOrd, Ord, Hash)]
|
||||
#[derive(HashStable_Generic)]
|
||||
#[derive(HashStable_Generic, PrintAttribute)]
|
||||
pub enum StableSince {
|
||||
/// also stores the original symbol for printing
|
||||
Version(RustcVersion),
|
||||
|
@ -171,7 +171,7 @@ impl StabilityLevel {
|
|||
}
|
||||
|
||||
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
|
||||
#[derive(HashStable_Generic)]
|
||||
#[derive(HashStable_Generic, PrintAttribute)]
|
||||
pub enum UnstableReason {
|
||||
None,
|
||||
Default,
|
||||
|
@ -180,7 +180,7 @@ pub enum UnstableReason {
|
|||
|
||||
/// Represents the `#[rustc_default_body_unstable]` attribute.
|
||||
#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[derive(HashStable_Generic)]
|
||||
#[derive(HashStable_Generic, PrintAttribute)]
|
||||
pub struct DefaultBodyStability {
|
||||
pub level: StabilityLevel,
|
||||
pub feature: Symbol,
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
use std::fmt::{self, Display};
|
||||
|
||||
use rustc_macros::{Decodable, Encodable, HashStable_Generic, current_rustc_version};
|
||||
use rustc_macros::{
|
||||
Decodable, Encodable, HashStable_Generic, PrintAttribute, current_rustc_version,
|
||||
};
|
||||
|
||||
use crate::PrintAttribute;
|
||||
|
||||
#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[derive(HashStable_Generic)]
|
||||
#[derive(HashStable_Generic, PrintAttribute)]
|
||||
pub struct RustcVersion {
|
||||
pub major: u16,
|
||||
pub minor: u16,
|
||||
|
|
|
@ -13,9 +13,11 @@ 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_macros = { path = "../rustc_macros" }
|
||||
rustc_serialize = { path = "../rustc_serialize" }
|
||||
rustc_session = { path = "../rustc_session" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
thin-vec = "0.2.12"
|
||||
# tidy-alphabetical-end
|
||||
|
|
|
@ -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
|
||||
|
@ -88,18 +90,20 @@ attr_parsing_multiple_stability_levels =
|
|||
attr_parsing_non_ident_feature =
|
||||
'feature' is not an identifier
|
||||
|
||||
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}
|
||||
|
@ -107,6 +111,10 @@ attr_parsing_unknown_meta_item =
|
|||
attr_parsing_unknown_version_literal =
|
||||
unknown version literal format, assuming it refers to a future version
|
||||
|
||||
attr_parsing_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`
|
||||
|
||||
attr_parsing_unstable_cfg_target_compact =
|
||||
compact `cfg(target(..))` is experimental and subject to change
|
||||
|
||||
|
@ -122,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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -1,17 +1,152 @@
|
|||
mod allow_unstable;
|
||||
mod cfg;
|
||||
mod confusables;
|
||||
mod deprecation;
|
||||
mod repr;
|
||||
mod stability;
|
||||
mod transparency;
|
||||
//! This module defines traits for attribute parsers, little state machines that recognize and parse
|
||||
//! attributes out of a longer list of attributes. The main trait is called [`AttributeParser`].
|
||||
//! You can find more docs about [`AttributeParser`]s on the trait itself.
|
||||
//! However, for many types of attributes, implementing [`AttributeParser`] is not necessary.
|
||||
//! It allows for a lot of flexibility you might not want.
|
||||
//!
|
||||
//! Specifically, you might not care about managing the state of your [`AttributeParser`]
|
||||
//! state machine yourself. In this case you can choose to implement:
|
||||
//!
|
||||
//! - [`SingleAttributeParser`]: makes it easy to implement an attribute which should error if it
|
||||
//! appears more than once in a list of attributes
|
||||
//! - [`CombineAttributeParser`]: makes it easy to implement an attribute which should combine the
|
||||
//! contents of attributes, if an attribute appear multiple times in a list
|
||||
//!
|
||||
//! Attributes should be added to [`ATTRIBUTE_MAPPING`](crate::context::ATTRIBUTE_MAPPING) to be parsed.
|
||||
|
||||
pub mod util;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub use allow_unstable::*;
|
||||
pub use cfg::*;
|
||||
pub use confusables::*;
|
||||
pub use deprecation::*;
|
||||
pub use repr::*;
|
||||
pub use stability::*;
|
||||
pub use transparency::*;
|
||||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_span::Span;
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use crate::context::{AcceptContext, FinalizeContext};
|
||||
use crate::parser::ArgParser;
|
||||
|
||||
pub(crate) mod allow_unstable;
|
||||
pub(crate) mod cfg;
|
||||
pub(crate) mod confusables;
|
||||
pub(crate) mod deprecation;
|
||||
pub(crate) mod repr;
|
||||
pub(crate) mod stability;
|
||||
pub(crate) mod transparency;
|
||||
pub(crate) mod util;
|
||||
|
||||
type AcceptFn<T> = fn(&mut T, &AcceptContext<'_>, &ArgParser<'_>);
|
||||
type AcceptMapping<T> = &'static [(&'static [rustc_span::Symbol], AcceptFn<T>)];
|
||||
|
||||
/// An [`AttributeParser`] is a type which searches for syntactic attributes.
|
||||
///
|
||||
/// Parsers are often tiny state machines that gets to see all syntactical attributes on an item.
|
||||
/// [`Default::default`] creates a fresh instance that sits in some kind of initial state, usually that the
|
||||
/// attribute it is looking for was not yet seen.
|
||||
///
|
||||
/// Then, it defines what paths this group will accept in [`AttributeParser::ATTRIBUTES`].
|
||||
/// These are listed as pairs, of symbols and function pointers. The function pointer will
|
||||
/// be called when that attribute is found on an item, which can influence the state of the little
|
||||
/// state machine.
|
||||
///
|
||||
/// Finally, after all attributes on an item have been seen, and possibly been accepted,
|
||||
/// the [`finalize`](AttributeParser::finalize) functions for all attribute parsers are called. Each can then report
|
||||
/// whether it has seen the attribute it has been looking for.
|
||||
///
|
||||
/// The state machine is automatically reset to parse attributes on the next item.
|
||||
pub(crate) trait AttributeParser: Default + 'static {
|
||||
/// The symbols for the attributes that this parser is interested in.
|
||||
///
|
||||
/// If an attribute has this symbol, the `accept` function will be called on it.
|
||||
const ATTRIBUTES: AcceptMapping<Self>;
|
||||
|
||||
/// The parser has gotten a chance to accept the attributes on an item,
|
||||
/// here it can produce an attribute.
|
||||
fn finalize(self, cx: &FinalizeContext<'_>) -> Option<AttributeKind>;
|
||||
}
|
||||
|
||||
/// Alternative to [`AttributeParser`] that automatically handles state management.
|
||||
/// A slightly simpler and more restricted way to convert attributes.
|
||||
/// Assumes that an attribute can only appear a single time on an item,
|
||||
/// and errors when it sees more.
|
||||
///
|
||||
/// [`Single<T> where T: SingleAttributeParser`](Single) implements [`AttributeParser`].
|
||||
///
|
||||
/// [`SingleAttributeParser`] can only convert attributes one-to-one, and cannot combine multiple
|
||||
/// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example.
|
||||
pub(crate) trait SingleAttributeParser: 'static {
|
||||
const PATH: &'static [rustc_span::Symbol];
|
||||
|
||||
/// Caled when a duplicate attribute is found.
|
||||
///
|
||||
/// `first_span` is the span of the first occurrence of this attribute.
|
||||
// FIXME(jdonszelmann): default error
|
||||
fn on_duplicate(cx: &AcceptContext<'_>, first_span: Span);
|
||||
|
||||
/// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`]
|
||||
fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind>;
|
||||
}
|
||||
|
||||
pub(crate) struct Single<T: SingleAttributeParser>(PhantomData<T>, Option<(AttributeKind, Span)>);
|
||||
|
||||
impl<T: SingleAttributeParser> Default for Single<T> {
|
||||
fn default() -> Self {
|
||||
Self(Default::default(), Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SingleAttributeParser> AttributeParser for Single<T> {
|
||||
const ATTRIBUTES: AcceptMapping<Self> = &[(T::PATH, |group: &mut Single<T>, cx, args| {
|
||||
if let Some((_, s)) = group.1 {
|
||||
T::on_duplicate(cx, s);
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(pa) = T::convert(cx, args) {
|
||||
group.1 = Some((pa, cx.attr_span));
|
||||
}
|
||||
})];
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
|
||||
Some(self.1?.0)
|
||||
}
|
||||
}
|
||||
|
||||
type ConvertFn<E> = fn(ThinVec<E>) -> AttributeKind;
|
||||
|
||||
/// Alternative to [`AttributeParser`] that automatically handles state management.
|
||||
/// If multiple attributes appear on an element, combines the values of each into a
|
||||
/// [`ThinVec`].
|
||||
/// [`Combine<T> where T: CombineAttributeParser`](Combine) implements [`AttributeParser`].
|
||||
///
|
||||
/// [`CombineAttributeParser`] can only convert a single kind of attribute, and cannot combine multiple
|
||||
/// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example.
|
||||
pub(crate) trait CombineAttributeParser: 'static {
|
||||
const PATH: &'static [rustc_span::Symbol];
|
||||
|
||||
type Item;
|
||||
const CONVERT: ConvertFn<Self::Item>;
|
||||
|
||||
/// Converts a single syntactical attribute to a number of elements of the semantic attribute, or [`AttributeKind`]
|
||||
fn extend<'a>(
|
||||
cx: &'a AcceptContext<'a>,
|
||||
args: &'a ArgParser<'a>,
|
||||
) -> impl IntoIterator<Item = Self::Item> + 'a;
|
||||
}
|
||||
|
||||
pub(crate) struct Combine<T: CombineAttributeParser>(
|
||||
PhantomData<T>,
|
||||
ThinVec<<T as CombineAttributeParser>::Item>,
|
||||
);
|
||||
|
||||
impl<T: CombineAttributeParser> Default for Combine<T> {
|
||||
fn default() -> Self {
|
||||
Self(Default::default(), Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: CombineAttributeParser> AttributeParser for Combine<T> {
|
||||
const ATTRIBUTES: AcceptMapping<Self> =
|
||||
&[(T::PATH, |group: &mut Combine<T>, cx, args| group.1.extend(T::extend(cx, args)))];
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
|
||||
if self.1.is_empty() { None } else { Some(T::CONVERT(self.1)) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
348
compiler/rustc_attr_parsing/src/context.rs
Normal file
348
compiler/rustc_attr_parsing/src/context.rs
Normal file
|
@ -0,0 +1,348 @@
|
|||
use std::cell::RefCell;
|
||||
use std::collections::BTreeMap;
|
||||
use std::ops::Deref;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use rustc_ast::{self as ast, DelimArgs};
|
||||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_errors::{DiagCtxtHandle, Diagnostic};
|
||||
use rustc_feature::Features;
|
||||
use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
|
||||
|
||||
use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
|
||||
use crate::attributes::confusables::ConfusablesParser;
|
||||
use crate::attributes::deprecation::DeprecationParser;
|
||||
use crate::attributes::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 {
|
||||
(
|
||||
pub(crate) static $name: ident = [$($names: ty),* $(,)?];
|
||||
) => {
|
||||
pub(crate) static $name: LazyLock<(
|
||||
BTreeMap<&'static [Symbol], Vec<Box<dyn Fn(&AcceptContext<'_>, &ArgParser<'_>) + Send + Sync>>>,
|
||||
Vec<Box<dyn Send + Sync + Fn(&FinalizeContext<'_>) -> Option<AttributeKind>>>
|
||||
)> = LazyLock::new(|| {
|
||||
let mut accepts = BTreeMap::<_, Vec<Box<dyn Fn(&AcceptContext<'_>, &ArgParser<'_>) + Send + Sync>>>::new();
|
||||
let mut finalizes = Vec::<Box<dyn Send + Sync + Fn(&FinalizeContext<'_>) -> Option<AttributeKind>>>::new();
|
||||
$(
|
||||
{
|
||||
thread_local! {
|
||||
static STATE_OBJECT: RefCell<$names> = RefCell::new(<$names>::default());
|
||||
};
|
||||
|
||||
for (k, v) in <$names>::ATTRIBUTES {
|
||||
accepts.entry(*k).or_default().push(Box::new(|cx, args| {
|
||||
STATE_OBJECT.with_borrow_mut(|s| {
|
||||
v(s, cx, args)
|
||||
})
|
||||
}));
|
||||
}
|
||||
|
||||
finalizes.push(Box::new(|cx| {
|
||||
let state = STATE_OBJECT.take();
|
||||
state.finalize(cx)
|
||||
}));
|
||||
}
|
||||
)*
|
||||
|
||||
(accepts, finalizes)
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
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
|
||||
];
|
||||
);
|
||||
|
||||
/// Context given to every attribute parser when accepting
|
||||
///
|
||||
/// Gives [`AttributeParser`]s enough information to create errors, for example.
|
||||
pub(crate) struct AcceptContext<'a> {
|
||||
pub(crate) group_cx: &'a FinalizeContext<'a>,
|
||||
/// The span of the attribute currently being parsed
|
||||
pub(crate) attr_span: Span,
|
||||
}
|
||||
|
||||
impl<'a> AcceptContext<'a> {
|
||||
pub(crate) fn emit_err(&self, diag: impl Diagnostic<'a>) -> ErrorGuaranteed {
|
||||
if self.limit_diagnostics {
|
||||
self.dcx().create_err(diag).delay_as_bug()
|
||||
} else {
|
||||
self.dcx().emit_err(diag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for AcceptContext<'a> {
|
||||
type Target = FinalizeContext<'a>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.group_cx
|
||||
}
|
||||
}
|
||||
|
||||
/// Context given to every attribute parser during finalization.
|
||||
///
|
||||
/// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create errors, for example.
|
||||
pub(crate) struct FinalizeContext<'a> {
|
||||
/// The parse context, gives access to the session and the
|
||||
/// diagnostics context.
|
||||
pub(crate) cx: &'a AttributeParser<'a>,
|
||||
/// The span of the syntactical component this attribute was applied to
|
||||
pub(crate) target_span: Span,
|
||||
}
|
||||
|
||||
impl<'a> Deref for FinalizeContext<'a> {
|
||||
type Target = AttributeParser<'a>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.cx
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Copy, Debug)]
|
||||
pub enum OmitDoc {
|
||||
Lower,
|
||||
Skip,
|
||||
}
|
||||
|
||||
/// Context created once, for example as part of the ast lowering
|
||||
/// context, through which all attributes can be lowered.
|
||||
pub struct AttributeParser<'sess> {
|
||||
#[expect(dead_code)] // FIXME(jdonszelmann): needed later to verify we parsed all attributes
|
||||
tools: Vec<Symbol>,
|
||||
sess: &'sess Session,
|
||||
features: Option<&'sess Features>,
|
||||
|
||||
/// *only* parse attributes with this symbol.
|
||||
///
|
||||
/// Used in cases where we want the lowering infrastructure for
|
||||
/// parse just a single attribute.
|
||||
parse_only: Option<Symbol>,
|
||||
|
||||
/// Can be used to instruct parsers to reduce the number of diagnostics it emits.
|
||||
/// Useful when using `parse_limited` and you know the attr will be reparsed later.
|
||||
pub(crate) limit_diagnostics: bool,
|
||||
}
|
||||
|
||||
impl<'sess> AttributeParser<'sess> {
|
||||
/// This method allows you to parse attributes *before* you have access to features or tools.
|
||||
/// One example where this is necessary, is to parse `feature` attributes themselves for
|
||||
/// example.
|
||||
///
|
||||
/// Try to use this as little as possible. Attributes *should* be lowered during `rustc_ast_lowering`.
|
||||
/// Some attributes require access to features to parse, which would crash if you tried to do so
|
||||
/// through [`parse_limited`](Self::parse_limited).
|
||||
///
|
||||
/// To make sure use is limited, supply a `Symbol` you'd like to parse. Only attributes with
|
||||
/// that symbol are picked out of the list of instructions and parsed. Those are returned.
|
||||
pub fn parse_limited(
|
||||
sess: &'sess Session,
|
||||
attrs: &[ast::Attribute],
|
||||
sym: Symbol,
|
||||
target_span: Span,
|
||||
limit_diagnostics: bool,
|
||||
) -> Option<Attribute> {
|
||||
let mut parsed = Self {
|
||||
sess,
|
||||
features: None,
|
||||
tools: Vec::new(),
|
||||
parse_only: Some(sym),
|
||||
limit_diagnostics,
|
||||
}
|
||||
.parse_attribute_list(attrs, target_span, OmitDoc::Skip, std::convert::identity);
|
||||
|
||||
assert!(parsed.len() <= 1);
|
||||
|
||||
parsed.pop()
|
||||
}
|
||||
|
||||
pub fn new(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self {
|
||||
Self { sess, features: Some(features), tools, parse_only: None, limit_diagnostics: false }
|
||||
}
|
||||
|
||||
pub(crate) fn sess(&self) -> &'sess Session {
|
||||
self.sess
|
||||
}
|
||||
|
||||
pub(crate) fn features(&self) -> &'sess Features {
|
||||
self.features.expect("features not available at this point in the compiler")
|
||||
}
|
||||
|
||||
pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> {
|
||||
self.sess.dcx()
|
||||
}
|
||||
|
||||
/// Parse a list of attributes.
|
||||
///
|
||||
/// `target_span` is the span of the thing this list of attributes is applied to,
|
||||
/// and when `omit_doc` is set, doc attributes are filtered out.
|
||||
pub fn parse_attribute_list<'a>(
|
||||
&'a self,
|
||||
attrs: &'a [ast::Attribute],
|
||||
target_span: Span,
|
||||
omit_doc: OmitDoc,
|
||||
|
||||
lower_span: impl Copy + Fn(Span) -> Span,
|
||||
) -> Vec<Attribute> {
|
||||
let mut attributes = Vec::new();
|
||||
|
||||
let group_cx = FinalizeContext { cx: self, target_span };
|
||||
|
||||
for attr in attrs {
|
||||
// if we're only looking for a single attribute,
|
||||
// skip all the ones we don't care about
|
||||
if let Some(expected) = self.parse_only {
|
||||
if attr.name_or_empty() != expected {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// sometimes, for example for `#![doc = include_str!("readme.md")]`,
|
||||
// doc still contains a non-literal. You might say, when we're lowering attributes
|
||||
// that's expanded right? But no, sometimes, when parsing attributes on macros,
|
||||
// we already use the lowering logic and these are still there. So, when `omit_doc`
|
||||
// is set we *also* want to ignore these
|
||||
if omit_doc == OmitDoc::Skip && attr.name_or_empty() == sym::doc {
|
||||
continue;
|
||||
}
|
||||
|
||||
match &attr.kind {
|
||||
ast::AttrKind::DocComment(comment_kind, symbol) => {
|
||||
if omit_doc == OmitDoc::Skip {
|
||||
continue;
|
||||
}
|
||||
|
||||
attributes.push(Attribute::Parsed(AttributeKind::DocComment {
|
||||
style: attr.style,
|
||||
kind: *comment_kind,
|
||||
span: lower_span(attr.span),
|
||||
comment: *symbol,
|
||||
}))
|
||||
}
|
||||
// // FIXME: make doc attributes go through a proper attribute parser
|
||||
// ast::AttrKind::Normal(n) if n.name_or_empty() == sym::doc => {
|
||||
// let p = GenericMetaItemParser::from_attr(&n, self.dcx());
|
||||
//
|
||||
// attributes.push(Attribute::Parsed(AttributeKind::DocComment {
|
||||
// style: attr.style,
|
||||
// kind: CommentKind::Line,
|
||||
// span: attr.span,
|
||||
// comment: p.args().name_value(),
|
||||
// }))
|
||||
// }
|
||||
ast::AttrKind::Normal(n) => {
|
||||
let parser = MetaItemParser::from_attr(n, self.dcx());
|
||||
let (path, args) = parser.deconstruct();
|
||||
let parts = path.segments().map(|i| i.name).collect::<Vec<_>>();
|
||||
|
||||
if let Some(accepts) = ATTRIBUTE_MAPPING.0.get(parts.as_slice()) {
|
||||
for f in accepts {
|
||||
let cx = AcceptContext {
|
||||
group_cx: &group_cx,
|
||||
attr_span: lower_span(attr.span),
|
||||
};
|
||||
|
||||
f(&cx, &args)
|
||||
}
|
||||
} else {
|
||||
// if we're here, we must be compiling a tool attribute... Or someone forgot to
|
||||
// parse their fancy new attribute. Let's warn them in any case. If you are that
|
||||
// person, and you really your attribute should remain unparsed, carefully read the
|
||||
// documentation in this module and if you still think so you can add an exception
|
||||
// to this assertion.
|
||||
|
||||
// FIXME(jdonszelmann): convert other attributes, and check with this that
|
||||
// we caught em all
|
||||
// const FIXME_TEMPORARY_ATTR_ALLOWLIST: &[Symbol] = &[sym::cfg];
|
||||
// assert!(
|
||||
// self.tools.contains(&parts[0]) || true,
|
||||
// // || FIXME_TEMPORARY_ATTR_ALLOWLIST.contains(&parts[0]),
|
||||
// "attribute {path} wasn't parsed and isn't a know tool attribute",
|
||||
// );
|
||||
|
||||
attributes.push(Attribute::Unparsed(Box::new(AttrItem {
|
||||
path: AttrPath::from_ast(&n.item.path),
|
||||
args: self.lower_attr_args(&n.item.args, lower_span),
|
||||
id: HashIgnoredAttrId { attr_id: attr.id },
|
||||
style: attr.style,
|
||||
span: lower_span(attr.span),
|
||||
})));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut parsed_attributes = Vec::new();
|
||||
for f in &ATTRIBUTE_MAPPING.1 {
|
||||
if let Some(attr) = f(&group_cx) {
|
||||
parsed_attributes.push(Attribute::Parsed(attr));
|
||||
}
|
||||
}
|
||||
|
||||
attributes.extend(parsed_attributes);
|
||||
|
||||
attributes
|
||||
}
|
||||
|
||||
fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs {
|
||||
match args {
|
||||
ast::AttrArgs::Empty => AttrArgs::Empty,
|
||||
ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(DelimArgs {
|
||||
dspan: args.dspan,
|
||||
delim: args.delim,
|
||||
tokens: args.tokens.flattened(),
|
||||
}),
|
||||
// This is an inert key-value attribute - it will never be visible to macros
|
||||
// after it gets lowered to HIR. Therefore, we can extract literals to handle
|
||||
// nonterminals in `#[doc]` (e.g. `#[doc = $e]`).
|
||||
ast::AttrArgs::Eq { eq_span, expr } => {
|
||||
// In valid code the value always ends up as a single literal. Otherwise, a dummy
|
||||
// literal suffices because the error is handled elsewhere.
|
||||
let lit = if let ast::ExprKind::Lit(token_lit) = expr.kind
|
||||
&& let Ok(lit) =
|
||||
ast::MetaItemLit::from_token_lit(token_lit, lower_span(expr.span))
|
||||
{
|
||||
lit
|
||||
} else {
|
||||
let guar = self.dcx().has_errors().unwrap();
|
||||
ast::MetaItemLit {
|
||||
symbol: kw::Empty,
|
||||
suffix: None,
|
||||
kind: ast::LitKind::Err(guar),
|
||||
span: DUMMY_SP,
|
||||
}
|
||||
};
|
||||
AttrArgs::Eq { eq_span: lower_span(*eq_span), expr: lit }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,79 @@
|
|||
//! Functions and types dealing with attributes and meta items.
|
||||
//! Centralized logic for parsing and attributes.
|
||||
//!
|
||||
//! FIXME(Centril): For now being, much of the logic is still in `rustc_ast::attr`.
|
||||
//! The goal is to move the definition of `MetaItem` and things that don't need to be in `syntax`
|
||||
//! to this crate.
|
||||
//! Part of a series of crates:
|
||||
//! - rustc_attr_data_structures: contains types that the parsers parse into
|
||||
//! - rustc_attr_parsing: this crate
|
||||
//! - (in the future): rustc_attr_validation
|
||||
//!
|
||||
//! History: Check out [#131229](https://github.com/rust-lang/rust/issues/131229).
|
||||
//! There used to be only one definition of attributes in the compiler: `ast::Attribute`.
|
||||
//! These were then parsed or validated or both in places distributed all over the compiler.
|
||||
//! This was a mess...
|
||||
//!
|
||||
//! Attributes are markers on items.
|
||||
//! Many of them are actually attribute-like proc-macros, and are expanded to some other rust syntax.
|
||||
//! This could either be a user provided proc macro, or something compiler provided.
|
||||
//! `derive` is an example of one that the compiler provides.
|
||||
//! These are built-in, but they have a valid expansion to Rust tokens and are thus called "active".
|
||||
//! I personally like calling these *active* compiler-provided attributes, built-in *macros*,
|
||||
//! because they still expand, and this helps to differentiate them from built-in *attributes*.
|
||||
//! However, I'll be the first to admit that the naming here can be confusing.
|
||||
//!
|
||||
//! The alternative to active attributes, are inert attributes.
|
||||
//! These can occur in user code (proc-macro helper attributes).
|
||||
//! But what's important is, many built-in attributes are inert like this.
|
||||
//! There is nothing they expand to during the macro expansion process,
|
||||
//! sometimes because they literally cannot expand to something that is valid Rust.
|
||||
//! They are really just markers to guide the compilation process.
|
||||
//! An example is `#[inline(...)]` which changes how code for functions is generated.
|
||||
//!
|
||||
//! ```text
|
||||
//! Active Inert
|
||||
//! ┌──────────────────────┬──────────────────────┐
|
||||
//! │ (mostly in) │ these are parsed │
|
||||
//! │ rustc_builtin_macros │ here! │
|
||||
//! │ │ │
|
||||
//! │ │ │
|
||||
//! │ #[derive(...)] │ #[stable()] │
|
||||
//! Built-in │ #[cfg()] │ #[inline()] │
|
||||
//! │ #[cfg_attr()] │ #[repr()] │
|
||||
//! │ │ │
|
||||
//! │ │ │
|
||||
//! │ │ │
|
||||
//! ├──────────────────────┼──────────────────────┤
|
||||
//! │ │ │
|
||||
//! │ │ │
|
||||
//! │ │ `b` in │
|
||||
//! │ │ #[proc_macro_derive( │
|
||||
//! User created │ #[proc_macro_attr()] │ a, │
|
||||
//! │ │ attributes(b) │
|
||||
//! │ │ ] │
|
||||
//! │ │ │
|
||||
//! │ │ │
|
||||
//! │ │ │
|
||||
//! └──────────────────────┴──────────────────────┘
|
||||
//! ```
|
||||
//!
|
||||
//! In this crate, syntactical attributes (sequences of tokens that look like
|
||||
//! `#[something(something else)]`) are parsed into more semantic attributes, markers on items.
|
||||
//! Multiple syntactic attributes might influence a single semantic attribute. For example,
|
||||
//! `#[stable(...)]` and `#[unstable()]` cannot occur together, and both semantically define
|
||||
//! a "stability" of an item. So, the stability attribute has an
|
||||
//! [`AttributeParser`](attributes::AttributeParser) that recognizes both the `#[stable()]`
|
||||
//! and `#[unstable()]` syntactic attributes, and at the end produce a single [`AttributeKind::Stability`].
|
||||
//!
|
||||
//! As a rule of thumb, when a syntactical attribute can be applied more than once, they should be
|
||||
//! combined into a single semantic attribute. For example:
|
||||
//!
|
||||
//! ```
|
||||
//! #[repr(C)]
|
||||
//! #[repr(packed)]
|
||||
//! struct Meow {}
|
||||
//! ```
|
||||
//!
|
||||
//! should result in a single `AttributeKind::Repr` containing a list of repr annotations, in this
|
||||
//! case `C` and `packed`. This is equivalent to writing `#[repr(C, packed)]` in a single
|
||||
//! syntactical annotation.
|
||||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
|
@ -12,11 +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)
|
||||
}};
|
||||
}
|
||||
|
|
624
compiler/rustc_attr_parsing/src/parser.rs
Normal file
624
compiler/rustc_attr_parsing/src/parser.rs
Normal file
|
@ -0,0 +1,624 @@
|
|||
//! This is in essence an (improved) duplicate of `rustc_ast/attr/mod.rs`.
|
||||
//! That module is intended to be deleted in its entirety.
|
||||
//!
|
||||
//! FIXME(jdonszelmann): delete `rustc_ast/attr/mod.rs`
|
||||
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::iter::Peekable;
|
||||
|
||||
use rustc_ast::token::{self, Delimiter, Token};
|
||||
use rustc_ast::tokenstream::{TokenStreamIter, TokenTree};
|
||||
use rustc_ast::{AttrArgs, DelimArgs, Expr, ExprKind, LitKind, MetaItemLit, NormalAttr, Path};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_errors::DiagCtxtHandle;
|
||||
use rustc_hir::{self as hir, AttrPath};
|
||||
use rustc_span::symbol::{Ident, kw};
|
||||
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol};
|
||||
|
||||
pub struct SegmentIterator<'a> {
|
||||
offset: usize,
|
||||
path: &'a PathParser<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for SegmentIterator<'a> {
|
||||
type Item = &'a Ident;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.offset >= self.path.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let res = match self.path {
|
||||
PathParser::Ast(ast_path) => &ast_path.segments[self.offset].ident,
|
||||
PathParser::Attr(attr_path) => &attr_path.segments[self.offset],
|
||||
};
|
||||
|
||||
self.offset += 1;
|
||||
Some(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum PathParser<'a> {
|
||||
Ast(&'a Path),
|
||||
Attr(AttrPath),
|
||||
}
|
||||
|
||||
impl<'a> PathParser<'a> {
|
||||
pub fn get_attribute_path(&self) -> hir::AttrPath {
|
||||
AttrPath {
|
||||
segments: self.segments().copied().collect::<Vec<_>>().into_boxed_slice(),
|
||||
span: self.span(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn segments(&'a self) -> impl Iterator<Item = &'a Ident> {
|
||||
SegmentIterator { offset: 0, path: self }
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
PathParser::Ast(path) => path.span,
|
||||
PathParser::Attr(attr_path) => attr_path.span,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
match self {
|
||||
PathParser::Ast(path) => path.segments.len(),
|
||||
PathParser::Attr(attr_path) => attr_path.segments.len(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn segments_is(&self, segments: &[Symbol]) -> bool {
|
||||
self.len() == segments.len() && self.segments().zip(segments).all(|(a, b)| a.name == *b)
|
||||
}
|
||||
|
||||
pub fn word(&self) -> Option<Ident> {
|
||||
(self.len() == 1).then(|| **self.segments().next().as_ref().unwrap())
|
||||
}
|
||||
|
||||
pub fn word_or_empty(&self) -> Ident {
|
||||
self.word().unwrap_or_else(Ident::empty)
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem is some specific word.
|
||||
///
|
||||
/// See [`word`](Self::word) for examples of what a word is.
|
||||
pub fn word_is(&self, sym: Symbol) -> bool {
|
||||
self.word().map(|i| i.name == sym).unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PathParser<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
PathParser::Ast(path) => write!(f, "{}", pprust::path_to_string(path)),
|
||||
PathParser::Attr(attr_path) => write!(f, "{attr_path}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[must_use]
|
||||
pub enum ArgParser<'a> {
|
||||
NoArgs,
|
||||
List(MetaItemListParser<'a>),
|
||||
NameValue(NameValueParser),
|
||||
}
|
||||
|
||||
impl<'a> ArgParser<'a> {
|
||||
pub fn span(&self) -> Option<Span> {
|
||||
match self {
|
||||
Self::NoArgs => None,
|
||||
Self::List(l) => Some(l.span),
|
||||
Self::NameValue(n) => Some(n.value_span.with_lo(n.eq_span.lo())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_attr_args(value: &'a AttrArgs, dcx: DiagCtxtHandle<'a>) -> Self {
|
||||
match value {
|
||||
AttrArgs::Empty => Self::NoArgs,
|
||||
AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => {
|
||||
Self::List(MetaItemListParser::new(args, dcx))
|
||||
}
|
||||
AttrArgs::Delimited(args) => {
|
||||
Self::List(MetaItemListParser { sub_parsers: vec![], span: args.dspan.entire() })
|
||||
}
|
||||
AttrArgs::Eq { eq_span, expr } => Self::NameValue(NameValueParser {
|
||||
eq_span: *eq_span,
|
||||
value: expr_to_lit(dcx, &expr),
|
||||
value_span: expr.span,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem is a list
|
||||
///
|
||||
/// Some examples:
|
||||
///
|
||||
/// - `#[allow(clippy::complexity)]`: `(clippy::complexity)` is a list
|
||||
/// - `#[rustfmt::skip::macros(target_macro_name)]`: `(target_macro_name)` is a list
|
||||
pub fn list(&self) -> Option<&MetaItemListParser<'a>> {
|
||||
match self {
|
||||
Self::List(l) => Some(l),
|
||||
Self::NameValue(_) | Self::NoArgs => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem is a name-value pair.
|
||||
///
|
||||
/// Some examples:
|
||||
///
|
||||
/// - `#[clippy::cyclomatic_complexity = "100"]`: `clippy::cyclomatic_complexity = "100"` is a name value pair,
|
||||
/// where the name is a path (`clippy::cyclomatic_complexity`). You already checked the path
|
||||
/// to get an `ArgParser`, so this method will effectively only assert that the `= "100"` is
|
||||
/// there
|
||||
/// - `#[doc = "hello"]`: `doc = "hello` is also a name value pair
|
||||
pub fn name_value(&self) -> Option<&NameValueParser> {
|
||||
match self {
|
||||
Self::NameValue(n) => Some(n),
|
||||
Self::List(_) | Self::NoArgs => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Asserts that there are no arguments
|
||||
pub fn no_args(&self) -> bool {
|
||||
matches!(self, Self::NoArgs)
|
||||
}
|
||||
}
|
||||
|
||||
/// Inside lists, values could be either literals, or more deeply nested meta items.
|
||||
/// This enum represents that.
|
||||
///
|
||||
/// Choose which one you want using the provided methods.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum MetaItemOrLitParser<'a> {
|
||||
MetaItemParser(MetaItemParser<'a>),
|
||||
Lit(MetaItemLit),
|
||||
Err(Span, ErrorGuaranteed),
|
||||
}
|
||||
|
||||
impl<'a> MetaItemOrLitParser<'a> {
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
MetaItemOrLitParser::MetaItemParser(generic_meta_item_parser) => {
|
||||
generic_meta_item_parser.span()
|
||||
}
|
||||
MetaItemOrLitParser::Lit(meta_item_lit) => meta_item_lit.span,
|
||||
MetaItemOrLitParser::Err(span, _) => *span,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lit(&self) -> Option<&MetaItemLit> {
|
||||
match self {
|
||||
MetaItemOrLitParser::Lit(meta_item_lit) => Some(meta_item_lit),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn meta_item(&self) -> Option<&MetaItemParser<'a>> {
|
||||
match self {
|
||||
MetaItemOrLitParser::MetaItemParser(parser) => Some(parser),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Utility that deconstructs a MetaItem into usable parts.
|
||||
///
|
||||
/// MetaItems are syntactically extremely flexible, but specific attributes want to parse
|
||||
/// them in custom, more restricted ways. This can be done using this struct.
|
||||
///
|
||||
/// MetaItems consist of some path, and some args. The args could be empty. In other words:
|
||||
///
|
||||
/// - `name` -> args are empty
|
||||
/// - `name(...)` -> args are a [`list`](ArgParser::list), which is the bit between the parentheses
|
||||
/// - `name = value`-> arg is [`name_value`](ArgParser::name_value), where the argument is the
|
||||
/// `= value` part
|
||||
///
|
||||
/// The syntax of MetaItems can be found at <https://doc.rust-lang.org/reference/attributes.html>
|
||||
#[derive(Clone)]
|
||||
pub struct MetaItemParser<'a> {
|
||||
path: PathParser<'a>,
|
||||
args: ArgParser<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Debug for MetaItemParser<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("MetaItemParser")
|
||||
.field("path", &self.path)
|
||||
.field("args", &self.args)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MetaItemParser<'a> {
|
||||
/// Create a new parser from a [`NormalAttr`], which is stored inside of any
|
||||
/// [`ast::Attribute`](rustc_ast::Attribute)
|
||||
pub fn from_attr(attr: &'a NormalAttr, dcx: DiagCtxtHandle<'a>) -> Self {
|
||||
Self {
|
||||
path: PathParser::Ast(&attr.item.path),
|
||||
args: ArgParser::from_attr_args(&attr.item.args, dcx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MetaItemParser<'a> {
|
||||
pub fn span(&self) -> Span {
|
||||
if let Some(other) = self.args.span() {
|
||||
self.path.span().with_hi(other.hi())
|
||||
} else {
|
||||
self.path.span()
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets just the path, without the args.
|
||||
pub fn path_without_args(&self) -> PathParser<'a> {
|
||||
self.path.clone()
|
||||
}
|
||||
|
||||
/// Gets just the args parser, without caring about the path.
|
||||
pub fn args(&self) -> &ArgParser<'a> {
|
||||
&self.args
|
||||
}
|
||||
|
||||
pub fn deconstruct(&self) -> (PathParser<'a>, &ArgParser<'a>) {
|
||||
(self.path_without_args(), self.args())
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem starts with a path. Some examples:
|
||||
///
|
||||
/// - `#[rustfmt::skip]`: `rustfmt::skip` is a path
|
||||
/// - `#[allow(clippy::complexity)]`: `clippy::complexity` is a path
|
||||
/// - `#[inline]`: `inline` is a single segment path
|
||||
pub fn path(&self) -> (PathParser<'a>, &ArgParser<'a>) {
|
||||
self.deconstruct()
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem starts with a word, or single segment path.
|
||||
/// Doesn't return the args parser.
|
||||
///
|
||||
/// For examples. see [`Self::word`]
|
||||
pub fn word_without_args(&self) -> Option<Ident> {
|
||||
Some(self.word()?.0)
|
||||
}
|
||||
|
||||
/// Like [`word`](Self::word), but returns an empty symbol instead of None
|
||||
pub fn word_or_empty_without_args(&self) -> Ident {
|
||||
self.word_or_empty().0
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem starts with a word, or single segment path.
|
||||
///
|
||||
/// Some examples:
|
||||
/// - `#[inline]`: `inline` is a word
|
||||
/// - `#[rustfmt::skip]`: `rustfmt::skip` is a path,
|
||||
/// and not a word and should instead be parsed using [`path`](Self::path)
|
||||
pub fn word(&self) -> Option<(Ident, &ArgParser<'a>)> {
|
||||
let (path, args) = self.deconstruct();
|
||||
Some((path.word()?, args))
|
||||
}
|
||||
|
||||
/// Like [`word`](Self::word), but returns an empty symbol instead of None
|
||||
pub fn word_or_empty(&self) -> (Ident, &ArgParser<'a>) {
|
||||
let (path, args) = self.deconstruct();
|
||||
(path.word().unwrap_or(Ident::empty()), args)
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem starts with some specific word.
|
||||
///
|
||||
/// See [`word`](Self::word) for examples of what a word is.
|
||||
pub fn word_is(&self, sym: Symbol) -> Option<&ArgParser<'a>> {
|
||||
self.path_without_args().word_is(sym).then(|| self.args())
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem starts with some specific path.
|
||||
///
|
||||
/// See [`word`](Self::path) for examples of what a word is.
|
||||
pub fn path_is(&self, segments: &[Symbol]) -> Option<&ArgParser<'a>> {
|
||||
self.path_without_args().segments_is(segments).then(|| self.args())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NameValueParser {
|
||||
pub eq_span: Span,
|
||||
value: MetaItemLit,
|
||||
pub value_span: Span,
|
||||
}
|
||||
|
||||
impl Debug for NameValueParser {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("NameValueParser")
|
||||
.field("eq_span", &self.eq_span)
|
||||
.field("value", &self.value)
|
||||
.field("value_span", &self.value_span)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl NameValueParser {
|
||||
pub fn value_as_lit(&self) -> &MetaItemLit {
|
||||
&self.value
|
||||
}
|
||||
|
||||
pub fn value_as_str(&self) -> Option<Symbol> {
|
||||
self.value_as_lit().kind.str()
|
||||
}
|
||||
}
|
||||
|
||||
fn expr_to_lit(dcx: DiagCtxtHandle<'_>, expr: &Expr) -> MetaItemLit {
|
||||
// In valid code the value always ends up as a single literal. Otherwise, a dummy
|
||||
// literal suffices because the error is handled elsewhere.
|
||||
if let ExprKind::Lit(token_lit) = expr.kind
|
||||
&& let Ok(lit) = MetaItemLit::from_token_lit(token_lit, expr.span)
|
||||
{
|
||||
lit
|
||||
} else {
|
||||
let guar = dcx.has_errors().unwrap();
|
||||
MetaItemLit { symbol: kw::Empty, suffix: None, kind: LitKind::Err(guar), span: DUMMY_SP }
|
||||
}
|
||||
}
|
||||
|
||||
struct MetaItemListParserContext<'a> {
|
||||
// the tokens inside the delimiters, so `#[some::attr(a b c)]` would have `a b c` inside
|
||||
inside_delimiters: Peekable<TokenStreamIter<'a>>,
|
||||
dcx: DiagCtxtHandle<'a>,
|
||||
}
|
||||
|
||||
impl<'a> MetaItemListParserContext<'a> {
|
||||
fn done(&mut self) -> bool {
|
||||
self.inside_delimiters.peek().is_none()
|
||||
}
|
||||
|
||||
fn next_path(&mut self) -> Option<AttrPath> {
|
||||
// FIXME: Share code with `parse_path`.
|
||||
let tt = self.inside_delimiters.next().map(|tt| TokenTree::uninterpolate(tt));
|
||||
|
||||
match tt.as_deref()? {
|
||||
&TokenTree::Token(
|
||||
Token { kind: ref kind @ (token::Ident(..) | token::PathSep), span },
|
||||
_,
|
||||
) => {
|
||||
// here we have either an ident or pathsep `::`.
|
||||
|
||||
let mut segments = if let &token::Ident(name, _) = kind {
|
||||
// when we lookahead another pathsep, more path's coming
|
||||
if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
|
||||
self.inside_delimiters.peek()
|
||||
{
|
||||
self.inside_delimiters.next();
|
||||
vec![Ident::new(name, span)]
|
||||
} else {
|
||||
// else we have a single identifier path, that's all
|
||||
return Some(AttrPath {
|
||||
segments: vec![Ident::new(name, span)].into_boxed_slice(),
|
||||
span,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// if `::` is all we get, we just got a path root
|
||||
vec![Ident::new(kw::PathRoot, span)]
|
||||
};
|
||||
|
||||
// one segment accepted. accept n more
|
||||
loop {
|
||||
// another ident?
|
||||
if let Some(&TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =
|
||||
self.inside_delimiters
|
||||
.next()
|
||||
.map(|tt| TokenTree::uninterpolate(tt))
|
||||
.as_deref()
|
||||
{
|
||||
segments.push(Ident::new(name, span));
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
// stop unless we see another `::`
|
||||
if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
|
||||
self.inside_delimiters.peek()
|
||||
{
|
||||
self.inside_delimiters.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let span = span.with_hi(segments.last().unwrap().span.hi());
|
||||
Some(AttrPath { segments: segments.into_boxed_slice(), span })
|
||||
}
|
||||
TokenTree::Token(Token { kind: token::OpenDelim(_) | token::CloseDelim(_), .. }, _) => {
|
||||
None
|
||||
}
|
||||
_ => {
|
||||
// malformed attributes can get here. We can't crash, but somewhere else should've
|
||||
// already warned for this.
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn value(&mut self) -> Option<MetaItemLit> {
|
||||
match self.inside_delimiters.next() {
|
||||
Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => {
|
||||
MetaItemListParserContext {
|
||||
inside_delimiters: inner_tokens.iter().peekable(),
|
||||
dcx: self.dcx,
|
||||
}
|
||||
.value()
|
||||
}
|
||||
Some(TokenTree::Token(token, _)) => MetaItemLit::from_token(token),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// parses one element on the inside of a list attribute like `#[my_attr( <insides> )]`
|
||||
///
|
||||
/// parses a path followed be either:
|
||||
/// 1. nothing (a word attr)
|
||||
/// 2. a parenthesized list
|
||||
/// 3. an equals sign and a literal (name-value)
|
||||
///
|
||||
/// Can also parse *just* a literal. This is for cases like as `#[my_attr("literal")]`
|
||||
/// where no path is given before the literal
|
||||
///
|
||||
/// Some exceptions too for interpolated attributes which are already pre-processed
|
||||
fn next(&mut self) -> Option<MetaItemOrLitParser<'a>> {
|
||||
// a list element is either a literal
|
||||
if let Some(TokenTree::Token(token, _)) = self.inside_delimiters.peek()
|
||||
&& let Some(lit) = MetaItemLit::from_token(token)
|
||||
{
|
||||
self.inside_delimiters.next();
|
||||
return Some(MetaItemOrLitParser::Lit(lit));
|
||||
}
|
||||
|
||||
// or a path.
|
||||
let path =
|
||||
if let Some(TokenTree::Token(Token { kind: token::Interpolated(nt), span, .. }, _)) =
|
||||
self.inside_delimiters.peek()
|
||||
{
|
||||
match &**nt {
|
||||
// or maybe a full nt meta including the path but we return immediately
|
||||
token::Nonterminal::NtMeta(item) => {
|
||||
self.inside_delimiters.next();
|
||||
|
||||
return Some(MetaItemOrLitParser::MetaItemParser(MetaItemParser {
|
||||
path: PathParser::Ast(&item.path),
|
||||
args: ArgParser::from_attr_args(&item.args, self.dcx),
|
||||
}));
|
||||
}
|
||||
// an already interpolated path from a macro expansion is a path, no need to parse
|
||||
// one from tokens
|
||||
token::Nonterminal::NtPath(path) => {
|
||||
self.inside_delimiters.next();
|
||||
|
||||
AttrPath::from_ast(path)
|
||||
}
|
||||
_ => {
|
||||
self.inside_delimiters.next();
|
||||
// we go into this path if an expr ended up in an attribute that
|
||||
// expansion did not turn into a literal. Say, `#[repr(align(macro!()))]`
|
||||
// where the macro didn't expand to a literal. An error is already given
|
||||
// for this at this point, and then we do continue. This makes this path
|
||||
// reachable...
|
||||
let e = self.dcx.span_delayed_bug(
|
||||
*span,
|
||||
"expr in place where literal is expected (builtin attr parsing)",
|
||||
);
|
||||
|
||||
return Some(MetaItemOrLitParser::Err(*span, e));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.next_path()?
|
||||
};
|
||||
|
||||
// Paths can be followed by:
|
||||
// - `(more meta items)` (another list)
|
||||
// - `= lit` (a name-value)
|
||||
// - nothing
|
||||
Some(MetaItemOrLitParser::MetaItemParser(match self.inside_delimiters.peek() {
|
||||
Some(TokenTree::Delimited(dspan, _, Delimiter::Parenthesis, inner_tokens)) => {
|
||||
self.inside_delimiters.next();
|
||||
|
||||
MetaItemParser {
|
||||
path: PathParser::Attr(path),
|
||||
args: ArgParser::List(MetaItemListParser::new_tts(
|
||||
inner_tokens.iter(),
|
||||
dspan.entire(),
|
||||
self.dcx,
|
||||
)),
|
||||
}
|
||||
}
|
||||
Some(TokenTree::Delimited(_, ..)) => {
|
||||
self.inside_delimiters.next();
|
||||
// self.dcx.span_delayed_bug(span.entire(), "wrong delimiters");
|
||||
return None;
|
||||
}
|
||||
Some(TokenTree::Token(Token { kind: token::Eq, span }, _)) => {
|
||||
self.inside_delimiters.next();
|
||||
let value = self.value()?;
|
||||
MetaItemParser {
|
||||
path: PathParser::Attr(path),
|
||||
args: ArgParser::NameValue(NameValueParser {
|
||||
eq_span: *span,
|
||||
value_span: value.span,
|
||||
value,
|
||||
}),
|
||||
}
|
||||
}
|
||||
_ => MetaItemParser { path: PathParser::Attr(path), args: ArgParser::NoArgs },
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse(mut self, span: Span) -> MetaItemListParser<'a> {
|
||||
let mut sub_parsers = Vec::new();
|
||||
|
||||
while !self.done() {
|
||||
let Some(n) = self.next() else {
|
||||
continue;
|
||||
};
|
||||
sub_parsers.push(n);
|
||||
|
||||
match self.inside_delimiters.peek() {
|
||||
None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => {
|
||||
self.inside_delimiters.next();
|
||||
}
|
||||
Some(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
MetaItemListParser { sub_parsers, span }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MetaItemListParser<'a> {
|
||||
sub_parsers: Vec<MetaItemOrLitParser<'a>>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl<'a> MetaItemListParser<'a> {
|
||||
fn new(delim: &'a DelimArgs, dcx: DiagCtxtHandle<'a>) -> MetaItemListParser<'a> {
|
||||
MetaItemListParser::new_tts(delim.tokens.iter(), delim.dspan.entire(), dcx)
|
||||
}
|
||||
|
||||
fn new_tts(tts: TokenStreamIter<'a>, span: Span, dcx: DiagCtxtHandle<'a>) -> Self {
|
||||
MetaItemListParserContext { inside_delimiters: tts.peekable(), dcx }.parse(span)
|
||||
}
|
||||
|
||||
/// Lets you pick and choose as what you want to parse each element in the list
|
||||
pub fn mixed<'s>(&'s self) -> impl Iterator<Item = &'s MetaItemOrLitParser<'a>> + 's {
|
||||
self.sub_parsers.iter()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.sub_parsers.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Asserts that every item in the list is another list starting with a word.
|
||||
///
|
||||
/// See [`MetaItemParser::word`] for examples of words.
|
||||
pub fn all_word_list<'s>(&'s self) -> Option<Vec<(Ident, &'s ArgParser<'a>)>> {
|
||||
self.mixed().map(|i| i.meta_item()?.word()).collect()
|
||||
}
|
||||
|
||||
/// Asserts that every item in the list is another list starting with a full path.
|
||||
///
|
||||
/// See [`MetaItemParser::path`] for examples of paths.
|
||||
pub fn all_path_list<'s>(&'s self) -> Option<Vec<(PathParser<'a>, &'s ArgParser<'a>)>> {
|
||||
self.mixed().map(|i| Some(i.meta_item()?.path())).collect()
|
||||
}
|
||||
|
||||
/// Returns Some if the list contains only a single element.
|
||||
///
|
||||
/// Inside the Some is the parser to parse this single element.
|
||||
pub fn single(&self) -> Option<&MetaItemOrLitParser<'a>> {
|
||||
let mut iter = self.mixed();
|
||||
iter.next().filter(|_| iter.next().is_none())
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -94,7 +94,7 @@ impl<'tcx> AssertModuleSource<'tcx> {
|
|||
other => {
|
||||
self.tcx
|
||||
.dcx()
|
||||
.emit_fatal(errors::UnknownReuseKind { span: attr.span, kind: other });
|
||||
.emit_fatal(errors::UnknownReuseKind { span: attr.span(), kind: other });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -102,7 +102,7 @@ impl<'tcx> AssertModuleSource<'tcx> {
|
|||
};
|
||||
|
||||
if !self.tcx.sess.opts.unstable_opts.query_dep_graph {
|
||||
self.tcx.dcx().emit_fatal(errors::MissingQueryDepGraph { span: attr.span });
|
||||
self.tcx.dcx().emit_fatal(errors::MissingQueryDepGraph { span: attr.span() });
|
||||
}
|
||||
|
||||
if !self.check_config(attr) {
|
||||
|
@ -115,7 +115,7 @@ impl<'tcx> AssertModuleSource<'tcx> {
|
|||
|
||||
if !user_path.starts_with(&crate_name) {
|
||||
self.tcx.dcx().emit_fatal(errors::MalformedCguName {
|
||||
span: attr.span,
|
||||
span: attr.span(),
|
||||
user_path,
|
||||
crate_name,
|
||||
});
|
||||
|
@ -145,7 +145,7 @@ impl<'tcx> AssertModuleSource<'tcx> {
|
|||
let cgu_names: Vec<&str> =
|
||||
self.available_cgus.items().map(|cgu| cgu.as_str()).into_sorted_stable_ord();
|
||||
self.tcx.dcx().emit_err(errors::NoModuleNamed {
|
||||
span: attr.span,
|
||||
span: attr.span(),
|
||||
user_path,
|
||||
cgu_name,
|
||||
cgu_names: cgu_names.join(", "),
|
||||
|
@ -155,7 +155,7 @@ impl<'tcx> AssertModuleSource<'tcx> {
|
|||
self.cgu_reuse_tracker.set_expectation(
|
||||
cgu_name,
|
||||
user_path,
|
||||
attr.span,
|
||||
attr.span(),
|
||||
expected_reuse,
|
||||
comp_kind,
|
||||
);
|
||||
|
@ -175,7 +175,7 @@ impl<'tcx> AssertModuleSource<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
self.tcx.dcx().emit_fatal(errors::NoField { span: attr.span, name });
|
||||
self.tcx.dcx().emit_fatal(errors::NoField { span: attr.span(), name });
|
||||
}
|
||||
|
||||
/// Scan for a `cfg="foo"` attribute and check whether we have a
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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};
|
||||
|
@ -104,12 +105,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
if let Fn | AssocFn | Variant | Ctor(..) = def_kind {
|
||||
Some(tcx.fn_sig(did))
|
||||
} else {
|
||||
tcx.dcx()
|
||||
.span_delayed_bug(attr.span, "this attribute can only be applied to functions");
|
||||
tcx.dcx().span_delayed_bug(
|
||||
attr.span(),
|
||||
"this attribute can only be applied to functions",
|
||||
);
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
@ -130,14 +145,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
if tcx.opt_item_name(did.to_def_id()).is_some() {
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
|
||||
mixed_export_name_no_mangle_lint_state.track_no_mangle(
|
||||
attr.span,
|
||||
attr.span(),
|
||||
tcx.local_def_id_to_hir_id(did),
|
||||
attr,
|
||||
);
|
||||
} else {
|
||||
tcx.dcx()
|
||||
.struct_span_err(
|
||||
attr.span,
|
||||
attr.span(),
|
||||
format!(
|
||||
"`#[no_mangle]` cannot be used on {} {} as it has no name",
|
||||
tcx.def_descr_article(did.to_def_id()),
|
||||
|
@ -158,7 +173,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
feature_err(
|
||||
&tcx.sess,
|
||||
sym::used_with_arg,
|
||||
attr.span,
|
||||
attr.span(),
|
||||
"`#[used(linker)]` is currently unstable",
|
||||
)
|
||||
.emit();
|
||||
|
@ -170,7 +185,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
feature_err(
|
||||
&tcx.sess,
|
||||
sym::used_with_arg,
|
||||
attr.span,
|
||||
attr.span(),
|
||||
"`#[used(compiler)]` is currently unstable",
|
||||
)
|
||||
.emit();
|
||||
|
@ -178,7 +193,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
|
||||
}
|
||||
Some(_) => {
|
||||
tcx.dcx().emit_err(errors::ExpectedUsedSymbol { span: attr.span });
|
||||
tcx.dcx().emit_err(errors::ExpectedUsedSymbol { span: attr.span() });
|
||||
}
|
||||
None => {
|
||||
// Unfortunately, unconditionally using `llvm.used` causes
|
||||
|
@ -223,7 +238,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
{
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
attr.span,
|
||||
attr.span(),
|
||||
E0737,
|
||||
"`#[track_caller]` requires Rust ABI"
|
||||
)
|
||||
|
@ -231,12 +246,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
}
|
||||
if is_closure
|
||||
&& !tcx.features().closure_track_caller()
|
||||
&& !attr.span.allows_unstable(sym::closure_track_caller)
|
||||
&& !attr.span().allows_unstable(sym::closure_track_caller)
|
||||
{
|
||||
feature_err(
|
||||
&tcx.sess,
|
||||
sym::closure_track_caller,
|
||||
attr.span,
|
||||
attr.span(),
|
||||
"`#[track_caller]` on closures is currently unstable",
|
||||
)
|
||||
.emit();
|
||||
|
@ -250,19 +265,19 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
// so it may not contain any null characters.
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
attr.span,
|
||||
attr.span(),
|
||||
E0648,
|
||||
"`export_name` may not contain null characters"
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
codegen_fn_attrs.export_name = Some(s);
|
||||
mixed_export_name_no_mangle_lint_state.track_export_name(attr.span);
|
||||
mixed_export_name_no_mangle_lint_state.track_export_name(attr.span());
|
||||
}
|
||||
}
|
||||
sym::target_feature => {
|
||||
let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else {
|
||||
tcx.dcx().span_delayed_bug(attr.span, "target_feature applied to non-fn");
|
||||
tcx.dcx().span_delayed_bug(attr.span(), "target_feature applied to non-fn");
|
||||
continue;
|
||||
};
|
||||
let safe_target_features =
|
||||
|
@ -292,7 +307,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
// `main`, `start`, and other functions that are not usually
|
||||
// allowed.
|
||||
} else {
|
||||
check_target_feature_trait_unsafe(tcx, did, attr.span);
|
||||
check_target_feature_trait_unsafe(tcx, did, attr.span());
|
||||
}
|
||||
}
|
||||
from_target_feature_attr(
|
||||
|
@ -310,7 +325,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
|
||||
if tcx.is_mutable_static(did.into()) {
|
||||
let mut diag = tcx.dcx().struct_span_err(
|
||||
attr.span,
|
||||
attr.span(),
|
||||
"extern mutable statics are not allowed with `#[linkage]`",
|
||||
);
|
||||
diag.note(
|
||||
|
@ -329,7 +344,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
if let Some(val) = attr.value_str() {
|
||||
if val.as_str().bytes().any(|b| b == 0) {
|
||||
let msg = format!("illegal null byte in link_section value: `{val}`");
|
||||
tcx.dcx().span_err(attr.span, msg);
|
||||
tcx.dcx().span_err(attr.span(), msg);
|
||||
} else {
|
||||
codegen_fn_attrs.link_section = Some(val);
|
||||
}
|
||||
|
@ -337,13 +352,13 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
}
|
||||
sym::link_name => codegen_fn_attrs.link_name = attr.value_str(),
|
||||
sym::link_ordinal => {
|
||||
link_ordinal_span = Some(attr.span);
|
||||
link_ordinal_span = Some(attr.span());
|
||||
if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {
|
||||
codegen_fn_attrs.link_ordinal = ordinal;
|
||||
}
|
||||
}
|
||||
sym::no_sanitize => {
|
||||
no_sanitize_span = Some(attr.span);
|
||||
no_sanitize_span = Some(attr.span());
|
||||
if let Some(list) = attr.meta_item_list() {
|
||||
for item in list.iter() {
|
||||
match item.name_or_empty() {
|
||||
|
@ -381,7 +396,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
{
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
attr.span,
|
||||
attr.span(),
|
||||
E0779,
|
||||
"target does not support `#[instruction_set]`"
|
||||
)
|
||||
|
@ -393,7 +408,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
_ => {
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
attr.span,
|
||||
attr.span(),
|
||||
E0779,
|
||||
"invalid instruction set specified",
|
||||
)
|
||||
|
@ -405,7 +420,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
[] => {
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
attr.span,
|
||||
attr.span(),
|
||||
E0778,
|
||||
"`#[instruction_set]` requires an argument"
|
||||
)
|
||||
|
@ -415,7 +430,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
_ => {
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
attr.span,
|
||||
attr.span(),
|
||||
E0779,
|
||||
"cannot specify more than one instruction set"
|
||||
)
|
||||
|
@ -424,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;
|
||||
|
@ -510,7 +504,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
}
|
||||
|
||||
if let (None, None) = (prefix, entry) {
|
||||
tcx.dcx().span_err(attr.span, "must specify at least one parameter");
|
||||
tcx.dcx().span_err(attr.span(), "must specify at least one parameter");
|
||||
}
|
||||
|
||||
Some(PatchableFunctionEntry::from_prefix_and_entry(
|
||||
|
@ -536,18 +530,19 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
let Some(ref items) = attr.meta_item_list() else {
|
||||
return ia;
|
||||
};
|
||||
inline_span = Some(attr.span());
|
||||
|
||||
inline_span = Some(attr.span);
|
||||
let [item] = &items[..] else {
|
||||
struct_span_code_err!(tcx.dcx(), attr.span, E0534, "expected one argument").emit();
|
||||
struct_span_code_err!(tcx.dcx(), attr.span(), E0534, "expected one argument").emit();
|
||||
return InlineAttr::None;
|
||||
};
|
||||
|
||||
if item.has_name(sym::always) {
|
||||
InlineAttr::Always
|
||||
} else if item.has_name(sym::never) {
|
||||
InlineAttr::Never
|
||||
} else {
|
||||
struct_span_code_err!(tcx.dcx(), item.span(), E0535, "invalid argument")
|
||||
struct_span_code_err!(tcx.dcx(), items[0].span(), E0535, "invalid argument")
|
||||
.with_help("valid inline arguments are `always` and `never`")
|
||||
.emit();
|
||||
|
||||
|
@ -560,9 +555,9 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
}
|
||||
|
||||
if attr.is_word() {
|
||||
InlineAttr::Force { attr_span: attr.span, reason: None }
|
||||
InlineAttr::Force { attr_span: attr.span(), reason: None }
|
||||
} else if let Some(val) = attr.value_str() {
|
||||
InlineAttr::Force { attr_span: attr.span, reason: Some(val) }
|
||||
InlineAttr::Force { attr_span: attr.span(), reason: Some(val) }
|
||||
} else {
|
||||
debug!("`rustc_force_inline` not checked by attribute validation");
|
||||
ia
|
||||
|
@ -582,16 +577,16 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
}
|
||||
let err = |sp, s| struct_span_code_err!(tcx.dcx(), sp, E0722, "{}", s).emit();
|
||||
if attr.is_word() {
|
||||
err(attr.span, "expected one argument");
|
||||
err(attr.span(), "expected one argument");
|
||||
return ia;
|
||||
}
|
||||
let Some(ref items) = attr.meta_item_list() else {
|
||||
return OptimizeAttr::Default;
|
||||
};
|
||||
|
||||
inline_span = Some(attr.span);
|
||||
inline_span = Some(attr.span());
|
||||
let [item] = &items[..] else {
|
||||
err(attr.span, "expected one argument");
|
||||
err(attr.span(), "expected one argument");
|
||||
return OptimizeAttr::Default;
|
||||
};
|
||||
if item.has_name(sym::size) {
|
||||
|
@ -703,7 +698,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
let span = tcx
|
||||
.get_attrs(did, sym::target_feature)
|
||||
.next()
|
||||
.map_or_else(|| tcx.def_span(did), |a| a.span);
|
||||
.map_or_else(|| tcx.def_span(did), |a| a.span());
|
||||
tcx.dcx()
|
||||
.create_err(errors::TargetFeatureDisableOrEnable {
|
||||
features,
|
||||
|
@ -752,7 +747,7 @@ fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Option<u16> {
|
|||
use rustc_ast::{LitIntType, LitKind, MetaItemLit};
|
||||
let meta_item_list = attr.meta_item_list()?;
|
||||
let [sole_meta_list] = &meta_item_list[..] else {
|
||||
tcx.dcx().emit_err(errors::InvalidLinkOrdinalNargs { span: attr.span });
|
||||
tcx.dcx().emit_err(errors::InvalidLinkOrdinalNargs { span: attr.span() });
|
||||
return None;
|
||||
};
|
||||
if let Some(MetaItemLit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) =
|
||||
|
@ -776,13 +771,13 @@ fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Option<u16> {
|
|||
} else {
|
||||
let msg = format!("ordinal value in `link_ordinal` is too large: `{ordinal}`");
|
||||
tcx.dcx()
|
||||
.struct_span_err(attr.span, msg)
|
||||
.struct_span_err(attr.span(), msg)
|
||||
.with_note("the value may not exceed `u16::MAX`")
|
||||
.emit();
|
||||
None
|
||||
}
|
||||
} else {
|
||||
tcx.dcx().emit_err(errors::InvalidLinkOrdinalFormat { span: attr.span });
|
||||
tcx.dcx().emit_err(errors::InvalidLinkOrdinalFormat { span: attr.span() });
|
||||
None
|
||||
}
|
||||
}
|
||||
|
@ -828,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(
|
||||
|
@ -837,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,
|
||||
},
|
||||
|
@ -869,7 +864,7 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
|
|||
_ => {
|
||||
//FIXME(ZuseZ4): Once we fixed our parser, we should also prohibit the two-attribute
|
||||
//branch above.
|
||||
span_bug!(attrs[1].span, "cg_ssa: rustc_autodiff should only exist once per source");
|
||||
span_bug!(attrs[1].span(), "cg_ssa: rustc_autodiff should only exist once per source");
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -881,12 +876,12 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
|
|||
}
|
||||
|
||||
let [mode, input_activities @ .., ret_activity] = &list[..] else {
|
||||
span_bug!(attr.span, "rustc_autodiff attribute must contain mode and activities");
|
||||
span_bug!(attr.span(), "rustc_autodiff attribute must contain mode and activities");
|
||||
};
|
||||
let mode = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = mode {
|
||||
p1.segments.first().unwrap().ident
|
||||
} else {
|
||||
span_bug!(attr.span, "rustc_autodiff attribute must contain mode");
|
||||
span_bug!(attr.span(), "rustc_autodiff attribute must contain mode");
|
||||
};
|
||||
|
||||
// parse mode
|
||||
|
@ -902,7 +897,7 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
|
|||
let ret_symbol = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = ret_activity {
|
||||
p1.segments.first().unwrap().ident
|
||||
} else {
|
||||
span_bug!(attr.span, "rustc_autodiff attribute must contain the return activity");
|
||||
span_bug!(attr.span(), "rustc_autodiff attribute must contain the return activity");
|
||||
};
|
||||
|
||||
// Then parse it into an actual DiffActivity
|
||||
|
@ -937,11 +932,11 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
|
|||
|
||||
for &input in &arg_activities {
|
||||
if !valid_input_activity(mode, input) {
|
||||
span_bug!(attr.span, "Invalid input activity {} for {} mode", input, mode);
|
||||
span_bug!(attr.span(), "Invalid input activity {} for {} mode", input, mode);
|
||||
}
|
||||
}
|
||||
if !valid_ret_activity(mode, ret_activity) {
|
||||
span_bug!(attr.span, "Invalid return activity {} for {} mode", ret_activity, mode);
|
||||
span_bug!(attr.span(), "Invalid return activity {} for {} mode", ret_activity, mode);
|
||||
}
|
||||
|
||||
Some(AutoDiffAttrs { mode, ret_activity, input_activity: arg_activities })
|
||||
|
|
|
@ -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".
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -9,6 +9,7 @@ odht = { version = "0.3.1", features = ["nightly"] }
|
|||
rustc_abi = { path = "../rustc_abi" }
|
||||
rustc_arena = { path = "../rustc_arena" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_attr_data_structures = { path = "../rustc_attr_data_structures" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_hashes = { path = "../rustc_hashes" }
|
||||
rustc_index = { path = "../rustc_index" }
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
// ignore-tidy-filelength
|
||||
use std::fmt;
|
||||
|
||||
use rustc_abi::ExternAbi;
|
||||
// ignore-tidy-filelength
|
||||
use rustc_ast::attr::AttributeExt;
|
||||
use rustc_ast::token::CommentKind;
|
||||
use rustc_ast::util::parser::{AssocOp, ExprPrecedence};
|
||||
use rustc_ast::{
|
||||
self as ast, AttrId, AttrStyle, DelimArgs, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece,
|
||||
IntTy, Label, LitIntType, LitKind, MetaItemInner, MetaItemLit, TraitObjectSyntax, UintTy,
|
||||
self as ast, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, LitIntType,
|
||||
LitKind, TraitObjectSyntax, UintTy, UnsafeBinderCastKind,
|
||||
};
|
||||
pub use rustc_ast::{
|
||||
BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity, ByRef, CaptureBy,
|
||||
ImplPolarity, IsAuto, Movability, Mutability, UnOp, UnsafeBinderCastKind,
|
||||
AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity,
|
||||
ByRef, CaptureBy, DelimArgs, ImplPolarity, IsAuto, MetaItemInner, MetaItemLit, Movability,
|
||||
Mutability, UnOp,
|
||||
};
|
||||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_data_structures::fingerprint::Fingerprint;
|
||||
use rustc_data_structures::sorted_map::SortedMap;
|
||||
use rustc_data_structures::tagged_ptr::TaggedRef;
|
||||
|
@ -1009,174 +1011,248 @@ pub enum AttrArgs {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Encodable, Decodable)]
|
||||
pub enum AttrKind {
|
||||
/// A normal attribute.
|
||||
Normal(Box<AttrItem>),
|
||||
|
||||
/// A doc comment (e.g. `/// ...`, `//! ...`, `/** ... */`, `/*! ... */`).
|
||||
/// Doc attributes (e.g. `#[doc="..."]`) are represented with the `Normal`
|
||||
/// variant (which is much less compact and thus more expensive).
|
||||
DocComment(CommentKind, Symbol),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable)]
|
||||
pub struct AttrPath {
|
||||
pub segments: Box<[Ident]>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl AttrPath {
|
||||
pub fn from_ast(path: &ast::Path) -> Self {
|
||||
AttrPath {
|
||||
segments: path.segments.iter().map(|i| i.ident).collect::<Vec<_>>().into_boxed_slice(),
|
||||
span: path.span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for AttrPath {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.segments.iter().map(|i| i.to_string()).collect::<Vec<_>>().join("::"))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable)]
|
||||
pub struct AttrItem {
|
||||
pub unsafety: Safety,
|
||||
// Not lowered to hir::Path because we have no NodeId to resolve to.
|
||||
pub path: AttrPath,
|
||||
pub args: AttrArgs,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Encodable, Decodable)]
|
||||
pub struct Attribute {
|
||||
pub kind: AttrKind,
|
||||
pub id: AttrId,
|
||||
pub id: HashIgnoredAttrId,
|
||||
/// Denotes if the attribute decorates the following construct (outer)
|
||||
/// or the construct this attribute is contained within (inner).
|
||||
pub style: AttrStyle,
|
||||
/// Span of the entire attribute
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
/// The derived implementation of [`HashStable_Generic`] on [`Attribute`]s shouldn't hash
|
||||
/// [`AttrId`]s. By wrapping them in this, we make sure we never do.
|
||||
#[derive(Copy, Debug, Encodable, Decodable, Clone)]
|
||||
pub struct HashIgnoredAttrId {
|
||||
pub attr_id: AttrId,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Encodable, Decodable, HashStable_Generic)]
|
||||
pub enum Attribute {
|
||||
/// A parsed built-in attribute.
|
||||
///
|
||||
/// Each attribute has a span connected to it. However, you must be somewhat careful using it.
|
||||
/// That's because sometimes we merge multiple attributes together, like when an item has
|
||||
/// multiple `repr` attributes. In this case the span might not be very useful.
|
||||
Parsed(AttributeKind),
|
||||
|
||||
/// An attribute that could not be parsed, out of a token-like representation.
|
||||
/// This is the case for custom tool attributes.
|
||||
Unparsed(Box<AttrItem>),
|
||||
}
|
||||
|
||||
impl Attribute {
|
||||
pub fn get_normal_item(&self) -> &AttrItem {
|
||||
match &self.kind {
|
||||
AttrKind::Normal(normal) => &normal,
|
||||
AttrKind::DocComment(..) => panic!("unexpected doc comment"),
|
||||
match &self {
|
||||
Attribute::Unparsed(normal) => &normal,
|
||||
_ => panic!("unexpected parsed attribute"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap_normal_item(self) -> AttrItem {
|
||||
match self.kind {
|
||||
AttrKind::Normal(normal) => *normal,
|
||||
AttrKind::DocComment(..) => panic!("unexpected doc comment"),
|
||||
match self {
|
||||
Attribute::Unparsed(normal) => *normal,
|
||||
_ => panic!("unexpected parsed attribute"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value_lit(&self) -> Option<&MetaItemLit> {
|
||||
match &self.kind {
|
||||
AttrKind::Normal(box AttrItem { args: AttrArgs::Eq { expr, .. }, .. }) => Some(expr),
|
||||
match &self {
|
||||
Attribute::Unparsed(n) => match n.as_ref() {
|
||||
AttrItem { args: AttrArgs::Eq { eq_span: _, expr }, .. } => Some(expr),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AttributeExt for Attribute {
|
||||
#[inline]
|
||||
fn id(&self) -> AttrId {
|
||||
self.id
|
||||
match &self {
|
||||
Attribute::Unparsed(u) => u.id.attr_id,
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn meta_item_list(&self) -> Option<ThinVec<ast::MetaItemInner>> {
|
||||
match &self.kind {
|
||||
AttrKind::Normal(box AttrItem { args: AttrArgs::Delimited(d), .. }) => {
|
||||
ast::MetaItemKind::list_from_tokens(d.tokens.clone())
|
||||
}
|
||||
match &self {
|
||||
Attribute::Unparsed(n) => match n.as_ref() {
|
||||
AttrItem { args: AttrArgs::Delimited(d), .. } => {
|
||||
ast::MetaItemKind::list_from_tokens(d.tokens.clone())
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_str(&self) -> Option<Symbol> {
|
||||
self.value_lit().and_then(|x| x.value_str())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_span(&self) -> Option<Span> {
|
||||
self.value_lit().map(|i| i.span)
|
||||
}
|
||||
|
||||
/// For a single-segment attribute, returns its name; otherwise, returns `None`.
|
||||
#[inline]
|
||||
fn ident(&self) -> Option<Ident> {
|
||||
match &self.kind {
|
||||
AttrKind::Normal(box AttrItem {
|
||||
path: AttrPath { segments: box [ident], .. }, ..
|
||||
}) => Some(*ident),
|
||||
match &self {
|
||||
Attribute::Unparsed(n) => {
|
||||
if let [ident] = n.path.segments.as_ref() {
|
||||
Some(*ident)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn path_matches(&self, name: &[Symbol]) -> bool {
|
||||
match &self.kind {
|
||||
AttrKind::Normal(n) => n.path.segments.iter().map(|segment| &segment.name).eq(name),
|
||||
AttrKind::DocComment(..) => false,
|
||||
match &self {
|
||||
Attribute::Unparsed(n) => {
|
||||
n.path.segments.len() == name.len()
|
||||
&& n.path.segments.iter().zip(name).all(|(s, n)| s.name == *n)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_doc_comment(&self) -> bool {
|
||||
matches!(self.kind, AttrKind::DocComment(..))
|
||||
matches!(self, Attribute::Parsed(AttributeKind::DocComment { .. }))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn span(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
|
||||
fn is_word(&self) -> bool {
|
||||
matches!(self.kind, AttrKind::Normal(box AttrItem { args: AttrArgs::Empty, .. }))
|
||||
}
|
||||
|
||||
fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> {
|
||||
match &self.kind {
|
||||
AttrKind::Normal(n) => Some(n.path.segments.iter().copied().collect()),
|
||||
AttrKind::DocComment(..) => None,
|
||||
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:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn doc_str(&self) -> Option<Symbol> {
|
||||
match &self.kind {
|
||||
AttrKind::DocComment(.., data) => Some(*data),
|
||||
AttrKind::Normal(_) if self.has_name(sym::doc) => self.value_str(),
|
||||
#[inline]
|
||||
fn is_word(&self) -> bool {
|
||||
match &self {
|
||||
Attribute::Unparsed(n) => {
|
||||
matches!(n.args, AttrArgs::Empty)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> {
|
||||
match &self {
|
||||
Attribute::Unparsed(n) => Some(n.path.segments.iter().copied().collect()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn doc_str(&self) -> Option<Symbol> {
|
||||
match &self {
|
||||
Attribute::Parsed(AttributeKind::DocComment { comment, .. }) => Some(*comment),
|
||||
Attribute::Unparsed(_) if self.has_name(sym::doc) => self.value_str(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
|
||||
match &self.kind {
|
||||
AttrKind::DocComment(kind, data) => Some((*data, *kind)),
|
||||
AttrKind::Normal(_) if self.name_or_empty() == sym::doc => {
|
||||
match &self {
|
||||
Attribute::Parsed(AttributeKind::DocComment { kind, comment, .. }) => {
|
||||
Some((*comment, *kind))
|
||||
}
|
||||
Attribute::Unparsed(_) if self.name_or_empty() == sym::doc => {
|
||||
self.value_str().map(|s| (s, CommentKind::Line))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn style(&self) -> AttrStyle {
|
||||
self.style
|
||||
match &self {
|
||||
Attribute::Unparsed(u) => u.style,
|
||||
Attribute::Parsed(AttributeKind::DocComment { style, .. }) => *style,
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(fn_delegation): use function delegation instead of manually forwarding
|
||||
impl Attribute {
|
||||
#[inline]
|
||||
pub fn id(&self) -> AttrId {
|
||||
AttributeExt::id(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn name_or_empty(&self) -> Symbol {
|
||||
AttributeExt::name_or_empty(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
|
||||
AttributeExt::meta_item_list(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn value_str(&self) -> Option<Symbol> {
|
||||
AttributeExt::value_str(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn value_span(&self) -> Option<Span> {
|
||||
AttributeExt::value_span(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ident(&self) -> Option<Ident> {
|
||||
AttributeExt::ident(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn path_matches(&self, name: &[Symbol]) -> bool {
|
||||
AttributeExt::path_matches(self, name)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_doc_comment(&self) -> bool {
|
||||
AttributeExt::is_doc_comment(self)
|
||||
}
|
||||
|
@ -1186,34 +1262,42 @@ impl Attribute {
|
|||
AttributeExt::has_name(self, name)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn span(&self) -> Span {
|
||||
AttributeExt::span(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_word(&self) -> bool {
|
||||
AttributeExt::is_word(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn path(&self) -> SmallVec<[Symbol; 1]> {
|
||||
AttributeExt::path(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> {
|
||||
AttributeExt::ident_path(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn doc_str(&self) -> Option<Symbol> {
|
||||
AttributeExt::doc_str(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_proc_macro_attr(&self) -> bool {
|
||||
AttributeExt::is_proc_macro_attr(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
|
||||
AttributeExt::doc_str_and_comment_kind(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn style(&self) -> AttrStyle {
|
||||
AttributeExt::style(self)
|
||||
}
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
|
||||
use rustc_span::def_id::DefPathHash;
|
||||
|
||||
use crate::HashIgnoredAttrId;
|
||||
use crate::hir::{
|
||||
Attribute, AttributeMap, BodyId, Crate, ForeignItemId, ImplItemId, ItemId, OwnerNodes,
|
||||
TraitItemId,
|
||||
AttributeMap, BodyId, Crate, ForeignItemId, ImplItemId, ItemId, OwnerNodes, TraitItemId,
|
||||
};
|
||||
use crate::hir_id::{HirId, ItemLocalId};
|
||||
|
||||
/// Requirements for a `StableHashingContext` to be used in this crate.
|
||||
/// This is a hack to allow using the `HashStable_Generic` derive macro
|
||||
/// instead of implementing everything in `rustc_middle`.
|
||||
pub trait HashStableContext: rustc_ast::HashStableContext + rustc_abi::HashStableContext {
|
||||
fn hash_attr(&mut self, _: &Attribute, hasher: &mut StableHasher);
|
||||
pub trait HashStableContext:
|
||||
rustc_attr_data_structures::HashStableContext
|
||||
+ rustc_ast::HashStableContext
|
||||
+ rustc_abi::HashStableContext
|
||||
{
|
||||
fn hash_attr_id(&mut self, id: &HashIgnoredAttrId, hasher: &mut StableHasher);
|
||||
}
|
||||
|
||||
impl<HirCtx: crate::HashStableContext> ToStableHashKey<HirCtx> for HirId {
|
||||
|
@ -114,8 +118,8 @@ impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for Crate<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for Attribute {
|
||||
impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for HashIgnoredAttrId {
|
||||
fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) {
|
||||
hcx.hash_attr(self, hasher)
|
||||
hcx.hash_attr_id(self, hasher)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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::*;
|
||||
|
@ -1114,7 +1116,7 @@ fn check_impl_items_against_trait<'tcx>(
|
|||
if let Some(missing_items) = must_implement_one_of {
|
||||
let attr_span = tcx
|
||||
.get_attr(trait_ref.def_id, sym::rustc_must_implement_one_of)
|
||||
.map(|attr| attr.span);
|
||||
.map(|attr| attr.span());
|
||||
|
||||
missing_items_must_implement_one_of_err(
|
||||
tcx,
|
||||
|
@ -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() {
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
attr.span,
|
||||
E0084,
|
||||
"unsupported representation for zero-variant enum"
|
||||
)
|
||||
.with_span_label(tcx.def_span(def_id), "zero-variant enum")
|
||||
.emit();
|
||||
}
|
||||
attr::find_attr!(
|
||||
tcx.get_all_attrs(def_id),
|
||||
AttributeKind::Repr(rs) => {
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
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);
|
||||
|
|
|
@ -99,7 +99,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
|
|||
}
|
||||
|
||||
for attr in tcx.get_attrs(main_def_id, sym::track_caller) {
|
||||
tcx.dcx().emit_err(errors::TrackCallerOnMain { span: attr.span, annotated: main_span });
|
||||
tcx.dcx().emit_err(errors::TrackCallerOnMain { span: attr.span(), annotated: main_span });
|
||||
error = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1202,7 +1202,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
|
|||
// and that they are all identifiers
|
||||
.and_then(|attr| match attr.meta_item_list() {
|
||||
Some(items) if items.len() < 2 => {
|
||||
tcx.dcx().emit_err(errors::MustImplementOneOfAttribute { span: attr.span });
|
||||
tcx.dcx().emit_err(errors::MustImplementOneOfAttribute { span: attr.span() });
|
||||
|
||||
None
|
||||
}
|
||||
|
@ -1214,7 +1214,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
|
|||
tcx.dcx().emit_err(errors::MustBeNameOfAssociatedFunction { span });
|
||||
})
|
||||
.ok()
|
||||
.zip(Some(attr.span)),
|
||||
.zip(Some(attr.span())),
|
||||
// Error is reported by `rustc_attr!`
|
||||
None => None,
|
||||
})
|
||||
|
|
|
@ -111,14 +111,14 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) {
|
|||
let trait_ref = tcx.impl_trait_ref(def_id).unwrap().instantiate_identity();
|
||||
if trait_ref.has_non_region_param() {
|
||||
tcx.dcx().span_err(
|
||||
attr.span,
|
||||
attr.span(),
|
||||
"`rustc_dump_vtable` must be applied to non-generic impl",
|
||||
);
|
||||
continue;
|
||||
}
|
||||
if !tcx.is_dyn_compatible(trait_ref.def_id) {
|
||||
tcx.dcx().span_err(
|
||||
attr.span,
|
||||
attr.span(),
|
||||
"`rustc_dump_vtable` must be applied to dyn-compatible trait",
|
||||
);
|
||||
continue;
|
||||
|
@ -127,7 +127,7 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) {
|
|||
.try_normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref)
|
||||
else {
|
||||
tcx.dcx().span_err(
|
||||
attr.span,
|
||||
attr.span(),
|
||||
"`rustc_dump_vtable` applied to impl header that cannot be normalized",
|
||||
);
|
||||
continue;
|
||||
|
@ -138,7 +138,7 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) {
|
|||
let ty = tcx.type_of(def_id).instantiate_identity();
|
||||
if ty.has_non_region_param() {
|
||||
tcx.dcx().span_err(
|
||||
attr.span,
|
||||
attr.span(),
|
||||
"`rustc_dump_vtable` must be applied to non-generic type",
|
||||
);
|
||||
continue;
|
||||
|
@ -147,13 +147,14 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) {
|
|||
tcx.try_normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), ty)
|
||||
else {
|
||||
tcx.dcx().span_err(
|
||||
attr.span,
|
||||
attr.span(),
|
||||
"`rustc_dump_vtable` applied to type alias that cannot be normalized",
|
||||
);
|
||||
continue;
|
||||
};
|
||||
let ty::Dynamic(data, _, _) = *ty.kind() else {
|
||||
tcx.dcx().span_err(attr.span, "`rustc_dump_vtable` to type alias of dyn type");
|
||||
tcx.dcx()
|
||||
.span_err(attr.span(), "`rustc_dump_vtable` to type alias of dyn type");
|
||||
continue;
|
||||
};
|
||||
if let Some(principal) = data.principal() {
|
||||
|
@ -166,7 +167,7 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) {
|
|||
}
|
||||
_ => {
|
||||
tcx.dcx().span_err(
|
||||
attr.span,
|
||||
attr.span(),
|
||||
"`rustc_dump_vtable` only applies to impl, or type alias of dyn type",
|
||||
);
|
||||
continue;
|
||||
|
|
|
@ -8,6 +8,7 @@ edition = "2024"
|
|||
rustc_abi = { path = "../rustc_abi" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
|
||||
rustc_attr_parsing = { path = "../rustc_attr_parsing" }
|
||||
rustc_hir = { path = "../rustc_hir" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
# tidy-alphabetical-end
|
||||
|
|
|
@ -11,11 +11,12 @@ use std::vec;
|
|||
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_ast::util::parser::{self, AssocOp, ExprPrecedence, Fixity};
|
||||
use rustc_ast::{DUMMY_NODE_ID, DelimArgs};
|
||||
use rustc_ast::{AttrStyle, DUMMY_NODE_ID, DelimArgs};
|
||||
use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent};
|
||||
use rustc_ast_pretty::pp::{self, Breaks};
|
||||
use rustc_ast_pretty::pprust::state::MacHeader;
|
||||
use rustc_ast_pretty::pprust::{Comments, PrintState};
|
||||
use rustc_attr_parsing::{AttributeKind, PrintAttribute};
|
||||
use rustc_hir::{
|
||||
BindingMode, ByRef, ConstArgKind, GenericArg, GenericBound, GenericParam, GenericParamKind,
|
||||
HirId, ImplicitSelfKind, LifetimeParamKind, Node, PatKind, PreciseCapturingArg, RangeEnd, Term,
|
||||
|
@ -80,65 +81,48 @@ impl<'a> State<'a> {
|
|||
(self.attrs)(id)
|
||||
}
|
||||
|
||||
fn print_inner_attributes(&mut self, attrs: &[hir::Attribute]) -> bool {
|
||||
self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, true)
|
||||
fn print_attrs_as_inner(&mut self, attrs: &[hir::Attribute]) {
|
||||
self.print_either_attributes(attrs, ast::AttrStyle::Inner)
|
||||
}
|
||||
|
||||
fn print_outer_attributes(&mut self, attrs: &[hir::Attribute]) -> bool {
|
||||
self.print_either_attributes(attrs, ast::AttrStyle::Outer, false, true)
|
||||
fn print_attrs_as_outer(&mut self, attrs: &[hir::Attribute]) {
|
||||
self.print_either_attributes(attrs, ast::AttrStyle::Outer)
|
||||
}
|
||||
|
||||
fn print_either_attributes(
|
||||
&mut self,
|
||||
attrs: &[hir::Attribute],
|
||||
kind: ast::AttrStyle,
|
||||
is_inline: bool,
|
||||
trailing_hardbreak: bool,
|
||||
) -> bool {
|
||||
let mut printed = false;
|
||||
fn print_either_attributes(&mut self, attrs: &[hir::Attribute], style: ast::AttrStyle) {
|
||||
if attrs.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
for attr in attrs {
|
||||
if attr.style == kind {
|
||||
self.print_attribute_inline(attr, is_inline);
|
||||
if is_inline {
|
||||
self.nbsp();
|
||||
}
|
||||
printed = true;
|
||||
}
|
||||
self.print_attribute_inline(attr, style);
|
||||
}
|
||||
if printed && trailing_hardbreak && !is_inline {
|
||||
self.hardbreak_if_not_bol();
|
||||
}
|
||||
printed
|
||||
self.hardbreak_if_not_bol();
|
||||
}
|
||||
|
||||
fn print_attribute_inline(&mut self, attr: &hir::Attribute, is_inline: bool) {
|
||||
if !is_inline {
|
||||
self.hardbreak_if_not_bol();
|
||||
}
|
||||
self.maybe_print_comment(attr.span.lo());
|
||||
match &attr.kind {
|
||||
hir::AttrKind::Normal(normal) => {
|
||||
match attr.style {
|
||||
fn print_attribute_inline(&mut self, attr: &hir::Attribute, style: AttrStyle) {
|
||||
match &attr {
|
||||
hir::Attribute::Unparsed(unparsed) => {
|
||||
self.maybe_print_comment(unparsed.span.lo());
|
||||
match style {
|
||||
ast::AttrStyle::Inner => self.word("#!["),
|
||||
ast::AttrStyle::Outer => self.word("#["),
|
||||
}
|
||||
if normal.unsafety == hir::Safety::Unsafe {
|
||||
self.word("unsafe(");
|
||||
}
|
||||
self.print_attr_item(&normal, attr.span);
|
||||
if normal.unsafety == hir::Safety::Unsafe {
|
||||
self.word(")");
|
||||
}
|
||||
self.print_attr_item(&unparsed, unparsed.span);
|
||||
self.word("]");
|
||||
}
|
||||
hir::AttrKind::DocComment(comment_kind, data) => {
|
||||
hir::Attribute::Parsed(AttributeKind::DocComment { style, kind, comment, .. }) => {
|
||||
self.word(rustc_ast_pretty::pprust::state::doc_comment_to_string(
|
||||
*comment_kind,
|
||||
attr.style,
|
||||
*data,
|
||||
*kind, *style, *comment,
|
||||
));
|
||||
self.hardbreak()
|
||||
}
|
||||
hir::Attribute::Parsed(pa) => {
|
||||
self.word("#[attr=\"");
|
||||
pa.print_attribute(self);
|
||||
self.word("\")]");
|
||||
self.hardbreak()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,7 +146,7 @@ impl<'a> State<'a> {
|
|||
false,
|
||||
None,
|
||||
*delim,
|
||||
tokens,
|
||||
&tokens,
|
||||
true,
|
||||
span,
|
||||
),
|
||||
|
@ -307,7 +291,7 @@ where
|
|||
}
|
||||
|
||||
pub fn attribute_to_string(ann: &dyn PpAnn, attr: &hir::Attribute) -> String {
|
||||
to_string(ann, |s| s.print_attribute_inline(attr, false))
|
||||
to_string(ann, |s| s.print_attribute_inline(attr, AttrStyle::Outer))
|
||||
}
|
||||
|
||||
pub fn ty_to_string(ann: &dyn PpAnn, ty: &hir::Ty<'_>) -> String {
|
||||
|
@ -370,7 +354,7 @@ impl<'a> State<'a> {
|
|||
}
|
||||
|
||||
fn print_mod(&mut self, _mod: &hir::Mod<'_>, attrs: &[hir::Attribute]) {
|
||||
self.print_inner_attributes(attrs);
|
||||
self.print_attrs_as_inner(attrs);
|
||||
for &item_id in _mod.item_ids {
|
||||
self.ann.nested(self, Nested::Item(item_id));
|
||||
}
|
||||
|
@ -487,7 +471,7 @@ impl<'a> State<'a> {
|
|||
fn print_foreign_item(&mut self, item: &hir::ForeignItem<'_>) {
|
||||
self.hardbreak_if_not_bol();
|
||||
self.maybe_print_comment(item.span.lo());
|
||||
self.print_outer_attributes(self.attrs(item.hir_id()));
|
||||
self.print_attrs_as_outer(self.attrs(item.hir_id()));
|
||||
match item.kind {
|
||||
hir::ForeignItemKind::Fn(sig, arg_names, generics) => {
|
||||
self.head("");
|
||||
|
@ -591,7 +575,7 @@ impl<'a> State<'a> {
|
|||
self.hardbreak_if_not_bol();
|
||||
self.maybe_print_comment(item.span.lo());
|
||||
let attrs = self.attrs(item.hir_id());
|
||||
self.print_outer_attributes(attrs);
|
||||
self.print_attrs_as_outer(attrs);
|
||||
self.ann.pre(self, AnnNode::Item(item));
|
||||
match item.kind {
|
||||
hir::ItemKind::ExternCrate(orig_name) => {
|
||||
|
@ -687,7 +671,7 @@ impl<'a> State<'a> {
|
|||
self.head("extern");
|
||||
self.word_nbsp(abi.to_string());
|
||||
self.bopen();
|
||||
self.print_inner_attributes(self.attrs(item.hir_id()));
|
||||
self.print_attrs_as_inner(self.attrs(item.hir_id()));
|
||||
for item in items {
|
||||
self.ann.nested(self, Nested::ForeignItem(item.id));
|
||||
}
|
||||
|
@ -755,7 +739,7 @@ impl<'a> State<'a> {
|
|||
|
||||
self.space();
|
||||
self.bopen();
|
||||
self.print_inner_attributes(attrs);
|
||||
self.print_attrs_as_inner(attrs);
|
||||
for impl_item in items {
|
||||
self.ann.nested(self, Nested::ImplItem(impl_item.id));
|
||||
}
|
||||
|
@ -847,7 +831,7 @@ impl<'a> State<'a> {
|
|||
for v in variants {
|
||||
self.space_if_not_bol();
|
||||
self.maybe_print_comment(v.span.lo());
|
||||
self.print_outer_attributes(self.attrs(v.hir_id));
|
||||
self.print_attrs_as_outer(self.attrs(v.hir_id));
|
||||
self.ibox(INDENT_UNIT);
|
||||
self.print_variant(v);
|
||||
self.word(",");
|
||||
|
@ -880,7 +864,7 @@ impl<'a> State<'a> {
|
|||
self.popen();
|
||||
self.commasep(Inconsistent, struct_def.fields(), |s, field| {
|
||||
s.maybe_print_comment(field.span.lo());
|
||||
s.print_outer_attributes(s.attrs(field.hir_id));
|
||||
s.print_attrs_as_outer(s.attrs(field.hir_id));
|
||||
s.print_type(field.ty);
|
||||
});
|
||||
self.pclose();
|
||||
|
@ -907,7 +891,7 @@ impl<'a> State<'a> {
|
|||
for field in fields {
|
||||
self.hardbreak_if_not_bol();
|
||||
self.maybe_print_comment(field.span.lo());
|
||||
self.print_outer_attributes(self.attrs(field.hir_id));
|
||||
self.print_attrs_as_outer(self.attrs(field.hir_id));
|
||||
self.print_ident(field.ident);
|
||||
self.word_nbsp(":");
|
||||
self.print_type(field.ty);
|
||||
|
@ -943,7 +927,7 @@ impl<'a> State<'a> {
|
|||
self.ann.pre(self, AnnNode::SubItem(ti.hir_id()));
|
||||
self.hardbreak_if_not_bol();
|
||||
self.maybe_print_comment(ti.span.lo());
|
||||
self.print_outer_attributes(self.attrs(ti.hir_id()));
|
||||
self.print_attrs_as_outer(self.attrs(ti.hir_id()));
|
||||
match ti.kind {
|
||||
hir::TraitItemKind::Const(ty, default) => {
|
||||
self.print_associated_const(ti.ident, ti.generics, ty, default);
|
||||
|
@ -971,7 +955,7 @@ impl<'a> State<'a> {
|
|||
self.ann.pre(self, AnnNode::SubItem(ii.hir_id()));
|
||||
self.hardbreak_if_not_bol();
|
||||
self.maybe_print_comment(ii.span.lo());
|
||||
self.print_outer_attributes(self.attrs(ii.hir_id()));
|
||||
self.print_attrs_as_outer(self.attrs(ii.hir_id()));
|
||||
|
||||
match ii.kind {
|
||||
hir::ImplItemKind::Const(ty, expr) => {
|
||||
|
@ -1074,7 +1058,7 @@ impl<'a> State<'a> {
|
|||
self.ann.pre(self, AnnNode::Block(blk));
|
||||
self.bopen();
|
||||
|
||||
self.print_inner_attributes(attrs);
|
||||
self.print_attrs_as_inner(attrs);
|
||||
|
||||
for st in blk.stmts {
|
||||
self.print_stmt(st);
|
||||
|
@ -1264,7 +1248,7 @@ impl<'a> State<'a> {
|
|||
self.space();
|
||||
}
|
||||
self.cbox(INDENT_UNIT);
|
||||
self.print_outer_attributes(self.attrs(field.hir_id));
|
||||
self.print_attrs_as_outer(self.attrs(field.hir_id));
|
||||
if !field.is_shorthand {
|
||||
self.print_ident(field.ident);
|
||||
self.word_space(":");
|
||||
|
@ -1461,7 +1445,7 @@ impl<'a> State<'a> {
|
|||
|
||||
fn print_expr(&mut self, expr: &hir::Expr<'_>) {
|
||||
self.maybe_print_comment(expr.span.lo());
|
||||
self.print_outer_attributes(self.attrs(expr.hir_id));
|
||||
self.print_attrs_as_outer(self.attrs(expr.hir_id));
|
||||
self.ibox(INDENT_UNIT);
|
||||
self.ann.pre(self, AnnNode::Expr(expr));
|
||||
match expr.kind {
|
||||
|
@ -1677,8 +1661,8 @@ impl<'a> State<'a> {
|
|||
}
|
||||
hir::ExprKind::UnsafeBinderCast(kind, expr, ty) => {
|
||||
match kind {
|
||||
hir::UnsafeBinderCastKind::Wrap => self.word("wrap_binder!("),
|
||||
hir::UnsafeBinderCastKind::Unwrap => self.word("unwrap_binder!("),
|
||||
ast::UnsafeBinderCastKind::Wrap => self.word("wrap_binder!("),
|
||||
ast::UnsafeBinderCastKind::Unwrap => self.word("unwrap_binder!("),
|
||||
}
|
||||
self.print_expr(expr);
|
||||
if let Some(ty) = ty {
|
||||
|
@ -2073,7 +2057,7 @@ impl<'a> State<'a> {
|
|||
self.space();
|
||||
}
|
||||
self.cbox(INDENT_UNIT);
|
||||
self.print_outer_attributes(self.attrs(field.hir_id));
|
||||
self.print_attrs_as_outer(self.attrs(field.hir_id));
|
||||
if !field.is_shorthand {
|
||||
self.print_ident(field.ident);
|
||||
self.word_nbsp(":");
|
||||
|
@ -2083,7 +2067,7 @@ impl<'a> State<'a> {
|
|||
}
|
||||
|
||||
fn print_param(&mut self, arg: &hir::Param<'_>) {
|
||||
self.print_outer_attributes(self.attrs(arg.hir_id));
|
||||
self.print_attrs_as_outer(self.attrs(arg.hir_id));
|
||||
self.print_pat(arg.pat);
|
||||
}
|
||||
|
||||
|
@ -2118,7 +2102,7 @@ impl<'a> State<'a> {
|
|||
self.cbox(INDENT_UNIT);
|
||||
self.ann.pre(self, AnnNode::Arm(arm));
|
||||
self.ibox(0);
|
||||
self.print_outer_attributes(self.attrs(arm.hir_id));
|
||||
self.print_attrs_as_outer(self.attrs(arm.hir_id));
|
||||
self.print_pat(arm.pat);
|
||||
self.space();
|
||||
if let Some(ref g) = arm.guard {
|
||||
|
|
|
@ -1656,13 +1656,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
fn check_expr_unsafe_binder_cast(
|
||||
&self,
|
||||
span: Span,
|
||||
kind: hir::UnsafeBinderCastKind,
|
||||
kind: ast::UnsafeBinderCastKind,
|
||||
inner_expr: &'tcx hir::Expr<'tcx>,
|
||||
hir_ty: Option<&'tcx hir::Ty<'tcx>>,
|
||||
expected: Expectation<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
match kind {
|
||||
hir::UnsafeBinderCastKind::Wrap => {
|
||||
ast::UnsafeBinderCastKind::Wrap => {
|
||||
let ascribed_ty =
|
||||
hir_ty.map(|hir_ty| self.lower_ty_saving_user_provided_ty(hir_ty));
|
||||
let expected_ty = expected.only_has_type(self);
|
||||
|
@ -1706,7 +1706,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
binder_ty
|
||||
}
|
||||
hir::UnsafeBinderCastKind::Unwrap => {
|
||||
ast::UnsafeBinderCastKind::Unwrap => {
|
||||
let ascribed_ty =
|
||||
hir_ty.map(|hir_ty| self.lower_ty_saving_user_provided_ty(hir_ty));
|
||||
let hint_ty = ascribed_ty.unwrap_or_else(|| self.next_ty_var(inner_expr.span));
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -137,13 +137,13 @@ impl<'tcx> IfThisChanged<'tcx> {
|
|||
match DepNode::from_label_string(self.tcx, n.as_str(), def_path_hash) {
|
||||
Ok(n) => n,
|
||||
Err(()) => self.tcx.dcx().emit_fatal(errors::UnrecognizedDepNode {
|
||||
span: attr.span,
|
||||
span: attr.span(),
|
||||
name: n,
|
||||
}),
|
||||
}
|
||||
}
|
||||
};
|
||||
self.if_this_changed.push((attr.span, def_id.to_def_id(), dep_node));
|
||||
self.if_this_changed.push((attr.span(), def_id.to_def_id(), dep_node));
|
||||
} else if attr.has_name(sym::rustc_then_this_would_need) {
|
||||
let dep_node_interned = self.argument(attr);
|
||||
let dep_node = match dep_node_interned {
|
||||
|
@ -151,17 +151,17 @@ impl<'tcx> IfThisChanged<'tcx> {
|
|||
match DepNode::from_label_string(self.tcx, n.as_str(), def_path_hash) {
|
||||
Ok(n) => n,
|
||||
Err(()) => self.tcx.dcx().emit_fatal(errors::UnrecognizedDepNode {
|
||||
span: attr.span,
|
||||
span: attr.span(),
|
||||
name: n,
|
||||
}),
|
||||
}
|
||||
}
|
||||
None => {
|
||||
self.tcx.dcx().emit_fatal(errors::MissingDepNode { span: attr.span });
|
||||
self.tcx.dcx().emit_fatal(errors::MissingDepNode { span: attr.span() });
|
||||
}
|
||||
};
|
||||
self.then_this_would_need.push((
|
||||
attr.span,
|
||||
attr.span(),
|
||||
dep_node_interned.unwrap(),
|
||||
hir_id,
|
||||
dep_node,
|
||||
|
|
|
@ -199,7 +199,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
|
|||
let loaded_from_disk = self.loaded_from_disk(attr);
|
||||
for e in except.items().into_sorted_stable_ord() {
|
||||
if !auto.remove(e) {
|
||||
self.tcx.dcx().emit_fatal(errors::AssertionAuto { span: attr.span, name, e });
|
||||
self.tcx.dcx().emit_fatal(errors::AssertionAuto { span: attr.span(), name, e });
|
||||
}
|
||||
}
|
||||
Assertion { clean: auto, dirty: except, loaded_from_disk }
|
||||
|
@ -282,7 +282,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
|
|||
HirItem::Impl { .. } => ("ItemKind::Impl", LABELS_IMPL),
|
||||
|
||||
_ => self.tcx.dcx().emit_fatal(errors::UndefinedCleanDirtyItem {
|
||||
span: attr.span,
|
||||
span: attr.span(),
|
||||
kind: format!("{:?}", item.kind),
|
||||
}),
|
||||
}
|
||||
|
@ -298,7 +298,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
|
|||
ImplItemKind::Type(..) => ("NodeImplType", LABELS_CONST_IN_IMPL),
|
||||
},
|
||||
_ => self.tcx.dcx().emit_fatal(errors::UndefinedCleanDirty {
|
||||
span: attr.span,
|
||||
span: attr.span(),
|
||||
kind: format!("{node:?}"),
|
||||
}),
|
||||
};
|
||||
|
@ -375,7 +375,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
|
|||
let Some(assertion) = self.assertion_maybe(item_id, attr) else {
|
||||
continue;
|
||||
};
|
||||
self.checked_attrs.insert(attr.id);
|
||||
self.checked_attrs.insert(attr.id());
|
||||
for label in assertion.clean.items().into_sorted_stable_ord() {
|
||||
let dep_node = DepNode::from_label_string(self.tcx, label, def_path_hash).unwrap();
|
||||
self.assert_clean(item_span, dep_node);
|
||||
|
@ -405,12 +405,13 @@ fn check_config(tcx: TyCtxt<'_>, attr: &Attribute) -> bool {
|
|||
debug!("check_config: searching for cfg {:?}", value);
|
||||
cfg = Some(config.contains(&(value, None)));
|
||||
} else if !(item.has_name(EXCEPT) || item.has_name(LOADED_FROM_DISK)) {
|
||||
tcx.dcx().emit_err(errors::UnknownItem { span: attr.span, name: item.name_or_empty() });
|
||||
tcx.dcx()
|
||||
.emit_err(errors::UnknownItem { span: attr.span(), name: item.name_or_empty() });
|
||||
}
|
||||
}
|
||||
|
||||
match cfg {
|
||||
None => tcx.dcx().emit_fatal(errors::NoCfg { span: attr.span }),
|
||||
None => tcx.dcx().emit_fatal(errors::NoCfg { span: attr.span() }),
|
||||
Some(c) => c,
|
||||
}
|
||||
}
|
||||
|
@ -444,9 +445,9 @@ impl<'tcx> FindAllAttrs<'tcx> {
|
|||
|
||||
fn report_unchecked_attrs(&self, mut checked_attrs: FxHashSet<ast::AttrId>) {
|
||||
for attr in &self.found_attrs {
|
||||
if !checked_attrs.contains(&attr.id) {
|
||||
self.tcx.dcx().emit_err(errors::UncheckedClean { span: attr.span });
|
||||
checked_attrs.insert(attr.id);
|
||||
if !checked_attrs.contains(&attr.id()) {
|
||||
self.tcx.dcx().emit_err(errors::UncheckedClean { span: attr.span() });
|
||||
checked_attrs.insert(attr.id());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1011,7 +1011,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
|
|||
cx.emit_span_lint(
|
||||
NO_MANGLE_GENERIC_ITEMS,
|
||||
span,
|
||||
BuiltinNoMangleGeneric { suggestion: no_mangle_attr.span },
|
||||
BuiltinNoMangleGeneric { suggestion: no_mangle_attr.span() },
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
@ -1226,7 +1226,7 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller {
|
|||
{
|
||||
cx.emit_span_lint(
|
||||
UNGATED_ASYNC_FN_TRACK_CALLER,
|
||||
attr.span,
|
||||
attr.span(),
|
||||
BuiltinUngatedAsyncFnTrackCaller { label: span, session: &cx.tcx.sess },
|
||||
);
|
||||
}
|
||||
|
|
|
@ -38,7 +38,8 @@ 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;
|
||||
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"),
|
||||
|
|
|
@ -182,7 +182,7 @@ fn name_of_extern_decl(tcx: TyCtxt<'_>, fi: hir::OwnerId) -> SymbolName {
|
|||
// information, we could have codegen_fn_attrs also give span information back for
|
||||
// where the attribute was defined. However, until this is found to be a
|
||||
// bottleneck, this does just fine.
|
||||
(overridden_link_name, tcx.get_attr(fi, sym::link_name).unwrap().span)
|
||||
(overridden_link_name, tcx.get_attr(fi, sym::link_name).unwrap().span())
|
||||
})
|
||||
{
|
||||
SymbolName::Link(overridden_link_name, overridden_link_name_span)
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
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, AttrKind, GenericParamKind, PatExprKind, PatKind};
|
||||
use rustc_hir::{AttrArgs, AttrItem, Attribute, GenericParamKind, PatExprKind, PatKind};
|
||||
use rustc_middle::ty;
|
||||
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;
|
||||
|
@ -343,7 +344,7 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase {
|
|||
} else {
|
||||
ast::attr::find_by_name(cx.tcx.hir().attrs(hir::CRATE_HIR_ID), sym::crate_name)
|
||||
.and_then(|attr| {
|
||||
if let AttrKind::Normal(n) = &attr.kind
|
||||
if let Attribute::Unparsed(n) = attr
|
||||
&& let AttrItem { args: AttrArgs::Eq { eq_span: _, expr: lit }, .. } =
|
||||
n.as_ref()
|
||||
&& let ast::LitKind::Str(name, ..) = lit.kind
|
||||
|
|
|
@ -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)) => {
|
||||
Some(Level::Expect(LintExpectationId::Unstable { attr_id, lint_index: None }))
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ mod diagnostics;
|
|||
mod extension;
|
||||
mod hash_stable;
|
||||
mod lift;
|
||||
mod print_attribute;
|
||||
mod query;
|
||||
mod serialize;
|
||||
mod symbols;
|
||||
|
@ -175,3 +176,11 @@ decl_derive! {
|
|||
/// The error type is `u32`.
|
||||
try_from::try_from_u32
|
||||
}
|
||||
decl_derive! {
|
||||
[PrintAttribute] =>
|
||||
/// Derives `PrintAttribute` for `AttributeKind`.
|
||||
/// This macro is pretty specific to `rustc_attr_data_structures` and likely not that useful in
|
||||
/// other places. It's deriving something close to `Debug` without printing some extraenous
|
||||
/// things like spans.
|
||||
print_attribute::print_attribute
|
||||
}
|
||||
|
|
145
compiler/rustc_macros/src/print_attribute.rs
Normal file
145
compiler/rustc_macros/src/print_attribute.rs
Normal file
|
@ -0,0 +1,145 @@
|
|||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote, quote_spanned};
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{Data, Fields, Ident};
|
||||
use synstructure::Structure;
|
||||
|
||||
fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream, TokenStream) {
|
||||
let string_name = name.to_string();
|
||||
let mut disps = vec![quote! {let mut __printed_anything = false;}];
|
||||
|
||||
match fields {
|
||||
Fields::Named(fields_named) => {
|
||||
let mut field_names = Vec::new();
|
||||
|
||||
for field in &fields_named.named {
|
||||
let name = field.ident.as_ref().unwrap();
|
||||
let string_name = name.to_string();
|
||||
disps.push(quote! {
|
||||
if __printed_anything && #name.print_something() {
|
||||
__p.word_space(",");
|
||||
__printed_anything = true;
|
||||
}
|
||||
__p.word(#string_name);
|
||||
__p.word_space(":");
|
||||
#name.print_attribute(__p);
|
||||
});
|
||||
field_names.push(name);
|
||||
}
|
||||
|
||||
(
|
||||
quote! { {#(#field_names),*} },
|
||||
quote! {
|
||||
__p.word(#string_name);
|
||||
if true #(&& !#field_names.print_something())* {
|
||||
return;
|
||||
}
|
||||
|
||||
__p.word("{");
|
||||
#(#disps)*
|
||||
__p.word("}");
|
||||
},
|
||||
quote! { true },
|
||||
)
|
||||
}
|
||||
Fields::Unnamed(fields_unnamed) => {
|
||||
let mut field_names = Vec::new();
|
||||
|
||||
for idx in 0..fields_unnamed.unnamed.len() {
|
||||
let name = format_ident!("f{idx}");
|
||||
disps.push(quote! {
|
||||
if __printed_anything && #name.print_something() {
|
||||
__p.word_space(",");
|
||||
__printed_anything = true;
|
||||
}
|
||||
#name.print_attribute(__p);
|
||||
});
|
||||
field_names.push(name);
|
||||
}
|
||||
|
||||
(
|
||||
quote! { (#(#field_names),*) },
|
||||
quote! {
|
||||
__p.word(#string_name);
|
||||
|
||||
if true #(&& !#field_names.print_something())* {
|
||||
return;
|
||||
}
|
||||
|
||||
__p.word("(");
|
||||
#(#disps)*
|
||||
__p.word(")");
|
||||
},
|
||||
quote! { true },
|
||||
)
|
||||
}
|
||||
Fields::Unit => (quote! {}, quote! { __p.word(#string_name) }, quote! { true }),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print_attribute(input: Structure<'_>) -> TokenStream {
|
||||
let span_error = |span, message: &str| {
|
||||
quote_spanned! { span => const _: () = ::core::compile_error!(#message); }
|
||||
};
|
||||
|
||||
// Must be applied to an enum type.
|
||||
let (code, printed) = match &input.ast().data {
|
||||
Data::Enum(e) => {
|
||||
let (arms, printed) = e
|
||||
.variants
|
||||
.iter()
|
||||
.map(|x| {
|
||||
let ident = &x.ident;
|
||||
let (pat, code, printed) = print_fields(ident, &x.fields);
|
||||
|
||||
(
|
||||
quote! {
|
||||
Self::#ident #pat => {#code}
|
||||
},
|
||||
quote! {
|
||||
Self::#ident #pat => {#printed}
|
||||
},
|
||||
)
|
||||
})
|
||||
.unzip::<_, _, Vec<_>, Vec<_>>();
|
||||
|
||||
(
|
||||
quote! {
|
||||
match self {
|
||||
#(#arms)*
|
||||
}
|
||||
},
|
||||
quote! {
|
||||
match self {
|
||||
#(#printed)*
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
Data::Struct(s) => {
|
||||
let (pat, code, printed) = print_fields(&input.ast().ident, &s.fields);
|
||||
(
|
||||
quote! {
|
||||
let Self #pat = self;
|
||||
#code
|
||||
},
|
||||
quote! {
|
||||
let Self #pat = self;
|
||||
#printed
|
||||
},
|
||||
)
|
||||
}
|
||||
Data::Union(u) => {
|
||||
return span_error(u.union_token.span(), "can't derive PrintAttribute on unions");
|
||||
}
|
||||
};
|
||||
|
||||
#[allow(keyword_idents_2024)]
|
||||
input.gen_impl(quote! {
|
||||
#[allow(unused)]
|
||||
gen impl PrintAttribute for @Self {
|
||||
fn print_something(&self) -> bool { #printed }
|
||||
fn print_attribute(&self, __p: &mut rustc_ast_pretty::pp::Printer) { #code }
|
||||
}
|
||||
})
|
||||
}
|
|
@ -450,7 +450,7 @@ impl<'tcx> Collector<'tcx> {
|
|||
(name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule));
|
||||
}
|
||||
let Some((name, name_span)) = name else {
|
||||
sess.dcx().emit_err(errors::LinkRequiresName { span: m.span });
|
||||
sess.dcx().emit_err(errors::LinkRequiresName { span: m.span() });
|
||||
continue;
|
||||
};
|
||||
|
||||
|
@ -485,7 +485,7 @@ impl<'tcx> Collector<'tcx> {
|
|||
let link_ordinal_attr =
|
||||
self.tcx.get_attr(child_item, sym::link_ordinal).unwrap();
|
||||
sess.dcx().emit_err(errors::LinkOrdinalRawDylib {
|
||||
span: link_ordinal_attr.span,
|
||||
span: link_ordinal_attr.span(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -75,7 +75,7 @@ impl OverlapMode {
|
|||
tcx.hir().attrs(tcx.local_def_id_to_hir_id(local_def_id))
|
||||
})
|
||||
.find(|attr| attr.has_name(sym::rustc_strict_coherence))
|
||||
.map(|attr| attr.span);
|
||||
.map(|attr| attr.span());
|
||||
tcx.dcx().emit_err(StrictCoherenceNeedsNegativeCoherence {
|
||||
span: tcx.def_span(trait_id),
|
||||
attr_span,
|
||||
|
|
|
@ -1542,7 +1542,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
Bound::Included(a.get())
|
||||
} else {
|
||||
self.dcx().span_delayed_bug(
|
||||
attr.span,
|
||||
attr.span(),
|
||||
"invalid rustc_layout_scalar_valid_range attribute",
|
||||
);
|
||||
Bound::Unbounded
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ fn coverage_attr_on(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
|||
Some(_) | None => {
|
||||
// Other possibilities should have been rejected by `rustc_parse::validate_attr`.
|
||||
// Use `span_delayed_bug` to avoid an ICE in failing builds (#127880).
|
||||
tcx.dcx().span_delayed_bug(attr.span, "unexpected value of coverage attribute");
|
||||
tcx.dcx().span_delayed_bug(attr.span(), "unexpected value of coverage attribute");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -46,7 +46,7 @@ fn entry_fn(tcx: TyCtxt<'_>, (): ()) -> Option<(DefId, EntryFnType)> {
|
|||
|
||||
fn attr_span_by_symbol(ctxt: &EntryContext<'_>, id: ItemId, sym: Symbol) -> Option<Span> {
|
||||
let attrs = ctxt.tcx.hir().attrs(id.hir_id());
|
||||
attr::find_by_name(attrs, sym).map(|attr| attr.span)
|
||||
attr::find_by_name(attrs, sym).map(|attr| attr.span())
|
||||
}
|
||||
|
||||
fn check_and_search_item(id: ItemId, ctxt: &mut EntryContext<'_>) {
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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,62 +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,
|
||||
];
|
||||
|
||||
// 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,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
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,
|
||||
};
|
||||
|
||||
None
|
||||
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,
|
||||
}),
|
||||
};
|
||||
|
||||
Some((feature, feature_stability, span))
|
||||
}
|
||||
|
||||
fn collect_feature(&mut self, feature: Symbol, stability: FeatureStability, span: Span) {
|
||||
|
|
|
@ -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 });
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ edition = "2024"
|
|||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
measureme = "11"
|
||||
rustc_attr_data_structures = { path = "../rustc_attr_data_structures" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
rustc_hashes = { path = "../rustc_hashes" }
|
||||
|
|
|
@ -9,6 +9,7 @@ parking_lot = "0.12"
|
|||
rustc-rayon-core = { version = "0.5.0" }
|
||||
rustc_abi = { path = "../rustc_abi" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_attr_data_structures = { path = "../rustc_attr_data_structures" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
rustc_feature = { path = "../rustc_feature" }
|
||||
|
|
|
@ -129,3 +129,4 @@ impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> {
|
|||
}
|
||||
|
||||
impl<'a> rustc_session::HashStableContext for StableHashingContext<'a> {}
|
||||
impl<'a> rustc_attr_data_structures::HashStableContext for StableHashingContext<'a> {}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
//! from various crates in no particular order.
|
||||
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::{self as hir, HashIgnoredAttrId};
|
||||
use rustc_span::SourceFile;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
|
@ -23,6 +23,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for [hir::Attribute] {
|
|||
.iter()
|
||||
.filter(|attr| {
|
||||
!attr.is_doc_comment()
|
||||
// FIXME(jdonszelmann) have a better way to handle ignored attrs
|
||||
&& !attr.ident().is_some_and(|ident| hcx.is_ignored_attr(ident.name))
|
||||
})
|
||||
.collect();
|
||||
|
@ -35,19 +36,8 @@ impl<'a> HashStable<StableHashingContext<'a>> for [hir::Attribute] {
|
|||
}
|
||||
|
||||
impl<'ctx> rustc_hir::HashStableContext for StableHashingContext<'ctx> {
|
||||
fn hash_attr(&mut self, attr: &hir::Attribute, hasher: &mut StableHasher) {
|
||||
// Make sure that these have been filtered out.
|
||||
debug_assert!(!attr.ident().is_some_and(|ident| self.is_ignored_attr(ident.name)));
|
||||
debug_assert!(!attr.is_doc_comment());
|
||||
|
||||
let hir::Attribute { kind, id: _, style, span } = attr;
|
||||
if let hir::AttrKind::Normal(item) = kind {
|
||||
item.hash_stable(self, hasher);
|
||||
style.hash_stable(self, hasher);
|
||||
span.hash_stable(self, hasher);
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
fn hash_attr_id(&mut self, _id: &HashIgnoredAttrId, _hasher: &mut StableHasher) {
|
||||
/* we don't hash HashIgnoredAttrId, we ignore them */
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,24 @@ 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,
|
||||
std::convert::identity,
|
||||
);
|
||||
|
||||
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)
|
||||
|
|
|
@ -1806,7 +1806,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
&& !def_id.is_local()
|
||||
&& let Some(attr) = self.tcx.get_attr(def_id, sym::non_exhaustive)
|
||||
{
|
||||
non_exhaustive = Some(attr.span);
|
||||
non_exhaustive = Some(attr.span());
|
||||
} else if let Some(span) = ctor_fields_span {
|
||||
let label = errors::ConstructorPrivateIfAnyFieldPrivate { span };
|
||||
err.subdiagnostic(label);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -468,7 +468,7 @@ pub(crate) fn encode_ty<'tcx>(
|
|||
)]
|
||||
tcx.dcx()
|
||||
.struct_span_err(
|
||||
cfi_encoding.span,
|
||||
cfi_encoding.span(),
|
||||
format!("invalid `cfi_encoding` for `{:?}`", ty.kind()),
|
||||
)
|
||||
.emit();
|
||||
|
@ -519,7 +519,7 @@ pub(crate) fn encode_ty<'tcx>(
|
|||
)]
|
||||
tcx.dcx()
|
||||
.struct_span_err(
|
||||
cfi_encoding.span,
|
||||
cfi_encoding.span(),
|
||||
format!("invalid `cfi_encoding` for `{:?}`", ty.kind()),
|
||||
)
|
||||
.emit();
|
||||
|
|
|
@ -9,7 +9,7 @@ use std::cell::RefCell;
|
|||
use std::iter;
|
||||
|
||||
use rustc_abi::HasDataLayout;
|
||||
use rustc_hir::LangItem;
|
||||
use rustc_hir::{Attribute, LangItem};
|
||||
use rustc_middle::ty::layout::{
|
||||
FnAbiOf, FnAbiOfHelpers, HasTyCtxt, HasTypingEnv, LayoutOf, LayoutOfHelpers,
|
||||
};
|
||||
|
@ -243,7 +243,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_attrs_by_path(
|
||||
fn tool_attrs(
|
||||
&self,
|
||||
def_id: stable_mir::DefId,
|
||||
attr: &[stable_mir::Symbol],
|
||||
|
@ -253,30 +253,40 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
|
|||
let did = tables[def_id];
|
||||
let attr_name: Vec<_> = attr.iter().map(|seg| rustc_span::Symbol::intern(&seg)).collect();
|
||||
tcx.get_attrs_by_path(did, &attr_name)
|
||||
.map(|attribute| {
|
||||
let attr_str = rustc_hir_pretty::attribute_to_string(&tcx, attribute);
|
||||
let span = attribute.span;
|
||||
stable_mir::crate_def::Attribute::new(attr_str, span.stable(&mut *tables))
|
||||
.filter_map(|attribute| {
|
||||
if let Attribute::Unparsed(u) = attribute {
|
||||
let attr_str = rustc_hir_pretty::attribute_to_string(&tcx, attribute);
|
||||
Some(stable_mir::crate_def::Attribute::new(
|
||||
attr_str,
|
||||
u.span.stable(&mut *tables),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn get_all_attrs(&self, def_id: stable_mir::DefId) -> Vec<stable_mir::crate_def::Attribute> {
|
||||
fn all_tool_attrs(&self, def_id: stable_mir::DefId) -> Vec<stable_mir::crate_def::Attribute> {
|
||||
let mut tables = self.0.borrow_mut();
|
||||
let tcx = tables.tcx;
|
||||
let did = tables[def_id];
|
||||
let filter_fn =
|
||||
move |a: &&rustc_hir::Attribute| matches!(a.kind, rustc_hir::AttrKind::Normal(_));
|
||||
let attrs_iter = if let Some(did) = did.as_local() {
|
||||
tcx.hir().attrs(tcx.local_def_id_to_hir_id(did)).iter().filter(filter_fn)
|
||||
tcx.hir().attrs(tcx.local_def_id_to_hir_id(did)).iter()
|
||||
} else {
|
||||
tcx.attrs_for_def(did).iter().filter(filter_fn)
|
||||
tcx.attrs_for_def(did).iter()
|
||||
};
|
||||
attrs_iter
|
||||
.map(|attribute| {
|
||||
let attr_str = rustc_hir_pretty::attribute_to_string(&tcx, attribute);
|
||||
let span = attribute.span;
|
||||
stable_mir::crate_def::Attribute::new(attr_str, span.stable(&mut *tables))
|
||||
.filter_map(|attribute| {
|
||||
if let Attribute::Unparsed(u) = attribute {
|
||||
let attr_str = rustc_hir_pretty::attribute_to_string(&tcx, attribute);
|
||||
Some(stable_mir::crate_def::Attribute::new(
|
||||
attr_str,
|
||||
u.span.stable(&mut *tables),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -62,18 +62,18 @@ impl SymbolNamesTest<'_> {
|
|||
);
|
||||
let mangled = tcx.symbol_name(instance);
|
||||
tcx.dcx().emit_err(TestOutput {
|
||||
span: attr.span,
|
||||
span: attr.span(),
|
||||
kind: Kind::SymbolName,
|
||||
content: format!("{mangled}"),
|
||||
});
|
||||
if let Ok(demangling) = rustc_demangle::try_demangle(mangled.name) {
|
||||
tcx.dcx().emit_err(TestOutput {
|
||||
span: attr.span,
|
||||
span: attr.span(),
|
||||
kind: Kind::Demangling,
|
||||
content: format!("{demangling}"),
|
||||
});
|
||||
tcx.dcx().emit_err(TestOutput {
|
||||
span: attr.span,
|
||||
span: attr.span(),
|
||||
kind: Kind::DemanglingAlt,
|
||||
content: format!("{demangling:#}"),
|
||||
});
|
||||
|
@ -82,7 +82,7 @@ impl SymbolNamesTest<'_> {
|
|||
|
||||
for attr in tcx.get_attrs(def_id, DEF_PATH) {
|
||||
tcx.dcx().emit_err(TestOutput {
|
||||
span: attr.span,
|
||||
span: attr.span(),
|
||||
kind: Kind::DefPath,
|
||||
content: with_no_trimmed_paths!(tcx.def_path_str(def_id)),
|
||||
});
|
||||
|
|
|
@ -522,7 +522,8 @@ impl<T> Trait<T> for X {
|
|||
}
|
||||
}
|
||||
TypeError::TargetFeatureCast(def_id) => {
|
||||
let target_spans = tcx.get_attrs(def_id, sym::target_feature).map(|attr| attr.span);
|
||||
let target_spans =
|
||||
tcx.get_attrs(def_id, sym::target_feature).map(|attr| attr.span());
|
||||
diag.note(
|
||||
"functions with `#[target_feature]` can only be coerced to `unsafe` function pointers"
|
||||
);
|
||||
|
|
|
@ -6,7 +6,7 @@ use rustc_data_structures::fx::FxHashMap;
|
|||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{ErrorGuaranteed, struct_span_code_err};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::{AttrArgs, AttrKind, Attribute};
|
||||
use rustc_hir::{AttrArgs, Attribute};
|
||||
use rustc_macros::LintDiagnostic;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::print::PrintTraitRefExt as _;
|
||||
|
@ -622,7 +622,14 @@ impl<'tcx> OnUnimplementedDirective {
|
|||
item_def_id: DefId,
|
||||
) -> Result<Option<Self>, ErrorGuaranteed> {
|
||||
let result = if let Some(items) = attr.meta_item_list() {
|
||||
Self::parse(tcx, item_def_id, &items, attr.span, true, is_diagnostic_namespace_variant)
|
||||
Self::parse(
|
||||
tcx,
|
||||
item_def_id,
|
||||
&items,
|
||||
attr.span(),
|
||||
true,
|
||||
is_diagnostic_namespace_variant,
|
||||
)
|
||||
} else if let Some(value) = attr.value_str() {
|
||||
if !is_diagnostic_namespace_variant {
|
||||
Ok(Some(OnUnimplementedDirective {
|
||||
|
@ -633,7 +640,7 @@ impl<'tcx> OnUnimplementedDirective {
|
|||
tcx,
|
||||
item_def_id,
|
||||
value,
|
||||
attr.span,
|
||||
attr.span(),
|
||||
is_diagnostic_namespace_variant,
|
||||
)?),
|
||||
notes: Vec::new(),
|
||||
|
@ -659,14 +666,14 @@ impl<'tcx> OnUnimplementedDirective {
|
|||
Ok(None)
|
||||
}
|
||||
} else if is_diagnostic_namespace_variant {
|
||||
match &attr.kind {
|
||||
AttrKind::Normal(p) if !matches!(p.args, AttrArgs::Empty) => {
|
||||
match attr {
|
||||
Attribute::Unparsed(p) if !matches!(p.args, AttrArgs::Empty) => {
|
||||
if let Some(item_def_id) = item_def_id.as_local() {
|
||||
tcx.emit_node_span_lint(
|
||||
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
||||
tcx.local_def_id_to_hir_id(item_def_id),
|
||||
attr.span,
|
||||
MalformedOnUnimplementedAttrLint::new(attr.span),
|
||||
attr.span(),
|
||||
MalformedOnUnimplementedAttrLint::new(attr.span()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -675,7 +682,7 @@ impl<'tcx> OnUnimplementedDirective {
|
|||
tcx.emit_node_span_lint(
|
||||
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
||||
tcx.local_def_id_to_hir_id(item_def_id),
|
||||
attr.span,
|
||||
attr.span(),
|
||||
MissingOptionsForOnUnimplementedAttr,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -62,14 +62,17 @@ pub trait Context {
|
|||
/// Returns the name of given `DefId`
|
||||
fn def_name(&self, def_id: DefId, trimmed: bool) -> Symbol;
|
||||
|
||||
/// Return attributes with the given attribute name.
|
||||
/// Return registered tool attributes with the given attribute name.
|
||||
///
|
||||
/// Single segmented name like `#[inline]` is specified as `&["inline".to_string()]`.
|
||||
/// FIXME(jdonszelmann): may panic on non-tool attributes. After more attribute work, non-tool
|
||||
/// attributes will simply return an empty list.
|
||||
///
|
||||
/// Single segmented name like `#[clippy]` is specified as `&["clippy".to_string()]`.
|
||||
/// Multi-segmented name like `#[rustfmt::skip]` is specified as `&["rustfmt".to_string(), "skip".to_string()]`.
|
||||
fn get_attrs_by_path(&self, def_id: DefId, attr: &[Symbol]) -> Vec<Attribute>;
|
||||
fn tool_attrs(&self, def_id: DefId, attr: &[Symbol]) -> Vec<Attribute>;
|
||||
|
||||
/// Get all attributes of a definition.
|
||||
fn get_all_attrs(&self, def_id: DefId) -> Vec<Attribute>;
|
||||
/// Get all tool attributes of a definition.
|
||||
fn all_tool_attrs(&self, def_id: DefId) -> Vec<Attribute>;
|
||||
|
||||
/// Returns printable, human readable form of `Span`
|
||||
fn span_to_string(&self, span: Span) -> String;
|
||||
|
|
|
@ -53,19 +53,22 @@ pub trait CrateDef {
|
|||
with(|cx| cx.span_of_an_item(def_id))
|
||||
}
|
||||
|
||||
/// Return attributes with the given attribute name.
|
||||
/// Return registered tool attributes with the given attribute name.
|
||||
///
|
||||
/// Single segmented name like `#[inline]` is specified as `&["inline".to_string()]`.
|
||||
/// FIXME(jdonszelmann): may panic on non-tool attributes. After more attribute work, non-tool
|
||||
/// attributes will simply return an empty list.
|
||||
///
|
||||
/// Single segmented name like `#[clippy]` is specified as `&["clippy".to_string()]`.
|
||||
/// Multi-segmented name like `#[rustfmt::skip]` is specified as `&["rustfmt".to_string(), "skip".to_string()]`.
|
||||
fn attrs_by_path(&self, attr: &[Symbol]) -> Vec<Attribute> {
|
||||
fn tool_attrs(&self, attr: &[Symbol]) -> Vec<Attribute> {
|
||||
let def_id = self.def_id();
|
||||
with(|cx| cx.get_attrs_by_path(def_id, attr))
|
||||
with(|cx| cx.tool_attrs(def_id, attr))
|
||||
}
|
||||
|
||||
/// Return all attributes of this definition.
|
||||
fn all_attrs(&self) -> Vec<Attribute> {
|
||||
/// Return all tool attributes of this definition.
|
||||
fn all_tool_attrs(&self) -> Vec<Attribute> {
|
||||
let def_id = self.def_id();
|
||||
with(|cx| cx.get_all_attrs(def_id))
|
||||
with(|cx| cx.all_tool_attrs(def_id))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2737,13 +2737,13 @@ fn add_without_unwanted_attributes<'hir>(
|
|||
import_parent: Option<DefId>,
|
||||
) {
|
||||
for attr in new_attrs {
|
||||
if matches!(attr.kind, hir::AttrKind::DocComment(..)) {
|
||||
if attr.is_doc_comment() {
|
||||
attrs.push((Cow::Borrowed(attr), import_parent));
|
||||
continue;
|
||||
}
|
||||
let mut attr = attr.clone();
|
||||
match attr.kind {
|
||||
hir::AttrKind::Normal(ref mut normal) => {
|
||||
match attr {
|
||||
hir::Attribute::Unparsed(ref mut normal) => {
|
||||
if let [ident] = &*normal.path.segments {
|
||||
let ident = ident.name;
|
||||
if ident == sym::doc {
|
||||
|
@ -2755,7 +2755,11 @@ fn add_without_unwanted_attributes<'hir>(
|
|||
}
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
hir::Attribute::Parsed(..) => {
|
||||
if is_inline {
|
||||
attrs.push((Cow::Owned(attr), import_parent));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -265,7 +265,7 @@ impl ExternalCrate {
|
|||
let attr_value = attr.value_str().expect("syntax should already be validated");
|
||||
let Some(prim) = PrimitiveType::from_symbol(attr_value) else {
|
||||
span_bug!(
|
||||
attr.span,
|
||||
attr.span(),
|
||||
"primitive `{attr_value}` is not a member of `PrimitiveType`"
|
||||
);
|
||||
};
|
||||
|
|
|
@ -123,7 +123,7 @@ impl HirCollector<'_> {
|
|||
.iter()
|
||||
.find(|attr| attr.doc_str().is_some())
|
||||
.map(|attr| {
|
||||
attr.span.ctxt().outer_expn().expansion_cause().unwrap_or(attr.span)
|
||||
attr.span().ctxt().outer_expn().expansion_cause().unwrap_or(attr.span())
|
||||
})
|
||||
.unwrap_or(DUMMY_SP)
|
||||
};
|
||||
|
|
|
@ -30,7 +30,7 @@ pub type FxHashMap<K, V> = HashMap<K, V>; // re-export for use in src/librustdoc
|
|||
/// This integer is incremented with every breaking change to the API,
|
||||
/// and is returned along with the JSON blob as [`Crate::format_version`].
|
||||
/// Consuming code should assert that this value matches the format version(s) that it supports.
|
||||
pub const FORMAT_VERSION: u32 = 39;
|
||||
pub const FORMAT_VERSION: u32 = 40;
|
||||
|
||||
/// The root of the emitted JSON blob.
|
||||
///
|
||||
|
@ -120,7 +120,9 @@ pub struct Item {
|
|||
pub docs: Option<String>,
|
||||
/// This mapping resolves [intra-doc links](https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md) from the docstring to their IDs
|
||||
pub links: HashMap<String, Id>,
|
||||
/// Stringified versions of the attributes on this item (e.g. `"#[inline]"`)
|
||||
/// Stringified versions of parsed attributes on this item.
|
||||
/// Essentially debug printed (e.g. `#[inline]` becomes something similar to `#[attr="Inline(Hint)"]`).
|
||||
/// Equivalent to the hir pretty-printing of attributes.
|
||||
pub attrs: Vec<String>,
|
||||
/// Information about the item’s deprecation, if present.
|
||||
pub deprecation: Option<Deprecation>,
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 1d1d646c06a84c1aa53967b394b7f1218f85db82
|
||||
Subproject commit ce948f4616e3d4277e30c75c8bb01e094910df39
|
|
@ -20,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Att
|
|||
span_lint(
|
||||
cx,
|
||||
INLINE_ALWAYS,
|
||||
attr.span,
|
||||
attr.span(),
|
||||
format!("you have declared `#[inline(always)]` on `{name}`. This is usually a bad idea"),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use rustc_attr_parsing::{find_attr, AttributeKind, ReprAttr};
|
||||
use rustc_hir::Attribute;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{Span, sym};
|
||||
use rustc_span::Span;
|
||||
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs;
|
||||
|
@ -14,30 +15,21 @@ pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute],
|
|||
}
|
||||
|
||||
fn check_packed(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute]) {
|
||||
if let Some(items) = attrs.iter().find_map(|attr| {
|
||||
if attr.ident().is_some_and(|ident| matches!(ident.name, sym::repr)) {
|
||||
attr.meta_item_list()
|
||||
} else {
|
||||
None
|
||||
if let Some(reprs) = find_attr!(attrs, AttributeKind::Repr(r) => r) {
|
||||
let packed_span = reprs.iter().find(|(r, _)| matches!(r, ReprAttr::ReprPacked(..))).map(|(_, s)| *s);
|
||||
|
||||
if let Some(packed_span) = packed_span && !reprs.iter().any(|(x, _)| *x == ReprAttr::ReprC || *x == ReprAttr::ReprRust) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
REPR_PACKED_WITHOUT_ABI,
|
||||
item_span,
|
||||
"item uses `packed` representation without ABI-qualification",
|
||||
|diag| {
|
||||
diag.warn("unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI")
|
||||
.help("qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]`")
|
||||
.span_label(packed_span, "`packed` representation set here");
|
||||
},
|
||||
);
|
||||
}
|
||||
}) && let Some(packed) = items
|
||||
.iter()
|
||||
.find(|item| item.ident().is_some_and(|ident| matches!(ident.name, sym::packed)))
|
||||
&& !items.iter().any(|item| {
|
||||
item.ident()
|
||||
.is_some_and(|ident| matches!(ident.name, sym::C | sym::Rust))
|
||||
})
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
REPR_PACKED_WITHOUT_ABI,
|
||||
item_span,
|
||||
"item uses `packed` representation without ABI-qualification",
|
||||
|diag| {
|
||||
diag.warn("unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI")
|
||||
.help("qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]`")
|
||||
.span_label(packed.span(), "`packed` representation set here");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ pub(super) fn check(
|
|||
) {
|
||||
if cfg_attr.has_name(sym::clippy)
|
||||
&& let Some(ident) = behind_cfg_attr.ident()
|
||||
&& Level::from_symbol(ident.name, Some(attr.id)).is_some()
|
||||
&& Level::from_symbol(ident.name, || Some(attr.id)).is_some()
|
||||
&& let Some(items) = behind_cfg_attr.meta_item_list()
|
||||
{
|
||||
let nb_items = items.len();
|
||||
|
|
|
@ -17,7 +17,7 @@ pub(super) fn is_word(nmi: &MetaItemInner, expected: Symbol) -> bool {
|
|||
}
|
||||
|
||||
pub(super) fn is_lint_level(symbol: Symbol, attr_id: AttrId) -> bool {
|
||||
Level::from_symbol(symbol, Some(attr_id)).is_some()
|
||||
Level::from_symbol(symbol, || Some(attr_id)).is_some()
|
||||
}
|
||||
|
||||
pub(super) fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue