1
Fork 0

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:
bors 2025-02-24 23:07:24 +00:00
commit 7d8c6e781d
167 changed files with 3963 additions and 2292 deletions

View file

@ -3207,6 +3207,7 @@ dependencies = [
"rustc_abi", "rustc_abi",
"rustc_ast", "rustc_ast",
"rustc_ast_pretty", "rustc_ast_pretty",
"rustc_attr_parsing",
"rustc_data_structures", "rustc_data_structures",
"rustc_errors", "rustc_errors",
"rustc_feature", "rustc_feature",
@ -3215,6 +3216,7 @@ dependencies = [
"rustc_index", "rustc_index",
"rustc_macros", "rustc_macros",
"rustc_middle", "rustc_middle",
"rustc_parse",
"rustc_session", "rustc_session",
"rustc_span", "rustc_span",
"rustc_target", "rustc_target",
@ -3263,14 +3265,10 @@ dependencies = [
"rustc_ast", "rustc_ast",
"rustc_ast_pretty", "rustc_ast_pretty",
"rustc_data_structures", "rustc_data_structures",
"rustc_errors",
"rustc_feature",
"rustc_fluent_macro",
"rustc_lexer",
"rustc_macros", "rustc_macros",
"rustc_serialize", "rustc_serialize",
"rustc_session",
"rustc_span", "rustc_span",
"thin-vec",
] ]
[[package]] [[package]]
@ -3285,11 +3283,13 @@ dependencies = [
"rustc_errors", "rustc_errors",
"rustc_feature", "rustc_feature",
"rustc_fluent_macro", "rustc_fluent_macro",
"rustc_hir",
"rustc_lexer", "rustc_lexer",
"rustc_macros", "rustc_macros",
"rustc_serialize", "rustc_serialize",
"rustc_session", "rustc_session",
"rustc_span", "rustc_span",
"thin-vec",
] ]
[[package]] [[package]]
@ -3342,6 +3342,7 @@ dependencies = [
"rustc_expand", "rustc_expand",
"rustc_feature", "rustc_feature",
"rustc_fluent_macro", "rustc_fluent_macro",
"rustc_hir",
"rustc_index", "rustc_index",
"rustc_lexer", "rustc_lexer",
"rustc_lint_defs", "rustc_lint_defs",
@ -3598,6 +3599,7 @@ dependencies = [
"rustc_abi", "rustc_abi",
"rustc_ast", "rustc_ast",
"rustc_ast_pretty", "rustc_ast_pretty",
"rustc_attr_data_structures",
"rustc_data_structures", "rustc_data_structures",
"rustc_error_codes", "rustc_error_codes",
"rustc_error_messages", "rustc_error_messages",
@ -3632,6 +3634,7 @@ dependencies = [
"rustc_errors", "rustc_errors",
"rustc_feature", "rustc_feature",
"rustc_fluent_macro", "rustc_fluent_macro",
"rustc_hir",
"rustc_lexer", "rustc_lexer",
"rustc_lint_defs", "rustc_lint_defs",
"rustc_macros", "rustc_macros",
@ -3690,6 +3693,7 @@ dependencies = [
"rustc_abi", "rustc_abi",
"rustc_arena", "rustc_arena",
"rustc_ast", "rustc_ast",
"rustc_attr_data_structures",
"rustc_data_structures", "rustc_data_structures",
"rustc_hashes", "rustc_hashes",
"rustc_index", "rustc_index",
@ -3737,6 +3741,7 @@ dependencies = [
"rustc_abi", "rustc_abi",
"rustc_ast", "rustc_ast",
"rustc_ast_pretty", "rustc_ast_pretty",
"rustc_attr_parsing",
"rustc_hir", "rustc_hir",
"rustc_span", "rustc_span",
] ]
@ -4244,6 +4249,7 @@ name = "rustc_query_impl"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"measureme", "measureme",
"rustc_attr_data_structures",
"rustc_data_structures", "rustc_data_structures",
"rustc_errors", "rustc_errors",
"rustc_hashes", "rustc_hashes",
@ -4266,6 +4272,7 @@ dependencies = [
"rustc-rayon-core", "rustc-rayon-core",
"rustc_abi", "rustc_abi",
"rustc_ast", "rustc_ast",
"rustc_attr_data_structures",
"rustc_data_structures", "rustc_data_structures",
"rustc_errors", "rustc_errors",
"rustc_feature", "rustc_feature",
@ -4316,6 +4323,7 @@ version = "0.0.0"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"rustc_abi", "rustc_abi",
"rustc_ast",
"rustc_data_structures", "rustc_data_structures",
"rustc_hir", "rustc_hir",
"rustc_middle", "rustc_middle",
@ -4412,6 +4420,7 @@ dependencies = [
"punycode", "punycode",
"rustc-demangle", "rustc-demangle",
"rustc_abi", "rustc_abi",
"rustc_ast",
"rustc_data_structures", "rustc_data_structures",
"rustc_errors", "rustc_errors",
"rustc_hashes", "rustc_hashes",

View file

@ -11,6 +11,7 @@ doctest = false
rustc_abi = { path = "../rustc_abi" } rustc_abi = { path = "../rustc_abi" }
rustc_ast = { path = "../rustc_ast" } rustc_ast = { path = "../rustc_ast" }
rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_ast_pretty = { path = "../rustc_ast_pretty" }
rustc_attr_parsing = { path = "../rustc_attr_parsing" }
rustc_data_structures = { path = "../rustc_data_structures" } rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" } rustc_errors = { path = "../rustc_errors" }
rustc_feature = { path = "../rustc_feature" } rustc_feature = { path = "../rustc_feature" }
@ -19,6 +20,7 @@ rustc_hir = { path = "../rustc_hir" }
rustc_index = { path = "../rustc_index" } rustc_index = { path = "../rustc_index" }
rustc_macros = { path = "../rustc_macros" } rustc_macros = { path = "../rustc_macros" }
rustc_middle = { path = "../rustc_middle" } rustc_middle = { path = "../rustc_middle" }
rustc_parse = { path = "../rustc_parse" }
rustc_session = { path = "../rustc_session" } rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" } rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" } rustc_target = { path = "../rustc_target" }

View file

@ -108,7 +108,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}; };
let span = self.lower_span(l.span); let span = self.lower_span(l.span);
let source = hir::LocalSource::Normal; 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 }) self.arena.alloc(hir::LetStmt { hir_id, ty, pat, init, els, span, source })
} }

View file

@ -77,9 +77,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.attrs.insert( self.attrs.insert(
ex.hir_id.local_id, ex.hir_id.local_id,
&*self.arena.alloc_from_iter( &*self.arena.alloc_from_iter(
e.attrs self.lower_attrs_vec(&e.attrs, e.span)
.iter() .into_iter()
.map(|a| self.lower_attr(a))
.chain(old_attrs.iter().cloned()), .chain(old_attrs.iter().cloned()),
), ),
); );
@ -98,7 +97,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
} }
let expr_hir_id = self.lower_node_id(e.id); 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 { let kind = match &e.kind {
ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), 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 guard = arm.guard.as_ref().map(|cond| self.lower_expr(cond));
let hir_id = self.next_id(); let hir_id = self.next_id();
let span = self.lower_span(arm.span); 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 is_never_pattern = pat.is_never_pattern();
let body = if let Some(body) = &arm.body let body = if let Some(body) = &arm.body
&& !is_never_pattern && !is_never_pattern
@ -839,6 +838,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
style: AttrStyle::Outer, style: AttrStyle::Outer,
span: unstable_span, span: unstable_span,
}], }],
span,
); );
} }
} }
@ -1673,7 +1673,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn lower_expr_field(&mut self, f: &ExprField) -> hir::ExprField<'hir> { fn lower_expr_field(&mut self, f: &ExprField) -> hir::ExprField<'hir> {
let hir_id = self.lower_node_id(f.id); 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::ExprField {
hir_id, hir_id,
ident: self.lower_ident(f.ident), ident: self.lower_ident(f.ident),
@ -1936,7 +1936,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
// //
// Also, add the attributes to the outer returned expr node. // Also, add the attributes to the outer returned expr node.
let expr = self.expr_drop_temps_mut(for_span, match_expr); 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 expr
} }
@ -1993,7 +1993,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let val_ident = Ident::with_dummy_span(sym::val); let val_ident = Ident::with_dummy_span(sym::val);
let (val_pat, val_pat_nid) = self.pat_ident(span, val_ident); let (val_pat, val_pat_nid) = self.pat_ident(span, val_ident);
let val_expr = self.expr_ident(span, val_ident, val_pat_nid); 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); let continue_pat = self.pat_cf_continue(unstable_span, val_pat);
self.arm(continue_pat, val_expr) self.arm(continue_pat, val_expr)
}; };
@ -2024,7 +2024,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let ret_expr = self.checked_return(Some(from_residual_expr)); let ret_expr = self.checked_return(Some(from_residual_expr));
self.arena.alloc(self.expr(try_span, ret_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); let break_pat = self.pat_cf_break(try_span, residual_local);
self.arm(break_pat, ret_expr) self.arm(break_pat, ret_expr)

View file

@ -11,7 +11,7 @@ use rustc_index::{IndexSlice, IndexVec};
use rustc_middle::span_bug; use rustc_middle::span_bug;
use rustc_middle::ty::{ResolverAstLowering, TyCtxt}; use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
use rustc_span::edit_distance::find_best_match_for_name; 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 smallvec::{SmallVec, smallvec};
use thin_vec::ThinVec; use thin_vec::ThinVec;
use tracing::instrument; 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); debug_assert_eq!(self.resolver.node_id_to_def_id[&CRATE_NODE_ID], CRATE_DEF_ID);
self.with_lctx(CRATE_NODE_ID, |lctx| { self.with_lctx(CRATE_NODE_ID, |lctx| {
let module = lctx.lower_mod(&c.items, &c.spans); 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) hir::OwnerNode::Crate(module)
}) })
} }
@ -157,7 +158,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let mut ident = i.ident; let mut ident = i.ident;
let vis_span = self.lower_span(i.vis.span); let vis_span = self.lower_span(i.vis.span);
let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id); 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 kind = self.lower_item_kind(i.span, i.id, hir_id, &mut ident, attrs, vis_span, &i.kind);
let item = hir::Item { let item = hir::Item {
owner_id: hir_id.expect_owner(), 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> { 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 hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
let owner_id = hir_id.expect_owner(); 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 { let item = hir::ForeignItem {
owner_id, owner_id,
ident: self.lower_ident(i.ident), ident: self.lower_ident(i.ident),
@ -678,7 +679,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn lower_variant(&mut self, v: &Variant) -> hir::Variant<'hir> { fn lower_variant(&mut self, v: &Variant) -> hir::Variant<'hir> {
let hir_id = self.lower_node_id(v.id); 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::Variant {
hir_id, hir_id,
def_id: self.local_def_id(v.id), def_id: self.local_def_id(v.id),
@ -740,7 +741,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
) -> hir::FieldDef<'hir> { ) -> hir::FieldDef<'hir> {
let ty = self.lower_ty(&f.ty, ImplTraitContext::Disallowed(ImplTraitPosition::FieldTy)); let ty = self.lower_ty(&f.ty, ImplTraitContext::Disallowed(ImplTraitPosition::FieldTy));
let hir_id = self.lower_node_id(f.id); 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 { hir::FieldDef {
span: self.lower_span(f.span), span: self.lower_span(f.span),
hir_id, hir_id,
@ -759,7 +760,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn lower_trait_item(&mut self, i: &AssocItem) -> &'hir hir::TraitItem<'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 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 trait_item_def_id = hir_id.expect_owner();
let (generics, kind, has_default) = match &i.kind { let (generics, kind, has_default) = match &i.kind {
@ -895,7 +896,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let has_value = true; let has_value = true;
let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value); 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 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 { let (generics, kind) = match &i.kind {
AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => self.lower_generics( 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> { fn lower_param(&mut self, param: &Param) -> hir::Param<'hir> {
let hir_id = self.lower_node_id(param.id); let hir_id = self.lower_node_id(param.id);
self.lower_attrs(hir_id, &param.attrs); self.lower_attrs(hir_id, &param.attrs, param.span);
hir::Param { hir::Param {
hir_id, hir_id,
pat: self.lower_pat(&param.pat), pat: self.lower_pat(&param.pat),

View file

@ -45,6 +45,7 @@ use std::sync::Arc;
use rustc_ast::node_id::NodeMap; use rustc_ast::node_id::NodeMap;
use rustc_ast::{self as ast, *}; use rustc_ast::{self as ast, *};
use rustc_attr_parsing::{AttributeParser, OmitDoc};
use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::sorted_map::SortedMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
@ -60,7 +61,8 @@ use rustc_macros::extension;
use rustc_middle::span_bug; use rustc_middle::span_bug;
use rustc_middle::ty::{ResolverAstLowering, TyCtxt}; use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
use rustc_session::parse::{add_feature_diagnostics, feature_err}; 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 smallvec::{SmallVec, smallvec};
use thin_vec::ThinVec; use thin_vec::ThinVec;
use tracing::{debug, instrument, trace}; use tracing::{debug, instrument, trace};
@ -137,10 +139,13 @@ struct LoweringContext<'a, 'hir> {
allow_async_iterator: Arc<[Symbol]>, allow_async_iterator: Arc<[Symbol]>,
allow_for_await: Arc<[Symbol]>, allow_for_await: Arc<[Symbol]>,
allow_async_fn_traits: Arc<[Symbol]>, allow_async_fn_traits: Arc<[Symbol]>,
attribute_parser: AttributeParser<'hir>,
} }
impl<'a, 'hir> LoweringContext<'a, 'hir> { impl<'a, 'hir> LoweringContext<'a, 'hir> {
fn new(tcx: TyCtxt<'hir>, resolver: &'a mut ResolverAstLowering) -> Self { fn new(tcx: TyCtxt<'hir>, resolver: &'a mut ResolverAstLowering) -> Self {
let registered_tools = tcx.registered_tools(()).iter().map(|x| x.name).collect();
Self { Self {
// Pseudo-globals. // Pseudo-globals.
tcx, tcx,
@ -181,6 +186,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// FIXME(gen_blocks): how does `closure_track_caller`/`async_fn_track_caller` // FIXME(gen_blocks): how does `closure_track_caller`/`async_fn_track_caller`
// interact with `gen`/`async gen` blocks // interact with `gen`/`async gen` blocks
allow_async_iterator: [sym::gen_future, sym::async_iterator].into(), 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 None
} }
/// Obtains resolution for a `NodeId` with a single resolution.
fn get_partial_res(&self, id: NodeId) -> Option<PartialRes> { fn get_partial_res(&self, id: NodeId) -> Option<PartialRes> {
self.partial_res_map.get(&id).copied() self.partial_res_map.get(&id).copied()
} }
@ -855,45 +861,38 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
ret 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() { if attrs.is_empty() {
&[] &[]
} else { } else {
let lowered_attrs = self.lower_attrs_vec(attrs, self.lower_span(target_span));
debug_assert_eq!(id.owner, self.current_hir_id_owner); 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))); let ret = self.arena.alloc_from_iter(lowered_attrs);
debug_assert!(!ret.is_empty());
self.attrs.insert(id.local_id, ret); // this is possible if an item contained syntactical attribute,
ret // 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 { fn lower_attrs_vec(&self, attrs: &[Attribute], target_span: Span) -> Vec<hir::Attribute> {
// Note that we explicitly do not walk the path. Since we don't really self.attribute_parser
// lower attributes (we use the AST version) there is nowhere to keep .parse_attribute_list(attrs, target_span, OmitDoc::Lower, |s| self.lower_span(s))
// 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 alias_attrs(&mut self, id: HirId, target_id: HirId) { 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 { fn lower_delim_args(&self, args: &DelimArgs) -> DelimArgs {
DelimArgs { dspan: args.dspan, delim: args.delim, tokens: args.tokens.flattened() } 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 (name, kind) = self.lower_generic_param_kind(param, source);
let hir_id = self.lower_node_id(param.id); let hir_id = self.lower_node_id(param.id);
self.lower_attrs(hir_id, &param.attrs); self.lower_attrs(hir_id, &param.attrs, param.span());
hir::GenericParam { hir::GenericParam {
hir_id, hir_id,
def_id: self.local_def_id(param.id), def_id: self.local_def_id(param.id),

View file

@ -93,7 +93,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let fs = self.arena.alloc_from_iter(fields.iter().map(|f| { let fs = self.arena.alloc_from_iter(fields.iter().map(|f| {
let hir_id = self.lower_node_id(f.id); 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::PatField {
hir_id, hir_id,

View file

@ -207,8 +207,6 @@ ast_passes_precise_capturing_duplicated = duplicate `use<...>` precise capturing
ast_passes_precise_capturing_not_allowed_here = `use<...>` precise capturing syntax not allowed in {$loc} ast_passes_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 = ast_passes_static_without_body =
free static item without body free static item without body
.suggestion = provide a definition for the static .suggestion = provide a definition for the static

View file

@ -732,13 +732,6 @@ pub(crate) struct AssociatedSuggestion2 {
pub potential_assoc: Ident, 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)] #[derive(Diagnostic)]
#[diag(ast_passes_feature_on_non_nightly, code = E0554)] #[diag(ast_passes_feature_on_non_nightly, code = E0554)]
pub(crate) struct FeatureOnNonNightly { pub(crate) struct FeatureOnNonNightly {

View file

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

View file

@ -5,16 +5,12 @@ edition = "2024"
[dependencies] [dependencies]
# tidy-alphabetical-start # tidy-alphabetical-start
rustc_abi = { path = "../rustc_abi" } rustc_abi = {path = "../rustc_abi"}
rustc_ast = { path = "../rustc_ast" } rustc_ast = {path = "../rustc_ast"}
rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_ast_pretty = {path = "../rustc_ast_pretty"}
rustc_data_structures = { path = "../rustc_data_structures" } rustc_data_structures = {path = "../rustc_data_structures"}
rustc_errors = { path = "../rustc_errors" } rustc_macros = {path = "../rustc_macros"}
rustc_feature = { path = "../rustc_feature" } rustc_serialize = {path = "../rustc_serialize"}
rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_span = {path = "../rustc_span"}
rustc_lexer = { path = "../rustc_lexer" } thin-vec = "0.2.12"
rustc_macros = { path = "../rustc_macros" }
rustc_serialize = { path = "../rustc_serialize" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
# tidy-alphabetical-end # tidy-alphabetical-end

View file

@ -1,9 +1,12 @@
use rustc_abi::Align; use rustc_abi::Align;
use rustc_ast as ast; use rustc_ast::token::CommentKind;
use rustc_macros::{Decodable, Encodable, HashStable_Generic}; 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 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)] #[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
pub enum InlineAttr { 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 { pub enum DiagnosticAttribute {
// tidy-alphabetical-start // tidy-alphabetical-start
DoNotRecommend, DoNotRecommend,
@ -62,7 +65,7 @@ pub enum DiagnosticAttribute {
// tidy-alphabetical-end // tidy-alphabetical-end
} }
#[derive(PartialEq, Debug, Encodable, Decodable, Copy, Clone)] #[derive(PartialEq, Debug, Encodable, Decodable, Copy, Clone, HashStable_Generic, PrintAttribute)]
pub enum ReprAttr { pub enum ReprAttr {
ReprInt(IntType), ReprInt(IntType),
ReprRust, ReprRust,
@ -71,6 +74,8 @@ pub enum ReprAttr {
ReprSimd, ReprSimd,
ReprTransparent, ReprTransparent,
ReprAlign(Align), ReprAlign(Align),
// this one is just so we can emit a lint for it
ReprEmpty,
} }
pub use ReprAttr::*; pub use ReprAttr::*;
@ -80,13 +85,13 @@ pub enum TransparencyError {
} }
#[derive(Eq, PartialEq, Debug, Copy, Clone)] #[derive(Eq, PartialEq, Debug, Copy, Clone)]
#[derive(Encodable, Decodable)] #[derive(Encodable, Decodable, HashStable_Generic, PrintAttribute)]
pub enum IntType { pub enum IntType {
SignedInt(ast::IntTy), SignedInt(ast::IntTy),
UnsignedInt(ast::UintTy), UnsignedInt(ast::UintTy),
} }
#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)] #[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic, PrintAttribute)]
pub struct Deprecation { pub struct Deprecation {
pub since: DeprecatedSince, pub since: DeprecatedSince,
/// The note to issue a reason. /// The note to issue a reason.
@ -98,7 +103,7 @@ pub struct Deprecation {
} }
/// Release in which an API is deprecated. /// 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 { pub enum DeprecatedSince {
RustcVersion(RustcVersion), RustcVersion(RustcVersion),
/// Deprecated in the future ("to be determined"). /// Deprecated in the future ("to be determined").
@ -132,3 +137,61 @@ impl Deprecation {
matches!(self.since, DeprecatedSince::RustcVersion(_)) 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
}

View file

@ -10,7 +10,142 @@ mod attributes;
mod stability; mod stability;
mod version; mod version;
use std::num::NonZero;
pub use attributes::*; 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::*; pub use stability::*;
use thin_vec::ThinVec;
pub use version::*; 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);

View file

@ -1,9 +1,9 @@
use std::num::NonZero; 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 rustc_span::{Symbol, sym};
use crate::RustcVersion; use crate::{PrintAttribute, RustcVersion};
/// The version placeholder that recently stabilized features contain inside the /// The version placeholder that recently stabilized features contain inside the
/// `since` field of the `#[stable]` attribute. /// `since` field of the `#[stable]` attribute.
@ -21,7 +21,7 @@ pub const VERSION_PLACEHOLDER: &str = concat!("CURRENT_RUSTC_VERSIO", "N");
/// - `#[stable]` /// - `#[stable]`
/// - `#[unstable]` /// - `#[unstable]`
#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(HashStable_Generic)] #[derive(HashStable_Generic, PrintAttribute)]
pub struct Stability { pub struct Stability {
pub level: StabilityLevel, pub level: StabilityLevel,
pub feature: Symbol, pub feature: Symbol,
@ -43,7 +43,7 @@ impl Stability {
/// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes. /// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes.
#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(HashStable_Generic)] #[derive(HashStable_Generic, PrintAttribute)]
pub struct ConstStability { pub struct ConstStability {
pub level: StabilityLevel, pub level: StabilityLevel,
pub feature: Symbol, pub feature: Symbol,
@ -83,7 +83,7 @@ impl ConstStability {
/// Excludes `const_stable_indirect`. This is necessary because when `-Zforce-unstable-if-unmarked` /// 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 /// is set, we need to encode standalone `#[rustc_const_stable_indirect]` attributes
#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(HashStable_Generic)] #[derive(HashStable_Generic, PrintAttribute)]
pub struct PartialConstStability { pub struct PartialConstStability {
pub level: StabilityLevel, pub level: StabilityLevel,
pub feature: Symbol, pub feature: Symbol,
@ -103,7 +103,7 @@ impl PartialConstStability {
/// The available stability levels. /// The available stability levels.
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)] #[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
#[derive(HashStable_Generic)] #[derive(HashStable_Generic, PrintAttribute)]
pub enum StabilityLevel { pub enum StabilityLevel {
/// `#[unstable]` /// `#[unstable]`
Unstable { Unstable {
@ -145,7 +145,7 @@ pub enum StabilityLevel {
/// Rust release in which a feature is stabilized. /// Rust release in which a feature is stabilized.
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, PartialOrd, Ord, Hash)] #[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, PartialOrd, Ord, Hash)]
#[derive(HashStable_Generic)] #[derive(HashStable_Generic, PrintAttribute)]
pub enum StableSince { pub enum StableSince {
/// also stores the original symbol for printing /// also stores the original symbol for printing
Version(RustcVersion), Version(RustcVersion),
@ -171,7 +171,7 @@ impl StabilityLevel {
} }
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)] #[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
#[derive(HashStable_Generic)] #[derive(HashStable_Generic, PrintAttribute)]
pub enum UnstableReason { pub enum UnstableReason {
None, None,
Default, Default,
@ -180,7 +180,7 @@ pub enum UnstableReason {
/// Represents the `#[rustc_default_body_unstable]` attribute. /// Represents the `#[rustc_default_body_unstable]` attribute.
#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(HashStable_Generic)] #[derive(HashStable_Generic, PrintAttribute)]
pub struct DefaultBodyStability { pub struct DefaultBodyStability {
pub level: StabilityLevel, pub level: StabilityLevel,
pub feature: Symbol, pub feature: Symbol,

View file

@ -1,9 +1,13 @@
use std::fmt::{self, Display}; 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(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(HashStable_Generic)] #[derive(HashStable_Generic, PrintAttribute)]
pub struct RustcVersion { pub struct RustcVersion {
pub major: u16, pub major: u16,
pub minor: u16, pub minor: u16,

View file

@ -13,9 +13,11 @@ rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" } rustc_errors = { path = "../rustc_errors" }
rustc_feature = { path = "../rustc_feature" } rustc_feature = { path = "../rustc_feature" }
rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_hir = { path = "../rustc_hir" }
rustc_lexer = { path = "../rustc_lexer" } rustc_lexer = { path = "../rustc_lexer" }
rustc_macros = { path = "../rustc_macros" } rustc_macros = { path = "../rustc_macros" }
rustc_serialize = { path = "../rustc_serialize" } rustc_serialize = { path = "../rustc_serialize" }
rustc_session = { path = "../rustc_session" } rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" } rustc_span = { path = "../rustc_span" }
thin-vec = "0.2.12"
# tidy-alphabetical-end # tidy-alphabetical-end

View file

@ -6,6 +6,8 @@ attr_parsing_deprecated_item_suggestion =
.help = add `#![feature(deprecated_suggestion)]` to the crate root .help = add `#![feature(deprecated_suggestion)]` to the crate root
.note = see #94785 for more details .note = see #94785 for more details
attr_parsing_empty_confusables =
expected at least one confusable name
attr_parsing_expected_one_cfg_pattern = attr_parsing_expected_one_cfg_pattern =
expected 1 cfg-pattern expected 1 cfg-pattern
@ -21,8 +23,8 @@ attr_parsing_expects_feature_list =
attr_parsing_expects_features = attr_parsing_expects_features =
`{$name}` expects feature names `{$name}` expects feature names
attr_parsing_incorrect_meta_item = attr_parsing_incorrect_meta_item = expected a quoted string literal
incorrect meta item attr_parsing_incorrect_meta_item_suggestion = consider surrounding this with quotes
attr_parsing_incorrect_repr_format_align_one_arg = attr_parsing_incorrect_repr_format_align_one_arg =
incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses 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 = attr_parsing_non_ident_feature =
'feature' is not an identifier 'feature' is not an identifier
attr_parsing_repr_ident =
meta item in `repr` must be an identifier
attr_parsing_rustc_allowed_unstable_pairing = attr_parsing_rustc_allowed_unstable_pairing =
`rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute `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 = attr_parsing_rustc_promotable_pairing =
`rustc_promotable` attribute must be paired with either a `rustc_const_unstable` or a `rustc_const_stable` attribute `rustc_promotable` attribute must be paired with either a `rustc_const_unstable` or a `rustc_const_stable` attribute
attr_parsing_soft_no_args = attr_parsing_soft_no_args =
`soft` should not have any arguments `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 = attr_parsing_unknown_meta_item =
unknown meta item '{$item}' unknown meta item '{$item}'
.label = expected one of {$expected} .label = expected one of {$expected}
@ -107,6 +111,10 @@ attr_parsing_unknown_meta_item =
attr_parsing_unknown_version_literal = attr_parsing_unknown_version_literal =
unknown version literal format, assuming it refers to a future version 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 = attr_parsing_unstable_cfg_target_compact =
compact `cfg(target(..))` is experimental and subject to change compact `cfg(target(..))` is experimental and subject to change
@ -122,3 +130,8 @@ attr_parsing_unsupported_literal_generic =
unsupported literal unsupported literal
attr_parsing_unsupported_literal_suggestion = attr_parsing_unsupported_literal_suggestion =
consider removing the prefix consider removing the prefix
attr_parsing_unused_multiple =
multiple `{$name}` attributes
.suggestion = remove this attribute
.note = attribute also specified here

View file

@ -1,49 +1,67 @@
use rustc_ast::attr::{AttributeExt, filter_by_name}; use std::iter;
use rustc_session::Session;
use rustc_span::{Symbol, sym};
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; use crate::session_diagnostics;
pub fn allow_internal_unstable( pub(crate) struct AllowInternalUnstableParser;
sess: &Session, impl CombineAttributeParser for AllowInternalUnstableParser {
attrs: &[impl AttributeExt], const PATH: &'static [rustc_span::Symbol] = &[sym::allow_internal_unstable];
) -> impl Iterator<Item = Symbol> { type Item = (Symbol, Span);
allow_unstable(sess, attrs, sym::allow_internal_unstable) 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( pub(crate) struct AllowConstFnUnstableParser;
sess: &Session, impl CombineAttributeParser for AllowConstFnUnstableParser {
attrs: &[impl AttributeExt], const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_allow_const_fn_unstable];
) -> impl Iterator<Item = Symbol> { type Item = Symbol;
allow_unstable(sess, attrs, sym::rustc_allow_const_fn_unstable) 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( fn parse_unstable<'a>(
sess: &Session, cx: &AcceptContext<'_>,
attrs: &[impl AttributeExt], args: &'a ArgParser<'a>,
symbol: Symbol, symbol: Symbol,
) -> impl Iterator<Item = Symbol> { ) -> impl IntoIterator<Item = Symbol> {
let attrs = filter_by_name(attrs, symbol); let mut res = Vec::new();
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();
list.into_iter().filter_map(move |it| { let Some(list) = args.list() else {
let name = it.ident().map(|ident| ident.name); cx.emit_err(session_diagnostics::ExpectsFeatureList {
if name.is_none() { span: cx.attr_span,
sess.dcx().emit_err(session_diagnostics::ExpectsFeatures { name: symbol.to_ident_string(),
span: it.span(), });
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: symbol.to_ident_string(),
}); });
} }
name }
})
res
} }

View file

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

View file

@ -1,21 +1,58 @@
//! Parsing and validation of builtin attributes use rustc_attr_data_structures::AttributeKind;
use rustc_span::{Span, Symbol, sym};
use thin_vec::ThinVec;
use rustc_ast::MetaItemInner; use super::{AcceptMapping, AttributeParser};
use rustc_ast::attr::AttributeExt; use crate::context::FinalizeContext;
use rustc_span::Symbol; use crate::session_diagnostics;
/// Read the content of a `rustc_confusables` attribute, and return the list of candidate names. #[derive(Default)]
pub fn parse_confusables(attr: &impl AttributeExt) -> Option<Vec<Symbol>> { pub(crate) struct ConfusablesParser {
let metas = attr.meta_item_list()?; confusables: ThinVec<Symbol>,
first_span: Option<Span>,
let mut candidates = Vec::new(); }
for meta in metas { impl AttributeParser for ConfusablesParser {
let MetaItemInner::Lit(meta_lit) = meta else { const ATTRIBUTES: AcceptMapping<Self> = &[(&[sym::rustc_confusables], |this, cx, args| {
return None; let Some(list) = args.list() else {
}; // FIXME(jdonszelmann): error when not a list? Bring validation code here.
candidates.push(meta_lit.symbol); // NOTE: currently subsequent attributes are silently ignored using
} // tcx.get_attr().
return;
Some(candidates) };
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(),
})
}
} }

View file

@ -1,121 +1,122 @@
//! Parsing and validation of builtin attributes use rustc_attr_data_structures::{AttributeKind, DeprecatedSince, Deprecation};
use rustc_span::symbol::Ident;
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_span::{Span, Symbol, sym}; use rustc_span::{Span, Symbol, sym};
use super::util::UnsupportedLiteralReason; use super::SingleAttributeParser;
use crate::{parse_version, session_diagnostics}; 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(crate) struct DeprecationParser;
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();
'outer: for attr in attrs { fn get(
if !attr.has_name(sym::deprecated) { cx: &AcceptContext<'_>,
continue; 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 since = None;
let mut note = None; let mut note = None;
let mut suggestion = None; let mut suggestion = None;
if attr.is_doc_comment() { let is_rustc = features.staged_api();
continue;
} else if attr.is_word() { if let Some(value) = args.name_value()
} else if let Some(value) = attr.value_str() { && let Some(value_str) = value.value_as_str()
note = Some(value) {
} else if let Some(list) = attr.meta_item_list() { note = Some(value_str)
let get = |meta: &MetaItem, item: &mut Option<Symbol>| { } else if let Some(list) = args.list() {
if item.is_some() { for param in list.mixed() {
sess.dcx().emit_err(session_diagnostics::MultipleItem { let param_span = param.span();
span: meta.span, let Some(param) = param.meta_item() else {
item: pprust::path_to_string(&meta.path), 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; return None;
} };
if let Some(v) = meta.value_str() {
*item = Some(v); let (ident, arg) = param.word_or_empty();
true
} else { match ident.name {
if let Some(lit) = meta.name_value_literal() { sym::since => {
sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral { since = Some(get(cx, ident, param_span, arg, &since)?);
span: lit.span,
reason: UnsupportedLiteralReason::DeprecatedString,
is_bytestr: lit.kind.is_bytestr(),
start_point_span: sess.source_map().start_point(lit.span),
});
} else {
sess.dcx()
.emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span });
} }
false sym::note => {
} note = Some(get(cx, ident, param_span, arg, &note)?);
}; }
sym::suggestion => {
for meta in &list { if !features.deprecated_suggestion() {
match meta { cx.emit_err(session_diagnostics::DeprecatedItemSuggestion {
MetaItemInner::MetaItem(mi) => match mi.name_or_empty() { span: param_span,
sym::since => { is_nightly: cx.sess().is_nightly_build(),
if !get(mi, &mut since) { details: (),
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"]
},
}); });
continue 'outer;
} }
},
MetaItemInner::Lit(lit) => { suggestion = Some(get(cx, ident, param_span, arg, &suggestion)?);
sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral { }
span: lit.span, _ => {
reason: UnsupportedLiteralReason::DeprecatedKvPair, cx.emit_err(session_diagnostics::UnknownMetaItem {
is_bytestr: false, span: param_span,
start_point_span: sess.source_map().start_point(lit.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 { let since = if let Some(since) = since {
@ -126,23 +127,24 @@ pub fn find_deprecation(
} else if let Some(version) = parse_version(since) { } else if let Some(version) = parse_version(since) {
DeprecatedSince::RustcVersion(version) DeprecatedSince::RustcVersion(version)
} else { } else {
sess.dcx().emit_err(session_diagnostics::InvalidSince { span: attr.span() }); cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span });
DeprecatedSince::Err DeprecatedSince::Err
} }
} else if is_rustc { } 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 DeprecatedSince::Err
} else { } else {
DeprecatedSince::Unspecified DeprecatedSince::Unspecified
}; };
if is_rustc && note.is_none() { if is_rustc && note.is_none() {
sess.dcx().emit_err(session_diagnostics::MissingNote { span: attr.span() }); cx.emit_err(session_diagnostics::MissingNote { span: cx.attr_span });
continue; return None;
} }
depr = Some((Deprecation { since, note, suggestion }, attr.span())); Some(AttributeKind::Deprecation {
deprecation: Deprecation { since, note, suggestion },
span: cx.attr_span,
})
} }
depr
} }

View file

@ -1,17 +1,152 @@
mod allow_unstable; //! This module defines traits for attribute parsers, little state machines that recognize and parse
mod cfg; //! attributes out of a longer list of attributes. The main trait is called [`AttributeParser`].
mod confusables; //! You can find more docs about [`AttributeParser`]s on the trait itself.
mod deprecation; //! However, for many types of attributes, implementing [`AttributeParser`] is not necessary.
mod repr; //! It allows for a lot of flexibility you might not want.
mod stability; //!
mod transparency; //! 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::*; use rustc_attr_data_structures::AttributeKind;
pub use cfg::*; use rustc_span::Span;
pub use confusables::*; use thin_vec::ThinVec;
pub use deprecation::*;
pub use repr::*; use crate::context::{AcceptContext, FinalizeContext};
pub use stability::*; use crate::parser::ArgParser;
pub use transparency::*;
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)) }
}
}

View file

@ -1,15 +1,13 @@
//! Parsing and validation of builtin attributes
use rustc_abi::Align; use rustc_abi::Align;
use rustc_ast::attr::AttributeExt; use rustc_ast::{IntTy, LitIntType, LitKind, UintTy};
use rustc_ast::{self as ast, MetaItemKind}; use rustc_attr_data_structures::{AttributeKind, IntType, ReprAttr};
use rustc_attr_data_structures::IntType; use rustc_span::{Span, Symbol, sym};
use rustc_attr_data_structures::ReprAttr::*;
use rustc_session::Session;
use rustc_span::{Symbol, sym};
use crate::ReprAttr; use super::{CombineAttributeParser, ConvertFn};
use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause}; use crate::context::AcceptContext;
use crate::parser::{ArgParser, MetaItemListParser, MetaItemParser};
use crate::session_diagnostics;
use crate::session_diagnostics::IncorrectReprFormatGenericCause;
/// Parse #[repr(...)] forms. /// 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 /// the same discriminant size that the corresponding C enum would or C
/// structure layout, `packed` to remove padding, and `transparent` to delegate representation /// structure layout, `packed` to remove padding, and `transparent` to delegate representation
/// concerns to the only non-ZST field. /// concerns to the only non-ZST field.
pub fn find_repr_attrs(sess: &Session, attr: &impl AttributeExt) -> Vec<ReprAttr> { // FIXME(jdonszelmann): is a vec the right representation here even? isn't it just a struct?
if attr.has_name(sym::repr) { parse_repr_attr(sess, attr) } else { Vec::new() } 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> { macro_rules! int_pat {
assert!(attr.has_name(sym::repr), "expected `#[repr(..)]`, found: {attr:?}"); () => {
let mut acc = Vec::new(); sym::i8
let dcx = sess.dcx(); | sym::u8
| sym::i16
if let Some(items) = attr.meta_item_list() { | sym::u16
for item in items { | sym::i32
let mut recognised = false; | sym::u32
if item.is_word() { | sym::i64
let hint = match item.name_or_empty() { | sym::u64
sym::Rust => Some(ReprRust), | sym::i128
sym::C => Some(ReprC), | sym::u128
sym::packed => Some(ReprPacked(Align::ONE)), | sym::isize
sym::simd => Some(ReprSimd), | sym::usize
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
} }
fn int_type_of_word(s: Symbol) -> Option<IntType> { fn int_type_of_word(s: Symbol) -> Option<IntType> {
use rustc_attr_data_structures::IntType::*; use IntType::*;
match s { match s {
sym::i8 => Some(SignedInt(ast::IntTy::I8)), sym::i8 => Some(SignedInt(IntTy::I8)),
sym::u8 => Some(UnsignedInt(ast::UintTy::U8)), sym::u8 => Some(UnsignedInt(UintTy::U8)),
sym::i16 => Some(SignedInt(ast::IntTy::I16)), sym::i16 => Some(SignedInt(IntTy::I16)),
sym::u16 => Some(UnsignedInt(ast::UintTy::U16)), sym::u16 => Some(UnsignedInt(UintTy::U16)),
sym::i32 => Some(SignedInt(ast::IntTy::I32)), sym::i32 => Some(SignedInt(IntTy::I32)),
sym::u32 => Some(UnsignedInt(ast::UintTy::U32)), sym::u32 => Some(UnsignedInt(UintTy::U32)),
sym::i64 => Some(SignedInt(ast::IntTy::I64)), sym::i64 => Some(SignedInt(IntTy::I64)),
sym::u64 => Some(UnsignedInt(ast::UintTy::U64)), sym::u64 => Some(UnsignedInt(UintTy::U64)),
sym::i128 => Some(SignedInt(ast::IntTy::I128)), sym::i128 => Some(SignedInt(IntTy::I128)),
sym::u128 => Some(UnsignedInt(ast::UintTy::U128)), sym::u128 => Some(UnsignedInt(UintTy::U128)),
sym::isize => Some(SignedInt(ast::IntTy::Isize)), sym::isize => Some(SignedInt(IntTy::Isize)),
sym::usize => Some(UnsignedInt(ast::UintTy::Usize)), sym::usize => Some(UnsignedInt(UintTy::Usize)),
_ => None, _ => None,
} }
} }
pub fn parse_alignment(node: &ast::LitKind) -> Result<Align, &'static str> { fn parse_repr(cx: &AcceptContext<'_>, param: &MetaItemParser<'_>) -> Option<ReprAttr> {
if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node { 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 // `Align::from_bytes` accepts 0 as an input, check is_power_of_two() first
if literal.get().is_power_of_two() { if literal.get().is_power_of_two() {
// Only possible error is larger than 2^29 // Only possible error is larger than 2^29

View file

@ -1,266 +1,258 @@
//! Parsing and validation of builtin attributes
use std::num::NonZero; use std::num::NonZero;
use rustc_ast::MetaItem;
use rustc_ast::attr::AttributeExt;
use rustc_ast_pretty::pprust;
use rustc_attr_data_structures::{ use rustc_attr_data_structures::{
ConstStability, DefaultBodyStability, Stability, StabilityLevel, StableSince, UnstableReason, AttributeKind, DefaultBodyStability, PartialConstStability, Stability, StabilityLevel,
VERSION_PLACEHOLDER, StableSince, UnstableReason, VERSION_PLACEHOLDER,
}; };
use rustc_errors::ErrorGuaranteed; use rustc_errors::ErrorGuaranteed;
use rustc_session::Session;
use rustc_span::{Span, Symbol, kw, sym}; use rustc_span::{Span, Symbol, kw, sym};
use crate::attributes::util::UnsupportedLiteralReason; use super::util::parse_version;
use crate::{parse_version, session_diagnostics}; 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` macro_rules! reject_outside_std {
/// attributes in `attrs`. Returns `None` if no stability attributes are found. ($cx: ident) => {
pub fn find_stability( // Emit errors for non-staged-api crates.
sess: &Session, if !$cx.features().staged_api() {
attrs: &[impl AttributeExt], $cx.emit_err(session_diagnostics::StabilityOutsideStd { span: $cx.attr_span });
item_sp: Span, return;
) -> Option<(Stability, Span)> { }
let mut stab: Option<(Stability, Span)> = None; };
let mut allowed_through_unstable_modules = None; }
for attr in attrs { #[derive(Default)]
match attr.name_or_empty() { pub(crate) struct StabilityParser {
sym::rustc_allowed_through_unstable_modules => { allowed_through_unstable_modules: Option<Symbol>,
// The value is mandatory, but avoid ICEs in case such code reaches this function. stability: Option<(Stability, Span)>,
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;
}
if let Some((feature, level)) = parse_unstability(sess, attr) { impl StabilityParser {
stab = Some((Stability { level, feature }, attr.span())); /// 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 {
sym::stable => { cx.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span });
if stab.is_some() { true
sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels { } else {
span: attr.span(), false
});
break;
}
if let Some((feature, level)) = parse_stability(sess, attr) {
stab = Some((Stability { level, feature }, attr.span()));
}
}
_ => {}
} }
} }
}
if let Some(allowed_through_unstable_modules) = allowed_through_unstable_modules { impl AttributeParser for StabilityParser {
match &mut stab { const ATTRIBUTES: AcceptMapping<Self> = &[
Some(( (&[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 { 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), )) = self.stability
_ => { {
sess.dcx() *allowed_through_unstable_modules = Some(atum);
.emit_err(session_diagnostics::RustcAllowedUnstablePairing { span: item_sp }); } 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` Some(AttributeKind::Stability { stability, span })
/// 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,
} }
} }
/// Collects stability info from `rustc_default_body_unstable` attributes in `attrs`. // FIXME(jdonszelmann) change to Single
/// Returns `None` if no stability attributes are found. #[derive(Default)]
pub fn find_body_stability( pub(crate) struct BodyStabilityParser {
sess: &Session, stability: Option<(DefaultBodyStability, Span)>,
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
} }
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() { if item.is_some() {
sess.dcx().emit_err(session_diagnostics::MultipleItem { cx.emit_err(session_diagnostics::MultipleItem {
span: meta.span, span: param.span(),
item: pprust::path_to_string(&meta.path), item: param.path_without_args().to_string(),
}); });
None None
} else if let Some(v) = meta.value_str() { } else if let Some(v) = param.args().name_value()
*item = Some(v); && let Some(s) = v.value_as_str()
{
*item = Some(s);
Some(()) Some(())
} else { } else {
sess.dcx().emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span }); cx.emit_err(session_diagnostics::IncorrectMetaItem {
span: param.span(),
suggestion: None,
});
None None
} }
} }
/// Read the content of a `stable`/`rustc_const_stable` attribute, and return the feature name and /// Read the content of a `stable`/`rustc_const_stable` attribute, and return the feature name and
/// its stability information. /// its stability information.
fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol, StabilityLevel)> { pub(crate) fn parse_stability(
let metas = attr.meta_item_list()?; cx: &AcceptContext<'_>,
args: &ArgParser<'_>,
) -> Option<(Symbol, StabilityLevel)> {
let mut feature = None; let mut feature = None;
let mut since = None; let mut since = None;
for meta in metas {
let Some(mi) = meta.meta_item() else { for param in args.list()?.mixed() {
sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral { let param_span = param.span();
span: meta.span(), let Some(param) = param.meta_item() else {
cx.emit_err(session_diagnostics::UnsupportedLiteral {
span: param_span,
reason: UnsupportedLiteralReason::Generic, reason: UnsupportedLiteralReason::Generic,
is_bytestr: false, 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; return None;
}; };
match mi.name_or_empty() { match param.word_or_empty_without_args().name {
sym::feature => insert_or_error(sess, mi, &mut feature)?, sym::feature => insert_value_into_option_or_error(cx, &param, &mut feature)?,
sym::since => insert_or_error(sess, mi, &mut since)?, sym::since => insert_value_into_option_or_error(cx, &param, &mut since)?,
_ => { _ => {
sess.dcx().emit_err(session_diagnostics::UnknownMetaItem { cx.emit_err(session_diagnostics::UnknownMetaItem {
span: meta.span(), span: param_span,
item: pprust::path_to_string(&mi.path), item: param.path_without_args().to_string(),
expected: &["feature", "since"], expected: &["feature", "since"],
}); });
return None; return None;
@ -271,9 +263,9 @@ fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol,
let feature = match feature { let feature = match feature {
Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature), Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature),
Some(_bad_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 { 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) { } else if let Some(version) = parse_version(since) {
StableSince::Version(version) StableSince::Version(version)
} else { } else {
sess.dcx().emit_err(session_diagnostics::InvalidSince { span: attr.span() }); cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span });
StableSince::Err StableSince::Err
} }
} else { } else {
sess.dcx().emit_err(session_diagnostics::MissingSince { span: attr.span() }); cx.emit_err(session_diagnostics::MissingSince { span: cx.attr_span });
StableSince::Err 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. /// attribute, and return the feature name and its stability information.
fn parse_unstability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol, StabilityLevel)> { pub(crate) fn parse_unstability(
let metas = attr.meta_item_list()?; cx: &AcceptContext<'_>,
args: &ArgParser<'_>,
) -> Option<(Symbol, StabilityLevel)> {
let mut feature = None; let mut feature = None;
let mut reason = None; let mut reason = None;
let mut issue = None; let mut issue = None;
let mut issue_num = None; let mut issue_num = None;
let mut is_soft = false; let mut is_soft = false;
let mut implied_by = None; let mut implied_by = None;
for meta in metas { for param in args.list()?.mixed() {
let Some(mi) = meta.meta_item() else { let Some(param) = param.meta_item() else {
sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral { cx.emit_err(session_diagnostics::UnsupportedLiteral {
span: meta.span(), span: param.span(),
reason: UnsupportedLiteralReason::Generic, reason: UnsupportedLiteralReason::Generic,
is_bytestr: false, 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; return None;
}; };
match mi.name_or_empty() { let (word, args) = param.word_or_empty();
sym::feature => insert_or_error(sess, mi, &mut feature)?, match word.name {
sym::reason => insert_or_error(sess, mi, &mut reason)?, sym::feature => insert_value_into_option_or_error(cx, &param, &mut feature)?,
sym::reason => insert_value_into_option_or_error(cx, &param, &mut reason)?,
sym::issue => { sym::issue => {
insert_or_error(sess, mi, &mut issue)?; insert_value_into_option_or_error(cx, &param, &mut issue)?;
// These unwraps are safe because `insert_or_error` ensures the meta item // These unwraps are safe because `insert_value_into_option_or_error` ensures the meta item
// is a name/value pair string literal. // is a name/value pair string literal.
issue_num = match issue.unwrap().as_str() { issue_num = match issue.unwrap().as_str() {
"none" => None, "none" => None,
issue => match issue.parse::<NonZero<u32>>() { issue_str => match issue_str.parse::<NonZero<u32>>() {
Ok(num) => Some(num), Ok(num) => Some(num),
Err(err) => { Err(err) => {
sess.dcx().emit_err( cx.emit_err(
session_diagnostics::InvalidIssueString { session_diagnostics::InvalidIssueString {
span: mi.span, span: param.span(),
cause: session_diagnostics::InvalidIssueStringCause::from_int_error_kind( cause: session_diagnostics::InvalidIssueStringCause::from_int_error_kind(
mi.name_value_literal_span().unwrap(), args.name_value().unwrap().value_span,
err.kind(), err.kind(),
), ),
}, },
@ -349,16 +343,16 @@ fn parse_unstability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol
}; };
} }
sym::soft => { sym::soft => {
if !mi.is_word() { if !args.no_args() {
sess.dcx().emit_err(session_diagnostics::SoftNoArgs { span: mi.span }); cx.emit_err(session_diagnostics::SoftNoArgs { span: param.span() });
} }
is_soft = true; is_soft = true;
} }
sym::implied_by => insert_or_error(sess, mi, &mut implied_by)?, sym::implied_by => insert_value_into_option_or_error(cx, &param, &mut implied_by)?,
_ => { _ => {
sess.dcx().emit_err(session_diagnostics::UnknownMetaItem { cx.emit_err(session_diagnostics::UnknownMetaItem {
span: meta.span(), span: param.span(),
item: pprust::path_to_string(&mi.path), item: param.path_without_args().to_string(),
expected: &["feature", "reason", "issue", "soft", "implied_by"], expected: &["feature", "reason", "issue", "soft", "implied_by"],
}); });
return None; return None;
@ -369,14 +363,13 @@ fn parse_unstability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol
let feature = match feature { let feature = match feature {
Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature), Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature),
Some(_bad_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(|| { let issue =
sess.dcx().emit_err(session_diagnostics::MissingIssue { span: attr.span() }) issue.ok_or_else(|| cx.emit_err(session_diagnostics::MissingIssue { span: cx.attr_span }));
});
match (feature, issue) { match (feature, issue) {
(Ok(feature), Ok(_)) => { (Ok(feature), Ok(_)) => {

View file

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

View file

@ -3,22 +3,6 @@ use rustc_attr_data_structures::RustcVersion;
use rustc_feature::is_builtin_attr_name; use rustc_feature::is_builtin_attr_name;
use rustc_span::{Symbol, sym}; 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, /// 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 /// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are
/// not accepted in this position, unlike when parsing CFG_RELEASE. /// 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()?; let patch = digits.next().unwrap_or("0").parse().ok()?;
Some(RustcVersion { major, minor, patch }) Some(RustcVersion { major, minor, patch })
} }
pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool {
attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
}
pub fn find_crate_name(attrs: &[impl AttributeExt]) -> Option<Symbol> {
first_attr_value_str_by_name(attrs, sym::crate_name)
}

View file

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

View file

@ -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`. //! Part of a series of crates:
//! The goal is to move the definition of `MetaItem` and things that don't need to be in `syntax` //! - rustc_attr_data_structures: contains types that the parsers parse into
//! to this crate. //! - 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 // tidy-alphabetical-start
#![allow(internal_features)] #![allow(internal_features)]
@ -12,11 +83,59 @@
#![warn(unreachable_pub)] #![warn(unreachable_pub)]
// tidy-alphabetical-end // tidy-alphabetical-end
#[macro_use]
mod attributes; mod attributes;
mod context;
pub mod parser;
mod session_diagnostics; 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 rustc_attr_data_structures::*;
pub use util::{find_crate_name, is_builtin_attr, parse_version};
rustc_fluent_macro::fluent_messages! { "../messages.ftl" } rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
/// Finds attributes in sequences of attributes by pattern matching.
///
/// A little like `matches` but for attributes.
///
/// ```rust,ignore (illustrative)
/// // finds the repr attribute
/// if let Some(r) = find_attr!(attrs, AttributeKind::Repr(r) => r) {
///
/// }
///
/// // checks if one has matched
/// if find_attr!(attrs, AttributeKind::Repr(_)) {
///
/// }
/// ```
///
/// Often this requires you to first end up with a list of attributes.
/// A common way to get those is through `tcx.get_all_attrs(did)`
#[macro_export]
macro_rules! find_attr {
($attributes_list: expr, $pattern: pat $(if $guard: expr)?) => {{
$crate::find_attr!($attributes_list, $pattern $(if $guard)? => ()).is_some()
}};
($attributes_list: expr, $pattern: pat $(if $guard: expr)? => $e: expr) => {{
fn check_attribute_iterator<'a>(_: &'_ impl IntoIterator<Item = &'a rustc_hir::Attribute>) {}
check_attribute_iterator(&$attributes_list);
let find_attribute = |iter| {
for i in $attributes_list {
match i {
rustc_hir::Attribute::Parsed($pattern) $(if $guard)? => {
return Some($e);
}
_ => {}
}
}
None
};
find_attribute($attributes_list)
}};
}

View file

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

View file

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

View file

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

View file

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

View file

@ -94,7 +94,7 @@ impl<'tcx> AssertModuleSource<'tcx> {
other => { other => {
self.tcx self.tcx
.dcx() .dcx()
.emit_fatal(errors::UnknownReuseKind { span: attr.span, kind: other }); .emit_fatal(errors::UnknownReuseKind { span: attr.span(), kind: other });
} }
} }
} else { } else {
@ -102,7 +102,7 @@ impl<'tcx> AssertModuleSource<'tcx> {
}; };
if !self.tcx.sess.opts.unstable_opts.query_dep_graph { 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) { if !self.check_config(attr) {
@ -115,7 +115,7 @@ impl<'tcx> AssertModuleSource<'tcx> {
if !user_path.starts_with(&crate_name) { if !user_path.starts_with(&crate_name) {
self.tcx.dcx().emit_fatal(errors::MalformedCguName { self.tcx.dcx().emit_fatal(errors::MalformedCguName {
span: attr.span, span: attr.span(),
user_path, user_path,
crate_name, crate_name,
}); });
@ -145,7 +145,7 @@ impl<'tcx> AssertModuleSource<'tcx> {
let cgu_names: Vec<&str> = let cgu_names: Vec<&str> =
self.available_cgus.items().map(|cgu| cgu.as_str()).into_sorted_stable_ord(); self.available_cgus.items().map(|cgu| cgu.as_str()).into_sorted_stable_ord();
self.tcx.dcx().emit_err(errors::NoModuleNamed { self.tcx.dcx().emit_err(errors::NoModuleNamed {
span: attr.span, span: attr.span(),
user_path, user_path,
cgu_name, cgu_name,
cgu_names: cgu_names.join(", "), cgu_names: cgu_names.join(", "),
@ -155,7 +155,7 @@ impl<'tcx> AssertModuleSource<'tcx> {
self.cgu_reuse_tracker.set_expectation( self.cgu_reuse_tracker.set_expectation(
cgu_name, cgu_name,
user_path, user_path,
attr.span, attr.span(),
expected_reuse, expected_reuse,
comp_kind, 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 /// Scan for a `cfg="foo"` attribute and check whether we have a

View file

@ -5,7 +5,9 @@ use std::time::{Duration, Instant};
use itertools::Itertools; use itertools::Itertools;
use rustc_abi::FIRST_VARIANT; use rustc_abi::FIRST_VARIANT;
use rustc_ast as ast;
use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, AllocatorKind, global_fn_name}; 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::fx::{FxHashMap, FxIndexSet};
use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry};
use rustc_data_structures::sync::par_map; 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::infer::{BoundRegionConversionTime, TyCtxtInferExt};
use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt}; use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt};
use tracing::{debug, info}; use tracing::{debug, info};
use {rustc_ast as ast, rustc_attr_parsing as attr};
use crate::assert_module_sources::CguReuse; use crate::assert_module_sources::CguReuse;
use crate::back::link::are_upstream_rust_objects_already_included; 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 any_for_speed = defids.items().any(|id| {
let CodegenFnAttrs { optimize, .. } = tcx.codegen_fn_attrs(*id); let CodegenFnAttrs { optimize, .. } = tcx.codegen_fn_attrs(*id);
matches!(optimize, attr::OptimizeAttr::Speed) matches!(optimize, OptimizeAttr::Speed)
}); });
if any_for_speed { if any_for_speed {

View file

@ -5,7 +5,8 @@ use rustc_ast::expand::autodiff_attrs::{
AutoDiffAttrs, DiffActivity, DiffMode, valid_input_activity, valid_ret_activity, AutoDiffAttrs, DiffActivity, DiffMode, valid_input_activity, valid_ret_activity,
}; };
use rustc_ast::{MetaItem, MetaItemInner, attr}; 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_data_structures::fx::FxHashMap;
use rustc_errors::codes::*; use rustc_errors::codes::*;
use rustc_errors::{DiagMessage, SubdiagMessage, struct_span_code_err}; 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 { if let Fn | AssocFn | Variant | Ctor(..) = def_kind {
Some(tcx.fn_sig(did)) Some(tcx.fn_sig(did))
} else { } else {
tcx.dcx() tcx.dcx().span_delayed_bug(
.span_delayed_bug(attr.span, "this attribute can only be applied to functions"); attr.span(),
"this attribute can only be applied to functions",
);
None 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 { let Some(Ident { name, .. }) = attr.ident() else {
continue; 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() { if tcx.opt_item_name(did.to_def_id()).is_some() {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
mixed_export_name_no_mangle_lint_state.track_no_mangle( mixed_export_name_no_mangle_lint_state.track_no_mangle(
attr.span, attr.span(),
tcx.local_def_id_to_hir_id(did), tcx.local_def_id_to_hir_id(did),
attr, attr,
); );
} else { } else {
tcx.dcx() tcx.dcx()
.struct_span_err( .struct_span_err(
attr.span, attr.span(),
format!( format!(
"`#[no_mangle]` cannot be used on {} {} as it has no name", "`#[no_mangle]` cannot be used on {} {} as it has no name",
tcx.def_descr_article(did.to_def_id()), tcx.def_descr_article(did.to_def_id()),
@ -158,7 +173,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
feature_err( feature_err(
&tcx.sess, &tcx.sess,
sym::used_with_arg, sym::used_with_arg,
attr.span, attr.span(),
"`#[used(linker)]` is currently unstable", "`#[used(linker)]` is currently unstable",
) )
.emit(); .emit();
@ -170,7 +185,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
feature_err( feature_err(
&tcx.sess, &tcx.sess,
sym::used_with_arg, sym::used_with_arg,
attr.span, attr.span(),
"`#[used(compiler)]` is currently unstable", "`#[used(compiler)]` is currently unstable",
) )
.emit(); .emit();
@ -178,7 +193,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED; codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
} }
Some(_) => { Some(_) => {
tcx.dcx().emit_err(errors::ExpectedUsedSymbol { span: attr.span }); tcx.dcx().emit_err(errors::ExpectedUsedSymbol { span: attr.span() });
} }
None => { None => {
// Unfortunately, unconditionally using `llvm.used` causes // Unfortunately, unconditionally using `llvm.used` causes
@ -223,7 +238,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
{ {
struct_span_code_err!( struct_span_code_err!(
tcx.dcx(), tcx.dcx(),
attr.span, attr.span(),
E0737, E0737,
"`#[track_caller]` requires Rust ABI" "`#[track_caller]` requires Rust ABI"
) )
@ -231,12 +246,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
} }
if is_closure if is_closure
&& !tcx.features().closure_track_caller() && !tcx.features().closure_track_caller()
&& !attr.span.allows_unstable(sym::closure_track_caller) && !attr.span().allows_unstable(sym::closure_track_caller)
{ {
feature_err( feature_err(
&tcx.sess, &tcx.sess,
sym::closure_track_caller, sym::closure_track_caller,
attr.span, attr.span(),
"`#[track_caller]` on closures is currently unstable", "`#[track_caller]` on closures is currently unstable",
) )
.emit(); .emit();
@ -250,19 +265,19 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
// so it may not contain any null characters. // so it may not contain any null characters.
struct_span_code_err!( struct_span_code_err!(
tcx.dcx(), tcx.dcx(),
attr.span, attr.span(),
E0648, E0648,
"`export_name` may not contain null characters" "`export_name` may not contain null characters"
) )
.emit(); .emit();
} }
codegen_fn_attrs.export_name = Some(s); 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 => { sym::target_feature => {
let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else { 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; continue;
}; };
let safe_target_features = 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 // `main`, `start`, and other functions that are not usually
// allowed. // allowed.
} else { } else {
check_target_feature_trait_unsafe(tcx, did, attr.span); check_target_feature_trait_unsafe(tcx, did, attr.span());
} }
} }
from_target_feature_attr( from_target_feature_attr(
@ -310,7 +325,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
if tcx.is_mutable_static(did.into()) { if tcx.is_mutable_static(did.into()) {
let mut diag = tcx.dcx().struct_span_err( let mut diag = tcx.dcx().struct_span_err(
attr.span, attr.span(),
"extern mutable statics are not allowed with `#[linkage]`", "extern mutable statics are not allowed with `#[linkage]`",
); );
diag.note( diag.note(
@ -329,7 +344,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
if let Some(val) = attr.value_str() { if let Some(val) = attr.value_str() {
if val.as_str().bytes().any(|b| b == 0) { if val.as_str().bytes().any(|b| b == 0) {
let msg = format!("illegal null byte in link_section value: `{val}`"); 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 { } else {
codegen_fn_attrs.link_section = Some(val); 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_name => codegen_fn_attrs.link_name = attr.value_str(),
sym::link_ordinal => { sym::link_ordinal => {
link_ordinal_span = Some(attr.span); link_ordinal_span = Some(attr.span());
if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) { if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {
codegen_fn_attrs.link_ordinal = ordinal; codegen_fn_attrs.link_ordinal = ordinal;
} }
} }
sym::no_sanitize => { sym::no_sanitize => {
no_sanitize_span = Some(attr.span); no_sanitize_span = Some(attr.span());
if let Some(list) = attr.meta_item_list() { if let Some(list) = attr.meta_item_list() {
for item in list.iter() { for item in list.iter() {
match item.name_or_empty() { match item.name_or_empty() {
@ -381,7 +396,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
{ {
struct_span_code_err!( struct_span_code_err!(
tcx.dcx(), tcx.dcx(),
attr.span, attr.span(),
E0779, E0779,
"target does not support `#[instruction_set]`" "target does not support `#[instruction_set]`"
) )
@ -393,7 +408,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
_ => { _ => {
struct_span_code_err!( struct_span_code_err!(
tcx.dcx(), tcx.dcx(),
attr.span, attr.span(),
E0779, E0779,
"invalid instruction set specified", "invalid instruction set specified",
) )
@ -405,7 +420,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
[] => { [] => {
struct_span_code_err!( struct_span_code_err!(
tcx.dcx(), tcx.dcx(),
attr.span, attr.span(),
E0778, E0778,
"`#[instruction_set]` requires an argument" "`#[instruction_set]` requires an argument"
) )
@ -415,7 +430,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
_ => { _ => {
struct_span_code_err!( struct_span_code_err!(
tcx.dcx(), tcx.dcx(),
attr.span, attr.span(),
E0779, E0779,
"cannot specify more than one instruction set" "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 => { sym::patchable_function_entry => {
codegen_fn_attrs.patchable_function_entry = attr.meta_item_list().and_then(|l| { codegen_fn_attrs.patchable_function_entry = attr.meta_item_list().and_then(|l| {
let mut prefix = None; let mut prefix = None;
@ -510,7 +504,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
} }
if let (None, None) = (prefix, entry) { 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( 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 { let Some(ref items) = attr.meta_item_list() else {
return ia; return ia;
}; };
inline_span = Some(attr.span());
inline_span = Some(attr.span);
let [item] = &items[..] else { 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; return InlineAttr::None;
}; };
if item.has_name(sym::always) { if item.has_name(sym::always) {
InlineAttr::Always InlineAttr::Always
} else if item.has_name(sym::never) { } else if item.has_name(sym::never) {
InlineAttr::Never InlineAttr::Never
} else { } 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`") .with_help("valid inline arguments are `always` and `never`")
.emit(); .emit();
@ -560,9 +555,9 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
} }
if attr.is_word() { 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() { } 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 { } else {
debug!("`rustc_force_inline` not checked by attribute validation"); debug!("`rustc_force_inline` not checked by attribute validation");
ia 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(); let err = |sp, s| struct_span_code_err!(tcx.dcx(), sp, E0722, "{}", s).emit();
if attr.is_word() { if attr.is_word() {
err(attr.span, "expected one argument"); err(attr.span(), "expected one argument");
return ia; return ia;
} }
let Some(ref items) = attr.meta_item_list() else { let Some(ref items) = attr.meta_item_list() else {
return OptimizeAttr::Default; return OptimizeAttr::Default;
}; };
inline_span = Some(attr.span); inline_span = Some(attr.span());
let [item] = &items[..] else { let [item] = &items[..] else {
err(attr.span, "expected one argument"); err(attr.span(), "expected one argument");
return OptimizeAttr::Default; return OptimizeAttr::Default;
}; };
if item.has_name(sym::size) { if item.has_name(sym::size) {
@ -703,7 +698,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
let span = tcx let span = tcx
.get_attrs(did, sym::target_feature) .get_attrs(did, sym::target_feature)
.next() .next()
.map_or_else(|| tcx.def_span(did), |a| a.span); .map_or_else(|| tcx.def_span(did), |a| a.span());
tcx.dcx() tcx.dcx()
.create_err(errors::TargetFeatureDisableOrEnable { .create_err(errors::TargetFeatureDisableOrEnable {
features, features,
@ -752,7 +747,7 @@ fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Option<u16> {
use rustc_ast::{LitIntType, LitKind, MetaItemLit}; use rustc_ast::{LitIntType, LitKind, MetaItemLit};
let meta_item_list = attr.meta_item_list()?; let meta_item_list = attr.meta_item_list()?;
let [sole_meta_list] = &meta_item_list[..] else { 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; return None;
}; };
if let Some(MetaItemLit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) = 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 { } else {
let msg = format!("ordinal value in `link_ordinal` is too large: `{ordinal}`"); let msg = format!("ordinal value in `link_ordinal` is too large: `{ordinal}`");
tcx.dcx() tcx.dcx()
.struct_span_err(attr.span, msg) .struct_span_err(attr.span(), msg)
.with_note("the value may not exceed `u16::MAX`") .with_note("the value may not exceed `u16::MAX`")
.emit(); .emit();
None None
} }
} else { } else {
tcx.dcx().emit_err(errors::InvalidLinkOrdinalFormat { span: attr.span }); tcx.dcx().emit_err(errors::InvalidLinkOrdinalFormat { span: attr.span() });
None None
} }
} }
@ -828,7 +823,7 @@ impl<'a> MixedExportNameAndNoMangleState<'a> {
export_name: Some(export_name), export_name: Some(export_name),
no_mangle: Some(no_mangle), no_mangle: Some(no_mangle),
hir_id: Some(hir_id), hir_id: Some(hir_id),
no_mangle_attr: Some(no_mangle_attr), no_mangle_attr: Some(_),
} = self } = self
{ {
tcx.emit_node_span_lint( tcx.emit_node_span_lint(
@ -837,7 +832,7 @@ impl<'a> MixedExportNameAndNoMangleState<'a> {
no_mangle, no_mangle,
errors::MixedExportNameAndNoMangle { errors::MixedExportNameAndNoMangle {
no_mangle, 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, export_name,
removal_span: no_mangle, 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 //FIXME(ZuseZ4): Once we fixed our parser, we should also prohibit the two-attribute
//branch above. //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 { 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 { let mode = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = mode {
p1.segments.first().unwrap().ident p1.segments.first().unwrap().ident
} else { } else {
span_bug!(attr.span, "rustc_autodiff attribute must contain mode"); span_bug!(attr.span(), "rustc_autodiff attribute must contain mode");
}; };
// parse 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 { let ret_symbol = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = ret_activity {
p1.segments.first().unwrap().ident p1.segments.first().unwrap().ident
} else { } 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 // 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 { for &input in &arg_activities {
if !valid_input_activity(mode, input) { 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) { 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 }) Some(AutoDiffAttrs { mode, ret_activity, input_activity: arg_activities })

View file

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

View file

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

View file

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

View file

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

View file

@ -11,11 +11,12 @@ use rustc_ast::token::Nonterminal;
use rustc_ast::tokenstream::TokenStream; use rustc_ast::tokenstream::TokenStream;
use rustc_ast::visit::{AssocCtxt, Visitor}; use rustc_ast::visit::{AssocCtxt, Visitor};
use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind}; 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::fx::FxIndexMap;
use rustc_data_structures::sync; use rustc_data_structures::sync;
use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult}; use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult};
use rustc_feature::Features; use rustc_feature::Features;
use rustc_hir as hir;
use rustc_lint_defs::{BufferedEarlyLint, RegisteredTools}; use rustc_lint_defs::{BufferedEarlyLint, RegisteredTools};
use rustc_parse::MACRO_ARGUMENTS; use rustc_parse::MACRO_ARGUMENTS;
use rustc_parse::parser::Parser; use rustc_parse::parser::Parser;
@ -838,19 +839,23 @@ impl SyntaxExtension {
/// and other properties converted from attributes. /// and other properties converted from attributes.
pub fn new( pub fn new(
sess: &Session, sess: &Session,
features: &Features,
kind: SyntaxExtensionKind, kind: SyntaxExtensionKind,
span: Span, span: Span,
helper_attrs: Vec<Symbol>, helper_attrs: Vec<Symbol>,
edition: Edition, edition: Edition,
name: Symbol, name: Symbol,
attrs: &[impl AttributeExt], attrs: &[hir::Attribute],
is_local: bool, is_local: bool,
) -> SyntaxExtension { ) -> SyntaxExtension {
let allow_internal_unstable = 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) let local_inner_macros = ast::attr::find_by_name(attrs, sym::macro_export)
.and_then(|macro_export| macro_export.meta_item_list()) .and_then(|macro_export| macro_export.meta_item_list())
.is_some_and(|l| ast::attr::list_contains_name(&l, sym::local_inner_macros)); .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)); .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 stability = find_attr!(attrs, AttributeKind::Stability{stability, ..} => *stability);
let body_stability = attr::find_body_stability(sess, attrs);
if let Some((_, sp)) = const_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 { sess.dcx().emit_err(errors::MacroConstStability {
span: sp, span: sp,
head_span: sess.source_map().guess_head_span(span), 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 { sess.dcx().emit_err(errors::MacroBodyStability {
span: sp, span: sp,
head_span: sess.source_map().guess_head_span(span), head_span: sess.source_map().guess_head_span(span),
@ -887,9 +893,10 @@ impl SyntaxExtension {
kind, kind,
span, span,
allow_internal_unstable: (!allow_internal_unstable.is_empty()) allow_internal_unstable: (!allow_internal_unstable.is_empty())
.then(|| allow_internal_unstable.into()), // FIXME(jdonszelmann): avoid the into_iter/collect?
stability: stability.map(|(s, _)| s), .then(|| allow_internal_unstable.iter().map(|i| i.0).collect::<Vec<_>>().into()),
deprecation: attr::find_deprecation(sess, features, attrs).map(|(d, _)| d), stability,
deprecation: find_attr!(attrs, AttributeKind::Deprecation{deprecation, ..} => *deprecation),
helper_attrs, helper_attrs,
edition, edition,
builtin_name, builtin_name,

View file

@ -3,17 +3,17 @@ use std::collections::hash_map::Entry;
use std::{mem, slice}; use std::{mem, slice};
use ast::token::IdentIsRaw; use ast::token::IdentIsRaw;
use rustc_ast::attr::AttributeExt;
use rustc_ast::token::NtPatKind::*; use rustc_ast::token::NtPatKind::*;
use rustc_ast::token::TokenKind::*; use rustc_ast::token::TokenKind::*;
use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind}; use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind};
use rustc_ast::tokenstream::{DelimSpan, TokenStream}; use rustc_ast::tokenstream::{DelimSpan, TokenStream};
use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId}; use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId};
use rustc_ast_pretty::pprust; 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_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_errors::{Applicability, ErrorGuaranteed}; use rustc_errors::{Applicability, ErrorGuaranteed};
use rustc_feature::Features; use rustc_feature::Features;
use rustc_hir as hir;
use rustc_lint_defs::BuiltinLintDiag; use rustc_lint_defs::BuiltinLintDiag;
use rustc_lint_defs::builtin::{ use rustc_lint_defs::builtin::{
RUST_2021_INCOMPATIBLE_OR_PATTERNS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, RUST_2021_INCOMPATIBLE_OR_PATTERNS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
@ -371,7 +371,7 @@ pub fn compile_declarative_macro(
features: &Features, features: &Features,
macro_def: &ast::MacroDef, macro_def: &ast::MacroDef,
ident: Ident, ident: Ident,
attrs: &[impl AttributeExt], attrs: &[hir::Attribute],
span: Span, span: Span,
node_id: NodeId, node_id: NodeId,
edition: Edition, edition: Edition,
@ -379,7 +379,6 @@ pub fn compile_declarative_macro(
let mk_syn_ext = |expander| { let mk_syn_ext = |expander| {
SyntaxExtension::new( SyntaxExtension::new(
sess, sess,
features,
SyntaxExtensionKind::LegacyBang(expander), SyntaxExtensionKind::LegacyBang(expander),
span, span,
Vec::new(), 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 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 lhs_nm = Ident::new(sym::lhs, span);
let rhs_nm = Ident::new(sym::rhs, span); let rhs_nm = Ident::new(sym::rhs, span);
let tt_spec = Some(NonterminalKind::TT); 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)); check_emission(macro_check::check_meta_variables(&sess.psess, node_id, span, &lhses, &rhses));
let (transparency, transparency_error) = attr::find_transparency(attrs, macro_rules); let transparency = find_attr!(attrs, AttributeKind::MacroTransparency(x) => *x)
match transparency_error { .unwrap_or(Transparency::fallback(macro_rules));
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 => {}
}
if let Some(guar) = guar { if let Some(guar) = guar {
// To avoid warning noise, only consider the rules of this // To avoid warning noise, only consider the rules of this

View file

@ -9,6 +9,7 @@ odht = { version = "0.3.1", features = ["nightly"] }
rustc_abi = { path = "../rustc_abi" } rustc_abi = { path = "../rustc_abi" }
rustc_arena = { path = "../rustc_arena" } rustc_arena = { path = "../rustc_arena" }
rustc_ast = { path = "../rustc_ast" } rustc_ast = { path = "../rustc_ast" }
rustc_attr_data_structures = { path = "../rustc_attr_data_structures" }
rustc_data_structures = { path = "../rustc_data_structures" } rustc_data_structures = { path = "../rustc_data_structures" }
rustc_hashes = { path = "../rustc_hashes" } rustc_hashes = { path = "../rustc_hashes" }
rustc_index = { path = "../rustc_index" } rustc_index = { path = "../rustc_index" }

View file

@ -1,18 +1,20 @@
// ignore-tidy-filelength
use std::fmt; use std::fmt;
use rustc_abi::ExternAbi; use rustc_abi::ExternAbi;
// ignore-tidy-filelength
use rustc_ast::attr::AttributeExt; use rustc_ast::attr::AttributeExt;
use rustc_ast::token::CommentKind; use rustc_ast::token::CommentKind;
use rustc_ast::util::parser::{AssocOp, ExprPrecedence}; use rustc_ast::util::parser::{AssocOp, ExprPrecedence};
use rustc_ast::{ use rustc_ast::{
self as ast, AttrId, AttrStyle, DelimArgs, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, self as ast, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, LitIntType,
IntTy, Label, LitIntType, LitKind, MetaItemInner, MetaItemLit, TraitObjectSyntax, UintTy, LitKind, TraitObjectSyntax, UintTy, UnsafeBinderCastKind,
}; };
pub use rustc_ast::{ pub use rustc_ast::{
BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity, ByRef, CaptureBy, AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity,
ImplPolarity, IsAuto, Movability, Mutability, UnOp, UnsafeBinderCastKind, 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::fingerprint::Fingerprint;
use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::sorted_map::SortedMap;
use rustc_data_structures::tagged_ptr::TaggedRef; 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)] #[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable)]
pub struct AttrPath { pub struct AttrPath {
pub segments: Box<[Ident]>, pub segments: Box<[Ident]>,
pub span: Span, 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)] #[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable)]
pub struct AttrItem { pub struct AttrItem {
pub unsafety: Safety,
// Not lowered to hir::Path because we have no NodeId to resolve to. // Not lowered to hir::Path because we have no NodeId to resolve to.
pub path: AttrPath, pub path: AttrPath,
pub args: AttrArgs, pub args: AttrArgs,
} pub id: HashIgnoredAttrId,
#[derive(Clone, Debug, Encodable, Decodable)]
pub struct Attribute {
pub kind: AttrKind,
pub id: AttrId,
/// Denotes if the attribute decorates the following construct (outer) /// Denotes if the attribute decorates the following construct (outer)
/// or the construct this attribute is contained within (inner). /// or the construct this attribute is contained within (inner).
pub style: AttrStyle, pub style: AttrStyle,
/// Span of the entire attribute
pub span: Span, 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 { impl Attribute {
pub fn get_normal_item(&self) -> &AttrItem { pub fn get_normal_item(&self) -> &AttrItem {
match &self.kind { match &self {
AttrKind::Normal(normal) => &normal, Attribute::Unparsed(normal) => &normal,
AttrKind::DocComment(..) => panic!("unexpected doc comment"), _ => panic!("unexpected parsed attribute"),
} }
} }
pub fn unwrap_normal_item(self) -> AttrItem { pub fn unwrap_normal_item(self) -> AttrItem {
match self.kind { match self {
AttrKind::Normal(normal) => *normal, Attribute::Unparsed(normal) => *normal,
AttrKind::DocComment(..) => panic!("unexpected doc comment"), _ => panic!("unexpected parsed attribute"),
} }
} }
pub fn value_lit(&self) -> Option<&MetaItemLit> { pub fn value_lit(&self) -> Option<&MetaItemLit> {
match &self.kind { match &self {
AttrKind::Normal(box AttrItem { args: AttrArgs::Eq { expr, .. }, .. }) => Some(expr), Attribute::Unparsed(n) => match n.as_ref() {
AttrItem { args: AttrArgs::Eq { eq_span: _, expr }, .. } => Some(expr),
_ => None,
},
_ => None, _ => None,
} }
} }
} }
impl AttributeExt for Attribute { impl AttributeExt for Attribute {
#[inline]
fn id(&self) -> AttrId { 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>> { fn meta_item_list(&self) -> Option<ThinVec<ast::MetaItemInner>> {
match &self.kind { match &self {
AttrKind::Normal(box AttrItem { args: AttrArgs::Delimited(d), .. }) => { Attribute::Unparsed(n) => match n.as_ref() {
ast::MetaItemKind::list_from_tokens(d.tokens.clone()) AttrItem { args: AttrArgs::Delimited(d), .. } => {
} ast::MetaItemKind::list_from_tokens(d.tokens.clone())
}
_ => None,
},
_ => None, _ => None,
} }
} }
#[inline]
fn value_str(&self) -> Option<Symbol> { fn value_str(&self) -> Option<Symbol> {
self.value_lit().and_then(|x| x.value_str()) self.value_lit().and_then(|x| x.value_str())
} }
#[inline]
fn value_span(&self) -> Option<Span> { fn value_span(&self) -> Option<Span> {
self.value_lit().map(|i| i.span) self.value_lit().map(|i| i.span)
} }
/// For a single-segment attribute, returns its name; otherwise, returns `None`. /// For a single-segment attribute, returns its name; otherwise, returns `None`.
#[inline]
fn ident(&self) -> Option<Ident> { fn ident(&self) -> Option<Ident> {
match &self.kind { match &self {
AttrKind::Normal(box AttrItem { Attribute::Unparsed(n) => {
path: AttrPath { segments: box [ident], .. }, .. if let [ident] = n.path.segments.as_ref() {
}) => Some(*ident), Some(*ident)
} else {
None
}
}
_ => None, _ => None,
} }
} }
#[inline]
fn path_matches(&self, name: &[Symbol]) -> bool { fn path_matches(&self, name: &[Symbol]) -> bool {
match &self.kind { match &self {
AttrKind::Normal(n) => n.path.segments.iter().map(|segment| &segment.name).eq(name), Attribute::Unparsed(n) => {
AttrKind::DocComment(..) => false, 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 { fn is_doc_comment(&self) -> bool {
matches!(self.kind, AttrKind::DocComment(..)) matches!(self, Attribute::Parsed(AttributeKind::DocComment { .. }))
} }
#[inline]
fn span(&self) -> Span { fn span(&self) -> Span {
self.span match &self {
} Attribute::Unparsed(u) => u.span,
// FIXME: should not be needed anymore when all attrs are parsed
fn is_word(&self) -> bool { Attribute::Parsed(AttributeKind::Deprecation { span, .. }) => *span,
matches!(self.kind, AttrKind::Normal(box AttrItem { args: AttrArgs::Empty, .. })) Attribute::Parsed(AttributeKind::DocComment { span, .. }) => *span,
} a => panic!("can't get the span of an arbitrary parsed attribute: {a:?}"),
fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> {
match &self.kind {
AttrKind::Normal(n) => Some(n.path.segments.iter().copied().collect()),
AttrKind::DocComment(..) => None,
} }
} }
fn doc_str(&self) -> Option<Symbol> { #[inline]
match &self.kind { fn is_word(&self) -> bool {
AttrKind::DocComment(.., data) => Some(*data), match &self {
AttrKind::Normal(_) if self.has_name(sym::doc) => self.value_str(), 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, _ => 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)> { fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
match &self.kind { match &self {
AttrKind::DocComment(kind, data) => Some((*data, *kind)), Attribute::Parsed(AttributeKind::DocComment { kind, comment, .. }) => {
AttrKind::Normal(_) if self.name_or_empty() == sym::doc => { Some((*comment, *kind))
}
Attribute::Unparsed(_) if self.name_or_empty() == sym::doc => {
self.value_str().map(|s| (s, CommentKind::Line)) self.value_str().map(|s| (s, CommentKind::Line))
} }
_ => None, _ => None,
} }
} }
#[inline]
fn style(&self) -> AttrStyle { 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 // FIXME(fn_delegation): use function delegation instead of manually forwarding
impl Attribute { impl Attribute {
#[inline]
pub fn id(&self) -> AttrId { pub fn id(&self) -> AttrId {
AttributeExt::id(self) AttributeExt::id(self)
} }
#[inline]
pub fn name_or_empty(&self) -> Symbol { pub fn name_or_empty(&self) -> Symbol {
AttributeExt::name_or_empty(self) AttributeExt::name_or_empty(self)
} }
#[inline]
pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> { pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
AttributeExt::meta_item_list(self) AttributeExt::meta_item_list(self)
} }
#[inline]
pub fn value_str(&self) -> Option<Symbol> { pub fn value_str(&self) -> Option<Symbol> {
AttributeExt::value_str(self) AttributeExt::value_str(self)
} }
#[inline]
pub fn value_span(&self) -> Option<Span> { pub fn value_span(&self) -> Option<Span> {
AttributeExt::value_span(self) AttributeExt::value_span(self)
} }
#[inline]
pub fn ident(&self) -> Option<Ident> { pub fn ident(&self) -> Option<Ident> {
AttributeExt::ident(self) AttributeExt::ident(self)
} }
#[inline]
pub fn path_matches(&self, name: &[Symbol]) -> bool { pub fn path_matches(&self, name: &[Symbol]) -> bool {
AttributeExt::path_matches(self, name) AttributeExt::path_matches(self, name)
} }
#[inline]
pub fn is_doc_comment(&self) -> bool { pub fn is_doc_comment(&self) -> bool {
AttributeExt::is_doc_comment(self) AttributeExt::is_doc_comment(self)
} }
@ -1186,34 +1262,42 @@ impl Attribute {
AttributeExt::has_name(self, name) AttributeExt::has_name(self, name)
} }
#[inline]
pub fn span(&self) -> Span { pub fn span(&self) -> Span {
AttributeExt::span(self) AttributeExt::span(self)
} }
#[inline]
pub fn is_word(&self) -> bool { pub fn is_word(&self) -> bool {
AttributeExt::is_word(self) AttributeExt::is_word(self)
} }
#[inline]
pub fn path(&self) -> SmallVec<[Symbol; 1]> { pub fn path(&self) -> SmallVec<[Symbol; 1]> {
AttributeExt::path(self) AttributeExt::path(self)
} }
#[inline]
pub fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> { pub fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> {
AttributeExt::ident_path(self) AttributeExt::ident_path(self)
} }
#[inline]
pub fn doc_str(&self) -> Option<Symbol> { pub fn doc_str(&self) -> Option<Symbol> {
AttributeExt::doc_str(self) AttributeExt::doc_str(self)
} }
#[inline]
pub fn is_proc_macro_attr(&self) -> bool { pub fn is_proc_macro_attr(&self) -> bool {
AttributeExt::is_proc_macro_attr(self) AttributeExt::is_proc_macro_attr(self)
} }
#[inline]
pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> { pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
AttributeExt::doc_str_and_comment_kind(self) AttributeExt::doc_str_and_comment_kind(self)
} }
#[inline]
pub fn style(&self) -> AttrStyle { pub fn style(&self) -> AttrStyle {
AttributeExt::style(self) AttributeExt::style(self)
} }

View file

@ -1,17 +1,21 @@
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
use rustc_span::def_id::DefPathHash; use rustc_span::def_id::DefPathHash;
use crate::HashIgnoredAttrId;
use crate::hir::{ use crate::hir::{
Attribute, AttributeMap, BodyId, Crate, ForeignItemId, ImplItemId, ItemId, OwnerNodes, AttributeMap, BodyId, Crate, ForeignItemId, ImplItemId, ItemId, OwnerNodes, TraitItemId,
TraitItemId,
}; };
use crate::hir_id::{HirId, ItemLocalId}; use crate::hir_id::{HirId, ItemLocalId};
/// Requirements for a `StableHashingContext` to be used in this crate. /// Requirements for a `StableHashingContext` to be used in this crate.
/// This is a hack to allow using the `HashStable_Generic` derive macro /// This is a hack to allow using the `HashStable_Generic` derive macro
/// instead of implementing everything in `rustc_middle`. /// instead of implementing everything in `rustc_middle`.
pub trait HashStableContext: rustc_ast::HashStableContext + rustc_abi::HashStableContext { pub trait HashStableContext:
fn hash_attr(&mut self, _: &Attribute, hasher: &mut StableHasher); 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 { 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) { fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) {
hcx.hash_attr(self, hasher) hcx.hash_attr_id(self, hasher)
} }
} }

View file

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

View file

@ -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) { 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; error = true;
} }

View file

@ -1202,7 +1202,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
// and that they are all identifiers // and that they are all identifiers
.and_then(|attr| match attr.meta_item_list() { .and_then(|attr| match attr.meta_item_list() {
Some(items) if items.len() < 2 => { 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 None
} }
@ -1214,7 +1214,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
tcx.dcx().emit_err(errors::MustBeNameOfAssociatedFunction { span }); tcx.dcx().emit_err(errors::MustBeNameOfAssociatedFunction { span });
}) })
.ok() .ok()
.zip(Some(attr.span)), .zip(Some(attr.span())),
// Error is reported by `rustc_attr!` // Error is reported by `rustc_attr!`
None => None, None => None,
}) })

View file

@ -111,14 +111,14 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) {
let trait_ref = tcx.impl_trait_ref(def_id).unwrap().instantiate_identity(); let trait_ref = tcx.impl_trait_ref(def_id).unwrap().instantiate_identity();
if trait_ref.has_non_region_param() { if trait_ref.has_non_region_param() {
tcx.dcx().span_err( tcx.dcx().span_err(
attr.span, attr.span(),
"`rustc_dump_vtable` must be applied to non-generic impl", "`rustc_dump_vtable` must be applied to non-generic impl",
); );
continue; continue;
} }
if !tcx.is_dyn_compatible(trait_ref.def_id) { if !tcx.is_dyn_compatible(trait_ref.def_id) {
tcx.dcx().span_err( tcx.dcx().span_err(
attr.span, attr.span(),
"`rustc_dump_vtable` must be applied to dyn-compatible trait", "`rustc_dump_vtable` must be applied to dyn-compatible trait",
); );
continue; continue;
@ -127,7 +127,7 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) {
.try_normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref) .try_normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref)
else { else {
tcx.dcx().span_err( tcx.dcx().span_err(
attr.span, attr.span(),
"`rustc_dump_vtable` applied to impl header that cannot be normalized", "`rustc_dump_vtable` applied to impl header that cannot be normalized",
); );
continue; continue;
@ -138,7 +138,7 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) {
let ty = tcx.type_of(def_id).instantiate_identity(); let ty = tcx.type_of(def_id).instantiate_identity();
if ty.has_non_region_param() { if ty.has_non_region_param() {
tcx.dcx().span_err( tcx.dcx().span_err(
attr.span, attr.span(),
"`rustc_dump_vtable` must be applied to non-generic type", "`rustc_dump_vtable` must be applied to non-generic type",
); );
continue; continue;
@ -147,13 +147,14 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) {
tcx.try_normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), ty) tcx.try_normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), ty)
else { else {
tcx.dcx().span_err( tcx.dcx().span_err(
attr.span, attr.span(),
"`rustc_dump_vtable` applied to type alias that cannot be normalized", "`rustc_dump_vtable` applied to type alias that cannot be normalized",
); );
continue; continue;
}; };
let ty::Dynamic(data, _, _) = *ty.kind() else { 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; continue;
}; };
if let Some(principal) = data.principal() { if let Some(principal) = data.principal() {
@ -166,7 +167,7 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) {
} }
_ => { _ => {
tcx.dcx().span_err( tcx.dcx().span_err(
attr.span, attr.span(),
"`rustc_dump_vtable` only applies to impl, or type alias of dyn type", "`rustc_dump_vtable` only applies to impl, or type alias of dyn type",
); );
continue; continue;

View file

@ -8,6 +8,7 @@ edition = "2024"
rustc_abi = { path = "../rustc_abi" } rustc_abi = { path = "../rustc_abi" }
rustc_ast = { path = "../rustc_ast" } rustc_ast = { path = "../rustc_ast" }
rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_ast_pretty = { path = "../rustc_ast_pretty" }
rustc_attr_parsing = { path = "../rustc_attr_parsing" }
rustc_hir = { path = "../rustc_hir" } rustc_hir = { path = "../rustc_hir" }
rustc_span = { path = "../rustc_span" } rustc_span = { path = "../rustc_span" }
# tidy-alphabetical-end # tidy-alphabetical-end

View file

@ -11,11 +11,12 @@ use std::vec;
use rustc_abi::ExternAbi; use rustc_abi::ExternAbi;
use rustc_ast::util::parser::{self, AssocOp, ExprPrecedence, Fixity}; 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::Breaks::{Consistent, Inconsistent};
use rustc_ast_pretty::pp::{self, Breaks}; use rustc_ast_pretty::pp::{self, Breaks};
use rustc_ast_pretty::pprust::state::MacHeader; use rustc_ast_pretty::pprust::state::MacHeader;
use rustc_ast_pretty::pprust::{Comments, PrintState}; use rustc_ast_pretty::pprust::{Comments, PrintState};
use rustc_attr_parsing::{AttributeKind, PrintAttribute};
use rustc_hir::{ use rustc_hir::{
BindingMode, ByRef, ConstArgKind, GenericArg, GenericBound, GenericParam, GenericParamKind, BindingMode, ByRef, ConstArgKind, GenericArg, GenericBound, GenericParam, GenericParamKind,
HirId, ImplicitSelfKind, LifetimeParamKind, Node, PatKind, PreciseCapturingArg, RangeEnd, Term, HirId, ImplicitSelfKind, LifetimeParamKind, Node, PatKind, PreciseCapturingArg, RangeEnd, Term,
@ -80,65 +81,48 @@ impl<'a> State<'a> {
(self.attrs)(id) (self.attrs)(id)
} }
fn print_inner_attributes(&mut self, attrs: &[hir::Attribute]) -> bool { fn print_attrs_as_inner(&mut self, attrs: &[hir::Attribute]) {
self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, true) self.print_either_attributes(attrs, ast::AttrStyle::Inner)
} }
fn print_outer_attributes(&mut self, attrs: &[hir::Attribute]) -> bool { fn print_attrs_as_outer(&mut self, attrs: &[hir::Attribute]) {
self.print_either_attributes(attrs, ast::AttrStyle::Outer, false, true) self.print_either_attributes(attrs, ast::AttrStyle::Outer)
} }
fn print_either_attributes( fn print_either_attributes(&mut self, attrs: &[hir::Attribute], style: ast::AttrStyle) {
&mut self, if attrs.is_empty() {
attrs: &[hir::Attribute], return;
kind: ast::AttrStyle, }
is_inline: bool,
trailing_hardbreak: bool,
) -> bool {
let mut printed = false;
for attr in attrs { for attr in attrs {
if attr.style == kind { self.print_attribute_inline(attr, style);
self.print_attribute_inline(attr, is_inline);
if is_inline {
self.nbsp();
}
printed = true;
}
} }
if printed && trailing_hardbreak && !is_inline { self.hardbreak_if_not_bol();
self.hardbreak_if_not_bol();
}
printed
} }
fn print_attribute_inline(&mut self, attr: &hir::Attribute, is_inline: bool) { fn print_attribute_inline(&mut self, attr: &hir::Attribute, style: AttrStyle) {
if !is_inline { match &attr {
self.hardbreak_if_not_bol(); hir::Attribute::Unparsed(unparsed) => {
} self.maybe_print_comment(unparsed.span.lo());
self.maybe_print_comment(attr.span.lo()); match style {
match &attr.kind {
hir::AttrKind::Normal(normal) => {
match attr.style {
ast::AttrStyle::Inner => self.word("#!["), ast::AttrStyle::Inner => self.word("#!["),
ast::AttrStyle::Outer => self.word("#["), ast::AttrStyle::Outer => self.word("#["),
} }
if normal.unsafety == hir::Safety::Unsafe { self.print_attr_item(&unparsed, unparsed.span);
self.word("unsafe(");
}
self.print_attr_item(&normal, attr.span);
if normal.unsafety == hir::Safety::Unsafe {
self.word(")");
}
self.word("]"); 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( self.word(rustc_ast_pretty::pprust::state::doc_comment_to_string(
*comment_kind, *kind, *style, *comment,
attr.style,
*data,
)); ));
self.hardbreak() 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, false,
None, None,
*delim, *delim,
tokens, &tokens,
true, true,
span, span,
), ),
@ -307,7 +291,7 @@ where
} }
pub fn attribute_to_string(ann: &dyn PpAnn, attr: &hir::Attribute) -> String { 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 { 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]) { 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 { for &item_id in _mod.item_ids {
self.ann.nested(self, Nested::Item(item_id)); 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<'_>) { fn print_foreign_item(&mut self, item: &hir::ForeignItem<'_>) {
self.hardbreak_if_not_bol(); self.hardbreak_if_not_bol();
self.maybe_print_comment(item.span.lo()); 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 { match item.kind {
hir::ForeignItemKind::Fn(sig, arg_names, generics) => { hir::ForeignItemKind::Fn(sig, arg_names, generics) => {
self.head(""); self.head("");
@ -591,7 +575,7 @@ impl<'a> State<'a> {
self.hardbreak_if_not_bol(); self.hardbreak_if_not_bol();
self.maybe_print_comment(item.span.lo()); self.maybe_print_comment(item.span.lo());
let attrs = self.attrs(item.hir_id()); 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)); self.ann.pre(self, AnnNode::Item(item));
match item.kind { match item.kind {
hir::ItemKind::ExternCrate(orig_name) => { hir::ItemKind::ExternCrate(orig_name) => {
@ -687,7 +671,7 @@ impl<'a> State<'a> {
self.head("extern"); self.head("extern");
self.word_nbsp(abi.to_string()); self.word_nbsp(abi.to_string());
self.bopen(); 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 { for item in items {
self.ann.nested(self, Nested::ForeignItem(item.id)); self.ann.nested(self, Nested::ForeignItem(item.id));
} }
@ -755,7 +739,7 @@ impl<'a> State<'a> {
self.space(); self.space();
self.bopen(); self.bopen();
self.print_inner_attributes(attrs); self.print_attrs_as_inner(attrs);
for impl_item in items { for impl_item in items {
self.ann.nested(self, Nested::ImplItem(impl_item.id)); self.ann.nested(self, Nested::ImplItem(impl_item.id));
} }
@ -847,7 +831,7 @@ impl<'a> State<'a> {
for v in variants { for v in variants {
self.space_if_not_bol(); self.space_if_not_bol();
self.maybe_print_comment(v.span.lo()); 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.ibox(INDENT_UNIT);
self.print_variant(v); self.print_variant(v);
self.word(","); self.word(",");
@ -880,7 +864,7 @@ impl<'a> State<'a> {
self.popen(); self.popen();
self.commasep(Inconsistent, struct_def.fields(), |s, field| { self.commasep(Inconsistent, struct_def.fields(), |s, field| {
s.maybe_print_comment(field.span.lo()); 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); s.print_type(field.ty);
}); });
self.pclose(); self.pclose();
@ -907,7 +891,7 @@ impl<'a> State<'a> {
for field in fields { for field in fields {
self.hardbreak_if_not_bol(); self.hardbreak_if_not_bol();
self.maybe_print_comment(field.span.lo()); 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.print_ident(field.ident);
self.word_nbsp(":"); self.word_nbsp(":");
self.print_type(field.ty); self.print_type(field.ty);
@ -943,7 +927,7 @@ impl<'a> State<'a> {
self.ann.pre(self, AnnNode::SubItem(ti.hir_id())); self.ann.pre(self, AnnNode::SubItem(ti.hir_id()));
self.hardbreak_if_not_bol(); self.hardbreak_if_not_bol();
self.maybe_print_comment(ti.span.lo()); 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 { match ti.kind {
hir::TraitItemKind::Const(ty, default) => { hir::TraitItemKind::Const(ty, default) => {
self.print_associated_const(ti.ident, ti.generics, 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.ann.pre(self, AnnNode::SubItem(ii.hir_id()));
self.hardbreak_if_not_bol(); self.hardbreak_if_not_bol();
self.maybe_print_comment(ii.span.lo()); 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 { match ii.kind {
hir::ImplItemKind::Const(ty, expr) => { hir::ImplItemKind::Const(ty, expr) => {
@ -1074,7 +1058,7 @@ impl<'a> State<'a> {
self.ann.pre(self, AnnNode::Block(blk)); self.ann.pre(self, AnnNode::Block(blk));
self.bopen(); self.bopen();
self.print_inner_attributes(attrs); self.print_attrs_as_inner(attrs);
for st in blk.stmts { for st in blk.stmts {
self.print_stmt(st); self.print_stmt(st);
@ -1264,7 +1248,7 @@ impl<'a> State<'a> {
self.space(); self.space();
} }
self.cbox(INDENT_UNIT); 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 { if !field.is_shorthand {
self.print_ident(field.ident); self.print_ident(field.ident);
self.word_space(":"); self.word_space(":");
@ -1461,7 +1445,7 @@ impl<'a> State<'a> {
fn print_expr(&mut self, expr: &hir::Expr<'_>) { fn print_expr(&mut self, expr: &hir::Expr<'_>) {
self.maybe_print_comment(expr.span.lo()); 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.ibox(INDENT_UNIT);
self.ann.pre(self, AnnNode::Expr(expr)); self.ann.pre(self, AnnNode::Expr(expr));
match expr.kind { match expr.kind {
@ -1677,8 +1661,8 @@ impl<'a> State<'a> {
} }
hir::ExprKind::UnsafeBinderCast(kind, expr, ty) => { hir::ExprKind::UnsafeBinderCast(kind, expr, ty) => {
match kind { match kind {
hir::UnsafeBinderCastKind::Wrap => self.word("wrap_binder!("), ast::UnsafeBinderCastKind::Wrap => self.word("wrap_binder!("),
hir::UnsafeBinderCastKind::Unwrap => self.word("unwrap_binder!("), ast::UnsafeBinderCastKind::Unwrap => self.word("unwrap_binder!("),
} }
self.print_expr(expr); self.print_expr(expr);
if let Some(ty) = ty { if let Some(ty) = ty {
@ -2073,7 +2057,7 @@ impl<'a> State<'a> {
self.space(); self.space();
} }
self.cbox(INDENT_UNIT); 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 { if !field.is_shorthand {
self.print_ident(field.ident); self.print_ident(field.ident);
self.word_nbsp(":"); self.word_nbsp(":");
@ -2083,7 +2067,7 @@ impl<'a> State<'a> {
} }
fn print_param(&mut self, arg: &hir::Param<'_>) { 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); self.print_pat(arg.pat);
} }
@ -2118,7 +2102,7 @@ impl<'a> State<'a> {
self.cbox(INDENT_UNIT); self.cbox(INDENT_UNIT);
self.ann.pre(self, AnnNode::Arm(arm)); self.ann.pre(self, AnnNode::Arm(arm));
self.ibox(0); 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.print_pat(arm.pat);
self.space(); self.space();
if let Some(ref g) = arm.guard { if let Some(ref g) = arm.guard {

View file

@ -1656,13 +1656,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn check_expr_unsafe_binder_cast( fn check_expr_unsafe_binder_cast(
&self, &self,
span: Span, span: Span,
kind: hir::UnsafeBinderCastKind, kind: ast::UnsafeBinderCastKind,
inner_expr: &'tcx hir::Expr<'tcx>, inner_expr: &'tcx hir::Expr<'tcx>,
hir_ty: Option<&'tcx hir::Ty<'tcx>>, hir_ty: Option<&'tcx hir::Ty<'tcx>>,
expected: Expectation<'tcx>, expected: Expectation<'tcx>,
) -> Ty<'tcx> { ) -> Ty<'tcx> {
match kind { match kind {
hir::UnsafeBinderCastKind::Wrap => { ast::UnsafeBinderCastKind::Wrap => {
let ascribed_ty = let ascribed_ty =
hir_ty.map(|hir_ty| self.lower_ty_saving_user_provided_ty(hir_ty)); hir_ty.map(|hir_ty| self.lower_ty_saving_user_provided_ty(hir_ty));
let expected_ty = expected.only_has_type(self); let expected_ty = expected.only_has_type(self);
@ -1706,7 +1706,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
binder_ty binder_ty
} }
hir::UnsafeBinderCastKind::Unwrap => { ast::UnsafeBinderCastKind::Unwrap => {
let ascribed_ty = let ascribed_ty =
hir_ty.map(|hir_ty| self.lower_ty_saving_user_provided_ty(hir_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)); let hint_ty = ascribed_ty.unwrap_or_else(|| self.next_ty_var(inner_expr.span));

View file

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

View file

@ -137,13 +137,13 @@ impl<'tcx> IfThisChanged<'tcx> {
match DepNode::from_label_string(self.tcx, n.as_str(), def_path_hash) { match DepNode::from_label_string(self.tcx, n.as_str(), def_path_hash) {
Ok(n) => n, Ok(n) => n,
Err(()) => self.tcx.dcx().emit_fatal(errors::UnrecognizedDepNode { Err(()) => self.tcx.dcx().emit_fatal(errors::UnrecognizedDepNode {
span: attr.span, span: attr.span(),
name: n, 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) { } else if attr.has_name(sym::rustc_then_this_would_need) {
let dep_node_interned = self.argument(attr); let dep_node_interned = self.argument(attr);
let dep_node = match dep_node_interned { 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) { match DepNode::from_label_string(self.tcx, n.as_str(), def_path_hash) {
Ok(n) => n, Ok(n) => n,
Err(()) => self.tcx.dcx().emit_fatal(errors::UnrecognizedDepNode { Err(()) => self.tcx.dcx().emit_fatal(errors::UnrecognizedDepNode {
span: attr.span, span: attr.span(),
name: n, name: n,
}), }),
} }
} }
None => { 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(( self.then_this_would_need.push((
attr.span, attr.span(),
dep_node_interned.unwrap(), dep_node_interned.unwrap(),
hir_id, hir_id,
dep_node, dep_node,

View file

@ -199,7 +199,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
let loaded_from_disk = self.loaded_from_disk(attr); let loaded_from_disk = self.loaded_from_disk(attr);
for e in except.items().into_sorted_stable_ord() { for e in except.items().into_sorted_stable_ord() {
if !auto.remove(e) { 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 } Assertion { clean: auto, dirty: except, loaded_from_disk }
@ -282,7 +282,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
HirItem::Impl { .. } => ("ItemKind::Impl", LABELS_IMPL), HirItem::Impl { .. } => ("ItemKind::Impl", LABELS_IMPL),
_ => self.tcx.dcx().emit_fatal(errors::UndefinedCleanDirtyItem { _ => self.tcx.dcx().emit_fatal(errors::UndefinedCleanDirtyItem {
span: attr.span, span: attr.span(),
kind: format!("{:?}", item.kind), kind: format!("{:?}", item.kind),
}), }),
} }
@ -298,7 +298,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
ImplItemKind::Type(..) => ("NodeImplType", LABELS_CONST_IN_IMPL), ImplItemKind::Type(..) => ("NodeImplType", LABELS_CONST_IN_IMPL),
}, },
_ => self.tcx.dcx().emit_fatal(errors::UndefinedCleanDirty { _ => self.tcx.dcx().emit_fatal(errors::UndefinedCleanDirty {
span: attr.span, span: attr.span(),
kind: format!("{node:?}"), kind: format!("{node:?}"),
}), }),
}; };
@ -375,7 +375,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
let Some(assertion) = self.assertion_maybe(item_id, attr) else { let Some(assertion) = self.assertion_maybe(item_id, attr) else {
continue; continue;
}; };
self.checked_attrs.insert(attr.id); self.checked_attrs.insert(attr.id());
for label in assertion.clean.items().into_sorted_stable_ord() { for label in assertion.clean.items().into_sorted_stable_ord() {
let dep_node = DepNode::from_label_string(self.tcx, label, def_path_hash).unwrap(); let dep_node = DepNode::from_label_string(self.tcx, label, def_path_hash).unwrap();
self.assert_clean(item_span, dep_node); 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); debug!("check_config: searching for cfg {:?}", value);
cfg = Some(config.contains(&(value, None))); cfg = Some(config.contains(&(value, None)));
} else if !(item.has_name(EXCEPT) || item.has_name(LOADED_FROM_DISK)) { } 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 { 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, Some(c) => c,
} }
} }
@ -444,9 +445,9 @@ impl<'tcx> FindAllAttrs<'tcx> {
fn report_unchecked_attrs(&self, mut checked_attrs: FxHashSet<ast::AttrId>) { fn report_unchecked_attrs(&self, mut checked_attrs: FxHashSet<ast::AttrId>) {
for attr in &self.found_attrs { for attr in &self.found_attrs {
if !checked_attrs.contains(&attr.id) { if !checked_attrs.contains(&attr.id()) {
self.tcx.dcx().emit_err(errors::UncheckedClean { span: attr.span }); self.tcx.dcx().emit_err(errors::UncheckedClean { span: attr.span() });
checked_attrs.insert(attr.id); checked_attrs.insert(attr.id());
} }
} }
} }

View file

@ -1011,7 +1011,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
cx.emit_span_lint( cx.emit_span_lint(
NO_MANGLE_GENERIC_ITEMS, NO_MANGLE_GENERIC_ITEMS,
span, span,
BuiltinNoMangleGeneric { suggestion: no_mangle_attr.span }, BuiltinNoMangleGeneric { suggestion: no_mangle_attr.span() },
); );
break; break;
} }
@ -1226,7 +1226,7 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller {
{ {
cx.emit_span_lint( cx.emit_span_lint(
UNGATED_ASYNC_FN_TRACK_CALLER, UNGATED_ASYNC_FN_TRACK_CALLER,
attr.span, attr.span(),
BuiltinUngatedAsyncFnTrackCaller { label: span, session: &cx.tcx.sess }, BuiltinUngatedAsyncFnTrackCaller { label: span, session: &cx.tcx.sess },
); );
} }

View file

@ -38,7 +38,8 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) {
} }
LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => { 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. // 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) (attr_id, lint_index)
} }
_ => panic!("fulfilled expectations must have a lint index"), _ => panic!("fulfilled expectations must have a lint index"),

View file

@ -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 // 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 // where the attribute was defined. However, until this is found to be a
// bottleneck, this does just fine. // 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) SymbolName::Link(overridden_link_name, overridden_link_name_span)

View file

@ -1,13 +1,14 @@
use rustc_abi::ExternAbi; use rustc_abi::ExternAbi;
use rustc_attr_parsing::{AttributeKind, AttributeParser, ReprAttr};
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::FnKind; 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_middle::ty;
use rustc_session::config::CrateType; use rustc_session::config::CrateType;
use rustc_session::{declare_lint, declare_lint_pass}; use rustc_session::{declare_lint, declare_lint_pass};
use rustc_span::def_id::LocalDefId; use rustc_span::def_id::LocalDefId;
use rustc_span::{BytePos, Ident, Span, sym}; 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::{ use crate::lints::{
NonCamelCaseType, NonCamelCaseTypeSub, NonSnakeCaseDiag, NonSnakeCaseDiagSub, NonCamelCaseType, NonCamelCaseTypeSub, NonSnakeCaseDiag, NonSnakeCaseDiagSub,
@ -161,10 +162,10 @@ impl NonCamelCaseTypes {
impl EarlyLintPass for NonCamelCaseTypes { impl EarlyLintPass for NonCamelCaseTypes {
fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) { fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) {
let has_repr_c = it let has_repr_c = matches!(
.attrs AttributeParser::parse_limited(cx.sess(), &it.attrs, sym::repr, it.span, true),
.iter() Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.iter().any(|(r, _)| r == &ReprAttr::ReprC)
.any(|attr| attr::find_repr_attrs(cx.sess(), attr).contains(&attr::ReprC)); );
if has_repr_c { if has_repr_c {
return; return;
@ -343,7 +344,7 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase {
} else { } else {
ast::attr::find_by_name(cx.tcx.hir().attrs(hir::CRATE_HIR_ID), sym::crate_name) ast::attr::find_by_name(cx.tcx.hir().attrs(hir::CRATE_HIR_ID), sym::crate_name)
.and_then(|attr| { .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 }, .. } = && let AttrItem { args: AttrArgs::Eq { eq_span: _, expr: lit }, .. } =
n.as_ref() n.as_ref()
&& let ast::LitKind::Str(name, ..) = lit.kind && let ast::LitKind::Str(name, ..) = lit.kind

View file

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

View file

@ -17,6 +17,7 @@ mod diagnostics;
mod extension; mod extension;
mod hash_stable; mod hash_stable;
mod lift; mod lift;
mod print_attribute;
mod query; mod query;
mod serialize; mod serialize;
mod symbols; mod symbols;
@ -175,3 +176,11 @@ decl_derive! {
/// The error type is `u32`. /// The error type is `u32`.
try_from::try_from_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
}

View 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 }
}
})
}

View file

@ -450,7 +450,7 @@ impl<'tcx> Collector<'tcx> {
(name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule)); (name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule));
} }
let Some((name, name_span)) = name else { 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; continue;
}; };
@ -485,7 +485,7 @@ impl<'tcx> Collector<'tcx> {
let link_ordinal_attr = let link_ordinal_attr =
self.tcx.get_attr(child_item, sym::link_ordinal).unwrap(); self.tcx.get_attr(child_item, sym::link_ordinal).unwrap();
sess.dcx().emit_err(errors::LinkOrdinalRawDylib { sess.dcx().emit_err(errors::LinkOrdinalRawDylib {
span: link_ordinal_attr.span, span: link_ordinal_attr.span(),
}); });
} }
} }

View file

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

View file

@ -75,7 +75,7 @@ impl OverlapMode {
tcx.hir().attrs(tcx.local_def_id_to_hir_id(local_def_id)) tcx.hir().attrs(tcx.local_def_id_to_hir_id(local_def_id))
}) })
.find(|attr| attr.has_name(sym::rustc_strict_coherence)) .find(|attr| attr.has_name(sym::rustc_strict_coherence))
.map(|attr| attr.span); .map(|attr| attr.span());
tcx.dcx().emit_err(StrictCoherenceNeedsNegativeCoherence { tcx.dcx().emit_err(StrictCoherenceNeedsNegativeCoherence {
span: tcx.def_span(trait_id), span: tcx.def_span(trait_id),
attr_span, attr_span,

View file

@ -1542,7 +1542,7 @@ impl<'tcx> TyCtxt<'tcx> {
Bound::Included(a.get()) Bound::Included(a.get())
} else { } else {
self.dcx().span_delayed_bug( self.dcx().span_delayed_bug(
attr.span, attr.span(),
"invalid rustc_layout_scalar_valid_range attribute", "invalid rustc_layout_scalar_valid_range attribute",
); );
Bound::Unbounded Bound::Unbounded

View file

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

View file

@ -65,7 +65,7 @@ fn coverage_attr_on(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
Some(_) | None => { Some(_) | None => {
// Other possibilities should have been rejected by `rustc_parse::validate_attr`. // Other possibilities should have been rejected by `rustc_parse::validate_attr`.
// Use `span_delayed_bug` to avoid an ICE in failing builds (#127880). // 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");
} }
} }
} }

View file

@ -311,9 +311,6 @@ passes_duplicate_lang_item_crate_depends =
.first_definition_path = first definition in `{$orig_crate_name}` loaded from {$orig_path} .first_definition_path = first definition in `{$orig_crate_name}` loaded from {$orig_path}
.second_definition_path = second definition in `{$crate_name}` loaded from {$path} .second_definition_path = second definition in `{$crate_name}` loaded from {$path}
passes_empty_confusables =
expected at least one confusable name
passes_export_name = passes_export_name =
attribute should be applied to a free function, impl method or static attribute should be applied to a free function, impl method or static
.label = not 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 = passes_incorrect_do_not_recommend_location =
`#[diagnostic::do_not_recommend]` can only be placed on trait implementations `#[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 = passes_incorrect_target =
`{$name}` lang item must be applied to a {$kind} with {$at_least -> `{$name}` lang item must be applied to a {$kind} with {$at_least ->
[true] at least {$num} [true] at least {$num}
@ -641,13 +635,12 @@ passes_repr_align_greater_than_target_max =
passes_repr_conflicting = passes_repr_conflicting =
conflicting representation hints conflicting representation hints
passes_repr_ident =
meta item in `repr` must be an identifier
passes_rustc_allow_const_fn_unstable = passes_rustc_allow_const_fn_unstable =
attribute should be applied to `const fn` attribute should be applied to `const fn`
.label = not a `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 = passes_rustc_dirty_clean =
attribute requires -Z query-dep-graph to be enabled attribute requires -Z query-dep-graph to be enabled
@ -774,10 +767,6 @@ passes_unreachable_due_to_uninhabited = unreachable {$descr}
passes_unrecognized_field = passes_unrecognized_field =
unrecognized field name `{$name}` 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 = passes_unstable_attr_for_already_stable_feature =
can't mark as unstable using an already stable feature can't mark as unstable using an already stable feature
.label = this feature is already stable .label = this feature is already stable

File diff suppressed because it is too large Load diff

View file

@ -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> { fn attr_span_by_symbol(ctxt: &EntryContext<'_>, id: ItemId, sym: Symbol) -> Option<Span> {
let attrs = ctxt.tcx.hir().attrs(id.hir_id()); 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<'_>) { fn check_and_search_item(id: ItemId, ctxt: &mut EntryContext<'_>) {

View file

@ -582,13 +582,6 @@ pub(crate) struct NoMangle {
pub span: Span, pub span: Span,
} }
#[derive(Diagnostic)]
#[diag(passes_repr_ident, code = E0565)]
pub(crate) struct ReprIdent {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(passes_repr_conflicting, code = E0566)] #[diag(passes_repr_conflicting, code = E0566)]
pub(crate) struct ReprConflicting { pub(crate) struct ReprConflicting {
@ -736,31 +729,6 @@ pub(crate) struct Linkage {
pub span: Span, 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)] #[derive(Diagnostic)]
#[diag(passes_stability_promotable)] #[diag(passes_stability_promotable)]
pub(crate) struct StabilityPromotable { pub(crate) struct StabilityPromotable {
@ -1475,14 +1443,6 @@ pub(crate) struct ObjectLifetimeErr {
pub repr: String, pub repr: String,
} }
#[derive(Diagnostic)]
#[diag(passes_unrecognized_repr_hint, code = E0552)]
#[help]
pub(crate) struct UnrecognizedReprHint {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)] #[derive(Diagnostic)]
pub(crate) enum AttrApplication { pub(crate) enum AttrApplication {
#[diag(passes_attr_application_enum, code = E0517)] #[diag(passes_attr_application_enum, code = E0517)]
@ -1902,3 +1862,11 @@ pub(crate) struct NoSanitize<'a> {
pub accepted_kind: &'a str, pub accepted_kind: &'a str,
pub attr_str: &'a str, pub attr_str: &'a str,
} }
// FIXME(jdonszelmann): move back to rustc_attr
#[derive(Diagnostic)]
#[diag(passes_rustc_const_stable_indirect_pairing)]
pub(crate) struct RustcConstStableIndirectPairing {
#[primary_span]
pub span: Span,
}

View file

@ -4,7 +4,7 @@
//! but are not declared in one single location (unlike lang features), which means we need to //! but are not declared in one single location (unlike lang features), which means we need to
//! collect them instead. //! collect them instead.
use rustc_attr_parsing::VERSION_PLACEHOLDER; use rustc_attr_parsing::{AttributeKind, StabilityLevel, StableSince};
use rustc_hir::Attribute; use rustc_hir::Attribute;
use rustc_hir::intravisit::Visitor; use rustc_hir::intravisit::Visitor;
use rustc_middle::hir::nested_filter; use rustc_middle::hir::nested_filter;
@ -26,62 +26,29 @@ impl<'tcx> LibFeatureCollector<'tcx> {
} }
fn extract(&self, attr: &Attribute) -> Option<(Symbol, FeatureStability, Span)> { fn extract(&self, attr: &Attribute) -> Option<(Symbol, FeatureStability, Span)> {
let stab_attrs = [ let (feature, level, span) = match attr {
sym::stable, Attribute::Parsed(AttributeKind::Stability { stability, span }) => {
sym::unstable, (stability.feature, stability.level, *span)
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.
} }
} 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) { fn collect_feature(&mut self, feature: Symbol, stability: FeatureStability, span: Span) {

View file

@ -6,8 +6,8 @@ use std::num::NonZero;
use rustc_ast_lowering::stability::extern_abi_stability; use rustc_ast_lowering::stability::extern_abi_stability;
use rustc_attr_parsing::{ use rustc_attr_parsing::{
self as attr, ConstStability, DeprecatedSince, Stability, StabilityLevel, StableSince, self as attr, AttributeKind, ConstStability, DeprecatedSince, PartialConstStability, Stability,
UnstableReason, VERSION_PLACEHOLDER, StabilityLevel, StableSince, UnstableReason, VERSION_PLACEHOLDER, find_attr,
}; };
use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet}; 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)); let attrs = self.tcx.hir().attrs(self.tcx.local_def_id_to_hir_id(def_id));
debug!("annotate(id = {:?}, attrs = {:?})", def_id, attrs); 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; let mut is_deprecated = false;
if let Some((depr, span)) = &depr { if let Some((depr, span)) = &depr {
is_deprecated = true; is_deprecated = true;
@ -154,9 +156,10 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
if inherit_deprecation.yes() && stab.is_unstable() { if inherit_deprecation.yes() && stab.is_unstable() {
self.index.stab_map.insert(def_id, stab); self.index.stab_map.insert(def_id, stab);
if fn_sig.is_some_and(|s| s.header.is_const()) { if fn_sig.is_some_and(|s| s.header.is_const()) {
let const_stab = self.index.const_stab_map.insert(
attr::unmarked_crate_const_stab(self.tcx.sess, attrs, stab); def_id,
self.index.const_stab_map.insert(def_id, const_stab); ConstStability::unmarked(const_stability_indirect, stab),
);
} }
} }
} }
@ -171,9 +174,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
} }
// # Regular and body stability // # Regular and body stability
let stab = attr::find_attr!(attrs, AttributeKind::Stability { stability, span } => (*stability, *span));
let stab = attr::find_stability(self.tcx.sess, attrs, item_sp); let body_stab =
let body_stab = attr::find_body_stability(self.tcx.sess, attrs); attr::find_attr!(attrs, AttributeKind::BodyStability { stability, .. } => *stability);
if let Some((depr, span)) = &depr if let Some((depr, span)) = &depr
&& depr.is_since_rustc_version() && depr.is_since_rustc_version()
@ -182,7 +185,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
self.tcx.dcx().emit_err(errors::DeprecatedAttribute { span: *span }); 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 // FIXME: check that this item can have body stability
self.index.default_body_stab_map.insert(def_id, body_stab); self.index.default_body_stab_map.insert(def_id, body_stab);
@ -260,10 +263,10 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
// # Const stability // # 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 // 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 if let Some(fn_sig) = fn_sig
&& !fn_sig.header.is_const() && !fn_sig.header.is_const()
&& const_stab.is_some() && 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. // Stable *language* features shouldn't be used as unstable library features.
// (Not doing this for stable library features is checked by tidy.) // (Not doing this for stable library features is checked by tidy.)
if let Some(( if let Some((
ConstStability { level: StabilityLevel::Unstable { .. }, feature, .. }, PartialConstStability { level: StabilityLevel::Unstable { .. }, feature, .. },
const_span, const_span,
)) = const_stab )) = 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 // After checking the immediate attributes, get rid of the span and compute implied
// const stability: inherit feature gate from regular stability. // 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 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() && 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(); let features = self.tcx.features();
if features.staged_api() { if features.staged_api() {
let attrs = self.tcx.hir().attrs(item.hir_id()); let attrs = self.tcx.hir().attrs(item.hir_id());
let stab = attr::find_stability(self.tcx.sess, attrs, item.span); let stab = attr::find_attr!(attrs, AttributeKind::Stability{stability, span} => (*stability, *span));
let const_stab = attr::find_const_stability(self.tcx.sess, attrs, item.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 // If this impl block has an #[unstable] attribute, give an
// error if all involved types and traits are stable, because // 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. // needs to have an error emitted.
if features.const_trait_impl() if features.const_trait_impl()
&& self.tcx.is_const_trait_impl(item.owner_id.to_def_id()) && 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 }); self.tcx.dcx().emit_err(errors::TraitImplConstStable { span: item.span });
} }

View file

@ -22,6 +22,7 @@ use errors::{
}; };
use rustc_ast::MacroDef; use rustc_ast::MacroDef;
use rustc_ast::visit::{VisitorResult, try_visit}; use rustc_ast::visit::{VisitorResult, try_visit};
use rustc_attr_parsing::AttributeKind;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::intern::Interned; use rustc_data_structures::intern::Interned;
use rustc_errors::{MultiSpan, listify}; 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. // 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 hir_id = self.tcx.local_def_id_to_hir_id(local_def_id);
let attrs = self.tcx.hir().attrs(hir_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; return;
} }

View file

@ -6,6 +6,7 @@ edition = "2024"
[dependencies] [dependencies]
# tidy-alphabetical-start # tidy-alphabetical-start
measureme = "11" measureme = "11"
rustc_attr_data_structures = { path = "../rustc_attr_data_structures" }
rustc_data_structures = { path = "../rustc_data_structures" } rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" } rustc_errors = { path = "../rustc_errors" }
rustc_hashes = { path = "../rustc_hashes" } rustc_hashes = { path = "../rustc_hashes" }

View file

@ -9,6 +9,7 @@ parking_lot = "0.12"
rustc-rayon-core = { version = "0.5.0" } rustc-rayon-core = { version = "0.5.0" }
rustc_abi = { path = "../rustc_abi" } rustc_abi = { path = "../rustc_abi" }
rustc_ast = { path = "../rustc_ast" } rustc_ast = { path = "../rustc_ast" }
rustc_attr_data_structures = { path = "../rustc_attr_data_structures" }
rustc_data_structures = { path = "../rustc_data_structures" } rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" } rustc_errors = { path = "../rustc_errors" }
rustc_feature = { path = "../rustc_feature" } rustc_feature = { path = "../rustc_feature" }

View file

@ -129,3 +129,4 @@ impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> {
} }
impl<'a> rustc_session::HashStableContext for StableHashingContext<'a> {} impl<'a> rustc_session::HashStableContext for StableHashingContext<'a> {}
impl<'a> rustc_attr_data_structures::HashStableContext for StableHashingContext<'a> {}

View file

@ -2,7 +2,7 @@
//! from various crates in no particular order. //! from various crates in no particular order.
use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; 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 rustc_span::SourceFile;
use smallvec::SmallVec; use smallvec::SmallVec;
@ -23,6 +23,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for [hir::Attribute] {
.iter() .iter()
.filter(|attr| { .filter(|attr| {
!attr.is_doc_comment() !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)) && !attr.ident().is_some_and(|ident| hcx.is_ignored_attr(ident.name))
}) })
.collect(); .collect();
@ -35,19 +36,8 @@ impl<'a> HashStable<StableHashingContext<'a>> for [hir::Attribute] {
} }
impl<'ctx> rustc_hir::HashStableContext for StableHashingContext<'ctx> { impl<'ctx> rustc_hir::HashStableContext for StableHashingContext<'ctx> {
fn hash_attr(&mut self, attr: &hir::Attribute, hasher: &mut StableHasher) { fn hash_attr_id(&mut self, _id: &HashIgnoredAttrId, _hasher: &mut StableHasher) {
// Make sure that these have been filtered out. /* we don't hash HashIgnoredAttrId, we ignore them */
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!();
}
} }
} }

View file

@ -3,6 +3,7 @@ use std::mem;
use rustc_ast::visit::FnKind; use rustc_ast::visit::FnKind;
use rustc_ast::*; use rustc_ast::*;
use rustc_ast_pretty::pprust; use rustc_ast_pretty::pprust;
use rustc_attr_parsing::{AttributeParser, OmitDoc};
use rustc_expand::expand::AstFragment; use rustc_expand::expand::AstFragment;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::{CtorKind, CtorOf, DefKind}; 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::Fn(..) | ItemKind::Delegation(..) => DefKind::Fn,
ItemKind::MacroDef(def) => { ItemKind::MacroDef(def) => {
let edition = i.span.edition(); 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 = 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(); let macro_kind = macro_data.ext.macro_kind();
opt_macro_data = Some(macro_data); opt_macro_data = Some(macro_data);
DefKind::Macro(macro_kind) DefKind::Macro(macro_kind)

View file

@ -1806,7 +1806,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
&& !def_id.is_local() && !def_id.is_local()
&& let Some(attr) = self.tcx.get_attr(def_id, sym::non_exhaustive) && 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 { } else if let Some(span) = ctor_fields_span {
let label = errors::ConstructorPrivateIfAnyFieldPrivate { span }; let label = errors::ConstructorPrivateIfAnyFieldPrivate { span };
err.subdiagnostic(label); err.subdiagnostic(label);

View file

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

View file

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

View file

@ -468,7 +468,7 @@ pub(crate) fn encode_ty<'tcx>(
)] )]
tcx.dcx() tcx.dcx()
.struct_span_err( .struct_span_err(
cfi_encoding.span, cfi_encoding.span(),
format!("invalid `cfi_encoding` for `{:?}`", ty.kind()), format!("invalid `cfi_encoding` for `{:?}`", ty.kind()),
) )
.emit(); .emit();
@ -519,7 +519,7 @@ pub(crate) fn encode_ty<'tcx>(
)] )]
tcx.dcx() tcx.dcx()
.struct_span_err( .struct_span_err(
cfi_encoding.span, cfi_encoding.span(),
format!("invalid `cfi_encoding` for `{:?}`", ty.kind()), format!("invalid `cfi_encoding` for `{:?}`", ty.kind()),
) )
.emit(); .emit();

View file

@ -9,7 +9,7 @@ use std::cell::RefCell;
use std::iter; use std::iter;
use rustc_abi::HasDataLayout; use rustc_abi::HasDataLayout;
use rustc_hir::LangItem; use rustc_hir::{Attribute, LangItem};
use rustc_middle::ty::layout::{ use rustc_middle::ty::layout::{
FnAbiOf, FnAbiOfHelpers, HasTyCtxt, HasTypingEnv, LayoutOf, LayoutOfHelpers, 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, &self,
def_id: stable_mir::DefId, def_id: stable_mir::DefId,
attr: &[stable_mir::Symbol], attr: &[stable_mir::Symbol],
@ -253,30 +253,40 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
let did = tables[def_id]; let did = tables[def_id];
let attr_name: Vec<_> = attr.iter().map(|seg| rustc_span::Symbol::intern(&seg)).collect(); let attr_name: Vec<_> = attr.iter().map(|seg| rustc_span::Symbol::intern(&seg)).collect();
tcx.get_attrs_by_path(did, &attr_name) tcx.get_attrs_by_path(did, &attr_name)
.map(|attribute| { .filter_map(|attribute| {
let attr_str = rustc_hir_pretty::attribute_to_string(&tcx, attribute); if let Attribute::Unparsed(u) = attribute {
let span = attribute.span; let attr_str = rustc_hir_pretty::attribute_to_string(&tcx, attribute);
stable_mir::crate_def::Attribute::new(attr_str, span.stable(&mut *tables)) Some(stable_mir::crate_def::Attribute::new(
attr_str,
u.span.stable(&mut *tables),
))
} else {
None
}
}) })
.collect() .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 mut tables = self.0.borrow_mut();
let tcx = tables.tcx; let tcx = tables.tcx;
let did = tables[def_id]; 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() { 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 { } else {
tcx.attrs_for_def(did).iter().filter(filter_fn) tcx.attrs_for_def(did).iter()
}; };
attrs_iter attrs_iter
.map(|attribute| { .filter_map(|attribute| {
let attr_str = rustc_hir_pretty::attribute_to_string(&tcx, attribute); if let Attribute::Unparsed(u) = attribute {
let span = attribute.span; let attr_str = rustc_hir_pretty::attribute_to_string(&tcx, attribute);
stable_mir::crate_def::Attribute::new(attr_str, span.stable(&mut *tables)) Some(stable_mir::crate_def::Attribute::new(
attr_str,
u.span.stable(&mut *tables),
))
} else {
None
}
}) })
.collect() .collect()
} }

View file

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

View file

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

View file

@ -62,18 +62,18 @@ impl SymbolNamesTest<'_> {
); );
let mangled = tcx.symbol_name(instance); let mangled = tcx.symbol_name(instance);
tcx.dcx().emit_err(TestOutput { tcx.dcx().emit_err(TestOutput {
span: attr.span, span: attr.span(),
kind: Kind::SymbolName, kind: Kind::SymbolName,
content: format!("{mangled}"), content: format!("{mangled}"),
}); });
if let Ok(demangling) = rustc_demangle::try_demangle(mangled.name) { if let Ok(demangling) = rustc_demangle::try_demangle(mangled.name) {
tcx.dcx().emit_err(TestOutput { tcx.dcx().emit_err(TestOutput {
span: attr.span, span: attr.span(),
kind: Kind::Demangling, kind: Kind::Demangling,
content: format!("{demangling}"), content: format!("{demangling}"),
}); });
tcx.dcx().emit_err(TestOutput { tcx.dcx().emit_err(TestOutput {
span: attr.span, span: attr.span(),
kind: Kind::DemanglingAlt, kind: Kind::DemanglingAlt,
content: format!("{demangling:#}"), content: format!("{demangling:#}"),
}); });
@ -82,7 +82,7 @@ impl SymbolNamesTest<'_> {
for attr in tcx.get_attrs(def_id, DEF_PATH) { for attr in tcx.get_attrs(def_id, DEF_PATH) {
tcx.dcx().emit_err(TestOutput { tcx.dcx().emit_err(TestOutput {
span: attr.span, span: attr.span(),
kind: Kind::DefPath, kind: Kind::DefPath,
content: with_no_trimmed_paths!(tcx.def_path_str(def_id)), content: with_no_trimmed_paths!(tcx.def_path_str(def_id)),
}); });

View file

@ -522,7 +522,8 @@ impl<T> Trait<T> for X {
} }
} }
TypeError::TargetFeatureCast(def_id) => { 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( diag.note(
"functions with `#[target_feature]` can only be coerced to `unsafe` function pointers" "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers"
); );

View file

@ -6,7 +6,7 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_errors::codes::*; use rustc_errors::codes::*;
use rustc_errors::{ErrorGuaranteed, struct_span_code_err}; use rustc_errors::{ErrorGuaranteed, struct_span_code_err};
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{AttrArgs, AttrKind, Attribute}; use rustc_hir::{AttrArgs, Attribute};
use rustc_macros::LintDiagnostic; use rustc_macros::LintDiagnostic;
use rustc_middle::bug; use rustc_middle::bug;
use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::print::PrintTraitRefExt as _;
@ -622,7 +622,14 @@ impl<'tcx> OnUnimplementedDirective {
item_def_id: DefId, item_def_id: DefId,
) -> Result<Option<Self>, ErrorGuaranteed> { ) -> Result<Option<Self>, ErrorGuaranteed> {
let result = if let Some(items) = attr.meta_item_list() { 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() { } else if let Some(value) = attr.value_str() {
if !is_diagnostic_namespace_variant { if !is_diagnostic_namespace_variant {
Ok(Some(OnUnimplementedDirective { Ok(Some(OnUnimplementedDirective {
@ -633,7 +640,7 @@ impl<'tcx> OnUnimplementedDirective {
tcx, tcx,
item_def_id, item_def_id,
value, value,
attr.span, attr.span(),
is_diagnostic_namespace_variant, is_diagnostic_namespace_variant,
)?), )?),
notes: Vec::new(), notes: Vec::new(),
@ -659,14 +666,14 @@ impl<'tcx> OnUnimplementedDirective {
Ok(None) Ok(None)
} }
} else if is_diagnostic_namespace_variant { } else if is_diagnostic_namespace_variant {
match &attr.kind { match attr {
AttrKind::Normal(p) if !matches!(p.args, AttrArgs::Empty) => { Attribute::Unparsed(p) if !matches!(p.args, AttrArgs::Empty) => {
if let Some(item_def_id) = item_def_id.as_local() { if let Some(item_def_id) = item_def_id.as_local() {
tcx.emit_node_span_lint( tcx.emit_node_span_lint(
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
tcx.local_def_id_to_hir_id(item_def_id), tcx.local_def_id_to_hir_id(item_def_id),
attr.span, attr.span(),
MalformedOnUnimplementedAttrLint::new(attr.span), MalformedOnUnimplementedAttrLint::new(attr.span()),
); );
} }
} }
@ -675,7 +682,7 @@ impl<'tcx> OnUnimplementedDirective {
tcx.emit_node_span_lint( tcx.emit_node_span_lint(
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
tcx.local_def_id_to_hir_id(item_def_id), tcx.local_def_id_to_hir_id(item_def_id),
attr.span, attr.span(),
MissingOptionsForOnUnimplementedAttr, MissingOptionsForOnUnimplementedAttr,
) )
} }

View file

@ -62,14 +62,17 @@ pub trait Context {
/// Returns the name of given `DefId` /// Returns the name of given `DefId`
fn def_name(&self, def_id: DefId, trimmed: bool) -> Symbol; 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()]`. /// 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. /// Get all tool attributes of a definition.
fn get_all_attrs(&self, def_id: DefId) -> Vec<Attribute>; fn all_tool_attrs(&self, def_id: DefId) -> Vec<Attribute>;
/// Returns printable, human readable form of `Span` /// Returns printable, human readable form of `Span`
fn span_to_string(&self, span: Span) -> String; fn span_to_string(&self, span: Span) -> String;

View file

@ -53,19 +53,22 @@ pub trait CrateDef {
with(|cx| cx.span_of_an_item(def_id)) 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()]`. /// 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(); 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. /// Return all tool attributes of this definition.
fn all_attrs(&self) -> Vec<Attribute> { fn all_tool_attrs(&self) -> Vec<Attribute> {
let def_id = self.def_id(); let def_id = self.def_id();
with(|cx| cx.get_all_attrs(def_id)) with(|cx| cx.all_tool_attrs(def_id))
} }
} }

View file

@ -2737,13 +2737,13 @@ fn add_without_unwanted_attributes<'hir>(
import_parent: Option<DefId>, import_parent: Option<DefId>,
) { ) {
for attr in new_attrs { for attr in new_attrs {
if matches!(attr.kind, hir::AttrKind::DocComment(..)) { if attr.is_doc_comment() {
attrs.push((Cow::Borrowed(attr), import_parent)); attrs.push((Cow::Borrowed(attr), import_parent));
continue; continue;
} }
let mut attr = attr.clone(); let mut attr = attr.clone();
match attr.kind { match attr {
hir::AttrKind::Normal(ref mut normal) => { hir::Attribute::Unparsed(ref mut normal) => {
if let [ident] = &*normal.path.segments { if let [ident] = &*normal.path.segments {
let ident = ident.name; let ident = ident.name;
if ident == sym::doc { 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));
}
}
} }
} }
} }

View file

@ -265,7 +265,7 @@ impl ExternalCrate {
let attr_value = attr.value_str().expect("syntax should already be validated"); let attr_value = attr.value_str().expect("syntax should already be validated");
let Some(prim) = PrimitiveType::from_symbol(attr_value) else { let Some(prim) = PrimitiveType::from_symbol(attr_value) else {
span_bug!( span_bug!(
attr.span, attr.span(),
"primitive `{attr_value}` is not a member of `PrimitiveType`" "primitive `{attr_value}` is not a member of `PrimitiveType`"
); );
}; };

View file

@ -123,7 +123,7 @@ impl HirCollector<'_> {
.iter() .iter()
.find(|attr| attr.doc_str().is_some()) .find(|attr| attr.doc_str().is_some())
.map(|attr| { .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) .unwrap_or(DUMMY_SP)
}; };

View file

@ -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, /// This integer is incremented with every breaking change to the API,
/// and is returned along with the JSON blob as [`Crate::format_version`]. /// 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. /// 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. /// The root of the emitted JSON blob.
/// ///
@ -120,7 +120,9 @@ pub struct Item {
pub docs: Option<String>, 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 /// 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>, 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>, pub attrs: Vec<String>,
/// Information about the items deprecation, if present. /// Information about the items deprecation, if present.
pub deprecation: Option<Deprecation>, pub deprecation: Option<Deprecation>,

@ -1 +1 @@
Subproject commit 1d1d646c06a84c1aa53967b394b7f1218f85db82 Subproject commit ce948f4616e3d4277e30c75c8bb01e094910df39

View file

@ -20,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Att
span_lint( span_lint(
cx, cx,
INLINE_ALWAYS, INLINE_ALWAYS,
attr.span, attr.span(),
format!("you have declared `#[inline(always)]` on `{name}`. This is usually a bad idea"), format!("you have declared `#[inline(always)]` on `{name}`. This is usually a bad idea"),
); );
} }

View file

@ -1,6 +1,7 @@
use rustc_attr_parsing::{find_attr, AttributeKind, ReprAttr};
use rustc_hir::Attribute; use rustc_hir::Attribute;
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::{Span, sym}; use rustc_span::Span;
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs; 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]) { fn check_packed(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute]) {
if let Some(items) = attrs.iter().find_map(|attr| { if let Some(reprs) = find_attr!(attrs, AttributeKind::Repr(r) => r) {
if attr.ident().is_some_and(|ident| matches!(ident.name, sym::repr)) { let packed_span = reprs.iter().find(|(r, _)| matches!(r, ReprAttr::ReprPacked(..))).map(|(_, s)| *s);
attr.meta_item_list()
} else { if let Some(packed_span) = packed_span && !reprs.iter().any(|(x, _)| *x == ReprAttr::ReprC || *x == ReprAttr::ReprRust) {
None 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");
},
);
} }
} }

View file

@ -15,7 +15,7 @@ pub(super) fn check(
) { ) {
if cfg_attr.has_name(sym::clippy) if cfg_attr.has_name(sym::clippy)
&& let Some(ident) = behind_cfg_attr.ident() && 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 Some(items) = behind_cfg_attr.meta_item_list()
{ {
let nb_items = items.len(); let nb_items = items.len();

View file

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