diff --git a/Cargo.lock b/Cargo.lock index 1dc403904ced3..fb4cf235c6f49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3207,6 +3207,7 @@ dependencies = [ "rustc_abi", "rustc_ast", "rustc_ast_pretty", + "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", "rustc_feature", @@ -3215,6 +3216,7 @@ dependencies = [ "rustc_index", "rustc_macros", "rustc_middle", + "rustc_parse", "rustc_session", "rustc_span", "rustc_target", @@ -3263,14 +3265,10 @@ dependencies = [ "rustc_ast", "rustc_ast_pretty", "rustc_data_structures", - "rustc_errors", - "rustc_feature", - "rustc_fluent_macro", - "rustc_lexer", "rustc_macros", "rustc_serialize", - "rustc_session", "rustc_span", + "thin-vec", ] [[package]] @@ -3285,11 +3283,13 @@ dependencies = [ "rustc_errors", "rustc_feature", "rustc_fluent_macro", + "rustc_hir", "rustc_lexer", "rustc_macros", "rustc_serialize", "rustc_session", "rustc_span", + "thin-vec", ] [[package]] @@ -3342,6 +3342,7 @@ dependencies = [ "rustc_expand", "rustc_feature", "rustc_fluent_macro", + "rustc_hir", "rustc_index", "rustc_lexer", "rustc_lint_defs", @@ -3598,6 +3599,7 @@ dependencies = [ "rustc_abi", "rustc_ast", "rustc_ast_pretty", + "rustc_attr_data_structures", "rustc_data_structures", "rustc_error_codes", "rustc_error_messages", @@ -3632,6 +3634,7 @@ dependencies = [ "rustc_errors", "rustc_feature", "rustc_fluent_macro", + "rustc_hir", "rustc_lexer", "rustc_lint_defs", "rustc_macros", @@ -3690,6 +3693,7 @@ dependencies = [ "rustc_abi", "rustc_arena", "rustc_ast", + "rustc_attr_data_structures", "rustc_data_structures", "rustc_hashes", "rustc_index", @@ -3737,6 +3741,7 @@ dependencies = [ "rustc_abi", "rustc_ast", "rustc_ast_pretty", + "rustc_attr_parsing", "rustc_hir", "rustc_span", ] @@ -4244,6 +4249,7 @@ name = "rustc_query_impl" version = "0.0.0" dependencies = [ "measureme", + "rustc_attr_data_structures", "rustc_data_structures", "rustc_errors", "rustc_hashes", @@ -4266,6 +4272,7 @@ dependencies = [ "rustc-rayon-core", "rustc_abi", "rustc_ast", + "rustc_attr_data_structures", "rustc_data_structures", "rustc_errors", "rustc_feature", @@ -4316,6 +4323,7 @@ version = "0.0.0" dependencies = [ "bitflags", "rustc_abi", + "rustc_ast", "rustc_data_structures", "rustc_hir", "rustc_middle", @@ -4412,6 +4420,7 @@ dependencies = [ "punycode", "rustc-demangle", "rustc_abi", + "rustc_ast", "rustc_data_structures", "rustc_errors", "rustc_hashes", diff --git a/compiler/rustc_ast_lowering/Cargo.toml b/compiler/rustc_ast_lowering/Cargo.toml index 3215ce6b0cb47..2ec4f4b055514 100644 --- a/compiler/rustc_ast_lowering/Cargo.toml +++ b/compiler/rustc_ast_lowering/Cargo.toml @@ -11,6 +11,7 @@ doctest = false rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } @@ -19,6 +20,7 @@ rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } +rustc_parse = { path = "../rustc_parse" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs index 88ce6f80e10bd..1d9ca6bb9c8cb 100644 --- a/compiler/rustc_ast_lowering/src/block.rs +++ b/compiler/rustc_ast_lowering/src/block.rs @@ -108,7 +108,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }; let span = self.lower_span(l.span); let source = hir::LocalSource::Normal; - self.lower_attrs(hir_id, &l.attrs); + self.lower_attrs(hir_id, &l.attrs, l.span); self.arena.alloc(hir::LetStmt { hir_id, ty, pat, init, els, span, source }) } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index af53c7ec21572..efbd1711daa92 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -77,9 +77,8 @@ impl<'hir> LoweringContext<'_, 'hir> { self.attrs.insert( ex.hir_id.local_id, &*self.arena.alloc_from_iter( - e.attrs - .iter() - .map(|a| self.lower_attr(a)) + self.lower_attrs_vec(&e.attrs, e.span) + .into_iter() .chain(old_attrs.iter().cloned()), ), ); @@ -98,7 +97,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } let expr_hir_id = self.lower_node_id(e.id); - self.lower_attrs(expr_hir_id, &e.attrs); + self.lower_attrs(expr_hir_id, &e.attrs, e.span); let kind = match &e.kind { ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), @@ -670,7 +669,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let guard = arm.guard.as_ref().map(|cond| self.lower_expr(cond)); let hir_id = self.next_id(); let span = self.lower_span(arm.span); - self.lower_attrs(hir_id, &arm.attrs); + self.lower_attrs(hir_id, &arm.attrs, arm.span); let is_never_pattern = pat.is_never_pattern(); let body = if let Some(body) = &arm.body && !is_never_pattern @@ -839,6 +838,7 @@ impl<'hir> LoweringContext<'_, 'hir> { style: AttrStyle::Outer, span: unstable_span, }], + span, ); } } @@ -1673,7 +1673,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_expr_field(&mut self, f: &ExprField) -> hir::ExprField<'hir> { let hir_id = self.lower_node_id(f.id); - self.lower_attrs(hir_id, &f.attrs); + self.lower_attrs(hir_id, &f.attrs, f.span); hir::ExprField { hir_id, ident: self.lower_ident(f.ident), @@ -1936,7 +1936,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // // Also, add the attributes to the outer returned expr node. let expr = self.expr_drop_temps_mut(for_span, match_expr); - self.lower_attrs(expr.hir_id, &e.attrs); + self.lower_attrs(expr.hir_id, &e.attrs, e.span); expr } @@ -1993,7 +1993,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let val_ident = Ident::with_dummy_span(sym::val); let (val_pat, val_pat_nid) = self.pat_ident(span, val_ident); let val_expr = self.expr_ident(span, val_ident, val_pat_nid); - self.lower_attrs(val_expr.hir_id, &attrs); + self.lower_attrs(val_expr.hir_id, &attrs, span); let continue_pat = self.pat_cf_continue(unstable_span, val_pat); self.arm(continue_pat, val_expr) }; @@ -2024,7 +2024,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let ret_expr = self.checked_return(Some(from_residual_expr)); self.arena.alloc(self.expr(try_span, ret_expr)) }; - self.lower_attrs(ret_expr.hir_id, &attrs); + self.lower_attrs(ret_expr.hir_id, &attrs, ret_expr.span); let break_pat = self.pat_cf_break(try_span, residual_local); self.arm(break_pat, ret_expr) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 1d3db64b47ed9..a4fc4b3e3a121 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -11,7 +11,7 @@ use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::span_bug; use rustc_middle::ty::{ResolverAstLowering, TyCtxt}; use rustc_span::edit_distance::find_best_match_for_name; -use rustc_span::{DesugaringKind, Ident, Span, Symbol, kw, sym}; +use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym}; use smallvec::{SmallVec, smallvec}; use thin_vec::ThinVec; use tracing::instrument; @@ -93,7 +93,8 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> { debug_assert_eq!(self.resolver.node_id_to_def_id[&CRATE_NODE_ID], CRATE_DEF_ID); self.with_lctx(CRATE_NODE_ID, |lctx| { let module = lctx.lower_mod(&c.items, &c.spans); - lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs); + // FIXME(jdonszelman): is dummy span ever a problem here? + lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs, DUMMY_SP); hir::OwnerNode::Crate(module) }) } @@ -157,7 +158,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let mut ident = i.ident; let vis_span = self.lower_span(i.vis.span); let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id); - let attrs = self.lower_attrs(hir_id, &i.attrs); + let attrs = self.lower_attrs(hir_id, &i.attrs, i.span); let kind = self.lower_item_kind(i.span, i.id, hir_id, &mut ident, attrs, vis_span, &i.kind); let item = hir::Item { owner_id: hir_id.expect_owner(), @@ -620,7 +621,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_foreign_item(&mut self, i: &ForeignItem) -> &'hir hir::ForeignItem<'hir> { let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id); let owner_id = hir_id.expect_owner(); - let attrs = self.lower_attrs(hir_id, &i.attrs); + let attrs = self.lower_attrs(hir_id, &i.attrs, i.span); let item = hir::ForeignItem { owner_id, ident: self.lower_ident(i.ident), @@ -678,7 +679,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_variant(&mut self, v: &Variant) -> hir::Variant<'hir> { let hir_id = self.lower_node_id(v.id); - self.lower_attrs(hir_id, &v.attrs); + self.lower_attrs(hir_id, &v.attrs, v.span); hir::Variant { hir_id, def_id: self.local_def_id(v.id), @@ -740,7 +741,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ) -> hir::FieldDef<'hir> { let ty = self.lower_ty(&f.ty, ImplTraitContext::Disallowed(ImplTraitPosition::FieldTy)); let hir_id = self.lower_node_id(f.id); - self.lower_attrs(hir_id, &f.attrs); + self.lower_attrs(hir_id, &f.attrs, f.span); hir::FieldDef { span: self.lower_span(f.span), hir_id, @@ -759,7 +760,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_trait_item(&mut self, i: &AssocItem) -> &'hir hir::TraitItem<'hir> { let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id); - let attrs = self.lower_attrs(hir_id, &i.attrs); + let attrs = self.lower_attrs(hir_id, &i.attrs, i.span); let trait_item_def_id = hir_id.expect_owner(); let (generics, kind, has_default) = match &i.kind { @@ -895,7 +896,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let has_value = true; let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value); let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id); - let attrs = self.lower_attrs(hir_id, &i.attrs); + let attrs = self.lower_attrs(hir_id, &i.attrs, i.span); let (generics, kind) = match &i.kind { AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => self.lower_generics( @@ -1056,7 +1057,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_param(&mut self, param: &Param) -> hir::Param<'hir> { let hir_id = self.lower_node_id(param.id); - self.lower_attrs(hir_id, ¶m.attrs); + self.lower_attrs(hir_id, ¶m.attrs, param.span); hir::Param { hir_id, pat: self.lower_pat(¶m.pat), diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index b865cbedbbb78..1c69937eed07a 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -45,6 +45,7 @@ use std::sync::Arc; use rustc_ast::node_id::NodeMap; use rustc_ast::{self as ast, *}; +use rustc_attr_parsing::{AttributeParser, OmitDoc}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; @@ -60,7 +61,8 @@ use rustc_macros::extension; use rustc_middle::span_bug; use rustc_middle::ty::{ResolverAstLowering, TyCtxt}; use rustc_session::parse::{add_feature_diagnostics, feature_err}; -use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym}; +use rustc_span::symbol::{Ident, Symbol, kw, sym}; +use rustc_span::{DUMMY_SP, DesugaringKind, Span}; use smallvec::{SmallVec, smallvec}; use thin_vec::ThinVec; use tracing::{debug, instrument, trace}; @@ -137,10 +139,13 @@ struct LoweringContext<'a, 'hir> { allow_async_iterator: Arc<[Symbol]>, allow_for_await: Arc<[Symbol]>, allow_async_fn_traits: Arc<[Symbol]>, + + attribute_parser: AttributeParser<'hir>, } impl<'a, 'hir> LoweringContext<'a, 'hir> { fn new(tcx: TyCtxt<'hir>, resolver: &'a mut ResolverAstLowering) -> Self { + let registered_tools = tcx.registered_tools(()).iter().map(|x| x.name).collect(); Self { // Pseudo-globals. tcx, @@ -181,6 +186,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // FIXME(gen_blocks): how does `closure_track_caller`/`async_fn_track_caller` // interact with `gen`/`async gen` blocks allow_async_iterator: [sym::gen_future, sym::async_iterator].into(), + + attribute_parser: AttributeParser::new(tcx.sess, tcx.features(), registered_tools), } } @@ -216,7 +223,6 @@ impl ResolverAstLowering { None } - /// Obtains resolution for a `NodeId` with a single resolution. fn get_partial_res(&self, id: NodeId) -> Option { self.partial_res_map.get(&id).copied() } @@ -855,45 +861,38 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ret } - fn lower_attrs(&mut self, id: HirId, attrs: &[Attribute]) -> &'hir [hir::Attribute] { + fn lower_attrs( + &mut self, + id: HirId, + attrs: &[Attribute], + target_span: Span, + ) -> &'hir [hir::Attribute] { if attrs.is_empty() { &[] } else { + let lowered_attrs = self.lower_attrs_vec(attrs, self.lower_span(target_span)); + debug_assert_eq!(id.owner, self.current_hir_id_owner); - let ret = self.arena.alloc_from_iter(attrs.iter().map(|a| self.lower_attr(a))); - debug_assert!(!ret.is_empty()); - self.attrs.insert(id.local_id, ret); - ret + let ret = self.arena.alloc_from_iter(lowered_attrs); + + // this is possible if an item contained syntactical attribute, + // but none of them parse succesfully or all of them were ignored + // for not being built-in attributes at all. They could be remaining + // unexpanded attributes used as markers in proc-macro derives for example. + // This will have emitted some diagnostics for the misparse, but will then + // not emit the attribute making the list empty. + if ret.is_empty() { + &[] + } else { + self.attrs.insert(id.local_id, ret); + ret + } } } - fn lower_attr(&self, attr: &Attribute) -> hir::Attribute { - // Note that we explicitly do not walk the path. Since we don't really - // lower attributes (we use the AST version) there is nowhere to keep - // the `HirId`s. We don't actually need HIR version of attributes anyway. - // Tokens are also not needed after macro expansion and parsing. - let kind = match attr.kind { - AttrKind::Normal(ref normal) => hir::AttrKind::Normal(Box::new(hir::AttrItem { - unsafety: self.lower_safety(normal.item.unsafety, hir::Safety::Safe), - path: hir::AttrPath { - segments: normal - .item - .path - .segments - .iter() - .map(|i| i.ident) - .collect::>() - .into_boxed_slice(), - span: normal.item.path.span, - }, - args: self.lower_attr_args(&normal.item.args), - })), - AttrKind::DocComment(comment_kind, data) => { - hir::AttrKind::DocComment(comment_kind, data) - } - }; - - hir::Attribute { kind, id: attr.id, style: attr.style, span: self.lower_span(attr.span) } + fn lower_attrs_vec(&self, attrs: &[Attribute], target_span: Span) -> Vec { + self.attribute_parser + .parse_attribute_list(attrs, target_span, OmitDoc::Lower, |s| self.lower_span(s)) } fn alias_attrs(&mut self, id: HirId, target_id: HirId) { @@ -905,34 +904,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } - fn lower_attr_args(&self, args: &AttrArgs) -> hir::AttrArgs { - match args { - AttrArgs::Empty => hir::AttrArgs::Empty, - AttrArgs::Delimited(args) => hir::AttrArgs::Delimited(self.lower_delim_args(args)), - // This is an inert key-value attribute - it will never be visible to macros - // after it gets lowered to HIR. Therefore, we can extract literals to handle - // nonterminals in `#[doc]` (e.g. `#[doc = $e]`). - &AttrArgs::Eq { eq_span, ref expr } => { - // In valid code the value always ends up as a single literal. Otherwise, a dummy - // literal suffices because the error is handled elsewhere. - let lit = if let ExprKind::Lit(token_lit) = expr.kind - && let Ok(lit) = MetaItemLit::from_token_lit(token_lit, expr.span) - { - lit - } else { - let guar = self.dcx().has_errors().unwrap(); - MetaItemLit { - symbol: kw::Empty, - suffix: None, - kind: LitKind::Err(guar), - span: DUMMY_SP, - } - }; - hir::AttrArgs::Eq { eq_span, expr: lit } - } - } - } - fn lower_delim_args(&self, args: &DelimArgs) -> DelimArgs { DelimArgs { dspan: args.dspan, delim: args.delim, tokens: args.tokens.flattened() } } @@ -1845,7 +1816,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let (name, kind) = self.lower_generic_param_kind(param, source); let hir_id = self.lower_node_id(param.id); - self.lower_attrs(hir_id, ¶m.attrs); + self.lower_attrs(hir_id, ¶m.attrs, param.span()); hir::GenericParam { hir_id, def_id: self.local_def_id(param.id), diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index e1f3afbcf5908..2dcfe7c745da5 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -93,7 +93,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let fs = self.arena.alloc_from_iter(fields.iter().map(|f| { let hir_id = self.lower_node_id(f.id); - self.lower_attrs(hir_id, &f.attrs); + self.lower_attrs(hir_id, &f.attrs, f.span); hir::PatField { hir_id, diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 5a0ec865f9d4d..25944392a52a9 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -207,8 +207,6 @@ ast_passes_precise_capturing_duplicated = duplicate `use<...>` precise capturing ast_passes_precise_capturing_not_allowed_here = `use<...>` precise capturing syntax not allowed in {$loc} -ast_passes_stability_outside_std = stability attributes may not be used outside of the standard library - ast_passes_static_without_body = free static item without body .suggestion = provide a definition for the static diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 6eb9bb1c0dafa..9f0d2325475a2 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -732,13 +732,6 @@ pub(crate) struct AssociatedSuggestion2 { pub potential_assoc: Ident, } -#[derive(Diagnostic)] -#[diag(ast_passes_stability_outside_std, code = E0734)] -pub(crate) struct StabilityOutsideStd { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(ast_passes_feature_on_non_nightly, code = E0554)] pub(crate) struct FeatureOnNonNightly { diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index e5d8013058f65..0f80e49320ed4 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -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) { diff --git a/compiler/rustc_attr_data_structures/Cargo.toml b/compiler/rustc_attr_data_structures/Cargo.toml index 19d0d5a306d6e..b18923c337ff8 100644 --- a/compiler/rustc_attr_data_structures/Cargo.toml +++ b/compiler/rustc_attr_data_structures/Cargo.toml @@ -5,16 +5,12 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -rustc_abi = { path = "../rustc_abi" } -rustc_ast = { path = "../rustc_ast" } -rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_data_structures = { path = "../rustc_data_structures" } -rustc_errors = { path = "../rustc_errors" } -rustc_feature = { path = "../rustc_feature" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } -rustc_lexer = { path = "../rustc_lexer" } -rustc_macros = { path = "../rustc_macros" } -rustc_serialize = { path = "../rustc_serialize" } -rustc_session = { path = "../rustc_session" } -rustc_span = { path = "../rustc_span" } +rustc_abi = {path = "../rustc_abi"} +rustc_ast = {path = "../rustc_ast"} +rustc_ast_pretty = {path = "../rustc_ast_pretty"} +rustc_data_structures = {path = "../rustc_data_structures"} +rustc_macros = {path = "../rustc_macros"} +rustc_serialize = {path = "../rustc_serialize"} +rustc_span = {path = "../rustc_span"} +thin-vec = "0.2.12" # tidy-alphabetical-end diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index b4027a096c532..9ac8de0227d79 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -1,9 +1,12 @@ use rustc_abi::Align; -use rustc_ast as ast; -use rustc_macros::{Decodable, Encodable, HashStable_Generic}; +use rustc_ast::token::CommentKind; +use rustc_ast::{self as ast, AttrStyle}; +use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute}; +use rustc_span::hygiene::Transparency; use rustc_span::{Span, Symbol}; +use thin_vec::ThinVec; -use crate::RustcVersion; +use crate::{DefaultBodyStability, PartialConstStability, PrintAttribute, RustcVersion, Stability}; #[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] pub enum InlineAttr { @@ -54,7 +57,7 @@ impl OptimizeAttr { } } -#[derive(Clone, Debug, Encodable, Decodable)] +#[derive(Clone, Debug, Encodable, Decodable, HashStable_Generic, PrintAttribute)] pub enum DiagnosticAttribute { // tidy-alphabetical-start DoNotRecommend, @@ -62,7 +65,7 @@ pub enum DiagnosticAttribute { // tidy-alphabetical-end } -#[derive(PartialEq, Debug, Encodable, Decodable, Copy, Clone)] +#[derive(PartialEq, Debug, Encodable, Decodable, Copy, Clone, HashStable_Generic, PrintAttribute)] pub enum ReprAttr { ReprInt(IntType), ReprRust, @@ -71,6 +74,8 @@ pub enum ReprAttr { ReprSimd, ReprTransparent, ReprAlign(Align), + // this one is just so we can emit a lint for it + ReprEmpty, } pub use ReprAttr::*; @@ -80,13 +85,13 @@ pub enum TransparencyError { } #[derive(Eq, PartialEq, Debug, Copy, Clone)] -#[derive(Encodable, Decodable)] +#[derive(Encodable, Decodable, HashStable_Generic, PrintAttribute)] pub enum IntType { SignedInt(ast::IntTy), UnsignedInt(ast::UintTy), } -#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)] +#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic, PrintAttribute)] pub struct Deprecation { pub since: DeprecatedSince, /// The note to issue a reason. @@ -98,7 +103,7 @@ pub struct Deprecation { } /// Release in which an API is deprecated. -#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)] +#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic, PrintAttribute)] pub enum DeprecatedSince { RustcVersion(RustcVersion), /// Deprecated in the future ("to be determined"). @@ -132,3 +137,61 @@ impl Deprecation { matches!(self.since, DeprecatedSince::RustcVersion(_)) } } + +/// Attributes represent parsed, *built in*, inert attributes. That means, +/// attributes that are not actually ever expanded. +/// For more information on this, see the module docs on the rustc_attr_parsing crate. +/// They're instead used as markers, to guide the compilation process in various way in most every stage of the compiler. +/// These are kept around after the AST, into the HIR and further on. +/// +/// The word parsed could be a little misleading here, because the parser already parses +/// attributes early on. However, the result, an [`ast::Attribute`] +/// is only parsed at a high level, still containing a token stream in many cases. That is +/// because the structure of the contents varies from attribute to attribute. +/// With a parsed attribute I mean that each attribute is processed individually into a +/// final structure, which on-site (the place where the attribute is useful for, think the +/// the place where `must_use` is checked) little to no extra parsing or validating needs to +/// happen. +/// +/// For more docs, look in [`rustc_attr`](https://doc.rust-lang.org/stable/nightly-rustc/rustc_attr/index.html) +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum AttributeKind { + // tidy-alphabetical-start + AllowConstFnUnstable(ThinVec), + AllowInternalUnstable(ThinVec<(Symbol, Span)>), + BodyStability { + stability: DefaultBodyStability, + /// Span of the `#[rustc_default_body_unstable(...)]` attribute + span: Span, + }, + Confusables { + symbols: ThinVec, + // 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 +} diff --git a/compiler/rustc_attr_data_structures/src/lib.rs b/compiler/rustc_attr_data_structures/src/lib.rs index 4f204aeab640f..e4bb459e6df5a 100644 --- a/compiler/rustc_attr_data_structures/src/lib.rs +++ b/compiler/rustc_attr_data_structures/src/lib.rs @@ -10,7 +10,142 @@ mod attributes; mod stability; mod version; +use std::num::NonZero; + pub use attributes::*; -pub(crate) use rustc_session::HashStableContext; +use rustc_abi::Align; +use rustc_ast::token::CommentKind; +use rustc_ast::{AttrStyle, IntTy, UintTy}; +use rustc_ast_pretty::pp::Printer; +use rustc_span::hygiene::Transparency; +use rustc_span::{Span, Symbol}; pub use stability::*; +use thin_vec::ThinVec; pub use version::*; + +/// Requirements for a `StableHashingContext` to be used in this crate. +/// This is a hack to allow using the `HashStable_Generic` derive macro +/// instead of implementing everything in `rustc_middle`. +pub trait HashStableContext: rustc_ast::HashStableContext + rustc_abi::HashStableContext {} + +/// This trait is used to print attributes in `rustc_hir_pretty`. +/// +/// For structs and enums it can be derived using [`rustc_macros::PrintAttribute`]. +/// The output will look a lot like a `Debug` implementation, but fields of several types +/// like [`Span`]s and empty tuples, are gracefully skipped so they don't clutter the +/// representation much. +pub trait PrintAttribute { + fn print_something(&self) -> bool; + fn print_attribute(&self, p: &mut Printer); +} + +impl 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 PrintAttribute for Option { + 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 PrintAttribute for ThinVec { + 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); +print_debug!(UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency); diff --git a/compiler/rustc_attr_data_structures/src/stability.rs b/compiler/rustc_attr_data_structures/src/stability.rs index c2213fc9ed881..c0ca08a60f8b3 100644 --- a/compiler/rustc_attr_data_structures/src/stability.rs +++ b/compiler/rustc_attr_data_structures/src/stability.rs @@ -1,9 +1,9 @@ use std::num::NonZero; -use rustc_macros::{Decodable, Encodable, HashStable_Generic}; +use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute}; use rustc_span::{Symbol, sym}; -use crate::RustcVersion; +use crate::{PrintAttribute, RustcVersion}; /// The version placeholder that recently stabilized features contain inside the /// `since` field of the `#[stable]` attribute. @@ -21,7 +21,7 @@ pub const VERSION_PLACEHOLDER: &str = concat!("CURRENT_RUSTC_VERSIO", "N"); /// - `#[stable]` /// - `#[unstable]` #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] -#[derive(HashStable_Generic)] +#[derive(HashStable_Generic, PrintAttribute)] pub struct Stability { pub level: StabilityLevel, pub feature: Symbol, @@ -43,7 +43,7 @@ impl Stability { /// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes. #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] -#[derive(HashStable_Generic)] +#[derive(HashStable_Generic, PrintAttribute)] pub struct ConstStability { pub level: StabilityLevel, pub feature: Symbol, @@ -83,7 +83,7 @@ impl ConstStability { /// Excludes `const_stable_indirect`. This is necessary because when `-Zforce-unstable-if-unmarked` /// is set, we need to encode standalone `#[rustc_const_stable_indirect]` attributes #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] -#[derive(HashStable_Generic)] +#[derive(HashStable_Generic, PrintAttribute)] pub struct PartialConstStability { pub level: StabilityLevel, pub feature: Symbol, @@ -103,7 +103,7 @@ impl PartialConstStability { /// The available stability levels. #[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)] -#[derive(HashStable_Generic)] +#[derive(HashStable_Generic, PrintAttribute)] pub enum StabilityLevel { /// `#[unstable]` Unstable { @@ -145,7 +145,7 @@ pub enum StabilityLevel { /// Rust release in which a feature is stabilized. #[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, PartialOrd, Ord, Hash)] -#[derive(HashStable_Generic)] +#[derive(HashStable_Generic, PrintAttribute)] pub enum StableSince { /// also stores the original symbol for printing Version(RustcVersion), @@ -171,7 +171,7 @@ impl StabilityLevel { } #[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)] -#[derive(HashStable_Generic)] +#[derive(HashStable_Generic, PrintAttribute)] pub enum UnstableReason { None, Default, @@ -180,7 +180,7 @@ pub enum UnstableReason { /// Represents the `#[rustc_default_body_unstable]` attribute. #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] -#[derive(HashStable_Generic)] +#[derive(HashStable_Generic, PrintAttribute)] pub struct DefaultBodyStability { pub level: StabilityLevel, pub feature: Symbol, diff --git a/compiler/rustc_attr_data_structures/src/version.rs b/compiler/rustc_attr_data_structures/src/version.rs index 6be875ad4be86..69b0e041d819d 100644 --- a/compiler/rustc_attr_data_structures/src/version.rs +++ b/compiler/rustc_attr_data_structures/src/version.rs @@ -1,9 +1,13 @@ use std::fmt::{self, Display}; -use rustc_macros::{Decodable, Encodable, HashStable_Generic, current_rustc_version}; +use rustc_macros::{ + Decodable, Encodable, HashStable_Generic, PrintAttribute, current_rustc_version, +}; + +use crate::PrintAttribute; #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[derive(HashStable_Generic)] +#[derive(HashStable_Generic, PrintAttribute)] pub struct RustcVersion { pub major: u16, pub minor: u16, diff --git a/compiler/rustc_attr_parsing/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml index f681e9397d9f1..c335eeb5f7120 100644 --- a/compiler/rustc_attr_parsing/Cargo.toml +++ b/compiler/rustc_attr_parsing/Cargo.toml @@ -13,9 +13,11 @@ rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } +rustc_hir = { path = "../rustc_hir" } rustc_lexer = { path = "../rustc_lexer" } rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } +thin-vec = "0.2.12" # tidy-alphabetical-end diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index faa2865cb9130..45174c9582d33 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -6,6 +6,8 @@ attr_parsing_deprecated_item_suggestion = .help = add `#![feature(deprecated_suggestion)]` to the crate root .note = see #94785 for more details +attr_parsing_empty_confusables = + expected at least one confusable name attr_parsing_expected_one_cfg_pattern = expected 1 cfg-pattern @@ -21,8 +23,8 @@ attr_parsing_expects_feature_list = attr_parsing_expects_features = `{$name}` expects feature names -attr_parsing_incorrect_meta_item = - incorrect meta item +attr_parsing_incorrect_meta_item = expected a quoted string literal +attr_parsing_incorrect_meta_item_suggestion = consider surrounding this with quotes attr_parsing_incorrect_repr_format_align_one_arg = incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses @@ -88,18 +90,20 @@ attr_parsing_multiple_stability_levels = attr_parsing_non_ident_feature = 'feature' is not an identifier +attr_parsing_repr_ident = + meta item in `repr` must be an identifier + attr_parsing_rustc_allowed_unstable_pairing = `rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute -attr_parsing_rustc_const_stable_indirect_pairing = - `const_stable_indirect` attribute does not make sense on `rustc_const_stable` function, its behavior is already implied - attr_parsing_rustc_promotable_pairing = `rustc_promotable` attribute must be paired with either a `rustc_const_unstable` or a `rustc_const_stable` attribute attr_parsing_soft_no_args = `soft` should not have any arguments +attr_parsing_stability_outside_std = stability attributes may not be used outside of the standard library + attr_parsing_unknown_meta_item = unknown meta item '{$item}' .label = expected one of {$expected} @@ -107,6 +111,10 @@ attr_parsing_unknown_meta_item = attr_parsing_unknown_version_literal = unknown version literal format, assuming it refers to a future version +attr_parsing_unrecognized_repr_hint = + unrecognized representation hint + .help = valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize` + attr_parsing_unstable_cfg_target_compact = compact `cfg(target(..))` is experimental and subject to change @@ -122,3 +130,8 @@ attr_parsing_unsupported_literal_generic = unsupported literal attr_parsing_unsupported_literal_suggestion = consider removing the prefix + +attr_parsing_unused_multiple = + multiple `{$name}` attributes + .suggestion = remove this attribute + .note = attribute also specified here diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs index 13d246b08a8f4..d37ede86cfd2a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs +++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs @@ -1,49 +1,67 @@ -use rustc_ast::attr::{AttributeExt, filter_by_name}; -use rustc_session::Session; -use rustc_span::{Symbol, sym}; +use std::iter; +use rustc_attr_data_structures::AttributeKind; +use rustc_span::{Span, Symbol, sym}; + +use super::{CombineAttributeParser, ConvertFn}; +use crate::context::AcceptContext; +use crate::parser::ArgParser; use crate::session_diagnostics; -pub fn allow_internal_unstable( - sess: &Session, - attrs: &[impl AttributeExt], -) -> impl Iterator { - allow_unstable(sess, attrs, sym::allow_internal_unstable) +pub(crate) struct AllowInternalUnstableParser; +impl CombineAttributeParser for AllowInternalUnstableParser { + const PATH: &'static [rustc_span::Symbol] = &[sym::allow_internal_unstable]; + type Item = (Symbol, Span); + const CONVERT: ConvertFn = AttributeKind::AllowInternalUnstable; + + fn extend<'a>( + cx: &'a AcceptContext<'a>, + args: &'a ArgParser<'a>, + ) -> impl IntoIterator + 'a { + parse_unstable(cx, args, Self::PATH[0]).into_iter().zip(iter::repeat(cx.attr_span)) + } } -pub fn rustc_allow_const_fn_unstable( - sess: &Session, - attrs: &[impl AttributeExt], -) -> impl Iterator { - allow_unstable(sess, attrs, sym::rustc_allow_const_fn_unstable) +pub(crate) struct AllowConstFnUnstableParser; +impl CombineAttributeParser for AllowConstFnUnstableParser { + const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_allow_const_fn_unstable]; + type Item = Symbol; + const CONVERT: ConvertFn = AttributeKind::AllowConstFnUnstable; + + fn extend<'a>( + cx: &'a AcceptContext<'a>, + args: &'a ArgParser<'a>, + ) -> impl IntoIterator + 'a { + parse_unstable(cx, args, Self::PATH[0]) + } } -fn allow_unstable( - sess: &Session, - attrs: &[impl AttributeExt], +fn parse_unstable<'a>( + cx: &AcceptContext<'_>, + args: &'a ArgParser<'a>, symbol: Symbol, -) -> impl Iterator { - let attrs = filter_by_name(attrs, symbol); - let list = attrs - .filter_map(move |attr| { - attr.meta_item_list().or_else(|| { - sess.dcx().emit_err(session_diagnostics::ExpectsFeatureList { - span: attr.span(), - name: symbol.to_ident_string(), - }); - None - }) - }) - .flatten(); - - list.into_iter().filter_map(move |it| { - let name = it.ident().map(|ident| ident.name); - if name.is_none() { - sess.dcx().emit_err(session_diagnostics::ExpectsFeatures { - span: it.span(), +) -> impl IntoIterator { + let mut res = Vec::new(); + + let Some(list) = args.list() else { + cx.emit_err(session_diagnostics::ExpectsFeatureList { + span: cx.attr_span, + name: symbol.to_ident_string(), + }); + return res; + }; + + for param in list.mixed() { + let param_span = param.span(); + if let Some(ident) = param.meta_item().and_then(|i| i.word_without_args()) { + res.push(ident.name); + } else { + cx.emit_err(session_diagnostics::ExpectsFeatures { + span: param_span, name: symbol.to_ident_string(), }); } - name - }) + } + + res } diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index bb9aaaa2fea9e..0d6d521b40c61 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -1,6 +1,4 @@ -//! Parsing and validation of builtin attributes - -use rustc_ast::{self as ast, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId}; +use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId}; use rustc_ast_pretty::pprust; use rustc_attr_data_structures::RustcVersion; use rustc_feature::{Features, GatedCfg, find_gated_cfg}; @@ -9,10 +7,11 @@ use rustc_session::config::ExpectedValues; use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::UNEXPECTED_CFGS; use rustc_session::parse::feature_err; -use rustc_span::{Span, Symbol, kw, sym}; +use rustc_span::symbol::kw; +use rustc_span::{Span, Symbol, sym}; -use crate::util::UnsupportedLiteralReason; -use crate::{fluent_generated, parse_version, session_diagnostics}; +use crate::session_diagnostics::{self, UnsupportedLiteralReason}; +use crate::{fluent_generated, parse_version}; #[derive(Clone, Debug)] pub struct Condition { @@ -25,7 +24,7 @@ pub struct Condition { /// Tests if a cfg-pattern matches the cfg set pub fn cfg_matches( - cfg: &ast::MetaItemInner, + cfg: &MetaItemInner, sess: &Session, lint_node_id: NodeId, features: Option<&Features>, @@ -80,7 +79,7 @@ fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Fea /// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to /// evaluate individual items. pub fn eval_condition( - cfg: &ast::MetaItemInner, + cfg: &MetaItemInner, sess: &Session, features: Option<&Features>, eval: &mut impl FnMut(Condition) -> bool, @@ -88,8 +87,8 @@ pub fn eval_condition( let dcx = sess.dcx(); let cfg = match cfg { - ast::MetaItemInner::MetaItem(meta_item) => meta_item, - ast::MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => { + MetaItemInner::MetaItem(meta_item) => meta_item, + MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => { if let Some(features) = features { // we can't use `try_gate_cfg` as symbols don't differentiate between `r#true` // and `true`, and we want to keep the former working without feature gate @@ -118,7 +117,7 @@ pub fn eval_condition( }; match &cfg.kind { - ast::MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => { + MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => { try_gate_cfg(sym::version, cfg.span, sess, features); let (min_version, span) = match &mis[..] { [MetaItemInner::Lit(MetaItemLit { kind: LitKind::Str(sym, ..), span, .. })] => { @@ -150,7 +149,7 @@ pub fn eval_condition( RustcVersion::CURRENT >= min_version } } - ast::MetaItemKind::List(mis) => { + MetaItemKind::List(mis) => { for mi in mis.iter() { if mi.meta_item_or_bool().is_none() { dcx.emit_err(session_diagnostics::UnsupportedLiteral { @@ -209,12 +208,7 @@ pub fn eval_condition( seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name)); } - res & eval_condition( - &ast::MetaItemInner::MetaItem(mi), - sess, - features, - eval, - ) + res & eval_condition(&MetaItemInner::MetaItem(mi), sess, features, eval) }) } _ => { @@ -226,7 +220,7 @@ pub fn eval_condition( } } } - ast::MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => { + MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => { dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span }); true } @@ -239,7 +233,7 @@ pub fn eval_condition( }); true } - ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => { + MetaItemKind::Word | MetaItemKind::NameValue(..) => { let ident = cfg.ident().expect("multi-segment cfg predicate"); eval(Condition { name: ident.name, diff --git a/compiler/rustc_attr_parsing/src/attributes/confusables.rs b/compiler/rustc_attr_parsing/src/attributes/confusables.rs index 2ced759fd8883..6cff952fcf229 100644 --- a/compiler/rustc_attr_parsing/src/attributes/confusables.rs +++ b/compiler/rustc_attr_parsing/src/attributes/confusables.rs @@ -1,21 +1,58 @@ -//! Parsing and validation of builtin attributes +use rustc_attr_data_structures::AttributeKind; +use rustc_span::{Span, Symbol, sym}; +use thin_vec::ThinVec; -use rustc_ast::MetaItemInner; -use rustc_ast::attr::AttributeExt; -use rustc_span::Symbol; +use super::{AcceptMapping, AttributeParser}; +use crate::context::FinalizeContext; +use crate::session_diagnostics; -/// Read the content of a `rustc_confusables` attribute, and return the list of candidate names. -pub fn parse_confusables(attr: &impl AttributeExt) -> Option> { - let metas = attr.meta_item_list()?; +#[derive(Default)] +pub(crate) struct ConfusablesParser { + confusables: ThinVec, + first_span: Option, +} + +impl AttributeParser for ConfusablesParser { + const ATTRIBUTES: AcceptMapping = &[(&[sym::rustc_confusables], |this, cx, args| { + let Some(list) = args.list() else { + // FIXME(jdonszelmann): error when not a list? Bring validation code here. + // NOTE: currently subsequent attributes are silently ignored using + // tcx.get_attr(). + return; + }; + + if list.is_empty() { + cx.emit_err(session_diagnostics::EmptyConfusables { span: cx.attr_span }); + } + + for param in list.mixed() { + let span = param.span(); - let mut candidates = Vec::new(); + 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; + }; - for meta in metas { - let MetaItemInner::Lit(meta_lit) = meta else { + this.confusables.push(lit.symbol); + } + + this.first_span.get_or_insert(cx.attr_span); + })]; + + fn finalize(self, _cx: &FinalizeContext<'_>) -> Option { + if self.confusables.is_empty() { return None; - }; - candidates.push(meta_lit.symbol); - } + } - Some(candidates) + Some(AttributeKind::Confusables { + symbols: self.confusables, + first_span: self.first_span.unwrap(), + }) + } } diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index d7415a7198fd6..7d1417446b21d 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -1,121 +1,122 @@ -//! Parsing and validation of builtin attributes - -use rustc_ast::attr::AttributeExt; -use rustc_ast::{MetaItem, MetaItemInner}; -use rustc_ast_pretty::pprust; -use rustc_attr_data_structures::{DeprecatedSince, Deprecation}; -use rustc_feature::Features; -use rustc_session::Session; +use rustc_attr_data_structures::{AttributeKind, DeprecatedSince, Deprecation}; +use rustc_span::symbol::Ident; use rustc_span::{Span, Symbol, sym}; -use super::util::UnsupportedLiteralReason; -use crate::{parse_version, session_diagnostics}; +use super::SingleAttributeParser; +use super::util::parse_version; +use crate::context::AcceptContext; +use crate::parser::ArgParser; +use crate::session_diagnostics; +use crate::session_diagnostics::UnsupportedLiteralReason; -/// Finds the deprecation attribute. `None` if none exists. -pub fn find_deprecation( - sess: &Session, - features: &Features, - attrs: &[impl AttributeExt], -) -> Option<(Deprecation, Span)> { - let mut depr: Option<(Deprecation, Span)> = None; - let is_rustc = features.staged_api(); +pub(crate) struct DeprecationParser; - 'outer: for attr in attrs { - if !attr.has_name(sym::deprecated) { - continue; +fn get( + cx: &AcceptContext<'_>, + ident: Ident, + param_span: Span, + arg: &ArgParser<'_>, + item: &Option, +) -> Option { + 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 { + let features = cx.features(); let mut since = None; let mut note = None; let mut suggestion = None; - if attr.is_doc_comment() { - continue; - } else if attr.is_word() { - } else if let Some(value) = attr.value_str() { - note = Some(value) - } else if let Some(list) = attr.meta_item_list() { - let get = |meta: &MetaItem, item: &mut Option| { - if item.is_some() { - sess.dcx().emit_err(session_diagnostics::MultipleItem { - span: meta.span, - item: pprust::path_to_string(&meta.path), + let is_rustc = features.staged_api(); + + if let Some(value) = args.name_value() + && let Some(value_str) = value.value_as_str() + { + note = Some(value_str) + } else if let Some(list) = args.list() { + for param in list.mixed() { + let param_span = param.span(); + let Some(param) = param.meta_item() else { + cx.emit_err(session_diagnostics::UnsupportedLiteral { + span: param_span, + reason: UnsupportedLiteralReason::DeprecatedKvPair, + is_bytestr: false, + start_point_span: cx.sess().source_map().start_point(param_span), }); - return false; - } - if let Some(v) = meta.value_str() { - *item = Some(v); - true - } else { - if let Some(lit) = meta.name_value_literal() { - sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral { - span: lit.span, - reason: UnsupportedLiteralReason::DeprecatedString, - is_bytestr: lit.kind.is_bytestr(), - start_point_span: sess.source_map().start_point(lit.span), - }); - } else { - sess.dcx() - .emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span }); - } - false - } - }; + return None; + }; - for meta in &list { - match meta { - MetaItemInner::MetaItem(mi) => match mi.name_or_empty() { - sym::since => { - if !get(mi, &mut since) { - continue 'outer; - } - } - sym::note => { - if !get(mi, &mut note) { - continue 'outer; - } - } - sym::suggestion => { - if !features.deprecated_suggestion() { - sess.dcx().emit_err( - session_diagnostics::DeprecatedItemSuggestion { - span: mi.span, - is_nightly: sess.is_nightly_build(), - details: (), - }, - ); - } + let (ident, arg) = param.word_or_empty(); - 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"] - }, + match ident.name { + sym::since => { + since = Some(get(cx, ident, param_span, arg, &since)?); + } + sym::note => { + note = Some(get(cx, ident, param_span, arg, ¬e)?); + } + sym::suggestion => { + if !features.deprecated_suggestion() { + cx.emit_err(session_diagnostics::DeprecatedItemSuggestion { + span: param_span, + is_nightly: cx.sess().is_nightly_build(), + details: (), }); - continue 'outer; } - }, - MetaItemInner::Lit(lit) => { - sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral { - span: lit.span, - reason: UnsupportedLiteralReason::DeprecatedKvPair, - is_bytestr: false, - start_point_span: sess.source_map().start_point(lit.span), + + suggestion = Some(get(cx, ident, param_span, arg, &suggestion)?); + } + _ => { + cx.emit_err(session_diagnostics::UnknownMetaItem { + span: param_span, + item: ident.to_string(), + expected: if features.deprecated_suggestion() { + &["since", "note", "suggestion"] + } else { + &["since", "note"] + }, }); - continue 'outer; + return None; } } } - } else { - continue; } let since = if let Some(since) = since { @@ -126,23 +127,24 @@ pub fn find_deprecation( } else if let Some(version) = parse_version(since) { DeprecatedSince::RustcVersion(version) } else { - sess.dcx().emit_err(session_diagnostics::InvalidSince { span: attr.span() }); + cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span }); DeprecatedSince::Err } } else if is_rustc { - sess.dcx().emit_err(session_diagnostics::MissingSince { span: attr.span() }); + cx.emit_err(session_diagnostics::MissingSince { span: cx.attr_span }); DeprecatedSince::Err } else { DeprecatedSince::Unspecified }; if is_rustc && note.is_none() { - sess.dcx().emit_err(session_diagnostics::MissingNote { span: attr.span() }); - continue; + cx.emit_err(session_diagnostics::MissingNote { span: cx.attr_span }); + return None; } - depr = Some((Deprecation { since, note, suggestion }, attr.span())); + Some(AttributeKind::Deprecation { + deprecation: Deprecation { since, note, suggestion }, + span: cx.attr_span, + }) } - - depr } diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index a78e0b54b64aa..6ecd6b4d7dbb7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -1,17 +1,152 @@ -mod allow_unstable; -mod cfg; -mod confusables; -mod deprecation; -mod repr; -mod stability; -mod transparency; - -pub mod util; - -pub use allow_unstable::*; -pub use cfg::*; -pub use confusables::*; -pub use deprecation::*; -pub use repr::*; -pub use stability::*; -pub use transparency::*; +//! This module defines traits for attribute parsers, little state machines that recognize and parse +//! attributes out of a longer list of attributes. The main trait is called [`AttributeParser`]. +//! You can find more docs about [`AttributeParser`]s on the trait itself. +//! However, for many types of attributes, implementing [`AttributeParser`] is not necessary. +//! It allows for a lot of flexibility you might not want. +//! +//! Specifically, you might not care about managing the state of your [`AttributeParser`] +//! state machine yourself. In this case you can choose to implement: +//! +//! - [`SingleAttributeParser`]: makes it easy to implement an attribute which should error if it +//! appears more than once in a list of attributes +//! - [`CombineAttributeParser`]: makes it easy to implement an attribute which should combine the +//! contents of attributes, if an attribute appear multiple times in a list +//! +//! Attributes should be added to [`ATTRIBUTE_MAPPING`](crate::context::ATTRIBUTE_MAPPING) to be parsed. + +use std::marker::PhantomData; + +use rustc_attr_data_structures::AttributeKind; +use rustc_span::Span; +use thin_vec::ThinVec; + +use crate::context::{AcceptContext, FinalizeContext}; +use crate::parser::ArgParser; + +pub(crate) mod allow_unstable; +pub(crate) mod cfg; +pub(crate) mod confusables; +pub(crate) mod deprecation; +pub(crate) mod repr; +pub(crate) mod stability; +pub(crate) mod transparency; +pub(crate) mod util; + +type AcceptFn = fn(&mut T, &AcceptContext<'_>, &ArgParser<'_>); +type AcceptMapping = &'static [(&'static [rustc_span::Symbol], AcceptFn)]; + +/// 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; + + /// 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; +} + +/// 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 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; +} + +pub(crate) struct Single(PhantomData, Option<(AttributeKind, Span)>); + +impl Default for Single { + fn default() -> Self { + Self(Default::default(), Default::default()) + } +} + +impl AttributeParser for Single { + const ATTRIBUTES: AcceptMapping = &[(T::PATH, |group: &mut Single, 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 { + Some(self.1?.0) + } +} + +type ConvertFn = fn(ThinVec) -> 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 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; + + /// 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 + 'a; +} + +pub(crate) struct Combine( + PhantomData, + ThinVec<::Item>, +); + +impl Default for Combine { + fn default() -> Self { + Self(Default::default(), Default::default()) + } +} + +impl AttributeParser for Combine { + const ATTRIBUTES: AcceptMapping = + &[(T::PATH, |group: &mut Combine, cx, args| group.1.extend(T::extend(cx, args)))]; + + fn finalize(self, _cx: &FinalizeContext<'_>) -> Option { + if self.1.is_empty() { None } else { Some(T::CONVERT(self.1)) } + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index 28c381160b874..26ca637faec68 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -1,15 +1,13 @@ -//! Parsing and validation of builtin attributes - use rustc_abi::Align; -use rustc_ast::attr::AttributeExt; -use rustc_ast::{self as ast, MetaItemKind}; -use rustc_attr_data_structures::IntType; -use rustc_attr_data_structures::ReprAttr::*; -use rustc_session::Session; -use rustc_span::{Symbol, sym}; +use rustc_ast::{IntTy, LitIntType, LitKind, UintTy}; +use rustc_attr_data_structures::{AttributeKind, IntType, ReprAttr}; +use rustc_span::{Span, Symbol, sym}; -use crate::ReprAttr; -use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause}; +use super::{CombineAttributeParser, ConvertFn}; +use crate::context::AcceptContext; +use crate::parser::{ArgParser, MetaItemListParser, MetaItemParser}; +use crate::session_diagnostics; +use crate::session_diagnostics::IncorrectReprFormatGenericCause; /// Parse #[repr(...)] forms. /// @@ -18,185 +16,216 @@ use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause}; /// the same discriminant size that the corresponding C enum would or C /// structure layout, `packed` to remove padding, and `transparent` to delegate representation /// concerns to the only non-ZST field. -pub fn find_repr_attrs(sess: &Session, attr: &impl AttributeExt) -> Vec { - if attr.has_name(sym::repr) { parse_repr_attr(sess, attr) } else { Vec::new() } -} +// FIXME(jdonszelmann): is a vec the right representation here even? isn't it just a struct? +pub(crate) struct ReprParser; -pub fn parse_repr_attr(sess: &Session, attr: &impl AttributeExt) -> Vec { - assert!(attr.has_name(sym::repr), "expected `#[repr(..)]`, found: {attr:?}"); - let mut acc = Vec::new(); - let dcx = sess.dcx(); - - if let Some(items) = attr.meta_item_list() { - for item in items { - let mut recognised = false; - if item.is_word() { - let hint = match item.name_or_empty() { - sym::Rust => Some(ReprRust), - sym::C => Some(ReprC), - sym::packed => Some(ReprPacked(Align::ONE)), - sym::simd => Some(ReprSimd), - sym::transparent => Some(ReprTransparent), - sym::align => { - sess.dcx().emit_err(session_diagnostics::InvalidReprAlignNeedArg { - span: item.span(), - }); - recognised = true; - None - } - name => int_type_of_word(name).map(ReprInt), - }; - - if let Some(h) = hint { - recognised = true; - acc.push(h); - } - } else if let Some((name, value)) = item.singleton_lit_list() { - let mut literal_error = None; - let mut err_span = item.span(); - if name == sym::align { - recognised = true; - match parse_alignment(&value.kind) { - Ok(literal) => acc.push(ReprAlign(literal)), - Err(message) => { - err_span = value.span; - literal_error = Some(message) - } - }; - } else if name == sym::packed { - recognised = true; - match parse_alignment(&value.kind) { - Ok(literal) => acc.push(ReprPacked(literal)), - Err(message) => { - err_span = value.span; - literal_error = Some(message) - } - }; - } else if matches!(name, sym::Rust | sym::C | sym::simd | sym::transparent) - || int_type_of_word(name).is_some() - { - recognised = true; - sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen { - span: item.span(), - name: name.to_ident_string(), - }); - } - if let Some(literal_error) = literal_error { - sess.dcx().emit_err(session_diagnostics::InvalidReprGeneric { - span: err_span, - repr_arg: name.to_ident_string(), - error_part: literal_error, - }); - } - } else if let Some(meta_item) = item.meta_item() { - match &meta_item.kind { - MetaItemKind::NameValue(value) => { - if meta_item.has_name(sym::align) || meta_item.has_name(sym::packed) { - let name = meta_item.name_or_empty().to_ident_string(); - recognised = true; - sess.dcx().emit_err(session_diagnostics::IncorrectReprFormatGeneric { - span: item.span(), - repr_arg: &name, - cause: IncorrectReprFormatGenericCause::from_lit_kind( - item.span(), - &value.kind, - &name, - ), - }); - } else if matches!( - meta_item.name_or_empty(), - sym::Rust | sym::C | sym::simd | sym::transparent - ) || int_type_of_word(meta_item.name_or_empty()).is_some() - { - recognised = true; - sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoValue { - span: meta_item.span, - name: meta_item.name_or_empty().to_ident_string(), - }); - } - } - MetaItemKind::List(nested_items) => { - if meta_item.has_name(sym::align) { - recognised = true; - if let [nested_item] = nested_items.as_slice() { - sess.dcx().emit_err( - session_diagnostics::IncorrectReprFormatExpectInteger { - span: nested_item.span(), - }, - ); - } else { - sess.dcx().emit_err( - session_diagnostics::IncorrectReprFormatAlignOneArg { - span: meta_item.span, - }, - ); - } - } else if meta_item.has_name(sym::packed) { - recognised = true; - if let [nested_item] = nested_items.as_slice() { - sess.dcx().emit_err( - session_diagnostics::IncorrectReprFormatPackedExpectInteger { - span: nested_item.span(), - }, - ); - } else { - sess.dcx().emit_err( - session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg { - span: meta_item.span, - }, - ); - } - } else if matches!( - meta_item.name_or_empty(), - sym::Rust | sym::C | sym::simd | sym::transparent - ) || int_type_of_word(meta_item.name_or_empty()).is_some() - { - recognised = true; - sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen { - span: meta_item.span, - name: meta_item.name_or_empty().to_ident_string(), - }); - } - } - _ => (), - } - } - if !recognised { - // Not a word we recognize. This will be caught and reported by - // the `check_mod_attrs` pass, but this pass doesn't always run - // (e.g. if we only pretty-print the source), so we have to gate - // the `span_delayed_bug` call as follows: - if sess.opts.pretty.is_none_or(|pp| pp.needs_analysis()) { - dcx.span_delayed_bug(item.span(), "unrecognized representation hint"); - } +impl CombineAttributeParser for ReprParser { + type Item = (ReprAttr, Span); + const PATH: &'static [rustc_span::Symbol] = &[sym::repr]; + const CONVERT: ConvertFn = AttributeKind::Repr; + + fn extend<'a>( + cx: &'a AcceptContext<'a>, + args: &'a ArgParser<'a>, + ) -> impl IntoIterator + '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 } - acc +} + +macro_rules! int_pat { + () => { + sym::i8 + | sym::u8 + | sym::i16 + | sym::u16 + | sym::i32 + | sym::u32 + | sym::i64 + | sym::u64 + | sym::i128 + | sym::u128 + | sym::isize + | sym::usize + }; } fn int_type_of_word(s: Symbol) -> Option { - use rustc_attr_data_structures::IntType::*; + use IntType::*; match s { - sym::i8 => Some(SignedInt(ast::IntTy::I8)), - sym::u8 => Some(UnsignedInt(ast::UintTy::U8)), - sym::i16 => Some(SignedInt(ast::IntTy::I16)), - sym::u16 => Some(UnsignedInt(ast::UintTy::U16)), - sym::i32 => Some(SignedInt(ast::IntTy::I32)), - sym::u32 => Some(UnsignedInt(ast::UintTy::U32)), - sym::i64 => Some(SignedInt(ast::IntTy::I64)), - sym::u64 => Some(UnsignedInt(ast::UintTy::U64)), - sym::i128 => Some(SignedInt(ast::IntTy::I128)), - sym::u128 => Some(UnsignedInt(ast::UintTy::U128)), - sym::isize => Some(SignedInt(ast::IntTy::Isize)), - sym::usize => Some(UnsignedInt(ast::UintTy::Usize)), + sym::i8 => Some(SignedInt(IntTy::I8)), + sym::u8 => Some(UnsignedInt(UintTy::U8)), + sym::i16 => Some(SignedInt(IntTy::I16)), + sym::u16 => Some(UnsignedInt(UintTy::U16)), + sym::i32 => Some(SignedInt(IntTy::I32)), + sym::u32 => Some(UnsignedInt(UintTy::U32)), + sym::i64 => Some(SignedInt(IntTy::I64)), + sym::u64 => Some(UnsignedInt(UintTy::U64)), + sym::i128 => Some(SignedInt(IntTy::I128)), + sym::u128 => Some(UnsignedInt(UintTy::U128)), + sym::isize => Some(SignedInt(IntTy::Isize)), + sym::usize => Some(UnsignedInt(UintTy::Usize)), _ => None, } } -pub fn parse_alignment(node: &ast::LitKind) -> Result { - if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node { +fn parse_repr(cx: &AcceptContext<'_>, param: &MetaItemParser<'_>) -> Option { + 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 { + 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 { + if let LitKind::Int(literal, LitIntType::Unsuffixed) = node { // `Align::from_bytes` accepts 0 as an input, check is_power_of_two() first if literal.get().is_power_of_two() { // Only possible error is larger than 2^29 diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 454b8b5de82b5..6d76456e83c80 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -1,266 +1,258 @@ -//! Parsing and validation of builtin attributes - use std::num::NonZero; -use rustc_ast::MetaItem; -use rustc_ast::attr::AttributeExt; -use rustc_ast_pretty::pprust; use rustc_attr_data_structures::{ - ConstStability, DefaultBodyStability, Stability, StabilityLevel, StableSince, UnstableReason, - VERSION_PLACEHOLDER, + AttributeKind, DefaultBodyStability, PartialConstStability, Stability, StabilityLevel, + StableSince, UnstableReason, VERSION_PLACEHOLDER, }; use rustc_errors::ErrorGuaranteed; -use rustc_session::Session; use rustc_span::{Span, Symbol, kw, sym}; -use crate::attributes::util::UnsupportedLiteralReason; -use crate::{parse_version, session_diagnostics}; - -/// Collects stability info from `stable`/`unstable`/`rustc_allowed_through_unstable_modules` -/// attributes in `attrs`. Returns `None` if no stability attributes are found. -pub fn find_stability( - sess: &Session, - attrs: &[impl AttributeExt], - item_sp: Span, -) -> Option<(Stability, Span)> { - let mut stab: Option<(Stability, Span)> = None; - let mut allowed_through_unstable_modules = None; - - for attr in attrs { - match attr.name_or_empty() { - sym::rustc_allowed_through_unstable_modules => { - // The value is mandatory, but avoid ICEs in case such code reaches this function. - allowed_through_unstable_modules = Some(attr.value_str().unwrap_or_else(|| { - sess.dcx().span_delayed_bug( - item_sp, - "`#[rustc_allowed_through_unstable_modules]` without deprecation message", - ); - kw::Empty - })) - } - sym::unstable => { - if stab.is_some() { - sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels { - span: attr.span(), - }); - break; - } +use super::util::parse_version; +use super::{AcceptMapping, AttributeParser, SingleAttributeParser}; +use crate::context::{AcceptContext, FinalizeContext}; +use crate::parser::{ArgParser, MetaItemParser}; +use crate::session_diagnostics::{self, UnsupportedLiteralReason}; + +macro_rules! reject_outside_std { + ($cx: ident) => { + // Emit errors for non-staged-api crates. + if !$cx.features().staged_api() { + $cx.emit_err(session_diagnostics::StabilityOutsideStd { span: $cx.attr_span }); + return; + } + }; +} - if let Some((feature, level)) = parse_unstability(sess, attr) { - stab = Some((Stability { level, feature }, attr.span())); - } - } - sym::stable => { - if stab.is_some() { - sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels { - span: attr.span(), - }); - break; - } - if let Some((feature, level)) = parse_stability(sess, attr) { - stab = Some((Stability { level, feature }, attr.span())); - } - } - _ => {} +#[derive(Default)] +pub(crate) struct StabilityParser { + allowed_through_unstable_modules: Option, + stability: Option<(Stability, Span)>, +} + +impl StabilityParser { + /// Checks, and emits an error when a stability (or unstability) was already set, which would be a duplicate. + fn check_duplicate(&self, cx: &AcceptContext<'_>) -> bool { + if let Some((_, _)) = self.stability { + cx.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span }); + true + } else { + false } } +} - if let Some(allowed_through_unstable_modules) = allowed_through_unstable_modules { - match &mut stab { - Some(( +impl AttributeParser for StabilityParser { + const ATTRIBUTES: AcceptMapping = &[ + (&[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 { + if let Some(atum) = self.allowed_through_unstable_modules { + if let Some(( Stability { - level: StabilityLevel::Stable { allowed_through_unstable_modules: in_stab, .. }, + level: StabilityLevel::Stable { ref mut allowed_through_unstable_modules, .. }, .. }, _, - )) => *in_stab = Some(allowed_through_unstable_modules), - _ => { - sess.dcx() - .emit_err(session_diagnostics::RustcAllowedUnstablePairing { span: item_sp }); + )) = self.stability + { + *allowed_through_unstable_modules = Some(atum); + } else { + cx.dcx().emit_err(session_diagnostics::RustcAllowedUnstablePairing { + span: cx.target_span, + }); } } - } - stab + let (stability, span) = self.stability?; + + Some(AttributeKind::Stability { stability, span }) + } } -/// Collects stability info from `rustc_const_stable`/`rustc_const_unstable`/`rustc_promotable` -/// attributes in `attrs`. Returns `None` if no stability attributes are found. -pub fn find_const_stability( - sess: &Session, - attrs: &[impl AttributeExt], - item_sp: Span, -) -> Option<(ConstStability, Span)> { - let mut const_stab: Option<(ConstStability, Span)> = None; - let mut promotable = false; - let mut const_stable_indirect = false; - - for attr in attrs { - match attr.name_or_empty() { - sym::rustc_promotable => promotable = true, - sym::rustc_const_stable_indirect => const_stable_indirect = true, - sym::rustc_const_unstable => { - if const_stab.is_some() { - sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels { - span: attr.span(), - }); - break; - } +// FIXME(jdonszelmann) change to Single +#[derive(Default)] +pub(crate) struct BodyStabilityParser { + stability: Option<(DefaultBodyStability, Span)>, +} - if let Some((feature, level)) = parse_unstability(sess, attr) { - const_stab = Some(( - ConstStability { - level, - feature, - const_stable_indirect: false, - promotable: false, - }, - attr.span(), - )); - } +impl AttributeParser for BodyStabilityParser { + const ATTRIBUTES: AcceptMapping = + &[(&[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)); } - 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 }) - } - } + fn finalize(self, _cx: &FinalizeContext<'_>) -> Option { + let (stability, span) = self.stability?; + + Some(AttributeKind::BodyStability { stability, span }) } - 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. - } - } +} + +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 { + Some(AttributeKind::ConstStabilityIndirect) } +} - const_stab +#[derive(Default)] +pub(crate) struct ConstStabilityParser { + promotable: bool, + stability: Option<(PartialConstStability, Span)>, } -/// 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, +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 + } } } -/// Collects stability info from `rustc_default_body_unstable` attributes in `attrs`. -/// Returns `None` if no stability attributes are found. -pub fn find_body_stability( - sess: &Session, - attrs: &[impl AttributeExt], -) -> Option<(DefaultBodyStability, Span)> { - let mut body_stab: Option<(DefaultBodyStability, Span)> = None; - - for attr in attrs { - if attr.has_name(sym::rustc_default_body_unstable) { - if body_stab.is_some() { - sess.dcx() - .emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span() }); - break; +impl AttributeParser for ConstStabilityParser { + const ATTRIBUTES: AcceptMapping = &[ + (&[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, + )); } - - if let Some((feature, level)) = parse_unstability(sess, attr) { - body_stab = Some((DefaultBodyStability { level, feature }, 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 { + 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 }); } } - } - body_stab + let (stability, span) = self.stability?; + + Some(AttributeKind::ConstStability { stability, span }) + } } -fn insert_or_error(sess: &Session, meta: &MetaItem, item: &mut Option) -> Option<()> { +/// 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, +) -> Option<()> { if item.is_some() { - sess.dcx().emit_err(session_diagnostics::MultipleItem { - span: meta.span, - item: pprust::path_to_string(&meta.path), + cx.emit_err(session_diagnostics::MultipleItem { + span: param.span(), + item: param.path_without_args().to_string(), }); None - } else if let Some(v) = meta.value_str() { - *item = Some(v); + } else if let Some(v) = param.args().name_value() + && let Some(s) = v.value_as_str() + { + *item = Some(s); Some(()) } else { - sess.dcx().emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span }); + cx.emit_err(session_diagnostics::IncorrectMetaItem { + span: param.span(), + suggestion: None, + }); None } } /// Read the content of a `stable`/`rustc_const_stable` attribute, and return the feature name and /// its stability information. -fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol, StabilityLevel)> { - let metas = attr.meta_item_list()?; - +pub(crate) fn parse_stability( + cx: &AcceptContext<'_>, + args: &ArgParser<'_>, +) -> Option<(Symbol, StabilityLevel)> { let mut feature = None; let mut since = None; - for meta in metas { - let Some(mi) = meta.meta_item() else { - sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral { - span: meta.span(), + + for param in args.list()?.mixed() { + let param_span = param.span(); + let Some(param) = param.meta_item() else { + cx.emit_err(session_diagnostics::UnsupportedLiteral { + span: param_span, reason: UnsupportedLiteralReason::Generic, is_bytestr: false, - start_point_span: sess.source_map().start_point(meta.span()), + start_point_span: cx.sess().source_map().start_point(param_span), }); return None; }; - match mi.name_or_empty() { - sym::feature => insert_or_error(sess, mi, &mut feature)?, - sym::since => insert_or_error(sess, mi, &mut since)?, + match param.word_or_empty_without_args().name { + sym::feature => insert_value_into_option_or_error(cx, ¶m, &mut feature)?, + sym::since => insert_value_into_option_or_error(cx, ¶m, &mut since)?, _ => { - sess.dcx().emit_err(session_diagnostics::UnknownMetaItem { - span: meta.span(), - item: pprust::path_to_string(&mi.path), + cx.emit_err(session_diagnostics::UnknownMetaItem { + span: param_span, + item: param.path_without_args().to_string(), expected: &["feature", "since"], }); return None; @@ -271,9 +263,9 @@ fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol, let feature = match feature { Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature), Some(_bad_feature) => { - Err(sess.dcx().emit_err(session_diagnostics::NonIdentFeature { span: attr.span() })) + Err(cx.emit_err(session_diagnostics::NonIdentFeature { span: cx.attr_span })) } - None => Err(sess.dcx().emit_err(session_diagnostics::MissingFeature { span: attr.span() })), + None => Err(cx.emit_err(session_diagnostics::MissingFeature { span: cx.attr_span })), }; let since = if let Some(since) = since { @@ -282,11 +274,11 @@ fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol, } else if let Some(version) = parse_version(since) { StableSince::Version(version) } else { - sess.dcx().emit_err(session_diagnostics::InvalidSince { span: attr.span() }); + cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span }); StableSince::Err } } else { - sess.dcx().emit_err(session_diagnostics::MissingSince { span: attr.span() }); + cx.emit_err(session_diagnostics::MissingSince { span: cx.attr_span }); StableSince::Err }; @@ -299,46 +291,48 @@ fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol, } } -/// Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable` +// Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable` /// attribute, and return the feature name and its stability information. -fn parse_unstability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol, StabilityLevel)> { - let metas = attr.meta_item_list()?; - +pub(crate) fn parse_unstability( + cx: &AcceptContext<'_>, + args: &ArgParser<'_>, +) -> Option<(Symbol, StabilityLevel)> { let mut feature = None; let mut reason = None; let mut issue = None; let mut issue_num = None; let mut is_soft = false; let mut implied_by = None; - for meta in metas { - let Some(mi) = meta.meta_item() else { - sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral { - span: meta.span(), + for param in args.list()?.mixed() { + let Some(param) = param.meta_item() else { + cx.emit_err(session_diagnostics::UnsupportedLiteral { + span: param.span(), reason: UnsupportedLiteralReason::Generic, is_bytestr: false, - start_point_span: sess.source_map().start_point(meta.span()), + start_point_span: cx.sess().source_map().start_point(param.span()), }); return None; }; - match mi.name_or_empty() { - sym::feature => insert_or_error(sess, mi, &mut feature)?, - sym::reason => insert_or_error(sess, mi, &mut reason)?, + let (word, args) = param.word_or_empty(); + match word.name { + sym::feature => insert_value_into_option_or_error(cx, ¶m, &mut feature)?, + sym::reason => insert_value_into_option_or_error(cx, ¶m, &mut reason)?, sym::issue => { - insert_or_error(sess, mi, &mut issue)?; + insert_value_into_option_or_error(cx, ¶m, &mut issue)?; - // These unwraps are safe because `insert_or_error` ensures the meta item + // These unwraps are safe because `insert_value_into_option_or_error` ensures the meta item // is a name/value pair string literal. issue_num = match issue.unwrap().as_str() { "none" => None, - issue => match issue.parse::>() { + issue_str => match issue_str.parse::>() { Ok(num) => Some(num), Err(err) => { - sess.dcx().emit_err( + cx.emit_err( session_diagnostics::InvalidIssueString { - span: mi.span, + span: param.span(), cause: session_diagnostics::InvalidIssueStringCause::from_int_error_kind( - mi.name_value_literal_span().unwrap(), + args.name_value().unwrap().value_span, err.kind(), ), }, @@ -349,16 +343,16 @@ fn parse_unstability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol }; } sym::soft => { - if !mi.is_word() { - sess.dcx().emit_err(session_diagnostics::SoftNoArgs { span: mi.span }); + if !args.no_args() { + cx.emit_err(session_diagnostics::SoftNoArgs { span: param.span() }); } is_soft = true; } - sym::implied_by => insert_or_error(sess, mi, &mut implied_by)?, + sym::implied_by => insert_value_into_option_or_error(cx, ¶m, &mut implied_by)?, _ => { - sess.dcx().emit_err(session_diagnostics::UnknownMetaItem { - span: meta.span(), - item: pprust::path_to_string(&mi.path), + cx.emit_err(session_diagnostics::UnknownMetaItem { + span: param.span(), + item: param.path_without_args().to_string(), expected: &["feature", "reason", "issue", "soft", "implied_by"], }); return None; @@ -369,14 +363,13 @@ fn parse_unstability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol let feature = match feature { Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature), Some(_bad_feature) => { - Err(sess.dcx().emit_err(session_diagnostics::NonIdentFeature { span: attr.span() })) + Err(cx.emit_err(session_diagnostics::NonIdentFeature { span: cx.attr_span })) } - None => Err(sess.dcx().emit_err(session_diagnostics::MissingFeature { span: attr.span() })), + None => Err(cx.emit_err(session_diagnostics::MissingFeature { span: cx.attr_span })), }; - let issue = issue.ok_or_else(|| { - sess.dcx().emit_err(session_diagnostics::MissingIssue { span: attr.span() }) - }); + let issue = + issue.ok_or_else(|| cx.emit_err(session_diagnostics::MissingIssue { span: cx.attr_span })); match (feature, issue) { (Ok(feature), Ok(_)) => { diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs index f4065a7704841..ad83a1f7af80c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs +++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs @@ -1,36 +1,33 @@ -use rustc_ast::attr::AttributeExt; -use rustc_attr_data_structures::TransparencyError; +use rustc_attr_data_structures::AttributeKind; use rustc_span::hygiene::Transparency; use rustc_span::sym; -pub fn find_transparency( - attrs: &[impl AttributeExt], - macro_rules: bool, -) -> (Transparency, Option) { - let mut transparency = None; - let mut error = None; - for attr in attrs { - if attr.has_name(sym::rustc_macro_transparency) { - if let Some((_, old_span)) = transparency { - error = Some(TransparencyError::MultipleTransparencyAttrs(old_span, attr.span())); - break; - } else if let Some(value) = attr.value_str() { - transparency = Some(( - match value { - sym::transparent => Transparency::Transparent, - sym::semitransparent => Transparency::SemiTransparent, - sym::opaque => Transparency::Opaque, - _ => { - error = - Some(TransparencyError::UnknownTransparency(value, attr.span())); - continue; - } - }, - attr.span(), - )); +use super::{AcceptContext, SingleAttributeParser}; +use crate::parser::ArgParser; + +pub(crate) struct TransparencyParser; + +// FIXME(jdonszelmann): make these proper diagnostics +#[allow(rustc::untranslatable_diagnostic)] +#[allow(rustc::diagnostic_outside_of_impl)] +impl SingleAttributeParser for TransparencyParser { + const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_macro_transparency]; + + fn on_duplicate(cx: &crate::context::AcceptContext<'_>, first_span: rustc_span::Span) { + cx.dcx().span_err(vec![first_span, cx.attr_span], "multiple macro transparency attributes"); + } + + fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option { + match args.name_value().and_then(|nv| nv.value_as_str()) { + Some(sym::transparent) => Some(Transparency::Transparent), + Some(sym::semitransparent) => Some(Transparency::SemiTransparent), + Some(sym::opaque) => Some(Transparency::Opaque), + Some(other) => { + cx.dcx().span_err(cx.attr_span, format!("unknown macro transparency: `{other}`")); + None } + None => None, } + .map(AttributeKind::MacroTransparency) } - let fallback = if macro_rules { Transparency::SemiTransparent } else { Transparency::Opaque }; - (transparency.map_or(fallback, |t| t.0), error) } diff --git a/compiler/rustc_attr_parsing/src/attributes/util.rs b/compiler/rustc_attr_parsing/src/attributes/util.rs index e36f7dfff5a5f..05a9029c59aa5 100644 --- a/compiler/rustc_attr_parsing/src/attributes/util.rs +++ b/compiler/rustc_attr_parsing/src/attributes/util.rs @@ -3,22 +3,6 @@ use rustc_attr_data_structures::RustcVersion; use rustc_feature::is_builtin_attr_name; use rustc_span::{Symbol, sym}; -pub(crate) enum UnsupportedLiteralReason { - Generic, - CfgString, - CfgBoolean, - DeprecatedString, - DeprecatedKvPair, -} - -pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool { - attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name)) -} - -pub fn find_crate_name(attrs: &[impl AttributeExt]) -> Option { - first_attr_value_str_by_name(attrs, sym::crate_name) -} - /// Parse a rustc version number written inside string literal in an attribute, /// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are /// not accepted in this position, unlike when parsing CFG_RELEASE. @@ -34,3 +18,11 @@ pub fn parse_version(s: Symbol) -> Option { let patch = digits.next().unwrap_or("0").parse().ok()?; Some(RustcVersion { major, minor, patch }) } + +pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool { + attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name)) +} + +pub fn find_crate_name(attrs: &[impl AttributeExt]) -> Option { + first_attr_value_str_by_name(attrs, sym::crate_name) +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs new file mode 100644 index 0000000000000..99eee0d3c4a29 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -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, &ArgParser<'_>) + Send + Sync>>>, + Vec) -> Option>> + )> = LazyLock::new(|| { + let mut accepts = BTreeMap::<_, Vec, &ArgParser<'_>) + Send + Sync>>>::new(); + let mut finalizes = Vec::) -> Option>>::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, + Combine, + Combine, + // tidy-alphabetical-end + + // tidy-alphabetical-start + Single, + Single, + Single, + // 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, + 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, + + /// 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 { + 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) -> 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 { + 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::>(); + + 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 } + } + } + } +} diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index a1264a6875f69..9841166b37dbd 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -1,8 +1,79 @@ -//! Functions and types dealing with attributes and meta items. +//! Centralized logic for parsing and attributes. //! -//! FIXME(Centril): For now being, much of the logic is still in `rustc_ast::attr`. -//! The goal is to move the definition of `MetaItem` and things that don't need to be in `syntax` -//! to this crate. +//! Part of a series of crates: +//! - rustc_attr_data_structures: contains types that the parsers parse into +//! - rustc_attr_parsing: this crate +//! - (in the future): rustc_attr_validation +//! +//! History: Check out [#131229](https://github.com/rust-lang/rust/issues/131229). +//! There used to be only one definition of attributes in the compiler: `ast::Attribute`. +//! These were then parsed or validated or both in places distributed all over the compiler. +//! This was a mess... +//! +//! Attributes are markers on items. +//! Many of them are actually attribute-like proc-macros, and are expanded to some other rust syntax. +//! This could either be a user provided proc macro, or something compiler provided. +//! `derive` is an example of one that the compiler provides. +//! These are built-in, but they have a valid expansion to Rust tokens and are thus called "active". +//! I personally like calling these *active* compiler-provided attributes, built-in *macros*, +//! because they still expand, and this helps to differentiate them from built-in *attributes*. +//! However, I'll be the first to admit that the naming here can be confusing. +//! +//! The alternative to active attributes, are inert attributes. +//! These can occur in user code (proc-macro helper attributes). +//! But what's important is, many built-in attributes are inert like this. +//! There is nothing they expand to during the macro expansion process, +//! sometimes because they literally cannot expand to something that is valid Rust. +//! They are really just markers to guide the compilation process. +//! An example is `#[inline(...)]` which changes how code for functions is generated. +//! +//! ```text +//! Active Inert +//! ┌──────────────────────┬──────────────────────┐ +//! │ (mostly in) │ these are parsed │ +//! │ rustc_builtin_macros │ here! │ +//! │ │ │ +//! │ │ │ +//! │ #[derive(...)] │ #[stable()] │ +//! Built-in │ #[cfg()] │ #[inline()] │ +//! │ #[cfg_attr()] │ #[repr()] │ +//! │ │ │ +//! │ │ │ +//! │ │ │ +//! ├──────────────────────┼──────────────────────┤ +//! │ │ │ +//! │ │ │ +//! │ │ `b` in │ +//! │ │ #[proc_macro_derive( │ +//! User created │ #[proc_macro_attr()] │ a, │ +//! │ │ attributes(b) │ +//! │ │ ] │ +//! │ │ │ +//! │ │ │ +//! │ │ │ +//! └──────────────────────┴──────────────────────┘ +//! ``` +//! +//! In this crate, syntactical attributes (sequences of tokens that look like +//! `#[something(something else)]`) are parsed into more semantic attributes, markers on items. +//! Multiple syntactic attributes might influence a single semantic attribute. For example, +//! `#[stable(...)]` and `#[unstable()]` cannot occur together, and both semantically define +//! a "stability" of an item. So, the stability attribute has an +//! [`AttributeParser`](attributes::AttributeParser) that recognizes both the `#[stable()]` +//! and `#[unstable()]` syntactic attributes, and at the end produce a single [`AttributeKind::Stability`]. +//! +//! As a rule of thumb, when a syntactical attribute can be applied more than once, they should be +//! combined into a single semantic attribute. For example: +//! +//! ``` +//! #[repr(C)] +//! #[repr(packed)] +//! struct Meow {} +//! ``` +//! +//! should result in a single `AttributeKind::Repr` containing a list of repr annotations, in this +//! case `C` and `packed`. This is equivalent to writing `#[repr(C, packed)]` in a single +//! syntactical annotation. // tidy-alphabetical-start #![allow(internal_features)] @@ -12,11 +83,59 @@ #![warn(unreachable_pub)] // tidy-alphabetical-end +#[macro_use] mod attributes; +mod context; +pub mod parser; mod session_diagnostics; -pub use attributes::*; +pub use attributes::cfg::*; +pub use attributes::util::{find_crate_name, is_builtin_attr, parse_version}; +pub use context::{AttributeParser, OmitDoc}; pub use rustc_attr_data_structures::*; -pub use util::{find_crate_name, is_builtin_attr, parse_version}; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } + +/// Finds attributes in sequences of attributes by pattern matching. +/// +/// A little like `matches` but for attributes. +/// +/// ```rust,ignore (illustrative) +/// // finds the repr attribute +/// if let Some(r) = find_attr!(attrs, AttributeKind::Repr(r) => r) { +/// +/// } +/// +/// // checks if one has matched +/// if find_attr!(attrs, AttributeKind::Repr(_)) { +/// +/// } +/// ``` +/// +/// Often this requires you to first end up with a list of attributes. +/// A common way to get those is through `tcx.get_all_attrs(did)` +#[macro_export] +macro_rules! find_attr { + ($attributes_list: expr, $pattern: pat $(if $guard: expr)?) => {{ + $crate::find_attr!($attributes_list, $pattern $(if $guard)? => ()).is_some() + }}; + + ($attributes_list: expr, $pattern: pat $(if $guard: expr)? => $e: expr) => {{ + fn check_attribute_iterator<'a>(_: &'_ impl IntoIterator) {} + 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) + }}; +} diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs new file mode 100644 index 0000000000000..0ee0ea4ea5953 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -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 { + 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::>().into_boxed_slice(), + span: self.span(), + } + } + + pub fn segments(&'a self) -> impl Iterator { + 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 { + (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 { + 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 +#[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 { + 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 { + 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>, + dcx: DiagCtxtHandle<'a>, +} + +impl<'a> MetaItemListParserContext<'a> { + fn done(&mut self) -> bool { + self.inside_delimiters.peek().is_none() + } + + fn next_path(&mut self) -> Option { + // 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 { + 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( )]` + /// + /// 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> { + // 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>, + 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> + '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)>> { + 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, &'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()) + } +} diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 92bc2a8aeb05e..9d34b807ac2fe 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -6,9 +6,16 @@ use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuar use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; -use crate::attributes::util::UnsupportedLiteralReason; use crate::fluent_generated as fluent; +pub(crate) enum UnsupportedLiteralReason { + Generic, + CfgString, + CfgBoolean, + DeprecatedString, + DeprecatedKvPair, +} + #[derive(Diagnostic)] #[diag(attr_parsing_expected_one_cfg_pattern, code = E0536)] pub(crate) struct ExpectedOneCfgPattern { @@ -39,6 +46,21 @@ pub(crate) struct MultipleItem { pub(crate) struct IncorrectMetaItem { #[primary_span] pub span: Span, + + #[subdiagnostic] + pub suggestion: Option, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + attr_parsing_incorrect_meta_item_suggestion, + applicability = "maybe-incorrect" +)] +pub(crate) struct IncorrectMetaItemSuggestion { + #[suggestion_part(code = "\"")] + pub lo: Span, + #[suggestion_part(code = "\"")] + pub hi: Span, } /// Error code: E0541 @@ -337,13 +359,6 @@ pub(crate) struct RustcPromotablePairing { pub span: Span, } -#[derive(Diagnostic)] -#[diag(attr_parsing_rustc_const_stable_indirect_pairing)] -pub(crate) struct RustcConstStableIndirectPairing { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(attr_parsing_rustc_allowed_unstable_pairing, code = E0789)] pub(crate) struct RustcAllowedUnstablePairing { @@ -423,3 +438,44 @@ pub(crate) struct UnknownVersionLiteral { #[primary_span] pub span: Span, } + +// FIXME(jdonszelmann) duplicated from `rustc_passes`, remove once `check_attr` is integrated. +#[derive(Diagnostic)] +#[diag(attr_parsing_unused_multiple)] +pub(crate) struct UnusedMultiple { + #[primary_span] + #[suggestion(code = "", applicability = "machine-applicable")] + pub this: Span, + #[note] + pub other: Span, + pub name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_stability_outside_std, code = E0734)] +pub(crate) struct StabilityOutsideStd { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_empty_confusables)] +pub(crate) struct EmptyConfusables { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_repr_ident, code = E0565)] +pub(crate) struct ReprIdent { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_unrecognized_repr_hint, code = E0552)] +#[help] +pub(crate) struct UnrecognizedReprHint { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_builtin_macros/Cargo.toml b/compiler/rustc_builtin_macros/Cargo.toml index f29be2ee81855..b5f4f2efd1f5b 100644 --- a/compiler/rustc_builtin_macros/Cargo.toml +++ b/compiler/rustc_builtin_macros/Cargo.toml @@ -20,6 +20,7 @@ rustc_errors = { path = "../rustc_errors" } rustc_expand = { path = "../rustc_expand" } rustc_feature = { path = "../rustc_feature" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } +rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_lexer = { path = "../rustc_lexer" } rustc_lint_defs = { path = "../rustc_lint_defs" } diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 234ec8582165b..6b59ac2582755 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -185,8 +185,9 @@ use rustc_ast::{ self as ast, AnonConst, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind, Generics, Mutability, PatKind, VariantData, }; -use rustc_attr_parsing as attr; +use rustc_attr_parsing::{AttributeKind, AttributeParser, ReprPacked}; use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_hir::Attribute; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use ty::{Bounds, Path, Ref, Self_, Ty}; @@ -480,14 +481,10 @@ impl<'a> TraitDef<'a> { ) { match item { Annotatable::Item(item) => { - let is_packed = item.attrs.iter().any(|attr| { - for r in attr::find_repr_attrs(cx.sess, attr) { - if let attr::ReprPacked(_) = r { - return true; - } - } - false - }); + let is_packed = matches!( + AttributeParser::parse_limited(cx.sess, &item.attrs, sym::repr, item.span, true), + Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.iter().any(|(x, _)| matches!(x, ReprPacked(..))) + ); let newitem = match &item.kind { ast::ItemKind::Struct(struct_def, generics) => self.expand_struct_def( diff --git a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs index 27331ce4ca66f..da9f8d6929720 100644 --- a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs +++ b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs @@ -94,7 +94,7 @@ impl<'tcx> AssertModuleSource<'tcx> { other => { self.tcx .dcx() - .emit_fatal(errors::UnknownReuseKind { span: attr.span, kind: other }); + .emit_fatal(errors::UnknownReuseKind { span: attr.span(), kind: other }); } } } else { @@ -102,7 +102,7 @@ impl<'tcx> AssertModuleSource<'tcx> { }; if !self.tcx.sess.opts.unstable_opts.query_dep_graph { - self.tcx.dcx().emit_fatal(errors::MissingQueryDepGraph { span: attr.span }); + self.tcx.dcx().emit_fatal(errors::MissingQueryDepGraph { span: attr.span() }); } if !self.check_config(attr) { @@ -115,7 +115,7 @@ impl<'tcx> AssertModuleSource<'tcx> { if !user_path.starts_with(&crate_name) { self.tcx.dcx().emit_fatal(errors::MalformedCguName { - span: attr.span, + span: attr.span(), user_path, crate_name, }); @@ -145,7 +145,7 @@ impl<'tcx> AssertModuleSource<'tcx> { let cgu_names: Vec<&str> = self.available_cgus.items().map(|cgu| cgu.as_str()).into_sorted_stable_ord(); self.tcx.dcx().emit_err(errors::NoModuleNamed { - span: attr.span, + span: attr.span(), user_path, cgu_name, cgu_names: cgu_names.join(", "), @@ -155,7 +155,7 @@ impl<'tcx> AssertModuleSource<'tcx> { self.cgu_reuse_tracker.set_expectation( cgu_name, user_path, - attr.span, + attr.span(), expected_reuse, comp_kind, ); @@ -175,7 +175,7 @@ impl<'tcx> AssertModuleSource<'tcx> { } } - self.tcx.dcx().emit_fatal(errors::NoField { span: attr.span, name }); + self.tcx.dcx().emit_fatal(errors::NoField { span: attr.span(), name }); } /// Scan for a `cfg="foo"` attribute and check whether we have a diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 40238f4b4915a..73a97d32c2d64 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -5,7 +5,9 @@ use std::time::{Duration, Instant}; use itertools::Itertools; use rustc_abi::FIRST_VARIANT; +use rustc_ast as ast; use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, AllocatorKind, global_fn_name}; +use rustc_attr_parsing::OptimizeAttr; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; use rustc_data_structures::sync::par_map; @@ -29,7 +31,6 @@ use rustc_span::{DUMMY_SP, Symbol, sym}; use rustc_trait_selection::infer::{BoundRegionConversionTime, TyCtxtInferExt}; use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt}; use tracing::{debug, info}; -use {rustc_ast as ast, rustc_attr_parsing as attr}; use crate::assert_module_sources::CguReuse; use crate::back::link::are_upstream_rust_objects_already_included; @@ -1061,7 +1062,7 @@ pub(crate) fn provide(providers: &mut Providers) { let any_for_speed = defids.items().any(|id| { let CodegenFnAttrs { optimize, .. } = tcx.codegen_fn_attrs(*id); - matches!(optimize, attr::OptimizeAttr::Speed) + matches!(optimize, OptimizeAttr::Speed) }); if any_for_speed { diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 97bc84c0b698b..673740b4aab9f 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -5,7 +5,8 @@ use rustc_ast::expand::autodiff_attrs::{ AutoDiffAttrs, DiffActivity, DiffMode, valid_input_activity, valid_ret_activity, }; use rustc_ast::{MetaItem, MetaItemInner, attr}; -use rustc_attr_parsing::{InlineAttr, InstructionSetAttr, OptimizeAttr}; +use rustc_attr_parsing::ReprAttr::ReprAlign; +use rustc_attr_parsing::{AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::codes::*; use rustc_errors::{DiagMessage, SubdiagMessage, struct_span_code_err}; @@ -104,12 +105,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { if let Fn | AssocFn | Variant | Ctor(..) = def_kind { Some(tcx.fn_sig(did)) } else { - tcx.dcx() - .span_delayed_bug(attr.span, "this attribute can only be applied to functions"); + tcx.dcx().span_delayed_bug( + attr.span(), + "this attribute can only be applied to functions", + ); None } }; + if let hir::Attribute::Parsed(p) = attr { + match p { + AttributeKind::Repr(reprs) => { + codegen_fn_attrs.alignment = reprs + .iter() + .find_map(|(r, _)| if let ReprAlign(x) = r { Some(*x) } else { None }); + } + + _ => {} + } + } + let Some(Ident { name, .. }) = attr.ident() else { continue; }; @@ -130,14 +145,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { if tcx.opt_item_name(did.to_def_id()).is_some() { codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; mixed_export_name_no_mangle_lint_state.track_no_mangle( - attr.span, + attr.span(), tcx.local_def_id_to_hir_id(did), attr, ); } else { tcx.dcx() .struct_span_err( - attr.span, + attr.span(), format!( "`#[no_mangle]` cannot be used on {} {} as it has no name", tcx.def_descr_article(did.to_def_id()), @@ -158,7 +173,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { feature_err( &tcx.sess, sym::used_with_arg, - attr.span, + attr.span(), "`#[used(linker)]` is currently unstable", ) .emit(); @@ -170,7 +185,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { feature_err( &tcx.sess, sym::used_with_arg, - attr.span, + attr.span(), "`#[used(compiler)]` is currently unstable", ) .emit(); @@ -178,7 +193,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED; } Some(_) => { - tcx.dcx().emit_err(errors::ExpectedUsedSymbol { span: attr.span }); + tcx.dcx().emit_err(errors::ExpectedUsedSymbol { span: attr.span() }); } None => { // Unfortunately, unconditionally using `llvm.used` causes @@ -223,7 +238,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { { struct_span_code_err!( tcx.dcx(), - attr.span, + attr.span(), E0737, "`#[track_caller]` requires Rust ABI" ) @@ -231,12 +246,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } if is_closure && !tcx.features().closure_track_caller() - && !attr.span.allows_unstable(sym::closure_track_caller) + && !attr.span().allows_unstable(sym::closure_track_caller) { feature_err( &tcx.sess, sym::closure_track_caller, - attr.span, + attr.span(), "`#[track_caller]` on closures is currently unstable", ) .emit(); @@ -250,19 +265,19 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { // so it may not contain any null characters. struct_span_code_err!( tcx.dcx(), - attr.span, + attr.span(), E0648, "`export_name` may not contain null characters" ) .emit(); } codegen_fn_attrs.export_name = Some(s); - mixed_export_name_no_mangle_lint_state.track_export_name(attr.span); + mixed_export_name_no_mangle_lint_state.track_export_name(attr.span()); } } sym::target_feature => { let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else { - tcx.dcx().span_delayed_bug(attr.span, "target_feature applied to non-fn"); + tcx.dcx().span_delayed_bug(attr.span(), "target_feature applied to non-fn"); continue; }; let safe_target_features = @@ -292,7 +307,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { // `main`, `start`, and other functions that are not usually // allowed. } else { - check_target_feature_trait_unsafe(tcx, did, attr.span); + check_target_feature_trait_unsafe(tcx, did, attr.span()); } } from_target_feature_attr( @@ -310,7 +325,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { if tcx.is_mutable_static(did.into()) { let mut diag = tcx.dcx().struct_span_err( - attr.span, + attr.span(), "extern mutable statics are not allowed with `#[linkage]`", ); diag.note( @@ -329,7 +344,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { if let Some(val) = attr.value_str() { if val.as_str().bytes().any(|b| b == 0) { let msg = format!("illegal null byte in link_section value: `{val}`"); - tcx.dcx().span_err(attr.span, msg); + tcx.dcx().span_err(attr.span(), msg); } else { codegen_fn_attrs.link_section = Some(val); } @@ -337,13 +352,13 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } sym::link_name => codegen_fn_attrs.link_name = attr.value_str(), sym::link_ordinal => { - link_ordinal_span = Some(attr.span); + link_ordinal_span = Some(attr.span()); if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) { codegen_fn_attrs.link_ordinal = ordinal; } } sym::no_sanitize => { - no_sanitize_span = Some(attr.span); + no_sanitize_span = Some(attr.span()); if let Some(list) = attr.meta_item_list() { for item in list.iter() { match item.name_or_empty() { @@ -381,7 +396,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { { struct_span_code_err!( tcx.dcx(), - attr.span, + attr.span(), E0779, "target does not support `#[instruction_set]`" ) @@ -393,7 +408,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { _ => { struct_span_code_err!( tcx.dcx(), - attr.span, + attr.span(), E0779, "invalid instruction set specified", ) @@ -405,7 +420,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { [] => { struct_span_code_err!( tcx.dcx(), - attr.span, + attr.span(), E0778, "`#[instruction_set]` requires an argument" ) @@ -415,7 +430,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { _ => { struct_span_code_err!( tcx.dcx(), - attr.span, + attr.span(), E0779, "cannot specify more than one instruction set" ) @@ -424,27 +439,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } }) } - sym::repr => { - codegen_fn_attrs.alignment = if let Some(items) = attr.meta_item_list() - && let [item] = items.as_slice() - && let Some((sym::align, literal)) = item.singleton_lit_list() - { - rustc_attr_parsing::parse_alignment(&literal.kind) - .inspect_err(|msg| { - struct_span_code_err!( - tcx.dcx(), - literal.span, - E0589, - "invalid `repr(align)` attribute: {}", - msg - ) - .emit(); - }) - .ok() - } else { - None - }; - } sym::patchable_function_entry => { codegen_fn_attrs.patchable_function_entry = attr.meta_item_list().and_then(|l| { let mut prefix = None; @@ -510,7 +504,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } if let (None, None) = (prefix, entry) { - tcx.dcx().span_err(attr.span, "must specify at least one parameter"); + tcx.dcx().span_err(attr.span(), "must specify at least one parameter"); } Some(PatchableFunctionEntry::from_prefix_and_entry( @@ -536,18 +530,19 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { let Some(ref items) = attr.meta_item_list() else { return ia; }; + inline_span = Some(attr.span()); - inline_span = Some(attr.span); let [item] = &items[..] else { - struct_span_code_err!(tcx.dcx(), attr.span, E0534, "expected one argument").emit(); + struct_span_code_err!(tcx.dcx(), attr.span(), E0534, "expected one argument").emit(); return InlineAttr::None; }; + if item.has_name(sym::always) { InlineAttr::Always } else if item.has_name(sym::never) { InlineAttr::Never } else { - struct_span_code_err!(tcx.dcx(), item.span(), E0535, "invalid argument") + struct_span_code_err!(tcx.dcx(), items[0].span(), E0535, "invalid argument") .with_help("valid inline arguments are `always` and `never`") .emit(); @@ -560,9 +555,9 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } if attr.is_word() { - InlineAttr::Force { attr_span: attr.span, reason: None } + InlineAttr::Force { attr_span: attr.span(), reason: None } } else if let Some(val) = attr.value_str() { - InlineAttr::Force { attr_span: attr.span, reason: Some(val) } + InlineAttr::Force { attr_span: attr.span(), reason: Some(val) } } else { debug!("`rustc_force_inline` not checked by attribute validation"); ia @@ -582,16 +577,16 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } let err = |sp, s| struct_span_code_err!(tcx.dcx(), sp, E0722, "{}", s).emit(); if attr.is_word() { - err(attr.span, "expected one argument"); + err(attr.span(), "expected one argument"); return ia; } let Some(ref items) = attr.meta_item_list() else { return OptimizeAttr::Default; }; - inline_span = Some(attr.span); + inline_span = Some(attr.span()); let [item] = &items[..] else { - err(attr.span, "expected one argument"); + err(attr.span(), "expected one argument"); return OptimizeAttr::Default; }; if item.has_name(sym::size) { @@ -703,7 +698,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { let span = tcx .get_attrs(did, sym::target_feature) .next() - .map_or_else(|| tcx.def_span(did), |a| a.span); + .map_or_else(|| tcx.def_span(did), |a| a.span()); tcx.dcx() .create_err(errors::TargetFeatureDisableOrEnable { features, @@ -752,7 +747,7 @@ fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Option { use rustc_ast::{LitIntType, LitKind, MetaItemLit}; let meta_item_list = attr.meta_item_list()?; let [sole_meta_list] = &meta_item_list[..] else { - tcx.dcx().emit_err(errors::InvalidLinkOrdinalNargs { span: attr.span }); + tcx.dcx().emit_err(errors::InvalidLinkOrdinalNargs { span: attr.span() }); return None; }; if let Some(MetaItemLit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) = @@ -776,13 +771,13 @@ fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Option { } else { let msg = format!("ordinal value in `link_ordinal` is too large: `{ordinal}`"); tcx.dcx() - .struct_span_err(attr.span, msg) + .struct_span_err(attr.span(), msg) .with_note("the value may not exceed `u16::MAX`") .emit(); None } } else { - tcx.dcx().emit_err(errors::InvalidLinkOrdinalFormat { span: attr.span }); + tcx.dcx().emit_err(errors::InvalidLinkOrdinalFormat { span: attr.span() }); None } } @@ -828,7 +823,7 @@ impl<'a> MixedExportNameAndNoMangleState<'a> { export_name: Some(export_name), no_mangle: Some(no_mangle), hir_id: Some(hir_id), - no_mangle_attr: Some(no_mangle_attr), + no_mangle_attr: Some(_), } = self { tcx.emit_node_span_lint( @@ -837,7 +832,7 @@ impl<'a> MixedExportNameAndNoMangleState<'a> { no_mangle, errors::MixedExportNameAndNoMangle { no_mangle, - no_mangle_attr: rustc_hir_pretty::attribute_to_string(&tcx, no_mangle_attr), + no_mangle_attr: "#[unsafe(no_mangle)]".to_string(), export_name, removal_span: no_mangle, }, @@ -869,7 +864,7 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option { _ => { //FIXME(ZuseZ4): Once we fixed our parser, we should also prohibit the two-attribute //branch above. - span_bug!(attrs[1].span, "cg_ssa: rustc_autodiff should only exist once per source"); + span_bug!(attrs[1].span(), "cg_ssa: rustc_autodiff should only exist once per source"); } }; @@ -881,12 +876,12 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option { } let [mode, input_activities @ .., ret_activity] = &list[..] else { - span_bug!(attr.span, "rustc_autodiff attribute must contain mode and activities"); + span_bug!(attr.span(), "rustc_autodiff attribute must contain mode and activities"); }; let mode = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = mode { p1.segments.first().unwrap().ident } else { - span_bug!(attr.span, "rustc_autodiff attribute must contain mode"); + span_bug!(attr.span(), "rustc_autodiff attribute must contain mode"); }; // parse mode @@ -902,7 +897,7 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option { let ret_symbol = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = ret_activity { p1.segments.first().unwrap().ident } else { - span_bug!(attr.span, "rustc_autodiff attribute must contain the return activity"); + span_bug!(attr.span(), "rustc_autodiff attribute must contain the return activity"); }; // Then parse it into an actual DiffActivity @@ -937,11 +932,11 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option { for &input in &arg_activities { if !valid_input_activity(mode, input) { - span_bug!(attr.span, "Invalid input activity {} for {} mode", input, mode); + span_bug!(attr.span(), "Invalid input activity {} for {} mode", input, mode); } } if !valid_ret_activity(mode, ret_activity) { - span_bug!(attr.span, "Invalid return activity {} for {} mode", ret_activity, mode); + span_bug!(attr.span(), "Invalid return activity {} for {} mode", ret_activity, mode); } Some(AutoDiffAttrs { mode, ret_activity, input_activity: arg_activities }) diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs index 52e000858b4c7..659d4a304563e 100644 --- a/compiler/rustc_const_eval/src/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/check_consts/mod.rs @@ -4,12 +4,13 @@ //! has interior mutability or needs to be dropped, as well as the visitor that emits errors when //! it finds operations that are invalid in a certain context. +use rustc_attr_parsing::{AttributeKind, find_attr}; use rustc_errors::DiagCtxtHandle; +use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::ty::{self, PolyFnSig, TyCtxt}; use rustc_middle::{bug, mir}; use rustc_span::Symbol; -use {rustc_attr_parsing as attr, rustc_hir as hir}; pub use self::qualifs::Qualif; @@ -81,7 +82,8 @@ pub fn rustc_allow_const_fn_unstable( feature_gate: Symbol, ) -> bool { let attrs = tcx.hir().attrs(tcx.local_def_id_to_hir_id(def_id)); - attr::rustc_allow_const_fn_unstable(tcx.sess, attrs).any(|name| name == feature_gate) + + find_attr!(attrs, AttributeKind::AllowConstFnUnstable(syms) if syms.contains(&feature_gate)) } /// Returns `true` if the given `def_id` (trait or function) is "safe to expose on stable". diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index c1d8cd9bb9ed2..b11793c190a14 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -10,6 +10,7 @@ derive_setters = "0.1.6" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_error_codes = { path = "../rustc_error_codes" } rustc_error_messages = { path = "../rustc_error_messages" } diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index 7f383946c1463..db6532f41eab2 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -8,6 +8,7 @@ use std::process::ExitStatus; use rustc_abi::TargetDataLayoutErrors; use rustc_ast::util::parser::ExprPrecedence; use rustc_ast_pretty::pprust; +use rustc_attr_data_structures::RustcVersion; use rustc_macros::Subdiagnostic; use rustc_span::edition::Edition; use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol}; @@ -96,6 +97,12 @@ into_diag_arg_using_display!( rustc_abi::ExternAbi, ); +impl IntoDiagArg for RustcVersion { + fn into_diag_arg(self) -> DiagArgValue { + DiagArgValue::Str(Cow::Owned(self.to_string())) + } +} + impl IntoDiagArg for rustc_type_ir::TraitRef { fn into_diag_arg(self) -> DiagArgValue { self.to_string().into_diag_arg() diff --git a/compiler/rustc_expand/Cargo.toml b/compiler/rustc_expand/Cargo.toml index 33bada106ca79..0ba139ea5cc57 100644 --- a/compiler/rustc_expand/Cargo.toml +++ b/compiler/rustc_expand/Cargo.toml @@ -17,6 +17,7 @@ rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } +rustc_hir = { path = "../rustc_hir" } rustc_lexer = { path = "../rustc_lexer" } rustc_lint_defs = { path = "../rustc_lint_defs" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 819694d1cdc1f..4a250145308cf 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -11,11 +11,12 @@ use rustc_ast::token::Nonterminal; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{AssocCtxt, Visitor}; use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind}; -use rustc_attr_parsing::{self as attr, Deprecation, Stability}; +use rustc_attr_parsing::{AttributeKind, Deprecation, Stability, find_attr}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync; use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult}; use rustc_feature::Features; +use rustc_hir as hir; use rustc_lint_defs::{BufferedEarlyLint, RegisteredTools}; use rustc_parse::MACRO_ARGUMENTS; use rustc_parse::parser::Parser; @@ -838,19 +839,23 @@ impl SyntaxExtension { /// and other properties converted from attributes. pub fn new( sess: &Session, - features: &Features, kind: SyntaxExtensionKind, span: Span, helper_attrs: Vec, edition: Edition, name: Symbol, - attrs: &[impl AttributeExt], + attrs: &[hir::Attribute], is_local: bool, ) -> SyntaxExtension { let allow_internal_unstable = - rustc_attr_parsing::allow_internal_unstable(sess, attrs).collect::>(); + find_attr!(attrs, AttributeKind::AllowInternalUnstable(i) => i) + .map(|i| i.as_slice()) + .unwrap_or_default(); + // FIXME(jdonszelman): allow_internal_unsafe isn't yet new-style + // let allow_internal_unsafe = find_attr!(attrs, AttributeKind::AllowInternalUnsafe); + let allow_internal_unsafe = + ast::attr::find_by_name(attrs, sym::allow_internal_unsafe).is_some(); - let allow_internal_unsafe = ast::attr::contains_name(attrs, sym::allow_internal_unsafe); let local_inner_macros = ast::attr::find_by_name(attrs, sym::macro_export) .and_then(|macro_export| macro_export.meta_item_list()) .is_some_and(|l| ast::attr::list_contains_name(&l, sym::local_inner_macros)); @@ -867,16 +872,17 @@ impl SyntaxExtension { ) }) .unwrap_or_else(|| (None, helper_attrs)); - let stability = attr::find_stability(sess, attrs, span); - let const_stability = attr::find_const_stability(sess, attrs, span); - let body_stability = attr::find_body_stability(sess, attrs); - if let Some((_, sp)) = const_stability { + + let stability = find_attr!(attrs, AttributeKind::Stability{stability, ..} => *stability); + + // FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem + if let Some(sp) = find_attr!(attrs, AttributeKind::ConstStability{span, ..} => *span) { sess.dcx().emit_err(errors::MacroConstStability { span: sp, head_span: sess.source_map().guess_head_span(span), }); } - if let Some((_, sp)) = body_stability { + if let Some(sp) = find_attr!(attrs, AttributeKind::BodyStability{span, ..} => *span) { sess.dcx().emit_err(errors::MacroBodyStability { span: sp, head_span: sess.source_map().guess_head_span(span), @@ -887,9 +893,10 @@ impl SyntaxExtension { kind, span, allow_internal_unstable: (!allow_internal_unstable.is_empty()) - .then(|| allow_internal_unstable.into()), - stability: stability.map(|(s, _)| s), - deprecation: attr::find_deprecation(sess, features, attrs).map(|(d, _)| d), + // FIXME(jdonszelmann): avoid the into_iter/collect? + .then(|| allow_internal_unstable.iter().map(|i| i.0).collect::>().into()), + stability, + deprecation: find_attr!(attrs, AttributeKind::Deprecation{deprecation, ..} => *deprecation), helper_attrs, edition, builtin_name, diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index b02a9b93c8af0..cc7e3e651050a 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -3,17 +3,17 @@ use std::collections::hash_map::Entry; use std::{mem, slice}; use ast::token::IdentIsRaw; -use rustc_ast::attr::AttributeExt; use rustc_ast::token::NtPatKind::*; use rustc_ast::token::TokenKind::*; use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind}; use rustc_ast::tokenstream::{DelimSpan, TokenStream}; use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId}; use rustc_ast_pretty::pprust; -use rustc_attr_parsing::{self as attr, TransparencyError}; +use rustc_attr_parsing::{AttributeKind, find_attr}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::{Applicability, ErrorGuaranteed}; use rustc_feature::Features; +use rustc_hir as hir; use rustc_lint_defs::BuiltinLintDiag; use rustc_lint_defs::builtin::{ RUST_2021_INCOMPATIBLE_OR_PATTERNS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, @@ -371,7 +371,7 @@ pub fn compile_declarative_macro( features: &Features, macro_def: &ast::MacroDef, ident: Ident, - attrs: &[impl AttributeExt], + attrs: &[hir::Attribute], span: Span, node_id: NodeId, edition: Edition, @@ -379,7 +379,6 @@ pub fn compile_declarative_macro( let mk_syn_ext = |expander| { SyntaxExtension::new( sess, - features, SyntaxExtensionKind::LegacyBang(expander), span, Vec::new(), @@ -391,7 +390,6 @@ pub fn compile_declarative_macro( }; let dummy_syn_ext = |guar| (mk_syn_ext(Box::new(DummyExpander(guar))), Vec::new()); - let dcx = sess.dcx(); let lhs_nm = Ident::new(sym::lhs, span); let rhs_nm = Ident::new(sym::rhs, span); let tt_spec = Some(NonterminalKind::TT); @@ -542,16 +540,8 @@ pub fn compile_declarative_macro( check_emission(macro_check::check_meta_variables(&sess.psess, node_id, span, &lhses, &rhses)); - let (transparency, transparency_error) = attr::find_transparency(attrs, macro_rules); - match transparency_error { - Some(TransparencyError::UnknownTransparency(value, span)) => { - dcx.span_err(span, format!("unknown macro transparency: `{value}`")); - } - Some(TransparencyError::MultipleTransparencyAttrs(old_span, new_span)) => { - dcx.span_err(vec![old_span, new_span], "multiple macro transparency attributes"); - } - None => {} - } + let transparency = find_attr!(attrs, AttributeKind::MacroTransparency(x) => *x) + .unwrap_or(Transparency::fallback(macro_rules)); if let Some(guar) = guar { // To avoid warning noise, only consider the rules of this diff --git a/compiler/rustc_hir/Cargo.toml b/compiler/rustc_hir/Cargo.toml index 98300fc40fbeb..7ca8539845a04 100644 --- a/compiler/rustc_hir/Cargo.toml +++ b/compiler/rustc_hir/Cargo.toml @@ -9,6 +9,7 @@ odht = { version = "0.3.1", features = ["nightly"] } rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_hashes = { path = "../rustc_hashes" } rustc_index = { path = "../rustc_index" } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 61f64e62058fc..f0eaec55dbdd3 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1,18 +1,20 @@ +// ignore-tidy-filelength use std::fmt; use rustc_abi::ExternAbi; -// ignore-tidy-filelength use rustc_ast::attr::AttributeExt; use rustc_ast::token::CommentKind; use rustc_ast::util::parser::{AssocOp, ExprPrecedence}; use rustc_ast::{ - self as ast, AttrId, AttrStyle, DelimArgs, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, - IntTy, Label, LitIntType, LitKind, MetaItemInner, MetaItemLit, TraitObjectSyntax, UintTy, + self as ast, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, LitIntType, + LitKind, TraitObjectSyntax, UintTy, UnsafeBinderCastKind, }; pub use rustc_ast::{ - BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity, ByRef, CaptureBy, - ImplPolarity, IsAuto, Movability, Mutability, UnOp, UnsafeBinderCastKind, + AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity, + ByRef, CaptureBy, DelimArgs, ImplPolarity, IsAuto, MetaItemInner, MetaItemLit, Movability, + Mutability, UnOp, }; +use rustc_attr_data_structures::AttributeKind; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::tagged_ptr::TaggedRef; @@ -1009,174 +1011,248 @@ pub enum AttrArgs { }, } -#[derive(Clone, Debug, Encodable, Decodable)] -pub enum AttrKind { - /// A normal attribute. - Normal(Box), - - /// A doc comment (e.g. `/// ...`, `//! ...`, `/** ... */`, `/*! ... */`). - /// Doc attributes (e.g. `#[doc="..."]`) are represented with the `Normal` - /// variant (which is much less compact and thus more expensive). - DocComment(CommentKind, Symbol), -} - #[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable)] pub struct AttrPath { pub segments: Box<[Ident]>, pub span: Span, } +impl AttrPath { + pub fn from_ast(path: &ast::Path) -> Self { + AttrPath { + segments: path.segments.iter().map(|i| i.ident).collect::>().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::>().join("::")) + } +} + #[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable)] pub struct AttrItem { - pub unsafety: Safety, // Not lowered to hir::Path because we have no NodeId to resolve to. pub path: AttrPath, pub args: AttrArgs, -} - -#[derive(Clone, Debug, Encodable, Decodable)] -pub struct Attribute { - pub kind: AttrKind, - pub id: AttrId, + pub id: HashIgnoredAttrId, /// Denotes if the attribute decorates the following construct (outer) /// or the construct this attribute is contained within (inner). pub style: AttrStyle, + /// Span of the entire attribute pub span: Span, } +/// The derived implementation of [`HashStable_Generic`] on [`Attribute`]s shouldn't hash +/// [`AttrId`]s. By wrapping them in this, we make sure we never do. +#[derive(Copy, Debug, Encodable, Decodable, Clone)] +pub struct HashIgnoredAttrId { + pub attr_id: AttrId, +} + +#[derive(Clone, Debug, Encodable, Decodable, HashStable_Generic)] +pub enum Attribute { + /// A parsed built-in attribute. + /// + /// Each attribute has a span connected to it. However, you must be somewhat careful using it. + /// That's because sometimes we merge multiple attributes together, like when an item has + /// multiple `repr` attributes. In this case the span might not be very useful. + Parsed(AttributeKind), + + /// An attribute that could not be parsed, out of a token-like representation. + /// This is the case for custom tool attributes. + Unparsed(Box), +} + impl Attribute { pub fn get_normal_item(&self) -> &AttrItem { - match &self.kind { - AttrKind::Normal(normal) => &normal, - AttrKind::DocComment(..) => panic!("unexpected doc comment"), + match &self { + Attribute::Unparsed(normal) => &normal, + _ => panic!("unexpected parsed attribute"), } } pub fn unwrap_normal_item(self) -> AttrItem { - match self.kind { - AttrKind::Normal(normal) => *normal, - AttrKind::DocComment(..) => panic!("unexpected doc comment"), + match self { + Attribute::Unparsed(normal) => *normal, + _ => panic!("unexpected parsed attribute"), } } pub fn value_lit(&self) -> Option<&MetaItemLit> { - match &self.kind { - AttrKind::Normal(box AttrItem { args: AttrArgs::Eq { expr, .. }, .. }) => Some(expr), + match &self { + Attribute::Unparsed(n) => match n.as_ref() { + AttrItem { args: AttrArgs::Eq { eq_span: _, expr }, .. } => Some(expr), + _ => None, + }, _ => None, } } } impl AttributeExt for Attribute { + #[inline] fn id(&self) -> AttrId { - self.id + match &self { + Attribute::Unparsed(u) => u.id.attr_id, + _ => panic!(), + } } + #[inline] fn meta_item_list(&self) -> Option> { - match &self.kind { - AttrKind::Normal(box AttrItem { args: AttrArgs::Delimited(d), .. }) => { - ast::MetaItemKind::list_from_tokens(d.tokens.clone()) - } + match &self { + Attribute::Unparsed(n) => match n.as_ref() { + AttrItem { args: AttrArgs::Delimited(d), .. } => { + ast::MetaItemKind::list_from_tokens(d.tokens.clone()) + } + _ => None, + }, _ => None, } } + #[inline] fn value_str(&self) -> Option { self.value_lit().and_then(|x| x.value_str()) } + #[inline] fn value_span(&self) -> Option { self.value_lit().map(|i| i.span) } /// For a single-segment attribute, returns its name; otherwise, returns `None`. + #[inline] fn ident(&self) -> Option { - match &self.kind { - AttrKind::Normal(box AttrItem { - path: AttrPath { segments: box [ident], .. }, .. - }) => Some(*ident), + match &self { + Attribute::Unparsed(n) => { + if let [ident] = n.path.segments.as_ref() { + Some(*ident) + } else { + None + } + } _ => None, } } + #[inline] fn path_matches(&self, name: &[Symbol]) -> bool { - match &self.kind { - AttrKind::Normal(n) => n.path.segments.iter().map(|segment| &segment.name).eq(name), - AttrKind::DocComment(..) => false, + match &self { + Attribute::Unparsed(n) => { + n.path.segments.len() == name.len() + && n.path.segments.iter().zip(name).all(|(s, n)| s.name == *n) + } + _ => false, } } + #[inline] fn is_doc_comment(&self) -> bool { - matches!(self.kind, AttrKind::DocComment(..)) + matches!(self, Attribute::Parsed(AttributeKind::DocComment { .. })) } + #[inline] fn span(&self) -> Span { - self.span + match &self { + Attribute::Unparsed(u) => u.span, + // FIXME: should not be needed anymore when all attrs are parsed + Attribute::Parsed(AttributeKind::Deprecation { span, .. }) => *span, + Attribute::Parsed(AttributeKind::DocComment { span, .. }) => *span, + a => panic!("can't get the span of an arbitrary parsed attribute: {a:?}"), + } } + #[inline] fn is_word(&self) -> bool { - matches!(self.kind, AttrKind::Normal(box AttrItem { args: AttrArgs::Empty, .. })) + match &self { + Attribute::Unparsed(n) => { + matches!(n.args, AttrArgs::Empty) + } + _ => false, + } } + #[inline] fn ident_path(&self) -> Option> { - match &self.kind { - AttrKind::Normal(n) => Some(n.path.segments.iter().copied().collect()), - AttrKind::DocComment(..) => None, + match &self { + Attribute::Unparsed(n) => Some(n.path.segments.iter().copied().collect()), + _ => None, } } + #[inline] fn doc_str(&self) -> Option { - match &self.kind { - AttrKind::DocComment(.., data) => Some(*data), - AttrKind::Normal(_) if self.has_name(sym::doc) => self.value_str(), + match &self { + Attribute::Parsed(AttributeKind::DocComment { comment, .. }) => Some(*comment), + Attribute::Unparsed(_) if self.has_name(sym::doc) => self.value_str(), _ => None, } } + #[inline] fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> { - match &self.kind { - AttrKind::DocComment(kind, data) => Some((*data, *kind)), - AttrKind::Normal(_) if self.name_or_empty() == sym::doc => { + match &self { + Attribute::Parsed(AttributeKind::DocComment { kind, comment, .. }) => { + Some((*comment, *kind)) + } + Attribute::Unparsed(_) if self.name_or_empty() == sym::doc => { self.value_str().map(|s| (s, CommentKind::Line)) } _ => None, } } + #[inline] fn style(&self) -> AttrStyle { - self.style + match &self { + Attribute::Unparsed(u) => u.style, + Attribute::Parsed(AttributeKind::DocComment { style, .. }) => *style, + _ => panic!(), + } } } // FIXME(fn_delegation): use function delegation instead of manually forwarding impl Attribute { + #[inline] pub fn id(&self) -> AttrId { AttributeExt::id(self) } + #[inline] pub fn name_or_empty(&self) -> Symbol { AttributeExt::name_or_empty(self) } + #[inline] pub fn meta_item_list(&self) -> Option> { AttributeExt::meta_item_list(self) } + #[inline] pub fn value_str(&self) -> Option { AttributeExt::value_str(self) } + #[inline] pub fn value_span(&self) -> Option { AttributeExt::value_span(self) } + #[inline] pub fn ident(&self) -> Option { AttributeExt::ident(self) } + #[inline] pub fn path_matches(&self, name: &[Symbol]) -> bool { AttributeExt::path_matches(self, name) } + #[inline] pub fn is_doc_comment(&self) -> bool { AttributeExt::is_doc_comment(self) } @@ -1186,34 +1262,42 @@ impl Attribute { AttributeExt::has_name(self, name) } + #[inline] pub fn span(&self) -> Span { AttributeExt::span(self) } + #[inline] pub fn is_word(&self) -> bool { AttributeExt::is_word(self) } + #[inline] pub fn path(&self) -> SmallVec<[Symbol; 1]> { AttributeExt::path(self) } + #[inline] pub fn ident_path(&self) -> Option> { AttributeExt::ident_path(self) } + #[inline] pub fn doc_str(&self) -> Option { AttributeExt::doc_str(self) } + #[inline] pub fn is_proc_macro_attr(&self) -> bool { AttributeExt::is_proc_macro_attr(self) } + #[inline] pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> { AttributeExt::doc_str_and_comment_kind(self) } + #[inline] pub fn style(&self) -> AttrStyle { AttributeExt::style(self) } diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs index d7c8a3d5c0a5f..2709a826549c5 100644 --- a/compiler/rustc_hir/src/stable_hash_impls.rs +++ b/compiler/rustc_hir/src/stable_hash_impls.rs @@ -1,17 +1,21 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; use rustc_span::def_id::DefPathHash; +use crate::HashIgnoredAttrId; use crate::hir::{ - Attribute, AttributeMap, BodyId, Crate, ForeignItemId, ImplItemId, ItemId, OwnerNodes, - TraitItemId, + AttributeMap, BodyId, Crate, ForeignItemId, ImplItemId, ItemId, OwnerNodes, TraitItemId, }; use crate::hir_id::{HirId, ItemLocalId}; /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro /// instead of implementing everything in `rustc_middle`. -pub trait HashStableContext: rustc_ast::HashStableContext + rustc_abi::HashStableContext { - fn hash_attr(&mut self, _: &Attribute, hasher: &mut StableHasher); +pub trait HashStableContext: + rustc_attr_data_structures::HashStableContext + + rustc_ast::HashStableContext + + rustc_abi::HashStableContext +{ + fn hash_attr_id(&mut self, id: &HashIgnoredAttrId, hasher: &mut StableHasher); } impl ToStableHashKey for HirId { @@ -114,8 +118,8 @@ impl HashStable for Crate<'_> { } } -impl HashStable for Attribute { +impl HashStable for HashIgnoredAttrId { fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { - hcx.hash_attr(self, hasher) + hcx.hash_attr_id(self, hasher) } } diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 3236e0a3644d9..09320b8687836 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -2,6 +2,8 @@ use std::cell::LazyCell; use std::ops::ControlFlow; use rustc_abi::FieldIdx; +use rustc_attr_parsing::AttributeKind; +use rustc_attr_parsing::ReprAttr::ReprPacked; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::MultiSpan; use rustc_errors::codes::*; @@ -1114,7 +1116,7 @@ fn check_impl_items_against_trait<'tcx>( if let Some(missing_items) = must_implement_one_of { let attr_span = tcx .get_attr(trait_ref.def_id, sym::rustc_must_implement_one_of) - .map(|attr| attr.span); + .map(|attr| attr.span()); missing_items_must_implement_one_of_err( tcx, @@ -1203,11 +1205,13 @@ fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) { pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) { let repr = def.repr(); if repr.packed() { - for attr in tcx.get_attrs(def.did(), sym::repr) { - for r in attr::parse_repr_attr(tcx.sess, attr) { - if let attr::ReprPacked(pack) = r + if let Some(reprs) = + attr::find_attr!(tcx.get_all_attrs(def.did()), AttributeKind::Repr(r) => r) + { + for (r, _) in reprs { + if let ReprPacked(pack) = r && let Some(repr_pack) = repr.pack - && pack != repr_pack + && pack != &repr_pack { struct_span_code_err!( tcx.dcx(), @@ -1419,16 +1423,19 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) { def.destructor(tcx); // force the destructor to be evaluated if def.variants().is_empty() { - if let Some(attr) = tcx.get_attrs(def_id, sym::repr).next() { - struct_span_code_err!( - tcx.dcx(), - attr.span, - E0084, - "unsupported representation for zero-variant enum" - ) - .with_span_label(tcx.def_span(def_id), "zero-variant enum") - .emit(); - } + attr::find_attr!( + tcx.get_all_attrs(def_id), + AttributeKind::Repr(rs) => { + struct_span_code_err!( + tcx.dcx(), + rs.first().unwrap().1, + E0084, + "unsupported representation for zero-variant enum" + ) + .with_span_label(tcx.def_span(def_id), "zero-variant enum") + .emit(); + } + ); } let repr_type_ty = def.repr().discr_type().to_ty(tcx); diff --git a/compiler/rustc_hir_analysis/src/check/entry.rs b/compiler/rustc_hir_analysis/src/check/entry.rs index 25c2f8554b7f4..3bad36da99909 100644 --- a/compiler/rustc_hir_analysis/src/check/entry.rs +++ b/compiler/rustc_hir_analysis/src/check/entry.rs @@ -99,7 +99,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { } for attr in tcx.get_attrs(main_def_id, sym::track_caller) { - tcx.dcx().emit_err(errors::TrackCallerOnMain { span: attr.span, annotated: main_span }); + tcx.dcx().emit_err(errors::TrackCallerOnMain { span: attr.span(), annotated: main_span }); error = true; } diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index dd91d70b004bb..2a2879c657747 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -1202,7 +1202,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { // and that they are all identifiers .and_then(|attr| match attr.meta_item_list() { Some(items) if items.len() < 2 => { - tcx.dcx().emit_err(errors::MustImplementOneOfAttribute { span: attr.span }); + tcx.dcx().emit_err(errors::MustImplementOneOfAttribute { span: attr.span() }); None } @@ -1214,7 +1214,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { tcx.dcx().emit_err(errors::MustBeNameOfAssociatedFunction { span }); }) .ok() - .zip(Some(attr.span)), + .zip(Some(attr.span())), // Error is reported by `rustc_attr!` None => None, }) diff --git a/compiler/rustc_hir_analysis/src/collect/dump.rs b/compiler/rustc_hir_analysis/src/collect/dump.rs index 63c445fa6a3d4..4debd3977f50a 100644 --- a/compiler/rustc_hir_analysis/src/collect/dump.rs +++ b/compiler/rustc_hir_analysis/src/collect/dump.rs @@ -111,14 +111,14 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) { let trait_ref = tcx.impl_trait_ref(def_id).unwrap().instantiate_identity(); if trait_ref.has_non_region_param() { tcx.dcx().span_err( - attr.span, + attr.span(), "`rustc_dump_vtable` must be applied to non-generic impl", ); continue; } if !tcx.is_dyn_compatible(trait_ref.def_id) { tcx.dcx().span_err( - attr.span, + attr.span(), "`rustc_dump_vtable` must be applied to dyn-compatible trait", ); continue; @@ -127,7 +127,7 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) { .try_normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref) else { tcx.dcx().span_err( - attr.span, + attr.span(), "`rustc_dump_vtable` applied to impl header that cannot be normalized", ); continue; @@ -138,7 +138,7 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) { let ty = tcx.type_of(def_id).instantiate_identity(); if ty.has_non_region_param() { tcx.dcx().span_err( - attr.span, + attr.span(), "`rustc_dump_vtable` must be applied to non-generic type", ); continue; @@ -147,13 +147,14 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) { tcx.try_normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), ty) else { tcx.dcx().span_err( - attr.span, + attr.span(), "`rustc_dump_vtable` applied to type alias that cannot be normalized", ); continue; }; let ty::Dynamic(data, _, _) = *ty.kind() else { - tcx.dcx().span_err(attr.span, "`rustc_dump_vtable` to type alias of dyn type"); + tcx.dcx() + .span_err(attr.span(), "`rustc_dump_vtable` to type alias of dyn type"); continue; }; if let Some(principal) = data.principal() { @@ -166,7 +167,7 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) { } _ => { tcx.dcx().span_err( - attr.span, + attr.span(), "`rustc_dump_vtable` only applies to impl, or type alias of dyn type", ); continue; diff --git a/compiler/rustc_hir_pretty/Cargo.toml b/compiler/rustc_hir_pretty/Cargo.toml index f5d7dbd3f96e4..86989d1e55b4d 100644 --- a/compiler/rustc_hir_pretty/Cargo.toml +++ b/compiler/rustc_hir_pretty/Cargo.toml @@ -8,6 +8,7 @@ edition = "2024" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_hir = { path = "../rustc_hir" } rustc_span = { path = "../rustc_span" } # tidy-alphabetical-end diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 167bed5f65029..5c7426d76b31c 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -11,11 +11,12 @@ use std::vec; use rustc_abi::ExternAbi; use rustc_ast::util::parser::{self, AssocOp, ExprPrecedence, Fixity}; -use rustc_ast::{DUMMY_NODE_ID, DelimArgs}; +use rustc_ast::{AttrStyle, DUMMY_NODE_ID, DelimArgs}; use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent}; use rustc_ast_pretty::pp::{self, Breaks}; use rustc_ast_pretty::pprust::state::MacHeader; use rustc_ast_pretty::pprust::{Comments, PrintState}; +use rustc_attr_parsing::{AttributeKind, PrintAttribute}; use rustc_hir::{ BindingMode, ByRef, ConstArgKind, GenericArg, GenericBound, GenericParam, GenericParamKind, HirId, ImplicitSelfKind, LifetimeParamKind, Node, PatKind, PreciseCapturingArg, RangeEnd, Term, @@ -80,65 +81,48 @@ impl<'a> State<'a> { (self.attrs)(id) } - fn print_inner_attributes(&mut self, attrs: &[hir::Attribute]) -> bool { - self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, true) + fn print_attrs_as_inner(&mut self, attrs: &[hir::Attribute]) { + self.print_either_attributes(attrs, ast::AttrStyle::Inner) } - fn print_outer_attributes(&mut self, attrs: &[hir::Attribute]) -> bool { - self.print_either_attributes(attrs, ast::AttrStyle::Outer, false, true) + fn print_attrs_as_outer(&mut self, attrs: &[hir::Attribute]) { + self.print_either_attributes(attrs, ast::AttrStyle::Outer) } - fn print_either_attributes( - &mut self, - attrs: &[hir::Attribute], - kind: ast::AttrStyle, - is_inline: bool, - trailing_hardbreak: bool, - ) -> bool { - let mut printed = false; - for attr in attrs { - if attr.style == kind { - self.print_attribute_inline(attr, is_inline); - if is_inline { - self.nbsp(); - } - printed = true; - } + fn print_either_attributes(&mut self, attrs: &[hir::Attribute], style: ast::AttrStyle) { + if attrs.is_empty() { + return; } - if printed && trailing_hardbreak && !is_inline { - self.hardbreak_if_not_bol(); + + for attr in attrs { + self.print_attribute_inline(attr, style); } - printed + self.hardbreak_if_not_bol(); } - fn print_attribute_inline(&mut self, attr: &hir::Attribute, is_inline: bool) { - if !is_inline { - self.hardbreak_if_not_bol(); - } - self.maybe_print_comment(attr.span.lo()); - match &attr.kind { - hir::AttrKind::Normal(normal) => { - match attr.style { + fn print_attribute_inline(&mut self, attr: &hir::Attribute, style: AttrStyle) { + match &attr { + hir::Attribute::Unparsed(unparsed) => { + self.maybe_print_comment(unparsed.span.lo()); + match style { ast::AttrStyle::Inner => self.word("#!["), ast::AttrStyle::Outer => self.word("#["), } - if normal.unsafety == hir::Safety::Unsafe { - self.word("unsafe("); - } - self.print_attr_item(&normal, attr.span); - if normal.unsafety == hir::Safety::Unsafe { - self.word(")"); - } + self.print_attr_item(&unparsed, unparsed.span); self.word("]"); } - hir::AttrKind::DocComment(comment_kind, data) => { + hir::Attribute::Parsed(AttributeKind::DocComment { style, kind, comment, .. }) => { self.word(rustc_ast_pretty::pprust::state::doc_comment_to_string( - *comment_kind, - attr.style, - *data, + *kind, *style, *comment, )); self.hardbreak() } + hir::Attribute::Parsed(pa) => { + self.word("#[attr=\""); + pa.print_attribute(self); + self.word("\")]"); + self.hardbreak() + } } } @@ -162,7 +146,7 @@ impl<'a> State<'a> { false, None, *delim, - tokens, + &tokens, true, span, ), @@ -307,7 +291,7 @@ where } pub fn attribute_to_string(ann: &dyn PpAnn, attr: &hir::Attribute) -> String { - to_string(ann, |s| s.print_attribute_inline(attr, false)) + to_string(ann, |s| s.print_attribute_inline(attr, AttrStyle::Outer)) } pub fn ty_to_string(ann: &dyn PpAnn, ty: &hir::Ty<'_>) -> String { @@ -370,7 +354,7 @@ impl<'a> State<'a> { } fn print_mod(&mut self, _mod: &hir::Mod<'_>, attrs: &[hir::Attribute]) { - self.print_inner_attributes(attrs); + self.print_attrs_as_inner(attrs); for &item_id in _mod.item_ids { self.ann.nested(self, Nested::Item(item_id)); } @@ -487,7 +471,7 @@ impl<'a> State<'a> { fn print_foreign_item(&mut self, item: &hir::ForeignItem<'_>) { self.hardbreak_if_not_bol(); self.maybe_print_comment(item.span.lo()); - self.print_outer_attributes(self.attrs(item.hir_id())); + self.print_attrs_as_outer(self.attrs(item.hir_id())); match item.kind { hir::ForeignItemKind::Fn(sig, arg_names, generics) => { self.head(""); @@ -591,7 +575,7 @@ impl<'a> State<'a> { self.hardbreak_if_not_bol(); self.maybe_print_comment(item.span.lo()); let attrs = self.attrs(item.hir_id()); - self.print_outer_attributes(attrs); + self.print_attrs_as_outer(attrs); self.ann.pre(self, AnnNode::Item(item)); match item.kind { hir::ItemKind::ExternCrate(orig_name) => { @@ -687,7 +671,7 @@ impl<'a> State<'a> { self.head("extern"); self.word_nbsp(abi.to_string()); self.bopen(); - self.print_inner_attributes(self.attrs(item.hir_id())); + self.print_attrs_as_inner(self.attrs(item.hir_id())); for item in items { self.ann.nested(self, Nested::ForeignItem(item.id)); } @@ -755,7 +739,7 @@ impl<'a> State<'a> { self.space(); self.bopen(); - self.print_inner_attributes(attrs); + self.print_attrs_as_inner(attrs); for impl_item in items { self.ann.nested(self, Nested::ImplItem(impl_item.id)); } @@ -847,7 +831,7 @@ impl<'a> State<'a> { for v in variants { self.space_if_not_bol(); self.maybe_print_comment(v.span.lo()); - self.print_outer_attributes(self.attrs(v.hir_id)); + self.print_attrs_as_outer(self.attrs(v.hir_id)); self.ibox(INDENT_UNIT); self.print_variant(v); self.word(","); @@ -880,7 +864,7 @@ impl<'a> State<'a> { self.popen(); self.commasep(Inconsistent, struct_def.fields(), |s, field| { s.maybe_print_comment(field.span.lo()); - s.print_outer_attributes(s.attrs(field.hir_id)); + s.print_attrs_as_outer(s.attrs(field.hir_id)); s.print_type(field.ty); }); self.pclose(); @@ -907,7 +891,7 @@ impl<'a> State<'a> { for field in fields { self.hardbreak_if_not_bol(); self.maybe_print_comment(field.span.lo()); - self.print_outer_attributes(self.attrs(field.hir_id)); + self.print_attrs_as_outer(self.attrs(field.hir_id)); self.print_ident(field.ident); self.word_nbsp(":"); self.print_type(field.ty); @@ -943,7 +927,7 @@ impl<'a> State<'a> { self.ann.pre(self, AnnNode::SubItem(ti.hir_id())); self.hardbreak_if_not_bol(); self.maybe_print_comment(ti.span.lo()); - self.print_outer_attributes(self.attrs(ti.hir_id())); + self.print_attrs_as_outer(self.attrs(ti.hir_id())); match ti.kind { hir::TraitItemKind::Const(ty, default) => { self.print_associated_const(ti.ident, ti.generics, ty, default); @@ -971,7 +955,7 @@ impl<'a> State<'a> { self.ann.pre(self, AnnNode::SubItem(ii.hir_id())); self.hardbreak_if_not_bol(); self.maybe_print_comment(ii.span.lo()); - self.print_outer_attributes(self.attrs(ii.hir_id())); + self.print_attrs_as_outer(self.attrs(ii.hir_id())); match ii.kind { hir::ImplItemKind::Const(ty, expr) => { @@ -1074,7 +1058,7 @@ impl<'a> State<'a> { self.ann.pre(self, AnnNode::Block(blk)); self.bopen(); - self.print_inner_attributes(attrs); + self.print_attrs_as_inner(attrs); for st in blk.stmts { self.print_stmt(st); @@ -1264,7 +1248,7 @@ impl<'a> State<'a> { self.space(); } self.cbox(INDENT_UNIT); - self.print_outer_attributes(self.attrs(field.hir_id)); + self.print_attrs_as_outer(self.attrs(field.hir_id)); if !field.is_shorthand { self.print_ident(field.ident); self.word_space(":"); @@ -1461,7 +1445,7 @@ impl<'a> State<'a> { fn print_expr(&mut self, expr: &hir::Expr<'_>) { self.maybe_print_comment(expr.span.lo()); - self.print_outer_attributes(self.attrs(expr.hir_id)); + self.print_attrs_as_outer(self.attrs(expr.hir_id)); self.ibox(INDENT_UNIT); self.ann.pre(self, AnnNode::Expr(expr)); match expr.kind { @@ -1677,8 +1661,8 @@ impl<'a> State<'a> { } hir::ExprKind::UnsafeBinderCast(kind, expr, ty) => { match kind { - hir::UnsafeBinderCastKind::Wrap => self.word("wrap_binder!("), - hir::UnsafeBinderCastKind::Unwrap => self.word("unwrap_binder!("), + ast::UnsafeBinderCastKind::Wrap => self.word("wrap_binder!("), + ast::UnsafeBinderCastKind::Unwrap => self.word("unwrap_binder!("), } self.print_expr(expr); if let Some(ty) = ty { @@ -2073,7 +2057,7 @@ impl<'a> State<'a> { self.space(); } self.cbox(INDENT_UNIT); - self.print_outer_attributes(self.attrs(field.hir_id)); + self.print_attrs_as_outer(self.attrs(field.hir_id)); if !field.is_shorthand { self.print_ident(field.ident); self.word_nbsp(":"); @@ -2083,7 +2067,7 @@ impl<'a> State<'a> { } fn print_param(&mut self, arg: &hir::Param<'_>) { - self.print_outer_attributes(self.attrs(arg.hir_id)); + self.print_attrs_as_outer(self.attrs(arg.hir_id)); self.print_pat(arg.pat); } @@ -2118,7 +2102,7 @@ impl<'a> State<'a> { self.cbox(INDENT_UNIT); self.ann.pre(self, AnnNode::Arm(arm)); self.ibox(0); - self.print_outer_attributes(self.attrs(arm.hir_id)); + self.print_attrs_as_outer(self.attrs(arm.hir_id)); self.print_pat(arm.pat); self.space(); if let Some(ref g) = arm.guard { diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 0c71ef4655c5c..4815627a0ce0b 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1656,13 +1656,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_expr_unsafe_binder_cast( &self, span: Span, - kind: hir::UnsafeBinderCastKind, + kind: ast::UnsafeBinderCastKind, inner_expr: &'tcx hir::Expr<'tcx>, hir_ty: Option<&'tcx hir::Ty<'tcx>>, expected: Expectation<'tcx>, ) -> Ty<'tcx> { match kind { - hir::UnsafeBinderCastKind::Wrap => { + ast::UnsafeBinderCastKind::Wrap => { let ascribed_ty = hir_ty.map(|hir_ty| self.lower_ty_saving_user_provided_ty(hir_ty)); let expected_ty = expected.only_has_type(self); @@ -1706,7 +1706,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { binder_ty } - hir::UnsafeBinderCastKind::Unwrap => { + ast::UnsafeBinderCastKind::Unwrap => { let ascribed_ty = hir_ty.map(|hir_ty| self.lower_ty_saving_user_provided_ty(hir_ty)); let hint_ty = ascribed_ty.unwrap_or_else(|| self.next_ty_var(inner_expr.span)); diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 8438a92219eae..18218a7a0a641 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -9,7 +9,7 @@ use std::path::PathBuf; use hir::Expr; use rustc_ast::ast::Mutability; -use rustc_attr_parsing::parse_confusables; +use rustc_attr_parsing::{AttributeKind, find_attr}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::unord::UnordSet; @@ -1884,9 +1884,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for inherent_method in self.tcx.associated_items(inherent_impl_did).in_definition_order() { - if let Some(attr) = - self.tcx.get_attr(inherent_method.def_id, sym::rustc_confusables) - && let Some(candidates) = parse_confusables(attr) + if let Some(candidates) = find_attr!(self.tcx.get_all_attrs(inherent_method.def_id), AttributeKind::Confusables{symbols, ..} => symbols) && candidates.contains(&item_name.name) && let ty::AssocKind::Fn = inherent_method.kind { diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index d4407559202f0..8f2ca6babea88 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -137,13 +137,13 @@ impl<'tcx> IfThisChanged<'tcx> { match DepNode::from_label_string(self.tcx, n.as_str(), def_path_hash) { Ok(n) => n, Err(()) => self.tcx.dcx().emit_fatal(errors::UnrecognizedDepNode { - span: attr.span, + span: attr.span(), name: n, }), } } }; - self.if_this_changed.push((attr.span, def_id.to_def_id(), dep_node)); + self.if_this_changed.push((attr.span(), def_id.to_def_id(), dep_node)); } else if attr.has_name(sym::rustc_then_this_would_need) { let dep_node_interned = self.argument(attr); let dep_node = match dep_node_interned { @@ -151,17 +151,17 @@ impl<'tcx> IfThisChanged<'tcx> { match DepNode::from_label_string(self.tcx, n.as_str(), def_path_hash) { Ok(n) => n, Err(()) => self.tcx.dcx().emit_fatal(errors::UnrecognizedDepNode { - span: attr.span, + span: attr.span(), name: n, }), } } None => { - self.tcx.dcx().emit_fatal(errors::MissingDepNode { span: attr.span }); + self.tcx.dcx().emit_fatal(errors::MissingDepNode { span: attr.span() }); } }; self.then_this_would_need.push(( - attr.span, + attr.span(), dep_node_interned.unwrap(), hir_id, dep_node, diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index 56858679af663..d40a0d514f6f9 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -199,7 +199,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { let loaded_from_disk = self.loaded_from_disk(attr); for e in except.items().into_sorted_stable_ord() { if !auto.remove(e) { - self.tcx.dcx().emit_fatal(errors::AssertionAuto { span: attr.span, name, e }); + self.tcx.dcx().emit_fatal(errors::AssertionAuto { span: attr.span(), name, e }); } } Assertion { clean: auto, dirty: except, loaded_from_disk } @@ -282,7 +282,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { HirItem::Impl { .. } => ("ItemKind::Impl", LABELS_IMPL), _ => self.tcx.dcx().emit_fatal(errors::UndefinedCleanDirtyItem { - span: attr.span, + span: attr.span(), kind: format!("{:?}", item.kind), }), } @@ -298,7 +298,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { ImplItemKind::Type(..) => ("NodeImplType", LABELS_CONST_IN_IMPL), }, _ => self.tcx.dcx().emit_fatal(errors::UndefinedCleanDirty { - span: attr.span, + span: attr.span(), kind: format!("{node:?}"), }), }; @@ -375,7 +375,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { let Some(assertion) = self.assertion_maybe(item_id, attr) else { continue; }; - self.checked_attrs.insert(attr.id); + self.checked_attrs.insert(attr.id()); for label in assertion.clean.items().into_sorted_stable_ord() { let dep_node = DepNode::from_label_string(self.tcx, label, def_path_hash).unwrap(); self.assert_clean(item_span, dep_node); @@ -405,12 +405,13 @@ fn check_config(tcx: TyCtxt<'_>, attr: &Attribute) -> bool { debug!("check_config: searching for cfg {:?}", value); cfg = Some(config.contains(&(value, None))); } else if !(item.has_name(EXCEPT) || item.has_name(LOADED_FROM_DISK)) { - tcx.dcx().emit_err(errors::UnknownItem { span: attr.span, name: item.name_or_empty() }); + tcx.dcx() + .emit_err(errors::UnknownItem { span: attr.span(), name: item.name_or_empty() }); } } match cfg { - None => tcx.dcx().emit_fatal(errors::NoCfg { span: attr.span }), + None => tcx.dcx().emit_fatal(errors::NoCfg { span: attr.span() }), Some(c) => c, } } @@ -444,9 +445,9 @@ impl<'tcx> FindAllAttrs<'tcx> { fn report_unchecked_attrs(&self, mut checked_attrs: FxHashSet) { for attr in &self.found_attrs { - if !checked_attrs.contains(&attr.id) { - self.tcx.dcx().emit_err(errors::UncheckedClean { span: attr.span }); - checked_attrs.insert(attr.id); + if !checked_attrs.contains(&attr.id()) { + self.tcx.dcx().emit_err(errors::UncheckedClean { span: attr.span() }); + checked_attrs.insert(attr.id()); } } } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 5e44583b2976b..f7be37dc4a2c3 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1011,7 +1011,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { cx.emit_span_lint( NO_MANGLE_GENERIC_ITEMS, span, - BuiltinNoMangleGeneric { suggestion: no_mangle_attr.span }, + BuiltinNoMangleGeneric { suggestion: no_mangle_attr.span() }, ); break; } @@ -1226,7 +1226,7 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller { { cx.emit_span_lint( UNGATED_ASYNC_FN_TRACK_CALLER, - attr.span, + attr.span(), BuiltinUngatedAsyncFnTrackCaller { label: span, session: &cx.tcx.sess }, ); } diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs index ef79f1301e5f6..9ca148e1f2549 100644 --- a/compiler/rustc_lint/src/expect.rs +++ b/compiler/rustc_lint/src/expect.rs @@ -38,7 +38,8 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option) { } LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => { // We are an `eval_always` query, so looking at the attribute's `AttrId` is ok. - let attr_id = tcx.hir().attrs(hir_id)[attr_index as usize].id; + let attr_id = tcx.hir().attrs(hir_id)[attr_index as usize].id(); + (attr_id, lint_index) } _ => panic!("fulfilled expectations must have a lint index"), diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs index 49c34d0edcc2a..6175415c31f98 100644 --- a/compiler/rustc_lint/src/foreign_modules.rs +++ b/compiler/rustc_lint/src/foreign_modules.rs @@ -182,7 +182,7 @@ fn name_of_extern_decl(tcx: TyCtxt<'_>, fi: hir::OwnerId) -> SymbolName { // information, we could have codegen_fn_attrs also give span information back for // where the attribute was defined. However, until this is found to be a // bottleneck, this does just fine. - (overridden_link_name, tcx.get_attr(fi, sym::link_name).unwrap().span) + (overridden_link_name, tcx.get_attr(fi, sym::link_name).unwrap().span()) }) { SymbolName::Link(overridden_link_name, overridden_link_name_span) diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index bc35e2f0538d0..49f9ad39780a3 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -1,13 +1,14 @@ use rustc_abi::ExternAbi; +use rustc_attr_parsing::{AttributeKind, AttributeParser, ReprAttr}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::FnKind; -use rustc_hir::{AttrArgs, AttrItem, AttrKind, GenericParamKind, PatExprKind, PatKind}; +use rustc_hir::{AttrArgs, AttrItem, Attribute, GenericParamKind, PatExprKind, PatKind}; use rustc_middle::ty; use rustc_session::config::CrateType; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::def_id::LocalDefId; use rustc_span::{BytePos, Ident, Span, sym}; -use {rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir}; +use {rustc_ast as ast, rustc_hir as hir}; use crate::lints::{ NonCamelCaseType, NonCamelCaseTypeSub, NonSnakeCaseDiag, NonSnakeCaseDiagSub, @@ -161,10 +162,10 @@ impl NonCamelCaseTypes { impl EarlyLintPass for NonCamelCaseTypes { fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) { - let has_repr_c = it - .attrs - .iter() - .any(|attr| attr::find_repr_attrs(cx.sess(), attr).contains(&attr::ReprC)); + let has_repr_c = matches!( + AttributeParser::parse_limited(cx.sess(), &it.attrs, sym::repr, it.span, true), + Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.iter().any(|(r, _)| r == &ReprAttr::ReprC) + ); if has_repr_c { return; @@ -343,7 +344,7 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase { } else { ast::attr::find_by_name(cx.tcx.hir().attrs(hir::CRATE_HIR_ID), sym::crate_name) .and_then(|attr| { - if let AttrKind::Normal(n) = &attr.kind + if let Attribute::Unparsed(n) = attr && let AttrItem { args: AttrArgs::Eq { eq_span: _, expr: lit }, .. } = n.as_ref() && let ast::LitKind::Str(name, ..) = lit.kind diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index ff3dae08ffc96..e564235c41a5e 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -251,19 +251,23 @@ impl Level { /// Converts an `Attribute` to a level. pub fn from_attr(attr: &impl AttributeExt) -> Option { - Self::from_symbol(attr.name_or_empty(), Some(attr.id())) + Self::from_symbol(attr.name_or_empty(), || Some(attr.id())) } /// Converts a `Symbol` to a level. - pub fn from_symbol(s: Symbol, id: Option) -> Option { - match (s, id) { - (sym::allow, _) => Some(Level::Allow), - (sym::expect, Some(attr_id)) => { - Some(Level::Expect(LintExpectationId::Unstable { attr_id, lint_index: None })) + pub fn from_symbol(s: Symbol, id: impl FnOnce() -> Option) -> Option { + match s { + sym::allow => Some(Level::Allow), + sym::expect => { + if let Some(attr_id) = id() { + Some(Level::Expect(LintExpectationId::Unstable { attr_id, lint_index: None })) + } else { + None + } } - (sym::warn, _) => Some(Level::Warn), - (sym::deny, _) => Some(Level::Deny), - (sym::forbid, _) => Some(Level::Forbid), + sym::warn => Some(Level::Warn), + sym::deny => Some(Level::Deny), + sym::forbid => Some(Level::Forbid), _ => None, } } diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 0df674eb4c909..34fc0f00320c0 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -17,6 +17,7 @@ mod diagnostics; mod extension; mod hash_stable; mod lift; +mod print_attribute; mod query; mod serialize; mod symbols; @@ -175,3 +176,11 @@ decl_derive! { /// The error type is `u32`. try_from::try_from_u32 } +decl_derive! { + [PrintAttribute] => + /// Derives `PrintAttribute` for `AttributeKind`. + /// This macro is pretty specific to `rustc_attr_data_structures` and likely not that useful in + /// other places. It's deriving something close to `Debug` without printing some extraenous + /// things like spans. + print_attribute::print_attribute +} diff --git a/compiler/rustc_macros/src/print_attribute.rs b/compiler/rustc_macros/src/print_attribute.rs new file mode 100644 index 0000000000000..3c6e30b851bf7 --- /dev/null +++ b/compiler/rustc_macros/src/print_attribute.rs @@ -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 } + } + }) +} diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 2a1e4b261e737..e2d043f47c0ef 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -450,7 +450,7 @@ impl<'tcx> Collector<'tcx> { (name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule)); } let Some((name, name_span)) = name else { - sess.dcx().emit_err(errors::LinkRequiresName { span: m.span }); + sess.dcx().emit_err(errors::LinkRequiresName { span: m.span() }); continue; }; @@ -485,7 +485,7 @@ impl<'tcx> Collector<'tcx> { let link_ordinal_attr = self.tcx.get_attr(child_item, sym::link_ordinal).unwrap(); sess.dcx().emit_err(errors::LinkOrdinalRawDylib { - span: link_ordinal_attr.span, + span: link_ordinal_attr.span(), }); } } diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 591c8ed50d57a..16149198303a2 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1071,7 +1071,6 @@ impl<'a> CrateMetadataRef<'a> { let attrs: Vec<_> = self.get_item_attrs(id, sess).collect(); SyntaxExtension::new( sess, - tcx.features(), kind, self.get_span(id, sess), helper_attrs, diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs index ed7c98ee0e011..9ce5373b03118 100644 --- a/compiler/rustc_middle/src/traits/specialization_graph.rs +++ b/compiler/rustc_middle/src/traits/specialization_graph.rs @@ -75,7 +75,7 @@ impl OverlapMode { tcx.hir().attrs(tcx.local_def_id_to_hir_id(local_def_id)) }) .find(|attr| attr.has_name(sym::rustc_strict_coherence)) - .map(|attr| attr.span); + .map(|attr| attr.span()); tcx.dcx().emit_err(StrictCoherenceNeedsNegativeCoherence { span: tcx.def_span(trait_id), attr_span, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 238ba127e2ecf..62a384af12f28 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1542,7 +1542,7 @@ impl<'tcx> TyCtxt<'tcx> { Bound::Included(a.get()) } else { self.dcx().span_delayed_bug( - attr.span, + attr.span(), "invalid rustc_layout_scalar_valid_range attribute", ); Bound::Unbounded diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 8ed5a118093f3..0c139e6fcb8ff 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -27,6 +27,7 @@ pub use intrinsic::IntrinsicDef; use rustc_abi::{Align, FieldIdx, Integer, IntegerType, ReprFlags, ReprOptions, VariantIdx}; use rustc_ast::expand::StrippedCfgItem; use rustc_ast::node_id::NodeMap; +use rustc_attr_parsing::AttributeKind; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::intern::Interned; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; @@ -1495,9 +1496,10 @@ impl<'tcx> TyCtxt<'tcx> { field_shuffle_seed ^= user_seed; } - for attr in self.get_attrs(did, sym::repr) { - for r in attr::parse_repr_attr(self.sess, attr) { - flags.insert(match r { + if let Some(reprs) = attr::find_attr!(self.get_all_attrs(did), AttributeKind::Repr(r) => r) + { + for (r, _) in reprs { + flags.insert(match *r { attr::ReprRust => ReprFlags::empty(), attr::ReprC => ReprFlags::IS_C, attr::ReprPacked(pack) => { @@ -1535,6 +1537,10 @@ impl<'tcx> TyCtxt<'tcx> { max_align = max_align.max(Some(align)); ReprFlags::empty() } + attr::ReprEmpty => { + /* skip these, they're just for diagnostics */ + ReprFlags::empty() + } }); } } @@ -1756,14 +1762,22 @@ impl<'tcx> TyCtxt<'tcx> { self, did: impl Into, attr: Symbol, + ) -> impl Iterator { + 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, ) -> impl Iterator { let did: DefId = did.into(); - let filter_fn = move |a: &&hir::Attribute| a.has_name(attr); if let Some(did) = did.as_local() { - self.hir().attrs(self.local_def_id_to_hir_id(did)).iter().filter(filter_fn) + self.hir().attrs(self.local_def_id_to_hir_id(did)).iter() } else { - debug_assert!(rustc_feature::encode_cross_crate(attr)); - self.attrs_for_def(did).iter().filter(filter_fn) + self.attrs_for_def(did).iter() } } diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index 58461be01f175..ccf76dc710874 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -65,7 +65,7 @@ fn coverage_attr_on(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { Some(_) | None => { // Other possibilities should have been rejected by `rustc_parse::validate_attr`. // Use `span_delayed_bug` to avoid an ICE in failing builds (#127880). - tcx.dcx().span_delayed_bug(attr.span, "unexpected value of coverage attribute"); + tcx.dcx().span_delayed_bug(attr.span(), "unexpected value of coverage attribute"); } } } diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 978cb7af2427f..bc43580a7f00d 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -311,9 +311,6 @@ passes_duplicate_lang_item_crate_depends = .first_definition_path = first definition in `{$orig_crate_name}` loaded from {$orig_path} .second_definition_path = second definition in `{$crate_name}` loaded from {$path} -passes_empty_confusables = - expected at least one confusable name - passes_export_name = attribute should be applied to a free function, impl method or static .label = not a free function, impl method or static @@ -365,9 +362,6 @@ passes_incorrect_do_not_recommend_args = passes_incorrect_do_not_recommend_location = `#[diagnostic::do_not_recommend]` can only be placed on trait implementations -passes_incorrect_meta_item = expected a quoted string literal -passes_incorrect_meta_item_suggestion = consider surrounding this with quotes - passes_incorrect_target = `{$name}` lang item must be applied to a {$kind} with {$at_least -> [true] at least {$num} @@ -641,13 +635,12 @@ passes_repr_align_greater_than_target_max = passes_repr_conflicting = conflicting representation hints -passes_repr_ident = - meta item in `repr` must be an identifier - passes_rustc_allow_const_fn_unstable = attribute should be applied to `const fn` .label = not a `const fn` +passes_rustc_const_stable_indirect_pairing = + `const_stable_indirect` attribute does not make sense on `rustc_const_stable` function, its behavior is already implied passes_rustc_dirty_clean = attribute requires -Z query-dep-graph to be enabled @@ -774,10 +767,6 @@ passes_unreachable_due_to_uninhabited = unreachable {$descr} passes_unrecognized_field = unrecognized field name `{$name}` -passes_unrecognized_repr_hint = - unrecognized representation hint - .help = valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize` - passes_unstable_attr_for_already_stable_feature = can't mark as unstable using an already stable feature .label = this feature is already stable diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 9a4db612cfed8..9be868122872d 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1,3 +1,4 @@ +// FIXME(jdonszelmann): should become rustc_attr_validation //! This module implements some validity checks for attributes. //! In particular it verifies that `#[inline]` and `#[repr]` attributes are //! attached to items that actually support them and if there are @@ -7,16 +8,17 @@ use std::cell::Cell; use std::collections::hash_map::Entry; -use rustc_abi::{ExternAbi, Size}; +use rustc_abi::{Align, ExternAbi, Size}; use rustc_ast::{AttrStyle, LitKind, MetaItemInner, MetaItemKind, MetaItemLit, ast}; +use rustc_attr_parsing::{AttributeKind, ReprAttr, find_attr}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey}; use rustc_feature::{AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute}; use rustc_hir::def_id::LocalModDefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{ - self as hir, self, AssocItemKind, AttrKind, Attribute, CRATE_HIR_ID, CRATE_OWNER_ID, FnSig, - ForeignItem, HirId, Item, ItemKind, MethodKind, Safety, Target, TraitItem, + self as hir, self, AssocItemKind, Attribute, CRATE_HIR_ID, CRATE_OWNER_ID, FnSig, ForeignItem, + HirId, Item, ItemKind, MethodKind, Safety, Target, TraitItem, }; use rustc_macros::LintDiagnostic; use rustc_middle::hir::nested_filter; @@ -113,190 +115,201 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let mut seen = FxHashMap::default(); let attrs = self.tcx.hir().attrs(hir_id); for attr in attrs { - match attr.path().as_slice() { - [sym::diagnostic, sym::do_not_recommend, ..] => { - self.check_do_not_recommend(attr.span, hir_id, target, attr, item) - } - [sym::diagnostic, sym::on_unimplemented, ..] => { - self.check_diagnostic_on_unimplemented(attr.span, hir_id, target) - } - [sym::inline, ..] => self.check_inline(hir_id, attr, span, target), - [sym::coverage, ..] => self.check_coverage(attr, span, target), - [sym::optimize, ..] => self.check_optimize(hir_id, attr, span, target), - [sym::no_sanitize, ..] => self.check_no_sanitize(attr, span, target), - [sym::non_exhaustive, ..] => self.check_non_exhaustive(hir_id, attr, span, target, item), - [sym::marker, ..] => self.check_marker(hir_id, attr, span, target), - [sym::target_feature, ..] => { - self.check_target_feature(hir_id, attr, span, target, attrs) - } - [sym::thread_local, ..] => self.check_thread_local(attr, span, target), - [sym::track_caller, ..] => { - self.check_track_caller(hir_id, attr.span, attrs, span, target) - } - [sym::doc, ..] => self.check_doc_attrs( - attr, - hir_id, - target, - &mut specified_inline, - &mut doc_aliases, - ), - [sym::no_link, ..] => self.check_no_link(hir_id, attr, span, target), - [sym::export_name, ..] => self.check_export_name(hir_id, attr, span, target), - [sym::rustc_layout_scalar_valid_range_start, ..] - | [sym::rustc_layout_scalar_valid_range_end, ..] => { - self.check_rustc_layout_scalar_valid_range(attr, span, target) - } - [sym::allow_internal_unstable, ..] => { - self.check_allow_internal_unstable(hir_id, attr, span, target, attrs) - } - [sym::debugger_visualizer, ..] => self.check_debugger_visualizer(attr, target), - [sym::rustc_allow_const_fn_unstable, ..] => { - self.check_rustc_allow_const_fn_unstable(hir_id, attr, span, target) - } - [sym::rustc_std_internal_symbol, ..] => { - self.check_rustc_std_internal_symbol(attr, span, target) - } - [sym::naked, ..] => self.check_naked(hir_id, attr, span, target, attrs), - [sym::rustc_as_ptr, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr, span, target) - } - [sym::rustc_never_returns_null_ptr, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr, span, target) - } - [sym::rustc_legacy_const_generics, ..] => { - self.check_rustc_legacy_const_generics(hir_id, attr, span, target, item) - } - [sym::rustc_lint_query_instability, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr, span, target) - } - [sym::rustc_lint_untracked_query_information, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr, span, target) - } - [sym::rustc_lint_diagnostics, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr, span, target) - } - [sym::rustc_lint_opt_ty, ..] => self.check_rustc_lint_opt_ty(attr, span, target), - [sym::rustc_lint_opt_deny_field_access, ..] => { - self.check_rustc_lint_opt_deny_field_access(attr, span, target) - } - [sym::rustc_clean, ..] - | [sym::rustc_dirty, ..] - | [sym::rustc_if_this_changed, ..] - | [sym::rustc_then_this_would_need, ..] => self.check_rustc_dirty_clean(attr), - [sym::rustc_coinductive, ..] - | [sym::rustc_must_implement_one_of, ..] - | [sym::rustc_deny_explicit_impl, ..] - | [sym::rustc_do_not_implement_via_object, ..] - | [sym::const_trait, ..] => self.check_must_be_applied_to_trait(attr, span, target), - [sym::collapse_debuginfo, ..] => self.check_collapse_debuginfo(attr, span, target), - [sym::must_not_suspend, ..] => self.check_must_not_suspend(attr, span, target), - [sym::must_use, ..] => self.check_must_use(hir_id, attr, target), - [sym::may_dangle, ..] => self.check_may_dangle(hir_id, attr), - [sym::rustc_pass_by_value, ..] => self.check_pass_by_value(attr, span, target), - [sym::rustc_allow_incoherent_impl, ..] => { - self.check_allow_incoherent_impl(attr, span, target) - } - [sym::rustc_has_incoherent_inherent_impls, ..] => { - self.check_has_incoherent_inherent_impls(attr, span, target) - } - [sym::ffi_pure, ..] => self.check_ffi_pure(attr.span, attrs, target), - [sym::ffi_const, ..] => self.check_ffi_const(attr.span, target), - [sym::rustc_const_unstable, ..] - | [sym::rustc_const_stable, ..] - | [sym::unstable, ..] - | [sym::stable, ..] - | [sym::rustc_allowed_through_unstable_modules, ..] - | [sym::rustc_promotable, ..] => self.check_stability_promotable(attr, target), - [sym::link_ordinal, ..] => self.check_link_ordinal(attr, span, target), - [sym::rustc_confusables, ..] => self.check_confusables(attr, target), - [sym::cold, ..] => self.check_cold(hir_id, attr, span, target), - [sym::link, ..] => self.check_link(hir_id, attr, span, target), - [sym::link_name, ..] => self.check_link_name(hir_id, attr, span, target), - [sym::link_section, ..] => self.check_link_section(hir_id, attr, span, target), - [sym::no_mangle, ..] => self.check_no_mangle(hir_id, attr, span, target), - [sym::deprecated, ..] => self.check_deprecated(hir_id, attr, span, target), - [sym::macro_use, ..] | [sym::macro_escape, ..] => { - self.check_macro_use(hir_id, attr, target) - } - [sym::path, ..] => self.check_generic_attr(hir_id, attr, target, Target::Mod), - [sym::macro_export, ..] => self.check_macro_export(hir_id, attr, target), - [sym::ignore, ..] | [sym::should_panic, ..] => { - self.check_generic_attr(hir_id, attr, target, Target::Fn) - } - [sym::automatically_derived, ..] => { - self.check_generic_attr(hir_id, attr, target, Target::Impl) - } - [sym::no_implicit_prelude, ..] => { - self.check_generic_attr(hir_id, attr, target, Target::Mod) - } - [sym::rustc_object_lifetime_default, ..] => self.check_object_lifetime_default(hir_id), - [sym::proc_macro, ..] => { - self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike) - } - [sym::proc_macro_attribute, ..] => { - self.check_proc_macro(hir_id, target, ProcMacroKind::Attribute); - } - [sym::proc_macro_derive, ..] => { - self.check_generic_attr(hir_id, attr, target, Target::Fn); - self.check_proc_macro(hir_id, target, ProcMacroKind::Derive) - } - [sym::autodiff, ..] => { - self.check_autodiff(hir_id, attr, span, target) - } - [sym::coroutine, ..] => { - self.check_coroutine(attr, target); - } - [sym::linkage, ..] => self.check_linkage(attr, span, target), - [sym::rustc_pub_transparent, ..] => self.check_rustc_pub_transparent(attr.span, span, attrs), - [ - // ok - sym::allow - | sym::expect - | sym::warn - | sym::deny - | sym::forbid - | sym::cfg - | sym::cfg_attr - // need to be fixed - | sym::cfi_encoding // FIXME(cfi_encoding) - | sym::pointee // FIXME(derive_coerce_pointee) - | sym::omit_gdb_pretty_printer_section // FIXME(omit_gdb_pretty_printer_section) - | sym::used // handled elsewhere to restrict to static items - | sym::repr // handled elsewhere to restrict to type decls items - | sym::instruction_set // broken on stable!!! - | sym::windows_subsystem // broken on stable!!! - | sym::patchable_function_entry // FIXME(patchable_function_entry) - | sym::deprecated_safe // FIXME(deprecated_safe) - // internal - | sym::prelude_import - | sym::panic_handler - | sym::allow_internal_unsafe - | sym::fundamental - | sym::lang - | sym::needs_allocator - | sym::default_lib_allocator - | sym::custom_mir, - .. - ] => {} - [name, ..] => { - match BUILTIN_ATTRIBUTE_MAP.get(name) { - // checked below - Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) => {} - Some(_) => { - // FIXME: differentiate between unstable and internal attributes just - // like we do with features instead of just accepting `rustc_` - // attributes by name. That should allow trimming the above list, too. - if !name.as_str().starts_with("rustc_") { - span_bug!( - attr.span, - "builtin attribute {name:?} not handled by `CheckAttrVisitor`" - ) + match attr { + Attribute::Parsed(AttributeKind::Confusables { first_span, .. }) => { + self.check_confusables(*first_span, target); + } + Attribute::Parsed( + AttributeKind::Stability { span, .. } + | AttributeKind::ConstStability { span, .. }, + ) => self.check_stability_promotable(*span, target), + Attribute::Parsed(AttributeKind::AllowInternalUnstable(syms)) => self + .check_allow_internal_unstable( + hir_id, + syms.first().unwrap().1, + span, + target, + attrs, + ), + _ => { + match attr.path().as_slice() { + [sym::diagnostic, sym::do_not_recommend, ..] => { + self.check_do_not_recommend(attr.span(), hir_id, target, attr, item) + } + [sym::diagnostic, sym::on_unimplemented, ..] => { + self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target) + } + [sym::inline, ..] => self.check_inline(hir_id, attr, span, target), + [sym::coverage, ..] => self.check_coverage(attr, span, target), + [sym::optimize, ..] => self.check_optimize(hir_id, attr, span, target), + [sym::no_sanitize, ..] => { + self.check_no_sanitize(attr, span, target) + } + [sym::non_exhaustive, ..] => self.check_non_exhaustive(hir_id, attr, span, target, item), + [sym::marker, ..] => self.check_marker(hir_id, attr, span, target), + [sym::target_feature, ..] => { + self.check_target_feature(hir_id, attr, span, target, attrs) + } + [sym::thread_local, ..] => self.check_thread_local(attr, span, target), + [sym::track_caller, ..] => { + self.check_track_caller(hir_id, attr.span(), attrs, span, target) + } + [sym::doc, ..] => self.check_doc_attrs( + attr, + hir_id, + target, + &mut specified_inline, + &mut doc_aliases, + ), + [sym::no_link, ..] => self.check_no_link(hir_id, attr, span, target), + [sym::export_name, ..] => self.check_export_name(hir_id, attr, span, target), + [sym::rustc_layout_scalar_valid_range_start, ..] + | [sym::rustc_layout_scalar_valid_range_end, ..] => { + self.check_rustc_layout_scalar_valid_range(attr, span, target) + } + [sym::debugger_visualizer, ..] => self.check_debugger_visualizer(attr, target), + [sym::rustc_allow_const_fn_unstable, ..] => { + self.check_rustc_allow_const_fn_unstable(hir_id, attr, span, target) + } + [sym::rustc_std_internal_symbol, ..] => { + self.check_rustc_std_internal_symbol(attr, span, target) + } + [sym::naked, ..] => self.check_naked(hir_id, attr, span, target, attrs), + [sym::rustc_as_ptr, ..] => { + self.check_applied_to_fn_or_method(hir_id, attr, span, target) + } + [sym::rustc_never_returns_null_ptr, ..] => { + self.check_applied_to_fn_or_method(hir_id, attr, span, target) + } + [sym::rustc_legacy_const_generics, ..] => { + self.check_rustc_legacy_const_generics(hir_id, attr, span, target, item) + } + [sym::rustc_lint_query_instability, ..] => { + self.check_applied_to_fn_or_method(hir_id, attr, span, target) + } + [sym::rustc_lint_untracked_query_information, ..] => { + self.check_applied_to_fn_or_method(hir_id, attr, span, target) + } + [sym::rustc_lint_diagnostics, ..] => { + self.check_applied_to_fn_or_method(hir_id, attr, span, target) + } + [sym::rustc_lint_opt_ty, ..] => self.check_rustc_lint_opt_ty(attr, span, target), + [sym::rustc_lint_opt_deny_field_access, ..] => { + self.check_rustc_lint_opt_deny_field_access(attr, span, target) + } + [sym::rustc_clean, ..] + | [sym::rustc_dirty, ..] + | [sym::rustc_if_this_changed, ..] + | [sym::rustc_then_this_would_need, ..] => self.check_rustc_dirty_clean(attr), + [sym::rustc_coinductive, ..] + | [sym::rustc_must_implement_one_of, ..] + | [sym::rustc_deny_explicit_impl, ..] + | [sym::rustc_do_not_implement_via_object, ..] + | [sym::const_trait, ..] => self.check_must_be_applied_to_trait(attr, span, target), + [sym::collapse_debuginfo, ..] => self.check_collapse_debuginfo(attr, span, target), + [sym::must_not_suspend, ..] => self.check_must_not_suspend(attr, span, target), + [sym::must_use, ..] => self.check_must_use(hir_id, attr, target), + [sym::may_dangle, ..] => self.check_may_dangle(hir_id, attr), + [sym::rustc_pass_by_value, ..] => self.check_pass_by_value(attr, span, target), + [sym::rustc_allow_incoherent_impl, ..] => { + self.check_allow_incoherent_impl(attr, span, target) + } + [sym::rustc_has_incoherent_inherent_impls, ..] => { + self.check_has_incoherent_inherent_impls(attr, span, target) + } + [sym::ffi_pure, ..] => self.check_ffi_pure(attr.span(), attrs, target), + [sym::ffi_const, ..] => self.check_ffi_const(attr.span(), target), + [sym::link_ordinal, ..] => self.check_link_ordinal(attr, span, target), + [sym::cold, ..] => self.check_cold(hir_id, attr, span, target), + [sym::link, ..] => self.check_link(hir_id, attr, span, target), + [sym::link_name, ..] => self.check_link_name(hir_id, attr, span, target), + [sym::link_section, ..] => self.check_link_section(hir_id, attr, span, target), + [sym::no_mangle, ..] => self.check_no_mangle(hir_id, attr, span, target), + [sym::deprecated, ..] => self.check_deprecated(hir_id, attr, span, target), + [sym::macro_use, ..] | [sym::macro_escape, ..] => { + self.check_macro_use(hir_id, attr, target) + } + [sym::path, ..] => self.check_generic_attr(hir_id, attr, target, Target::Mod), + [sym::macro_export, ..] => self.check_macro_export(hir_id, attr, target), + [sym::ignore, ..] | [sym::should_panic, ..] => { + self.check_generic_attr(hir_id, attr, target, Target::Fn) + } + [sym::automatically_derived, ..] => { + self.check_generic_attr(hir_id, attr, target, Target::Impl) + } + [sym::no_implicit_prelude, ..] => { + self.check_generic_attr(hir_id, attr, target, Target::Mod) + } + [sym::rustc_object_lifetime_default, ..] => self.check_object_lifetime_default(hir_id), + [sym::proc_macro, ..] => { + self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike) + } + [sym::proc_macro_attribute, ..] => { + self.check_proc_macro(hir_id, target, ProcMacroKind::Attribute); + } + [sym::proc_macro_derive, ..] => { + self.check_generic_attr(hir_id, attr, target, Target::Fn); + self.check_proc_macro(hir_id, target, ProcMacroKind::Derive) + } + [sym::autodiff, ..] => { + self.check_autodiff(hir_id, attr, span, target) + } + [sym::coroutine, ..] => { + self.check_coroutine(attr, target); + } + [sym::linkage, ..] => self.check_linkage(attr, span, target), + [sym::rustc_pub_transparent, ..] => self.check_rustc_pub_transparent(attr.span(), span, attrs), + [ + // ok + sym::allow + | sym::expect + | sym::warn + | sym::deny + | sym::forbid + | sym::cfg + | sym::cfg_attr + // need to be fixed + | sym::cfi_encoding // FIXME(cfi_encoding) + | sym::pointee // FIXME(derive_coerce_pointee) + | sym::omit_gdb_pretty_printer_section // FIXME(omit_gdb_pretty_printer_section) + | sym::used // handled elsewhere to restrict to static items + | sym::repr // handled elsewhere to restrict to type decls items + | sym::instruction_set // broken on stable!!! + | sym::windows_subsystem // broken on stable!!! + | sym::patchable_function_entry // FIXME(patchable_function_entry) + | sym::deprecated_safe // FIXME(deprecated_safe) + // internal + | sym::prelude_import + | sym::panic_handler + | sym::allow_internal_unsafe + | sym::fundamental + | sym::lang + | sym::needs_allocator + | sym::default_lib_allocator + | sym::custom_mir, + .. + ] => {} + [name, ..] => { + match BUILTIN_ATTRIBUTE_MAP.get(name) { + // checked below + Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) => {} + Some(_) => { + // FIXME: differentiate between unstable and internal attributes just + // like we do with features instead of just accepting `rustc_` + // attributes by name. That should allow trimming the above list, too. + if !name.as_str().starts_with("rustc_") { + span_bug!( + attr.span(), + "builtin attribute {name:?} not handled by `CheckAttrVisitor`" + ) + } + } + None => (), } } - None => (), + [] => unreachable!(), } } - [] => unreachable!(), } let builtin = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)); @@ -305,17 +318,17 @@ impl<'tcx> CheckAttrVisitor<'tcx> { if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)) { - match attr.style { + match attr.style() { ast::AttrStyle::Outer => self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::OuterCrateLevelAttr, ), ast::AttrStyle::Inner => self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::InnerCrateLevelAttr, ), } @@ -338,16 +351,16 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::IgnoredAttrWithMacro { sym }, ); } - fn inline_attr_str_error_without_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) { + fn inline_attr_str_error_without_macro_def(&self, hir_id: HirId, attr_span: Span, sym: &str) { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr_span, errors::IgnoredAttr { sym }, ); } @@ -407,7 +420,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::IgnoredInlineAttrFnProto, ) } @@ -418,7 +431,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Target::AssocConst => self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::IgnoredInlineAttrConstants, ), // FIXME(#80564): Same for fields, arms, and macro defs @@ -427,7 +440,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } _ => { self.dcx().emit_err(errors::InlineNotFnOrClosure { - attr_span: attr.span, + attr_span: attr.span(), defn_span: span, }); } @@ -459,7 +472,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } self.dcx().emit_err(errors::CoverageAttributeNotAllowed { - attr_span: attr.span, + attr_span: attr.span(), not_fn_impl_mod, no_body, help: (), @@ -477,7 +490,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ); if !is_valid { self.dcx().emit_err(errors::OptimizeInvalidTarget { - attr_span: attr.span, + attr_span: attr.span(), defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); @@ -528,7 +541,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::OnlyHasEffectOn { attr_name: attr.name_or_empty(), target_name: allowed_target.name().replace(' ', "_"), @@ -568,6 +581,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { sym::warn, sym::deny, sym::forbid, + // FIXME(jdonszelmann): not used, because already a new-style attr (ugh) sym::deprecated, sym::must_use, // abi, linking and FFI @@ -595,10 +609,21 @@ impl<'tcx> CheckAttrVisitor<'tcx> { continue; } + // FIXME(jdonszelmann): once naked uses new-style parsing, + // this check can be part of the parser and be removed here + match other_attr { + Attribute::Parsed( + AttributeKind::Deprecation { .. } | AttributeKind::Repr { .. }, + ) => { + continue; + } + _ => {} + } + if !ALLOW_LIST.iter().any(|name| other_attr.has_name(*name)) { self.dcx().emit_err(errors::NakedFunctionIncompatibleAttribute { - span: other_attr.span, - naked_span: attr.span, + span: other_attr.span(), + naked_span: attr.span(), attr: other_attr.name_or_empty(), }); @@ -615,7 +640,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { - attr_span: attr.span, + attr_span: attr.span(), defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); @@ -648,9 +673,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match target { Target::MacroDef => {} _ => { - self.tcx - .dcx() - .emit_err(errors::CollapseDebuginfo { attr_span: attr.span, defn_span: span }); + self.tcx.dcx().emit_err(errors::CollapseDebuginfo { + attr_span: attr.span(), + defn_span: span, + }); } } } @@ -720,7 +746,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { && fields.iter().any(|f| f.default.is_some()) { self.dcx().emit_err(errors::NonExhaustiveWithDefaultFieldValues { - attr_span: attr.span, + attr_span: attr.span(), defn_span: span, }); } @@ -735,7 +761,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } _ => { self.dcx().emit_err(errors::NonExhaustiveWrongLocation { - attr_span: attr.span, + attr_span: attr.span(), defn_span: span, }); } @@ -755,7 +781,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToTrait { - attr_span: attr.span, + attr_span: attr.span(), defn_span: span, }); } @@ -784,7 +810,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap(); self.dcx().emit_err(errors::LangItemWithTargetFeature { - attr_span: attr.span, + attr_span: attr.span(), name: lang_item, sig_span: sig.span, }); @@ -796,7 +822,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::TargetFeatureOnStatement, ); } @@ -809,7 +835,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { - attr_span: attr.span, + attr_span: attr.span(), defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); @@ -823,7 +849,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Target::ForeignStatic | Target::Static => {} _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToStatic { - attr_span: attr.span, + attr_span: attr.span(), defn_span: span, }); } @@ -1093,7 +1119,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { meta.span(), errors::DocInlineOnlyUse { attr_span: meta.span(), - item_span: (attr.style == AttrStyle::Outer) + item_span: (attr.style() == AttrStyle::Outer) .then(|| self.tcx.hir().span(hir_id)), }, ); @@ -1115,7 +1141,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { meta.span(), errors::DocMaskedOnlyExternCrate { attr_span: meta.span(), - item_span: (attr.style == AttrStyle::Outer) + item_span: (attr.style() == AttrStyle::Outer) .then(|| self.tcx.hir().span(hir_id)), }, ); @@ -1129,7 +1155,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { meta.span(), errors::DocMaskedNotExternCrateSelf { attr_span: meta.span(), - item_span: (attr.style == AttrStyle::Outer) + item_span: (attr.style() == AttrStyle::Outer) .then(|| self.tcx.hir().span(hir_id)), }, ); @@ -1159,11 +1185,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ) -> bool { if hir_id != CRATE_HIR_ID { // insert a bang between `#` and `[...` - let bang_span = attr.span.lo() + BytePos(1); - let sugg = (attr.style == AttrStyle::Outer + let bang_span = attr.span().lo() + BytePos(1); + let sugg = (attr.style() == AttrStyle::Outer && self.tcx.hir_get_parent_item(hir_id) == CRATE_OWNER_ID) .then_some(errors::AttrCrateLevelOnlySugg { - attr: attr.span.with_lo(bang_span).with_hi(bang_span), + attr: attr.span().with_lo(bang_span).with_hi(bang_span), }); self.tcx.emit_node_span_lint( INVALID_DOC_ATTRIBUTES, @@ -1337,11 +1363,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { errors::DocTestUnknownInclude { path, value: value.to_string(), - inner: match attr.style { + inner: match attr.style() { AttrStyle::Inner => "!", AttrStyle::Outer => "", }, - sugg: (attr.span, applicability), + sugg: (attr.span(), applicability), }, ); } else if i_meta.has_name(sym::passes) @@ -1387,7 +1413,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match target { Target::Struct | Target::Enum | Target::TyAlias => {} _ => { - self.dcx().emit_err(errors::PassByValue { attr_span: attr.span, span }); + self.dcx().emit_err(errors::PassByValue { attr_span: attr.span(), span }); } } } @@ -1396,7 +1422,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match target { Target::Method(MethodKind::Inherent) => {} _ => { - self.dcx().emit_err(errors::AllowIncoherentImpl { attr_span: attr.span, span }); + self.dcx().emit_err(errors::AllowIncoherentImpl { attr_span: attr.span(), span }); } } } @@ -1407,7 +1433,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { _ => { self.tcx .dcx() - .emit_err(errors::HasIncoherentInherentImpl { attr_span: attr.span, span }); + .emit_err(errors::HasIncoherentInherentImpl { attr_span: attr.span(), span }); } } } @@ -1470,7 +1496,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::MustUseNoEffect { article, target }, ); } @@ -1480,7 +1506,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match target { Target::Struct | Target::Enum | Target::Union | Target::Trait => {} _ => { - self.dcx().emit_err(errors::MustNotSuspend { attr_span: attr.span, span }); + self.dcx().emit_err(errors::MustNotSuspend { attr_span: attr.span(), span }); } } } @@ -1503,7 +1529,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { return; } - self.dcx().emit_err(errors::InvalidMayDangle { attr_span: attr.span }); + self.dcx().emit_err(errors::InvalidMayDangle { attr_span: attr.span() }); } /// Checks if `#[cold]` is applied to a non-function. @@ -1523,7 +1549,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::Cold { span, on_crate: hir_id == CRATE_HIR_ID }, ); } @@ -1543,7 +1569,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::Link { span: (target != Target::ForeignMod).then_some(span) }, ); } @@ -1562,19 +1588,19 @@ impl<'tcx> CheckAttrVisitor<'tcx> { _ => { // FIXME: #[cold] was previously allowed on non-functions/statics and some crates // used this, so only emit a warning. - let attr_span = matches!(target, Target::ForeignMod).then_some(attr.span); + let attr_span = matches!(target, Target::ForeignMod).then_some(attr.span()); if let Some(s) = attr.value_str() { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::LinkName { span, attr_span, value: s.as_str() }, ); } else { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::LinkName { span, attr_span, value: "..." }, ); }; @@ -1594,7 +1620,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_link"); } _ => { - self.dcx().emit_err(errors::NoLink { attr_span: attr.span, span }); + self.dcx().emit_err(errors::NoLink { attr_span: attr.span(), span }); } } } @@ -1616,7 +1642,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.inline_attr_str_error_with_macro_def(hir_id, attr, "export_name"); } _ => { - self.dcx().emit_err(errors::ExportName { attr_span: attr.span, span }); + self.dcx().emit_err(errors::ExportName { attr_span: attr.span(), span }); } } } @@ -1624,7 +1650,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { fn check_rustc_layout_scalar_valid_range(&self, attr: &Attribute, span: Span, target: Target) { if target != Target::Struct { self.dcx().emit_err(errors::RustcLayoutScalarValidRangeNotStruct { - attr_span: attr.span, + attr_span: attr.span(), span, }); return; @@ -1637,7 +1663,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { if !matches!(&list[..], &[MetaItemInner::Lit(MetaItemLit { kind: LitKind::Int(..), .. })]) { self.tcx .dcx() - .emit_err(errors::RustcLayoutScalarValidRangeArg { attr_span: attr.span }); + .emit_err(errors::RustcLayoutScalarValidRangeArg { attr_span: attr.span() }); } } @@ -1653,7 +1679,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let is_function = matches!(target, Target::Fn); if !is_function { self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { - attr_span: attr.span, + attr_span: attr.span(), defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); @@ -1678,7 +1704,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { hir::GenericParamKind::Const { .. } => {} _ => { self.dcx().emit_err(errors::RustcLegacyConstGenericsOnly { - attr_span: attr.span, + attr_span: attr.span(), param_span: param.span, }); return; @@ -1688,7 +1714,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { if list.len() != generics.params.len() { self.dcx().emit_err(errors::RustcLegacyConstGenericsIndex { - attr_span: attr.span, + attr_span: attr.span(), generics_span: generics.span, }); return; @@ -1728,7 +1754,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let is_function = matches!(target, Target::Fn | Target::Method(..)); if !is_function { self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { - attr_span: attr.span, + attr_span: attr.span(), defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); @@ -1740,7 +1766,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match target { Target::Struct => {} _ => { - self.dcx().emit_err(errors::RustcLintOptTy { attr_span: attr.span, span }); + self.dcx().emit_err(errors::RustcLintOptTy { attr_span: attr.span(), span }); } } } @@ -1752,7 +1778,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { _ => { self.tcx .dcx() - .emit_err(errors::RustcLintOptDenyFieldAccess { attr_span: attr.span, span }); + .emit_err(errors::RustcLintOptDenyFieldAccess { attr_span: attr.span(), span }); } } } @@ -1761,7 +1787,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// option is passed to the compiler. fn check_rustc_dirty_clean(&self, attr: &Attribute) { if !self.tcx.sess.opts.unstable_opts.query_dep_graph { - self.dcx().emit_err(errors::RustcDirtyClean { span: attr.span }); + self.dcx().emit_err(errors::RustcDirtyClean { span: attr.span() }); } } @@ -1771,7 +1797,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Target::Trait => {} _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToTrait { - attr_span: attr.span, + attr_span: attr.span(), defn_span: span, }); } @@ -1795,7 +1821,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::LinkSection { span }, ); } @@ -1826,8 +1852,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, - errors::NoMangleForeign { span, attr_span: attr.span, foreign_item_kind }, + attr.span(), + errors::NoMangleForeign { span, attr_span: attr.span(), foreign_item_kind }, ); } _ => { @@ -1836,7 +1862,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::NoMangle { span }, ); } @@ -1857,12 +1883,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // #[repr(foo)] // #[repr(bar, align(8))] // ``` - let hints: Vec<_> = attrs - .iter() - .filter(|attr| attr.has_name(sym::repr)) - .filter_map(|attr| attr.meta_item_list()) - .flatten() - .collect(); + let reprs = find_attr!(attrs, AttributeKind::Repr(r) => r.as_slice()).unwrap_or(&[]); let mut int_reprs = 0; let mut is_explicit_rust = false; @@ -1870,66 +1891,33 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let mut is_simd = false; let mut is_transparent = false; - // catch `repr()` with no arguments, applied to an item (i.e. not `#![repr()]`) - if hints.is_empty() && item.is_some() { - for attr in attrs.iter().filter(|attr| attr.has_name(sym::repr)) { - match target { - Target::Struct | Target::Union | Target::Enum => {} - Target::Fn | Target::Method(_) => { - feature_err( - &self.tcx.sess, - sym::fn_align, - attr.span, - fluent::passes_repr_align_function, - ) - .emit(); - } - _ => { - self.dcx().emit_err( - errors::AttrApplication::StructEnumFunctionMethodUnion { - hint_span: attr.span, - span, - }, - ); - } - } - } - - return; - } - - for hint in &hints { - if !hint.is_meta_item() { - self.dcx().emit_err(errors::ReprIdent { span: hint.span() }); - continue; - } - - match hint.name_or_empty() { - sym::Rust => { + for (repr, repr_span) in reprs { + match repr { + ReprAttr::ReprRust => { is_explicit_rust = true; match target { Target::Struct | Target::Union | Target::Enum => continue, _ => { self.dcx().emit_err(errors::AttrApplication::StructEnumUnion { - hint_span: hint.span(), + hint_span: *repr_span, span, }); } } } - sym::C => { + ReprAttr::ReprC => { is_c = true; match target { Target::Struct | Target::Union | Target::Enum => continue, _ => { self.dcx().emit_err(errors::AttrApplication::StructEnumUnion { - hint_span: hint.span(), + hint_span: *repr_span, span, }); } } } - sym::align => { + ReprAttr::ReprAlign(align) => { match target { Target::Struct | Target::Union | Target::Enum => {} Target::Fn | Target::Method(_) => { @@ -1937,7 +1925,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { feature_err( &self.tcx.sess, sym::fn_align, - hint.span(), + *repr_span, fluent::passes_repr_align_function, ) .emit(); @@ -1946,83 +1934,97 @@ impl<'tcx> CheckAttrVisitor<'tcx> { _ => { self.dcx().emit_err( errors::AttrApplication::StructEnumFunctionMethodUnion { - hint_span: hint.span(), + hint_span: *repr_span, span, }, ); } } - self.check_align_value(hint); + self.check_align_value(*align, *repr_span); } - sym::packed => { + ReprAttr::ReprPacked(_) => { if target != Target::Struct && target != Target::Union { self.dcx().emit_err(errors::AttrApplication::StructUnion { - hint_span: hint.span(), + hint_span: *repr_span, span, }); } else { continue; } } - sym::simd => { + ReprAttr::ReprSimd => { is_simd = true; if target != Target::Struct { self.dcx().emit_err(errors::AttrApplication::Struct { - hint_span: hint.span(), + hint_span: *repr_span, span, }); } else { continue; } } - sym::transparent => { + ReprAttr::ReprTransparent => { is_transparent = true; match target { Target::Struct | Target::Union | Target::Enum => continue, _ => { self.dcx().emit_err(errors::AttrApplication::StructEnumUnion { - hint_span: hint.span(), + hint_span: *repr_span, span, }); } } } - sym::i8 - | sym::u8 - | sym::i16 - | sym::u16 - | sym::i32 - | sym::u32 - | sym::i64 - | sym::u64 - | sym::i128 - | sym::u128 - | sym::isize - | sym::usize => { + ReprAttr::ReprInt(_) => { int_reprs += 1; if target != Target::Enum { self.dcx().emit_err(errors::AttrApplication::Enum { - hint_span: hint.span(), + hint_span: *repr_span, span, }); } else { continue; } } - _ => { - self.dcx().emit_err(errors::UnrecognizedReprHint { span: hint.span() }); - continue; + // FIXME(jdonszelmann): move the diagnostic for unused repr attrs here, I think + // it's a better place for it. + ReprAttr::ReprEmpty => { + // catch `repr()` with no arguments, applied to an item (i.e. not `#![repr()]`) + if item.is_some() { + match target { + Target::Struct | Target::Union | Target::Enum => {} + Target::Fn | Target::Method(_) => { + feature_err( + &self.tcx.sess, + sym::fn_align, + *repr_span, + fluent::passes_repr_align_function, + ) + .emit(); + } + _ => { + self.dcx().emit_err( + errors::AttrApplication::StructEnumFunctionMethodUnion { + hint_span: *repr_span, + span, + }, + ); + } + } + } + + return; } }; } // Just point at all repr hints if there are any incompatibilities. // This is not ideal, but tracking precisely which ones are at fault is a huge hassle. - let hint_spans = hints.iter().map(|hint| hint.span()); + let hint_spans = reprs.iter().map(|(_, span)| *span); // Error on repr(transparent, ). - if is_transparent && hints.len() > 1 { + if is_transparent && reprs.len() > 1 { let hint_spans = hint_spans.clone().collect(); self.dcx().emit_err(errors::TransparentIncompatible { hint_spans, @@ -2051,41 +2053,21 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_align_value(&self, item: &MetaItemInner) { - match item.singleton_lit_list() { - Some(( - _, - MetaItemLit { - kind: ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed), .. - }, - )) => { - let val = literal.get() as u64; - if val > 2_u64.pow(29) { - // for values greater than 2^29, a different error will be emitted, make sure that happens - self.dcx().span_delayed_bug( - item.span(), - "alignment greater than 2^29 should be errored on elsewhere", - ); - } else { - // only do this check when <= 2^29 to prevent duplicate errors: - // alignment greater than 2^29 not supported - // alignment is too large for the current target - - let max = - Size::from_bits(self.tcx.sess.target.pointer_width).signed_int_max() as u64; - if val > max { - self.dcx().emit_err(errors::InvalidReprAlignForTarget { - span: item.span(), - size: max, - }); - } - } - } + fn check_align_value(&self, align: Align, span: Span) { + if align.bytes() > 2_u64.pow(29) { + // for values greater than 2^29, a different error will be emitted, make sure that happens + self.dcx().span_delayed_bug( + span, + "alignment greater than 2^29 should be errored on elsewhere", + ); + } else { + // only do this check when <= 2^29 to prevent duplicate errors: + // alignment greater than 2^29 not supported + // alignment is too large for the current target - // if the attribute is malformed, singleton_lit_list may not be of the expected type or may be None - // but an error will have already been emitted, so this code should just skip such attributes - Some((_, _)) | None => { - self.dcx().span_delayed_bug(item.span(), "malformed repr(align(N))"); + let max = Size::from_bits(self.tcx.sess.target.pointer_width).signed_int_max() as u64; + if align.bytes() > max { + self.dcx().emit_err(errors::InvalidReprAlignForTarget { span, size: max }); } } } @@ -2096,7 +2078,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { for attr in attrs.iter().filter(|attr| attr.has_name(sym::used)) { if target != Target::Static { self.dcx().emit_err(errors::UsedStatic { - attr_span: attr.span, + attr_span: attr.span(), span: target_span, target: target.name(), }); @@ -2105,12 +2087,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match inner.as_deref() { Some([item]) if item.has_name(sym::linker) => { if used_linker_span.is_none() { - used_linker_span = Some(attr.span); + used_linker_span = Some(attr.span()); } } Some([item]) if item.has_name(sym::compiler) => { if used_compiler_span.is_none() { - used_compiler_span = Some(attr.span); + used_compiler_span = Some(attr.span()); } } Some(_) => { @@ -2119,7 +2101,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { None => { // Default case (compiler) when arg isn't defined. if used_compiler_span.is_none() { - used_compiler_span = Some(attr.span); + used_compiler_span = Some(attr.span()); } } } @@ -2133,41 +2115,44 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros. /// (Allows proc_macro functions) + // FIXME(jdonszelmann): if possible, move to attr parsing fn check_allow_internal_unstable( &self, hir_id: HirId, - attr: &Attribute, + attr_span: Span, span: Span, target: Target, attrs: &[Attribute], ) { - debug!("Checking target: {:?}", target); match target { Target::Fn => { for attr in attrs { if attr.is_proc_macro_attr() { - debug!("Is proc macro attr"); + // return on proc macros return; } } - debug!("Is not proc macro attr"); + // continue out of the match } - Target::MacroDef => {} + // return on decl macros + Target::MacroDef => return, // FIXME(#80564): We permit struct fields and match arms to have an // `#[allow_internal_unstable]` attribute with just a lint, because we previously // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. - Target::Field | Target::Arm => self.inline_attr_str_error_without_macro_def( - hir_id, - attr, - "allow_internal_unstable", - ), - _ => { - self.tcx - .dcx() - .emit_err(errors::AllowInternalUnstable { attr_span: attr.span, span }); + Target::Field | Target::Arm => { + self.inline_attr_str_error_without_macro_def( + hir_id, + attr_span, + "allow_internal_unstable", + ); + return; } + // otherwise continue out of the match + _ => {} } + + self.tcx.dcx().emit_err(errors::AllowInternalUnstable { attr_span, span }); } /// Checks if the items on the `#[debugger_visualizer]` attribute are valid. @@ -2179,7 +2164,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match target { Target::Mod => {} _ => { - self.dcx().emit_err(errors::DebugVisualizerPlacement { span: attr.span }); + self.dcx().emit_err(errors::DebugVisualizerPlacement { span: attr.span() }); } } } @@ -2206,7 +2191,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { _ => { self.tcx .dcx() - .emit_err(errors::RustcAllowConstFnUnstable { attr_span: attr.span, span }); + .emit_err(errors::RustcAllowConstFnUnstable { attr_span: attr.span(), span }); } } } @@ -2217,15 +2202,15 @@ impl<'tcx> CheckAttrVisitor<'tcx> { _ => { self.tcx .dcx() - .emit_err(errors::RustcStdInternalSymbol { attr_span: attr.span, span }); + .emit_err(errors::RustcStdInternalSymbol { attr_span: attr.span(), span }); } } } - fn check_stability_promotable(&self, attr: &Attribute, target: Target) { + fn check_stability_promotable(&self, span: Span, target: Target) { match target { Target::Expression => { - self.dcx().emit_err(errors::StabilityPromotable { attr_span: attr.span }); + self.dcx().emit_err(errors::StabilityPromotable { attr_span: span }); } _ => {} } @@ -2235,41 +2220,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match target { Target::ForeignFn | Target::ForeignStatic => {} _ => { - self.dcx().emit_err(errors::LinkOrdinal { attr_span: attr.span }); + self.dcx().emit_err(errors::LinkOrdinal { attr_span: attr.span() }); } } } - fn check_confusables(&self, attr: &Attribute, target: Target) { - match target { - Target::Method(MethodKind::Inherent) => { - let Some(metas) = attr.meta_item_list() else { - return; - }; - - let mut candidates = Vec::new(); - - for meta in metas { - let MetaItemInner::Lit(meta_lit) = meta else { - self.dcx().emit_err(errors::IncorrectMetaItem { - span: meta.span(), - suggestion: errors::IncorrectMetaItemSuggestion { - lo: meta.span().shrink_to_lo(), - hi: meta.span().shrink_to_hi(), - }, - }); - return; - }; - candidates.push(meta_lit.symbol); - } - - if candidates.is_empty() { - self.dcx().emit_err(errors::EmptyConfusables { span: attr.span }); - } - } - _ => { - self.dcx().emit_err(errors::Confusables { attr_span: attr.span }); - } + fn check_confusables(&self, span: Span, target: Target) { + if !matches!(target, Target::Method(MethodKind::Inherent)) { + self.dcx().emit_err(errors::Confusables { attr_span: span }); } } @@ -2279,7 +2237,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::Deprecated, ); } @@ -2295,7 +2253,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::MacroUse { name }, ); } @@ -2307,7 +2265,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::MacroExport::Normal, ); } else if let Some(meta_item_list) = attr.meta_item_list() @@ -2317,7 +2275,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( INVALID_MACRO_EXPORT_ARGUMENTS, hir_id, - attr.span, + attr.span(), errors::MacroExport::TooManyItems, ); } else if meta_item_list[0].name_or_empty() != sym::local_inner_macros { @@ -2337,7 +2295,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::MacroExport::OnDeclMacro, ); } @@ -2345,8 +2303,32 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute) { + // FIXME(jdonszelmann): deduplicate these checks after more attrs are parsed. This is very + // ugly now but can 100% be removed later. + if let Attribute::Parsed(p) = attr { + match p { + AttributeKind::Repr(reprs) => { + for (r, span) in reprs { + if let ReprAttr::ReprEmpty = r { + self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + *span, + errors::Unused { + attr_span: *span, + note: errors::UnusedNote::EmptyList { name: sym::repr }, + }, + ); + } + } + return; + } + _ => {} + } + } + // Warn on useless empty attributes. - let note = if matches!( + let note = if (matches!( attr.name_or_empty(), sym::macro_use | sym::allow @@ -2355,9 +2337,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::deny | sym::forbid | sym::feature - | sym::repr | sym::target_feature - ) && attr.meta_item_list().is_some_and(|list| list.is_empty()) + ) && attr.meta_item_list().is_some_and(|list| list.is_empty())) { errors::UnusedNote::EmptyList { name: attr.name_or_empty() } } else if matches!( @@ -2379,17 +2360,17 @@ impl<'tcx> CheckAttrVisitor<'tcx> { }) { if hir_id != CRATE_HIR_ID { - match attr.style { + match attr.style() { ast::AttrStyle::Outer => self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::OuterCrateLevelAttr, ), ast::AttrStyle::Inner => self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::InnerCrateLevelAttr, ), }; @@ -2415,8 +2396,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, - errors::Unused { attr_span: attr.span, note }, + attr.span(), + errors::Unused { attr_span: attr.span(), note }, ); } @@ -2532,7 +2513,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match target { Target::Closure => return, _ => { - self.dcx().emit_err(errors::CoroutineOnNonClosure { span: attr.span }); + self.dcx().emit_err(errors::CoroutineOnNonClosure { span: attr.span() }); } } } @@ -2545,18 +2526,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | Target::ForeignStatic | Target::ForeignFn => {} _ => { - self.dcx().emit_err(errors::Linkage { attr_span: attr.span, span }); + self.dcx().emit_err(errors::Linkage { attr_span: attr.span(), span }); } } } fn check_rustc_pub_transparent(&self, attr_span: Span, span: Span, attrs: &[Attribute]) { - if !attrs - .iter() - .filter(|attr| attr.has_name(sym::repr)) - .filter_map(|attr| attr.meta_item_list()) - .flatten() - .any(|nmi| nmi.has_name(sym::transparent)) + if !find_attr!(attrs, AttributeKind::Repr(r) => r.iter().any(|(r, _)| r == &ReprAttr::ReprTransparent)) + .unwrap_or(false) { self.dcx().emit_err(errors::RustcPubTransparent { span, attr_span }); } @@ -2588,14 +2565,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { && is_coro { self.dcx().emit_err(errors::RustcForceInlineCoro { - attr_span: attr.span, + attr_span: attr.span(), span: parent_span, }); } } (Target::Fn, _) => (), (_, Some(attr)) => { - self.dcx().emit_err(errors::RustcForceInline { attr_span: attr.span, span }); + self.dcx().emit_err(errors::RustcForceInline { attr_span: attr.span(), span }); } (_, None) => (), } @@ -2733,7 +2710,6 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { // resolution for the attribute macro error. const ATTRS_TO_CHECK: &[Symbol] = &[ sym::macro_export, - sym::repr, sym::path, sym::automatically_derived, sym::rustc_main, @@ -2745,47 +2721,46 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { ]; for attr in attrs { - // This function should only be called with crate attributes - // which are inner attributes always but lets check to make sure - if attr.style == AttrStyle::Inner { - for attr_to_check in ATTRS_TO_CHECK { - if attr.has_name(*attr_to_check) { - let item = tcx - .hir_free_items() - .map(|id| tcx.hir_item(id)) - .find(|item| !item.span.is_dummy()) // Skip prelude `use`s - .map(|item| errors::ItemFollowingInnerAttr { - span: item.ident.span, - kind: item.kind.descr(), - }); - let err = tcx.dcx().create_err(errors::InvalidAttrAtCrateLevel { - span: attr.span, - sugg_span: tcx - .sess - .source_map() - .span_to_snippet(attr.span) - .ok() - .filter(|src| src.starts_with("#![")) - .map(|_| { - attr.span - .with_lo(attr.span.lo() + BytePos(1)) - .with_hi(attr.span.lo() + BytePos(2)) - }), - name: *attr_to_check, - item, - }); + // FIXME(jdonszelmann): all attrs should be combined here cleaning this up some day. + let (span, name) = if let Some(a) = + ATTRS_TO_CHECK.iter().find(|attr_to_check| attr.has_name(**attr_to_check)) + { + (attr.span(), *a) + } else if let Attribute::Parsed(AttributeKind::Repr(r)) = attr { + (r.first().unwrap().1, sym::repr) + } else { + continue; + }; - if let AttrKind::Normal(ref p) = attr.kind { - tcx.dcx().try_steal_replace_and_emit_err( - p.path.span, - StashKey::UndeterminedMacroResolution, - err, - ); - } else { - err.emit(); - } - } - } + let item = tcx + .hir_free_items() + .map(|id| tcx.hir_item(id)) + .find(|item| !item.span.is_dummy()) // Skip prelude `use`s + .map(|item| errors::ItemFollowingInnerAttr { + span: item.ident.span, + kind: item.kind.descr(), + }); + let err = tcx.dcx().create_err(errors::InvalidAttrAtCrateLevel { + span, + sugg_span: tcx + .sess + .source_map() + .span_to_snippet(span) + .ok() + .filter(|src| src.starts_with("#![")) + .map(|_| span.with_lo(span.lo() + BytePos(1)).with_hi(span.lo() + BytePos(2))), + name, + item, + }); + + if let Attribute::Unparsed(p) = attr { + tcx.dcx().try_steal_replace_and_emit_err( + p.path.span, + StashKey::UndeterminedMacroResolution, + err, + ); + } else { + err.emit(); } } } @@ -2795,7 +2770,7 @@ fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>) for attr in attrs { if attr.has_name(sym::inline) { - tcx.dcx().emit_err(errors::NonExportedMacroInvalidAttrs { attr_span: attr.span }); + tcx.dcx().emit_err(errors::NonExportedMacroInvalidAttrs { attr_span: attr.span() }); } } } @@ -2833,10 +2808,10 @@ fn check_duplicates( match seen.entry(attr.name_or_empty()) { Entry::Occupied(mut entry) => { let (this, other) = if matches!(duplicates, FutureWarnPreceding) { - let to_remove = entry.insert(attr.span); - (to_remove, attr.span) + let to_remove = entry.insert(attr.span()); + (to_remove, attr.span()) } else { - (attr.span, *entry.get()) + (attr.span(), *entry.get()) }; tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, @@ -2853,17 +2828,17 @@ fn check_duplicates( ); } Entry::Vacant(entry) => { - entry.insert(attr.span); + entry.insert(attr.span()); } } } ErrorFollowing | ErrorPreceding => match seen.entry(attr.name_or_empty()) { Entry::Occupied(mut entry) => { let (this, other) = if matches!(duplicates, ErrorPreceding) { - let to_remove = entry.insert(attr.span); - (to_remove, attr.span) + let to_remove = entry.insert(attr.span()); + (to_remove, attr.span()) } else { - (attr.span, *entry.get()) + (attr.span(), *entry.get()) }; tcx.dcx().emit_err(errors::UnusedMultiple { this, @@ -2872,7 +2847,7 @@ fn check_duplicates( }); } Entry::Vacant(entry) => { - entry.insert(attr.span); + entry.insert(attr.span()); } }, } diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs index c2225ea1e642d..25e679a84601e 100644 --- a/compiler/rustc_passes/src/entry.rs +++ b/compiler/rustc_passes/src/entry.rs @@ -46,7 +46,7 @@ fn entry_fn(tcx: TyCtxt<'_>, (): ()) -> Option<(DefId, EntryFnType)> { fn attr_span_by_symbol(ctxt: &EntryContext<'_>, id: ItemId, sym: Symbol) -> Option { 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<'_>) { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 9bcdd2385470c..5f686f38babda 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -582,13 +582,6 @@ pub(crate) struct NoMangle { pub span: Span, } -#[derive(Diagnostic)] -#[diag(passes_repr_ident, code = E0565)] -pub(crate) struct ReprIdent { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(passes_repr_conflicting, code = E0566)] pub(crate) struct ReprConflicting { @@ -736,31 +729,6 @@ pub(crate) struct Linkage { pub span: Span, } -#[derive(Diagnostic)] -#[diag(passes_empty_confusables)] -pub(crate) struct EmptyConfusables { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(passes_incorrect_meta_item, code = E0539)] -pub(crate) struct IncorrectMetaItem { - #[primary_span] - pub span: Span, - #[subdiagnostic] - pub suggestion: IncorrectMetaItemSuggestion, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion(passes_incorrect_meta_item_suggestion, applicability = "maybe-incorrect")] -pub(crate) struct IncorrectMetaItemSuggestion { - #[suggestion_part(code = "\"")] - pub lo: Span, - #[suggestion_part(code = "\"")] - pub hi: Span, -} - #[derive(Diagnostic)] #[diag(passes_stability_promotable)] pub(crate) struct StabilityPromotable { @@ -1475,14 +1443,6 @@ pub(crate) struct ObjectLifetimeErr { pub repr: String, } -#[derive(Diagnostic)] -#[diag(passes_unrecognized_repr_hint, code = E0552)] -#[help] -pub(crate) struct UnrecognizedReprHint { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] pub(crate) enum AttrApplication { #[diag(passes_attr_application_enum, code = E0517)] @@ -1902,3 +1862,11 @@ pub(crate) struct NoSanitize<'a> { pub accepted_kind: &'a str, pub attr_str: &'a str, } + +// FIXME(jdonszelmann): move back to rustc_attr +#[derive(Diagnostic)] +#[diag(passes_rustc_const_stable_indirect_pairing)] +pub(crate) struct RustcConstStableIndirectPairing { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs index e123fbac1bee6..7353c1ead5a78 100644 --- a/compiler/rustc_passes/src/lib_features.rs +++ b/compiler/rustc_passes/src/lib_features.rs @@ -4,7 +4,7 @@ //! but are not declared in one single location (unlike lang features), which means we need to //! collect them instead. -use rustc_attr_parsing::VERSION_PLACEHOLDER; +use rustc_attr_parsing::{AttributeKind, StabilityLevel, StableSince}; use rustc_hir::Attribute; use rustc_hir::intravisit::Visitor; use rustc_middle::hir::nested_filter; @@ -26,62 +26,29 @@ impl<'tcx> LibFeatureCollector<'tcx> { } fn extract(&self, attr: &Attribute) -> Option<(Symbol, FeatureStability, Span)> { - let stab_attrs = [ - sym::stable, - sym::unstable, - sym::rustc_const_stable, - sym::rustc_const_unstable, - sym::rustc_default_body_unstable, - ]; - - // Find a stability attribute: one of #[stable(…)], #[unstable(…)], - // #[rustc_const_stable(…)], #[rustc_const_unstable(…)] or #[rustc_default_body_unstable]. - if let Some(stab_attr) = stab_attrs.iter().find(|stab_attr| attr.has_name(**stab_attr)) { - if let Some(metas) = attr.meta_item_list() { - let mut feature = None; - let mut since = None; - for meta in metas { - if let Some(mi) = meta.meta_item() { - // Find the `feature = ".."` meta-item. - match (mi.name_or_empty(), mi.value_str()) { - (sym::feature, val) => feature = val, - (sym::since, val) => since = val, - _ => {} - } - } - } - - if let Some(s) = since - && s.as_str() == VERSION_PLACEHOLDER - { - since = Some(sym::env_CFG_RELEASE); - } - - if let Some(feature) = feature { - // This additional check for stability is to make sure we - // don't emit additional, irrelevant errors for malformed - // attributes. - let is_unstable = matches!( - *stab_attr, - sym::unstable - | sym::rustc_const_unstable - | sym::rustc_default_body_unstable - ); - if is_unstable { - return Some((feature, FeatureStability::Unstable, attr.span)); - } - if let Some(since) = since { - return Some((feature, FeatureStability::AcceptedSince(since), attr.span)); - } - } - // We need to iterate over the other attributes, because - // `rustc_const_unstable` is not mutually exclusive with - // the other stability attributes, so we can't just `break` - // here. + let (feature, level, span) = match attr { + Attribute::Parsed(AttributeKind::Stability { stability, span }) => { + (stability.feature, stability.level, *span) } - } - - None + Attribute::Parsed(AttributeKind::ConstStability { stability, span }) => { + (stability.feature, stability.level, *span) + } + Attribute::Parsed(AttributeKind::BodyStability { stability, span }) => { + (stability.feature, stability.level, *span) + } + _ => return 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) { diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index d92edf959aff4..8a4bdf3875c4b 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -6,8 +6,8 @@ use std::num::NonZero; use rustc_ast_lowering::stability::extern_abi_stability; use rustc_attr_parsing::{ - self as attr, ConstStability, DeprecatedSince, Stability, StabilityLevel, StableSince, - UnstableReason, VERSION_PLACEHOLDER, + self as attr, AttributeKind, ConstStability, DeprecatedSince, PartialConstStability, Stability, + StabilityLevel, StableSince, UnstableReason, VERSION_PLACEHOLDER, find_attr, }; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet}; @@ -121,7 +121,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { let attrs = self.tcx.hir().attrs(self.tcx.local_def_id_to_hir_id(def_id)); debug!("annotate(id = {:?}, attrs = {:?})", def_id, attrs); - let depr = attr::find_deprecation(self.tcx.sess, self.tcx.features(), attrs); + let depr = attr::find_attr!(attrs, AttributeKind::Deprecation{deprecation, span} => (*deprecation, *span)); + let const_stability_indirect = find_attr!(attrs, AttributeKind::ConstStabilityIndirect); + let mut is_deprecated = false; if let Some((depr, span)) = &depr { is_deprecated = true; @@ -154,9 +156,10 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { if inherit_deprecation.yes() && stab.is_unstable() { self.index.stab_map.insert(def_id, stab); if fn_sig.is_some_and(|s| s.header.is_const()) { - let const_stab = - attr::unmarked_crate_const_stab(self.tcx.sess, attrs, stab); - self.index.const_stab_map.insert(def_id, const_stab); + self.index.const_stab_map.insert( + def_id, + ConstStability::unmarked(const_stability_indirect, stab), + ); } } } @@ -171,9 +174,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } // # Regular and body stability - - let stab = attr::find_stability(self.tcx.sess, attrs, item_sp); - let body_stab = attr::find_body_stability(self.tcx.sess, attrs); + let stab = attr::find_attr!(attrs, AttributeKind::Stability { stability, span } => (*stability, *span)); + let body_stab = + attr::find_attr!(attrs, AttributeKind::BodyStability { stability, .. } => *stability); if let Some((depr, span)) = &depr && depr.is_since_rustc_version() @@ -182,7 +185,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { self.tcx.dcx().emit_err(errors::DeprecatedAttribute { span: *span }); } - if let Some((body_stab, _span)) = body_stab { + if let Some(body_stab) = body_stab { // FIXME: check that this item can have body stability self.index.default_body_stab_map.insert(def_id, body_stab); @@ -260,10 +263,10 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { // # Const stability - let const_stab = attr::find_const_stability(self.tcx.sess, attrs, item_sp); + let const_stab = attr::find_attr!(attrs, AttributeKind::ConstStability { stability, span } => (*stability, *span)); // If the current node is a function with const stability attributes (directly given or - // implied), check if the function/method is const. + // implied), check if the function/method is const or the parent impl block is const. if let Some(fn_sig) = fn_sig && !fn_sig.header.is_const() && const_stab.is_some() @@ -285,7 +288,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { // Stable *language* features shouldn't be used as unstable library features. // (Not doing this for stable library features is checked by tidy.) if let Some(( - ConstStability { level: StabilityLevel::Unstable { .. }, feature, .. }, + PartialConstStability { level: StabilityLevel::Unstable { .. }, feature, .. }, const_span, )) = const_stab { @@ -297,9 +300,17 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } } + if let Some((stab, span)) = &const_stab + && stab.is_const_stable() + && const_stability_indirect + { + self.tcx.dcx().emit_err(errors::RustcConstStableIndirectPairing { span: *span }); + } + // After checking the immediate attributes, get rid of the span and compute implied // const stability: inherit feature gate from regular stability. - let mut const_stab = const_stab.map(|(stab, _span)| stab); + let mut const_stab = const_stab + .map(|(stab, _span)| ConstStability::from_partial(stab, const_stability_indirect)); // If this is a const fn but not annotated with stability markers, see if we can inherit regular stability. if fn_sig.is_some_and(|s| s.header.is_const()) && const_stab.is_none() && @@ -785,8 +796,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { let features = self.tcx.features(); if features.staged_api() { let attrs = self.tcx.hir().attrs(item.hir_id()); - let stab = attr::find_stability(self.tcx.sess, attrs, item.span); - let const_stab = attr::find_const_stability(self.tcx.sess, attrs, item.span); + let stab = attr::find_attr!(attrs, AttributeKind::Stability{stability, span} => (*stability, *span)); + + // FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem + let const_stab = attr::find_attr!(attrs, AttributeKind::ConstStability{stability, ..} => *stability); // If this impl block has an #[unstable] attribute, give an // error if all involved types and traits are stable, because @@ -817,7 +830,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { // needs to have an error emitted. if features.const_trait_impl() && self.tcx.is_const_trait_impl(item.owner_id.to_def_id()) - && const_stab.is_some_and(|(stab, _)| stab.is_const_stable()) + && const_stab.is_some_and(|stab| stab.is_const_stable()) { self.tcx.dcx().emit_err(errors::TraitImplConstStable { span: item.span }); } diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 41725d0c6a4f9..5271d03a6f617 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -22,6 +22,7 @@ use errors::{ }; use rustc_ast::MacroDef; use rustc_ast::visit::{VisitorResult, try_visit}; +use rustc_attr_parsing::AttributeKind; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::intern::Interned; use rustc_errors::{MultiSpan, listify}; @@ -493,7 +494,11 @@ impl<'tcx> EmbargoVisitor<'tcx> { // Non-opaque macros cannot make other items more accessible than they already are. let hir_id = self.tcx.local_def_id_to_hir_id(local_def_id); let attrs = self.tcx.hir().attrs(hir_id); - if attr::find_transparency(attrs, md.macro_rules).0 != Transparency::Opaque { + + if attr::find_attr!(attrs, AttributeKind::MacroTransparency(x) => *x) + .unwrap_or(Transparency::fallback(md.macro_rules)) + != Transparency::Opaque + { return; } diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml index d89e1355ca671..c85156e059e5a 100644 --- a/compiler/rustc_query_impl/Cargo.toml +++ b/compiler/rustc_query_impl/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start measureme = "11" +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_hashes = { path = "../rustc_hashes" } diff --git a/compiler/rustc_query_system/Cargo.toml b/compiler/rustc_query_system/Cargo.toml index 839465f9273bb..3e8ccb51021e1 100644 --- a/compiler/rustc_query_system/Cargo.toml +++ b/compiler/rustc_query_system/Cargo.toml @@ -9,6 +9,7 @@ parking_lot = "0.12" rustc-rayon-core = { version = "0.5.0" } rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } diff --git a/compiler/rustc_query_system/src/ich/hcx.rs b/compiler/rustc_query_system/src/ich/hcx.rs index cf50e61e72ba1..e1b6adc6cc17d 100644 --- a/compiler/rustc_query_system/src/ich/hcx.rs +++ b/compiler/rustc_query_system/src/ich/hcx.rs @@ -129,3 +129,4 @@ impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> { } impl<'a> rustc_session::HashStableContext for StableHashingContext<'a> {} +impl<'a> rustc_attr_data_structures::HashStableContext for StableHashingContext<'a> {} diff --git a/compiler/rustc_query_system/src/ich/impls_syntax.rs b/compiler/rustc_query_system/src/ich/impls_syntax.rs index 7d508b8201bdf..1dcd5d9058f4e 100644 --- a/compiler/rustc_query_system/src/ich/impls_syntax.rs +++ b/compiler/rustc_query_system/src/ich/impls_syntax.rs @@ -2,7 +2,7 @@ //! from various crates in no particular order. use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_hir as hir; +use rustc_hir::{self as hir, HashIgnoredAttrId}; use rustc_span::SourceFile; use smallvec::SmallVec; @@ -23,6 +23,7 @@ impl<'a> HashStable> for [hir::Attribute] { .iter() .filter(|attr| { !attr.is_doc_comment() + // FIXME(jdonszelmann) have a better way to handle ignored attrs && !attr.ident().is_some_and(|ident| hcx.is_ignored_attr(ident.name)) }) .collect(); @@ -35,19 +36,8 @@ impl<'a> HashStable> for [hir::Attribute] { } impl<'ctx> rustc_hir::HashStableContext for StableHashingContext<'ctx> { - fn hash_attr(&mut self, attr: &hir::Attribute, hasher: &mut StableHasher) { - // Make sure that these have been filtered out. - debug_assert!(!attr.ident().is_some_and(|ident| self.is_ignored_attr(ident.name))); - debug_assert!(!attr.is_doc_comment()); - - let hir::Attribute { kind, id: _, style, span } = attr; - if let hir::AttrKind::Normal(item) = kind { - item.hash_stable(self, hasher); - style.hash_stable(self, hasher); - span.hash_stable(self, hasher); - } else { - unreachable!(); - } + fn hash_attr_id(&mut self, _id: &HashIgnoredAttrId, _hasher: &mut StableHasher) { + /* we don't hash HashIgnoredAttrId, we ignore them */ } } diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 5eb8e420fa4ed..75972a71c8e57 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -3,6 +3,7 @@ use std::mem; use rustc_ast::visit::FnKind; use rustc_ast::*; use rustc_ast_pretty::pprust; +use rustc_attr_parsing::{AttributeParser, OmitDoc}; use rustc_expand::expand::AstFragment; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind}; @@ -132,8 +133,24 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { ItemKind::Fn(..) | ItemKind::Delegation(..) => DefKind::Fn, ItemKind::MacroDef(def) => { let edition = i.span.edition(); + + // FIXME(jdonszelmann) make one of these in the resolver? + // FIXME(jdonszelmann) don't care about tools here maybe? Just parse what we can. + // Does that prevents errors from happening? maybe + let parser = AttributeParser::new( + &self.resolver.tcx.sess, + self.resolver.tcx.features(), + Vec::new(), + ); + let attrs = parser.parse_attribute_list( + &i.attrs, + i.span, + OmitDoc::Skip, + std::convert::identity, + ); + let macro_data = - self.resolver.compile_macro(def, i.ident, &i.attrs, i.span, i.id, edition); + self.resolver.compile_macro(def, i.ident, &attrs, i.span, i.id, edition); let macro_kind = macro_data.ext.macro_kind(); opt_macro_data = Some(macro_data); DefKind::Macro(macro_kind) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 5db6f83f3ee51..55db336d85f35 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1806,7 +1806,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { && !def_id.is_local() && let Some(attr) = self.tcx.get_attr(def_id, sym::non_exhaustive) { - non_exhaustive = Some(attr.span); + non_exhaustive = Some(attr.span()); } else if let Some(span) = ctor_fields_span { let label = errors::ConstructorPrivateIfAnyFieldPrivate { span }; err.subdiagnostic(label); diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index a70def2f6c93c..984dfff3ea56e 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -5,7 +5,6 @@ use std::cell::Cell; use std::mem; use std::sync::Arc; -use rustc_ast::attr::AttributeExt; use rustc_ast::expand::StrippedCfgItem; use rustc_ast::{self as ast, Crate, NodeId, attr}; use rustc_ast_pretty::pprust; @@ -1112,7 +1111,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { &mut self, macro_def: &ast::MacroDef, ident: Ident, - attrs: &[impl AttributeExt], + attrs: &[rustc_hir::Attribute], span: Span, node_id: NodeId, edition: Edition, diff --git a/compiler/rustc_sanitizers/Cargo.toml b/compiler/rustc_sanitizers/Cargo.toml index 66488bc962594..900cd4243b13c 100644 --- a/compiler/rustc_sanitizers/Cargo.toml +++ b/compiler/rustc_sanitizers/Cargo.toml @@ -8,6 +8,7 @@ bitflags = "2.5.0" tracing = "0.1" twox-hash = "1.6.3" rustc_abi = { path = "../rustc_abi" } +rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_hir = { path = "../rustc_hir" } rustc_middle = { path = "../rustc_middle" } diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs index 5f0c1afdf6420..e088417d72e35 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -468,7 +468,7 @@ pub(crate) fn encode_ty<'tcx>( )] tcx.dcx() .struct_span_err( - cfi_encoding.span, + cfi_encoding.span(), format!("invalid `cfi_encoding` for `{:?}`", ty.kind()), ) .emit(); @@ -519,7 +519,7 @@ pub(crate) fn encode_ty<'tcx>( )] tcx.dcx() .struct_span_err( - cfi_encoding.span, + cfi_encoding.span(), format!("invalid `cfi_encoding` for `{:?}`", ty.kind()), ) .emit(); diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index 71e7b9c04ca16..acd3b75835105 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -9,7 +9,7 @@ use std::cell::RefCell; use std::iter; use rustc_abi::HasDataLayout; -use rustc_hir::LangItem; +use rustc_hir::{Attribute, LangItem}; use rustc_middle::ty::layout::{ FnAbiOf, FnAbiOfHelpers, HasTyCtxt, HasTypingEnv, LayoutOf, LayoutOfHelpers, }; @@ -243,7 +243,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { } } - fn get_attrs_by_path( + fn tool_attrs( &self, def_id: stable_mir::DefId, attr: &[stable_mir::Symbol], @@ -253,30 +253,40 @@ impl<'tcx> Context for TablesWrapper<'tcx> { let did = tables[def_id]; let attr_name: Vec<_> = attr.iter().map(|seg| rustc_span::Symbol::intern(&seg)).collect(); tcx.get_attrs_by_path(did, &attr_name) - .map(|attribute| { - let attr_str = rustc_hir_pretty::attribute_to_string(&tcx, attribute); - let span = attribute.span; - stable_mir::crate_def::Attribute::new(attr_str, span.stable(&mut *tables)) + .filter_map(|attribute| { + if let Attribute::Unparsed(u) = attribute { + let attr_str = rustc_hir_pretty::attribute_to_string(&tcx, attribute); + Some(stable_mir::crate_def::Attribute::new( + attr_str, + u.span.stable(&mut *tables), + )) + } else { + None + } }) .collect() } - fn get_all_attrs(&self, def_id: stable_mir::DefId) -> Vec { + fn all_tool_attrs(&self, def_id: stable_mir::DefId) -> Vec { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let did = tables[def_id]; - let filter_fn = - move |a: &&rustc_hir::Attribute| matches!(a.kind, rustc_hir::AttrKind::Normal(_)); let attrs_iter = if let Some(did) = did.as_local() { - tcx.hir().attrs(tcx.local_def_id_to_hir_id(did)).iter().filter(filter_fn) + tcx.hir().attrs(tcx.local_def_id_to_hir_id(did)).iter() } else { - tcx.attrs_for_def(did).iter().filter(filter_fn) + tcx.attrs_for_def(did).iter() }; attrs_iter - .map(|attribute| { - let attr_str = rustc_hir_pretty::attribute_to_string(&tcx, attribute); - let span = attribute.span; - stable_mir::crate_def::Attribute::new(attr_str, span.stable(&mut *tables)) + .filter_map(|attribute| { + if let Attribute::Unparsed(u) = attribute { + let attr_str = rustc_hir_pretty::attribute_to_string(&tcx, attribute); + Some(stable_mir::crate_def::Attribute::new( + attr_str, + u.span.stable(&mut *tables), + )) + } else { + None + } }) .collect() } diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 9bf1d305e5413..84e89ff4b7dab 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -175,6 +175,12 @@ pub enum Transparency { Opaque, } +impl Transparency { + pub fn fallback(macro_rules: bool) -> Self { + if macro_rules { Transparency::SemiTransparent } else { Transparency::Opaque } + } +} + impl LocalExpnId { /// The ID of the theoretical expansion that generates freshly parsed, unexpanded AST. pub const ROOT: LocalExpnId = LocalExpnId::ZERO; diff --git a/compiler/rustc_symbol_mangling/Cargo.toml b/compiler/rustc_symbol_mangling/Cargo.toml index 12fe6b719f9b8..90ddf4c8a0403 100644 --- a/compiler/rustc_symbol_mangling/Cargo.toml +++ b/compiler/rustc_symbol_mangling/Cargo.toml @@ -9,6 +9,7 @@ punycode = "0.4.0" rustc-demangle = "0.1.21" rustc_abi = { path = "../rustc_abi" } +rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_hashes = { path = "../rustc_hashes" } diff --git a/compiler/rustc_symbol_mangling/src/test.rs b/compiler/rustc_symbol_mangling/src/test.rs index 7fda81126c443..ddeeadff13d17 100644 --- a/compiler/rustc_symbol_mangling/src/test.rs +++ b/compiler/rustc_symbol_mangling/src/test.rs @@ -62,18 +62,18 @@ impl SymbolNamesTest<'_> { ); let mangled = tcx.symbol_name(instance); tcx.dcx().emit_err(TestOutput { - span: attr.span, + span: attr.span(), kind: Kind::SymbolName, content: format!("{mangled}"), }); if let Ok(demangling) = rustc_demangle::try_demangle(mangled.name) { tcx.dcx().emit_err(TestOutput { - span: attr.span, + span: attr.span(), kind: Kind::Demangling, content: format!("{demangling}"), }); tcx.dcx().emit_err(TestOutput { - span: attr.span, + span: attr.span(), kind: Kind::DemanglingAlt, content: format!("{demangling:#}"), }); @@ -82,7 +82,7 @@ impl SymbolNamesTest<'_> { for attr in tcx.get_attrs(def_id, DEF_PATH) { tcx.dcx().emit_err(TestOutput { - span: attr.span, + span: attr.span(), kind: Kind::DefPath, content: with_no_trimmed_paths!(tcx.def_path_str(def_id)), }); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index 36726cc6cae0d..514615735a507 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -522,7 +522,8 @@ impl Trait for X { } } TypeError::TargetFeatureCast(def_id) => { - let target_spans = tcx.get_attrs(def_id, sym::target_feature).map(|attr| attr.span); + let target_spans = + tcx.get_attrs(def_id, sym::target_feature).map(|attr| attr.span()); diag.note( "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers" ); diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index 518323f6526a6..f0c6e51f2a4c4 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -6,7 +6,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::codes::*; use rustc_errors::{ErrorGuaranteed, struct_span_code_err}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{AttrArgs, AttrKind, Attribute}; +use rustc_hir::{AttrArgs, Attribute}; use rustc_macros::LintDiagnostic; use rustc_middle::bug; use rustc_middle::ty::print::PrintTraitRefExt as _; @@ -622,7 +622,14 @@ impl<'tcx> OnUnimplementedDirective { item_def_id: DefId, ) -> Result, ErrorGuaranteed> { let result = if let Some(items) = attr.meta_item_list() { - Self::parse(tcx, item_def_id, &items, attr.span, true, is_diagnostic_namespace_variant) + Self::parse( + tcx, + item_def_id, + &items, + attr.span(), + true, + is_diagnostic_namespace_variant, + ) } else if let Some(value) = attr.value_str() { if !is_diagnostic_namespace_variant { Ok(Some(OnUnimplementedDirective { @@ -633,7 +640,7 @@ impl<'tcx> OnUnimplementedDirective { tcx, item_def_id, value, - attr.span, + attr.span(), is_diagnostic_namespace_variant, )?), notes: Vec::new(), @@ -659,14 +666,14 @@ impl<'tcx> OnUnimplementedDirective { Ok(None) } } else if is_diagnostic_namespace_variant { - match &attr.kind { - AttrKind::Normal(p) if !matches!(p.args, AttrArgs::Empty) => { + match attr { + Attribute::Unparsed(p) if !matches!(p.args, AttrArgs::Empty) => { if let Some(item_def_id) = item_def_id.as_local() { tcx.emit_node_span_lint( UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, tcx.local_def_id_to_hir_id(item_def_id), - attr.span, - MalformedOnUnimplementedAttrLint::new(attr.span), + attr.span(), + MalformedOnUnimplementedAttrLint::new(attr.span()), ); } } @@ -675,7 +682,7 @@ impl<'tcx> OnUnimplementedDirective { tcx.emit_node_span_lint( UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, tcx.local_def_id_to_hir_id(item_def_id), - attr.span, + attr.span(), MissingOptionsForOnUnimplementedAttr, ) } diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs index a6f7c254583e2..e82c957c34ea6 100644 --- a/compiler/stable_mir/src/compiler_interface.rs +++ b/compiler/stable_mir/src/compiler_interface.rs @@ -62,14 +62,17 @@ pub trait Context { /// Returns the name of given `DefId` fn def_name(&self, def_id: DefId, trimmed: bool) -> Symbol; - /// Return attributes with the given attribute name. + /// Return registered tool attributes with the given attribute name. /// - /// Single segmented name like `#[inline]` is specified as `&["inline".to_string()]`. + /// FIXME(jdonszelmann): may panic on non-tool attributes. After more attribute work, non-tool + /// attributes will simply return an empty list. + /// + /// Single segmented name like `#[clippy]` is specified as `&["clippy".to_string()]`. /// Multi-segmented name like `#[rustfmt::skip]` is specified as `&["rustfmt".to_string(), "skip".to_string()]`. - fn get_attrs_by_path(&self, def_id: DefId, attr: &[Symbol]) -> Vec; + fn tool_attrs(&self, def_id: DefId, attr: &[Symbol]) -> Vec; - /// Get all attributes of a definition. - fn get_all_attrs(&self, def_id: DefId) -> Vec; + /// Get all tool attributes of a definition. + fn all_tool_attrs(&self, def_id: DefId) -> Vec; /// Returns printable, human readable form of `Span` fn span_to_string(&self, span: Span) -> String; diff --git a/compiler/stable_mir/src/crate_def.rs b/compiler/stable_mir/src/crate_def.rs index cf29176dbbdda..8c6fd99f98a1c 100644 --- a/compiler/stable_mir/src/crate_def.rs +++ b/compiler/stable_mir/src/crate_def.rs @@ -53,19 +53,22 @@ pub trait CrateDef { with(|cx| cx.span_of_an_item(def_id)) } - /// Return attributes with the given attribute name. + /// Return registered tool attributes with the given attribute name. /// - /// Single segmented name like `#[inline]` is specified as `&["inline".to_string()]`. + /// FIXME(jdonszelmann): may panic on non-tool attributes. After more attribute work, non-tool + /// attributes will simply return an empty list. + /// + /// Single segmented name like `#[clippy]` is specified as `&["clippy".to_string()]`. /// Multi-segmented name like `#[rustfmt::skip]` is specified as `&["rustfmt".to_string(), "skip".to_string()]`. - fn attrs_by_path(&self, attr: &[Symbol]) -> Vec { + fn tool_attrs(&self, attr: &[Symbol]) -> Vec { let def_id = self.def_id(); - with(|cx| cx.get_attrs_by_path(def_id, attr)) + with(|cx| cx.tool_attrs(def_id, attr)) } - /// Return all attributes of this definition. - fn all_attrs(&self) -> Vec { + /// Return all tool attributes of this definition. + fn all_tool_attrs(&self) -> Vec { let def_id = self.def_id(); - with(|cx| cx.get_all_attrs(def_id)) + with(|cx| cx.all_tool_attrs(def_id)) } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index dcc5fd12d81bf..ceffe5e5ce04e 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2737,13 +2737,13 @@ fn add_without_unwanted_attributes<'hir>( import_parent: Option, ) { for attr in new_attrs { - if matches!(attr.kind, hir::AttrKind::DocComment(..)) { + if attr.is_doc_comment() { attrs.push((Cow::Borrowed(attr), import_parent)); continue; } let mut attr = attr.clone(); - match attr.kind { - hir::AttrKind::Normal(ref mut normal) => { + match attr { + hir::Attribute::Unparsed(ref mut normal) => { if let [ident] = &*normal.path.segments { let ident = ident.name; if ident == sym::doc { @@ -2755,7 +2755,11 @@ fn add_without_unwanted_attributes<'hir>( } } } - _ => unreachable!(), + hir::Attribute::Parsed(..) => { + if is_inline { + attrs.push((Cow::Owned(attr), import_parent)); + } + } } } } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index fc7c4b42047db..178b6a60b41f7 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -265,7 +265,7 @@ impl ExternalCrate { let attr_value = attr.value_str().expect("syntax should already be validated"); let Some(prim) = PrimitiveType::from_symbol(attr_value) else { span_bug!( - attr.span, + attr.span(), "primitive `{attr_value}` is not a member of `PrimitiveType`" ); }; diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs index 1ac3c040b5918..3ac7abd0aa5d0 100644 --- a/src/librustdoc/doctest/rust.rs +++ b/src/librustdoc/doctest/rust.rs @@ -123,7 +123,7 @@ impl HirCollector<'_> { .iter() .find(|attr| attr.doc_str().is_some()) .map(|attr| { - attr.span.ctxt().outer_expn().expansion_cause().unwrap_or(attr.span) + attr.span().ctxt().outer_expn().expansion_cause().unwrap_or(attr.span()) }) .unwrap_or(DUMMY_SP) }; diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index a92f3ded77466..8f6496e9626c8 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -30,7 +30,7 @@ pub type FxHashMap = HashMap; // re-export for use in src/librustdoc /// This integer is incremented with every breaking change to the API, /// and is returned along with the JSON blob as [`Crate::format_version`]. /// Consuming code should assert that this value matches the format version(s) that it supports. -pub const FORMAT_VERSION: u32 = 39; +pub const FORMAT_VERSION: u32 = 40; /// The root of the emitted JSON blob. /// @@ -120,7 +120,9 @@ pub struct Item { pub docs: Option, /// 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, - /// 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, /// Information about the item’s deprecation, if present. pub deprecation: Option, diff --git a/src/tools/cargo b/src/tools/cargo index 1d1d646c06a84..ce948f4616e3d 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 1d1d646c06a84c1aa53967b394b7f1218f85db82 +Subproject commit ce948f4616e3d4277e30c75c8bb01e094910df39 diff --git a/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs b/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs index 2325f914b0b0c..cb63fadb4e21c 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs @@ -20,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Att span_lint( cx, INLINE_ALWAYS, - attr.span, + attr.span(), format!("you have declared `#[inline(always)]` on `{name}`. This is usually a bad idea"), ); } diff --git a/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs index 6d1ab46aa0c1f..6cc47596bbb61 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs @@ -1,6 +1,7 @@ +use rustc_attr_parsing::{find_attr, AttributeKind, ReprAttr}; use rustc_hir::Attribute; use rustc_lint::LateContext; -use rustc_span::{Span, sym}; +use rustc_span::Span; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs; @@ -14,30 +15,21 @@ pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute], } fn check_packed(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute]) { - if let Some(items) = attrs.iter().find_map(|attr| { - if attr.ident().is_some_and(|ident| matches!(ident.name, sym::repr)) { - attr.meta_item_list() - } else { - None + if let Some(reprs) = find_attr!(attrs, AttributeKind::Repr(r) => r) { + let packed_span = reprs.iter().find(|(r, _)| matches!(r, ReprAttr::ReprPacked(..))).map(|(_, s)| *s); + + if let Some(packed_span) = packed_span && !reprs.iter().any(|(x, _)| *x == ReprAttr::ReprC || *x == ReprAttr::ReprRust) { + span_lint_and_then( + cx, + REPR_PACKED_WITHOUT_ABI, + item_span, + "item uses `packed` representation without ABI-qualification", + |diag| { + diag.warn("unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI") + .help("qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]`") + .span_label(packed_span, "`packed` representation set here"); + }, + ); } - }) && let Some(packed) = items - .iter() - .find(|item| item.ident().is_some_and(|ident| matches!(ident.name, sym::packed))) - && !items.iter().any(|item| { - item.ident() - .is_some_and(|ident| matches!(ident.name, sym::C | sym::Rust)) - }) - { - span_lint_and_then( - cx, - REPR_PACKED_WITHOUT_ABI, - item_span, - "item uses `packed` representation without ABI-qualification", - |diag| { - diag.warn("unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI") - .help("qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]`") - .span_label(packed.span(), "`packed` representation set here"); - }, - ); } } diff --git a/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs b/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs index 478ba7a187be5..6ee3290fa761d 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs @@ -15,7 +15,7 @@ pub(super) fn check( ) { if cfg_attr.has_name(sym::clippy) && let Some(ident) = behind_cfg_attr.ident() - && Level::from_symbol(ident.name, Some(attr.id)).is_some() + && Level::from_symbol(ident.name, || Some(attr.id)).is_some() && let Some(items) = behind_cfg_attr.meta_item_list() { let nb_items = items.len(); diff --git a/src/tools/clippy/clippy_lints/src/attrs/utils.rs b/src/tools/clippy/clippy_lints/src/attrs/utils.rs index a667649f73435..0e650e4939252 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/utils.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/utils.rs @@ -17,7 +17,7 @@ pub(super) fn is_word(nmi: &MetaItemInner, expected: Symbol) -> bool { } pub(super) fn is_lint_level(symbol: Symbol, attr_id: AttrId) -> bool { - Level::from_symbol(symbol, Some(attr_id)).is_some() + Level::from_symbol(symbol, || Some(attr_id)).is_some() } pub(super) fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/default_union_representation.rs b/src/tools/clippy/clippy_lints/src/default_union_representation.rs index 9f020d3081c40..6e6d81db11c04 100644 --- a/src/tools/clippy/clippy_lints/src/default_union_representation.rs +++ b/src/tools/clippy/clippy_lints/src/default_union_representation.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; +use rustc_attr_parsing::{find_attr, AttributeKind, ReprAttr}; use rustc_hir::{HirId, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, FieldDef}; use rustc_session::declare_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -97,16 +97,7 @@ fn is_zst<'tcx>(cx: &LateContext<'tcx>, field: &FieldDef, args: ty::GenericArgsR } fn has_c_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { - cx.tcx.hir().attrs(hir_id).iter().any(|attr| { - if attr.has_name(sym::repr) { - if let Some(items) = attr.meta_item_list() { - for item in items { - if item.is_word() && matches!(item.name_or_empty(), sym::C) { - return true; - } - } - } - } - false - }) + let attrs = cx.tcx.hir().attrs(hir_id); + + find_attr!(attrs, AttributeKind::Repr(r) if r.iter().any(|(x, _)| *x == ReprAttr::ReprC)) } diff --git a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs index 4e8853821c3ef..6de16e306c9ae 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs @@ -1,3 +1,4 @@ + use clippy_config::Conf; use clippy_config::types::create_disallowed_map; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; diff --git a/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs b/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs index 4b40fc0b1ee2c..aa29705cf9386 100644 --- a/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs +++ b/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs @@ -1,18 +1,17 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; -use rustc_ast::AttrStyle; use rustc_errors::Applicability; -use rustc_hir::{AttrArgs, AttrKind, Attribute}; -use rustc_lint::LateContext; +use rustc_lint::EarlyContext; +use rustc_ast::{Attribute, AttrKind, AttrArgs, AttrStyle}; use super::DOC_INCLUDE_WITHOUT_CFG; -pub fn check(cx: &LateContext<'_>, attrs: &[Attribute]) { +pub fn check(cx: &EarlyContext<'_>, attrs: &[Attribute]) { for attr in attrs { if !attr.span.from_expansion() && let AttrKind::Normal(ref item) = attr.kind && attr.doc_str().is_some() - && let AttrArgs::Eq { expr: meta, .. } = &item.args + && let AttrArgs::Eq { expr: meta, .. } = &item.item.args && !attr.span.contains(meta.span) // Since the `include_str` is already expanded at this point, we can only take the // whole attribute snippet and then modify for our suggestion. diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index 713d62a8801d8..42192801af7c4 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -20,7 +20,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{AnonConst, Attribute, Expr, ImplItemKind, ItemKind, Node, Safety, TraitItemKind}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; use rustc_middle::ty; use rustc_resolve::rustdoc::{ @@ -577,6 +577,13 @@ impl_lint_pass!(Documentation => [ DOC_INCLUDE_WITHOUT_CFG, ]); + +impl EarlyLintPass for Documentation { + fn check_attributes(&mut self, cx: &EarlyContext<'_>, attrs: &[rustc_ast::Attribute]) { + include_in_doc_without_cfg::check(cx, attrs); + } +} + impl<'tcx> LateLintPass<'tcx> for Documentation { fn check_attributes(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) { let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { @@ -704,14 +711,13 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ Some(("fake".into(), "fake".into())) } - include_in_doc_without_cfg::check(cx, attrs); if suspicious_doc_comments::check(cx, attrs) || is_doc_hidden(attrs) { return None; } let (fragments, _) = attrs_to_doc_fragments( attrs.iter().filter_map(|attr| { - if attr.span.in_external_macro(cx.sess().source_map()) { + if !attr.doc_str_and_comment_kind().is_some() || attr.span().in_external_macro(cx.sess().source_map()) { None } else { Some((attr, None)) diff --git a/src/tools/clippy/clippy_lints/src/doc/suspicious_doc_comments.rs b/src/tools/clippy/clippy_lints/src/doc/suspicious_doc_comments.rs index 84393213e6f06..bfc36deea7b29 100644 --- a/src/tools/clippy/clippy_lints/src/doc/suspicious_doc_comments.rs +++ b/src/tools/clippy/clippy_lints/src/doc/suspicious_doc_comments.rs @@ -3,6 +3,7 @@ use rustc_ast::AttrStyle; use rustc_ast::token::CommentKind; use rustc_errors::Applicability; use rustc_hir::Attribute; +use rustc_attr_parsing::AttributeKind; use rustc_lint::LateContext; use rustc_span::Span; @@ -36,15 +37,14 @@ fn collect_doc_replacements(attrs: &[Attribute]) -> Vec<(Span, String)> { attrs .iter() .filter_map(|attr| { - if let Some((sym, com_kind)) = attr.doc_str_and_comment_kind() - && let AttrStyle::Outer = attr.style - && let Some(com) = sym.as_str().strip_prefix('!') + if let Attribute::Parsed(AttributeKind::DocComment{ style: AttrStyle::Outer, kind, comment, ..}) = attr + && let Some(com) = comment.as_str().strip_prefix('!') { - let sugg = match com_kind { + let sugg = match kind { CommentKind::Line => format!("//!{com}"), CommentKind::Block => format!("/*!{com}*/"), }; - Some((attr.span, sugg)) + Some((attr.span(), sugg)) } else { None } diff --git a/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs index 1f89cab91480e..1eda73a96727d 100644 --- a/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs +++ b/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs @@ -1,5 +1,6 @@ use rustc_errors::Applicability; use rustc_hir::{Attribute, Item, ItemKind}; +use rustc_attr_parsing::AttributeKind; use rustc_lint::LateContext; use clippy_utils::diagnostics::span_lint_and_then; @@ -43,9 +44,9 @@ pub(super) fn check( let mut should_suggest_empty_doc = false; for attr in attrs { - if let Some(doc) = attr.doc_str() { - spans.push(attr.span); - let doc = doc.as_str(); + if let Attribute::Parsed(AttributeKind::DocComment {span, comment, ..}) = attr { + spans.push(span); + let doc = comment.as_str(); let doc = doc.trim(); if spans.len() == 1 { // We make this suggestion only if the first doc line ends with a punctuation @@ -78,7 +79,7 @@ pub(super) fn check( && let new_span = first_span.with_hi(second_span.lo()).with_lo(first_span.hi()) && let Some(snippet) = snippet_opt(cx, new_span) { - let Some(first) = snippet_opt(cx, first_span) else { + let Some(first) = snippet_opt(cx, *first_span) else { return; }; let Some(comment_form) = first.get(..3) else { diff --git a/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs b/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs index 0599e08e6c0af..0bdb99d7b9a4d 100644 --- a/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs +++ b/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs @@ -46,7 +46,8 @@ impl<'tcx> LateLintPass<'tcx> for FourForwardSlashes { .hir() .attrs(item.hir_id()) .iter() - .fold(item.span.shrink_to_lo(), |span, attr| span.to(attr.span)); + .filter(|i| i.is_doc_comment()) + .fold(item.span.shrink_to_lo(), |span, attr| span.to(attr.span())); let (Some(file), _, _, end_line, _) = sm.span_to_location_info(span) else { return; }; diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs index e6e3ea59a9f67..0ed4426d6e92f 100644 --- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs +++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs @@ -95,6 +95,7 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr } } +// FIXME: needs to be an EARLY LINT. all attribute lints should be #[allow(clippy::too_many_arguments)] fn check_needless_must_use( cx: &LateContext<'_>, @@ -117,38 +118,22 @@ fn check_needless_must_use( fn_header_span, "this unit-returning function has a `#[must_use]` attribute", |diag| { - diag.span_suggestion(attr.span, "remove the attribute", "", Applicability::MachineApplicable); + diag.span_suggestion(attr.span(), "remove the attribute", "", Applicability::MachineApplicable); }, ); } else { // When there are multiple attributes, it is not sufficient to simply make `must_use` empty, see // issue #12320. - span_lint_and_then( + // FIXME(jdonszelmann): this used to give a machine-applicable fix. However, it was super fragile, + // honestly looked incorrect, and is a little hard to support for a little bit now. Some day this could be + // re-added. + span_lint_and_help( cx, - MUST_USE_UNIT, + DOUBLE_MUST_USE, fn_header_span, "this unit-returning function has a `#[must_use]` attribute", - |diag| { - let mut attrs_without_must_use = attrs.to_vec(); - attrs_without_must_use.retain(|a| a.id != attr.id); - let sugg_str = attrs_without_must_use - .iter() - .map(|a| { - if a.value_str().is_none() { - return a.name_or_empty().to_string(); - } - format!("{} = \"{}\"", a.name_or_empty(), a.value_str().unwrap()) - }) - .collect::>() - .join(", "); - - diag.span_suggestion( - attrs[0].span.with_hi(attrs[attrs.len() - 1].span.hi()), - "change these attributes to", - sugg_str, - Applicability::MachineApplicable, - ); - }, + Some(attr.span()), + "remove `must_use`", ); } } else if attr.value_str().is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) { diff --git a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs index 39ff3c13bcce9..5b58113169b1e 100644 --- a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs @@ -183,7 +183,7 @@ fn suggestion<'tcx>( fn field_with_attrs_span(tcx: TyCtxt<'_>, field: &hir::ExprField<'_>) -> Span { if let Some(attr) = tcx.hir().attrs(field.hir_id).first() { - field.span.with_lo(attr.span.lo()) + field.span.with_lo(attr.span().lo()) } else { field.span } diff --git a/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs index 1b900f6be8e85..9b4a3b3f9c84c 100644 --- a/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs +++ b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs @@ -42,10 +42,10 @@ impl<'tcx> LateLintPass<'tcx> for InlineFnWithoutBody { span_lint_and_then( cx, INLINE_FN_WITHOUT_BODY, - attr.span, + attr.span(), format!("use of `#[inline]` on trait method `{}` which has no body", item.ident), |diag| { - diag.suggest_remove_item(cx, attr.span, "remove", Applicability::MachineApplicable); + diag.suggest_remove_item(cx, attr.span(), "remove", Applicability::MachineApplicable); }, ); } diff --git a/src/tools/clippy/clippy_lints/src/large_include_file.rs b/src/tools/clippy/clippy_lints/src/large_include_file.rs index f3d62b513e840..53dc070833b52 100644 --- a/src/tools/clippy/clippy_lints/src/large_include_file.rs +++ b/src/tools/clippy/clippy_lints/src/large_include_file.rs @@ -3,10 +3,11 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::snippet_opt; use rustc_ast::LitKind; -use rustc_hir::{AttrArgs, AttrKind, Attribute, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_hir::{Expr, ExprKind}; +use rustc_ast::{Attribute, AttrArgs, AttrKind}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -52,24 +53,6 @@ impl LargeIncludeFile { impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]); -impl LargeIncludeFile { - fn emit_lint(&self, cx: &LateContext<'_>, span: Span) { - #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] - span_lint_and_then( - cx, - LARGE_INCLUDE_FILE, - span, - "attempted to include a large file", - |diag| { - diag.note(format!( - "the configuration allows a maximum size of {} bytes", - self.max_file_size - )); - }, - ); - } -} - impl LateLintPass<'_> for LargeIncludeFile { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { if let ExprKind::Lit(lit) = &expr.kind @@ -85,18 +68,32 @@ impl LateLintPass<'_> for LargeIncludeFile { && (cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id) || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id)) { - self.emit_lint(cx, expr.span.source_callsite()); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( + cx, + LARGE_INCLUDE_FILE, + expr.span.source_callsite(), + "attempted to include a large file", + |diag| { + diag.note(format!( + "the configuration allows a maximum size of {} bytes", + self.max_file_size + )); + }, + ); } } +} - fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &Attribute) { +impl EarlyLintPass for LargeIncludeFile { + fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { if !attr.span.from_expansion() // Currently, rustc limits the usage of macro at the top-level of attributes, // so we don't need to recurse into each level. && let AttrKind::Normal(ref item) = attr.kind && let Some(doc) = attr.doc_str() && doc.as_str().len() as u64 > self.max_file_size - && let AttrArgs::Eq { expr: meta, .. } = &item.args + && let AttrArgs::Eq { expr: meta, .. } = &item.item.args && !attr.span.contains(meta.span) // Since the `include_str` is already expanded at this point, we can only take the // whole attribute snippet and then modify for our suggestion. @@ -113,7 +110,19 @@ impl LateLintPass<'_> for LargeIncludeFile { && let sub_snippet = sub_snippet.trim() && (sub_snippet.starts_with("include_str!") || sub_snippet.starts_with("include_bytes!")) { - self.emit_lint(cx, attr.span); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( + cx, + LARGE_INCLUDE_FILE, + attr.span, + "attempted to include a large file", + |diag| { + diag.note(format!( + "the configuration allows a maximum size of {} bytes", + self.max_file_size + )); + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 13218331a67b2..177f83921cd54 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -407,9 +407,9 @@ mod zombie_processes; use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation}; use clippy_utils::macros::FormatArgsStorage; +use utils::attr_collector::{AttrCollector, AttrStorage}; use rustc_data_structures::fx::FxHashSet; use rustc_lint::{Lint, LintId}; -use utils::attr_collector::{AttrCollector, AttrStorage}; /// Register all pre expansion lints /// @@ -717,6 +717,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(conf))); store.register_late_pass(move |tcx| Box::new(functions::Functions::new(tcx, conf))); store.register_late_pass(move |_| Box::new(doc::Documentation::new(conf))); + store.register_early_pass(move || Box::new(doc::Documentation::new(conf))); store.register_late_pass(|_| Box::new(neg_multiply::NegMultiply)); store.register_late_pass(|_| Box::new(let_if_seq::LetIfSeq)); store.register_late_pass(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence)); @@ -860,6 +861,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_early_pass(|| Box::new(pub_use::PubUse)); store.register_late_pass(|_| Box::new(format_push_string::FormatPushString)); store.register_late_pass(move |_| Box::new(large_include_file::LargeIncludeFile::new(conf))); + store.register_early_pass(move || Box::new(large_include_file::LargeIncludeFile::new(conf))); store.register_late_pass(|_| Box::new(strings::TrimSplitWhitespace)); store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); store.register_early_pass(|| Box::::default()); diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs index 3741286653929..165e8c2ea05aa 100644 --- a/src/tools/clippy/clippy_lints/src/macro_use.rs +++ b/src/tools/clippy/clippy_lints/src/macro_use.rs @@ -94,7 +94,7 @@ impl LateLintPass<'_> for MacroUseImports { { for kid in cx.tcx.module_children(id) { if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res { - let span = mac_attr.span; + let span = mac_attr.span(); let def_path = cx.tcx.def_path_str(mac_id); self.imports.push((def_path, span, hir_id)); } @@ -104,8 +104,8 @@ impl LateLintPass<'_> for MacroUseImports { } } fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &hir::Attribute) { - if attr.span.from_expansion() { - self.push_unique_macro(cx, attr.span); + if attr.span().from_expansion() { + self.push_unique_macro(cx, attr.span()); } } fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs index 00800231fe46e..83d8a50939069 100644 --- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs +++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs @@ -116,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive { if let Some(non_exhaustive) = attr::find_by_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive) { - diag.span_note(non_exhaustive.span, "the struct is already non-exhaustive"); + diag.span_note(non_exhaustive.span(), "the struct is already non-exhaustive"); } else { let indent = snippet_indent(cx, item.span).unwrap_or_default(); diag.span_suggestion_verbose( diff --git a/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs b/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs index b73b9083a9919..6eca3f12cf252 100644 --- a/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs +++ b/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs @@ -54,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi { .span .with_lo(fn_sig.span.lo() + BytePos::from_usize(fn_attrs.len())) .shrink_to_lo(); - let attr_snippet = snippet(cx, attr.span, ".."); + let attr_snippet = snippet(cx, attr.span(), ".."); span_lint_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index 93abf95e35773..16916e3aaad57 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -432,7 +432,7 @@ fn include_attrs_in_span(cx: &LateContext<'_>, hir_id: HirId, span: Span) -> Spa .hir() .attrs(hir_id) .iter() - .fold(span, |acc, attr| acc.to(attr.span))) + .fold(span, |acc, attr| acc.to(attr.span()))) } enum HasSafetyComment { diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 40ddd75b7fad1..7fc25e3617d07 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -93,6 +93,7 @@ use std::sync::{Mutex, MutexGuard, OnceLock}; use itertools::Itertools; use rustc_ast::ast::{self, LitKind, RangeLimits}; +use rustc_attr_parsing::{find_attr, AttributeKind}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::packed::Pu128; use rustc_data_structures::unhash::UnhashMap; @@ -1949,7 +1950,7 @@ pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool { } pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { - has_attr(cx.tcx.hir().attrs(hir_id), sym::repr) + find_attr!(cx.tcx.hir().attrs(hir_id), AttributeKind::Repr(..)) } pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool { diff --git a/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr index 82b926cc53ba2..c9f0e661dbd18 100644 --- a/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr +++ b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr @@ -1,26 +1,26 @@ error: attempted to include a large file - --> tests/ui-toml/large_include_file/large_include_file.rs:14:43 + --> tests/ui-toml/large_include_file/large_include_file.rs:19:1 | -LL | const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[doc = include_str!("too_big.txt")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the configuration allows a maximum size of 600 bytes = note: `-D clippy::large-include-file` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::large_include_file)]` error: attempted to include a large file - --> tests/ui-toml/large_include_file/large_include_file.rs:16:35 + --> tests/ui-toml/large_include_file/large_include_file.rs:14:43 | -LL | const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the configuration allows a maximum size of 600 bytes error: attempted to include a large file - --> tests/ui-toml/large_include_file/large_include_file.rs:19:1 + --> tests/ui-toml/large_include_file/large_include_file.rs:16:35 | -LL | #[doc = include_str!("too_big.txt")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the configuration allows a maximum size of 600 bytes diff --git a/src/tools/clippy/tests/ui/must_use_unit.fixed b/src/tools/clippy/tests/ui/must_use_unit.fixed index b92d9379c904b..7e2a72960492c 100644 --- a/src/tools/clippy/tests/ui/must_use_unit.fixed +++ b/src/tools/clippy/tests/ui/must_use_unit.fixed @@ -24,8 +24,3 @@ fn main() { ); } -#[cfg_attr(all(), deprecated)] -fn issue_12320() {} - -#[cfg_attr(all(), deprecated, doc = "foo")] -fn issue_12320_2() {} diff --git a/src/tools/clippy/tests/ui/must_use_unit.rs b/src/tools/clippy/tests/ui/must_use_unit.rs index c77e728275048..f41b1a7c80051 100644 --- a/src/tools/clippy/tests/ui/must_use_unit.rs +++ b/src/tools/clippy/tests/ui/must_use_unit.rs @@ -27,8 +27,3 @@ fn main() { ); } -#[cfg_attr(all(), must_use, deprecated)] -fn issue_12320() {} - -#[cfg_attr(all(), deprecated, doc = "foo", must_use)] -fn issue_12320_2() {} diff --git a/src/tools/clippy/tests/ui/must_use_unit.stderr b/src/tools/clippy/tests/ui/must_use_unit.stderr index b435568deeab8..c2ee2edda7dc5 100644 --- a/src/tools/clippy/tests/ui/must_use_unit.stderr +++ b/src/tools/clippy/tests/ui/must_use_unit.stderr @@ -25,21 +25,5 @@ LL | #[must_use = "With note"] LL | pub fn must_use_with_note() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: this unit-returning function has a `#[must_use]` attribute - --> tests/ui/must_use_unit.rs:31:1 - | -LL | #[cfg_attr(all(), must_use, deprecated)] - | -------------------- help: change these attributes to: `deprecated` -LL | fn issue_12320() {} - | ^^^^^^^^^^^^^^^^ - -error: this unit-returning function has a `#[must_use]` attribute - --> tests/ui/must_use_unit.rs:34:1 - | -LL | #[cfg_attr(all(), deprecated, doc = "foo", must_use)] - | --------------------------------- help: change these attributes to: `deprecated, doc = "foo"` -LL | fn issue_12320_2() {} - | ^^^^^^^^^^^^^^^^^^ - -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/must_use_unit_12320.rs b/src/tools/clippy/tests/ui/must_use_unit_12320.rs new file mode 100644 index 0000000000000..39dcafdb38b5e --- /dev/null +++ b/src/tools/clippy/tests/ui/must_use_unit_12320.rs @@ -0,0 +1,11 @@ +//@aux-build:proc_macros.rs +//@no-rustfix + +#![warn(clippy::must_use_unit)] +#![allow(clippy::unused_unit)] + +#[cfg_attr(all(), must_use, deprecated)] +fn issue_12320() {} + +#[cfg_attr(all(), deprecated, doc = "foo", must_use)] +fn issue_12320_2() {} diff --git a/src/tools/clippy/tests/ui/must_use_unit_12320.stderr b/src/tools/clippy/tests/ui/must_use_unit_12320.stderr new file mode 100644 index 0000000000000..b3e1cbc045765 --- /dev/null +++ b/src/tools/clippy/tests/ui/must_use_unit_12320.stderr @@ -0,0 +1,28 @@ +error: this unit-returning function has a `#[must_use]` attribute + --> tests/ui/must_use_unit_12320.rs:8:1 + | +LL | fn issue_12320() {} + | ^^^^^^^^^^^^^^^^ + | +help: remove `must_use` + --> tests/ui/must_use_unit_12320.rs:7:19 + | +LL | #[cfg_attr(all(), must_use, deprecated)] + | ^^^^^^^^ + = note: `-D clippy::double-must-use` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::double_must_use)]` + +error: this unit-returning function has a `#[must_use]` attribute + --> tests/ui/must_use_unit_12320.rs:11:1 + | +LL | fn issue_12320_2() {} + | ^^^^^^^^^^^^^^^^^^ + | +help: remove `must_use` + --> tests/ui/must_use_unit_12320.rs:10:44 + | +LL | #[cfg_attr(all(), deprecated, doc = "foo", must_use)] + | ^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/pretty/hir-pretty-attr.pp b/tests/pretty/hir-pretty-attr.pp new file mode 100644 index 0000000000000..586810b004662 --- /dev/null +++ b/tests/pretty/hir-pretty-attr.pp @@ -0,0 +1,11 @@ +#[prelude_import] +use ::std::prelude::rust_2015::*; +#[macro_use] +extern crate std; +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:hir-pretty-attr.pp + +#[attr="Repr([ReprC, ReprPacked(Align(4 bytes)), ReprTransparent])")] +struct Example { +} diff --git a/tests/pretty/hir-pretty-attr.rs b/tests/pretty/hir-pretty-attr.rs new file mode 100644 index 0000000000000..eb5a677024a8e --- /dev/null +++ b/tests/pretty/hir-pretty-attr.rs @@ -0,0 +1,7 @@ +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:hir-pretty-attr.pp + +#[repr(C, packed(4))] +#[repr(transparent)] +struct Example {} diff --git a/tests/rustdoc-json/enums/discriminant/struct.rs b/tests/rustdoc-json/enums/discriminant/struct.rs index 0ac40cda73388..24d5f5b08c203 100644 --- a/tests/rustdoc-json/enums/discriminant/struct.rs +++ b/tests/rustdoc-json/enums/discriminant/struct.rs @@ -1,7 +1,7 @@ // ignore-tidy-linelength #[repr(i32)] -//@ is "$.index[*][?(@.name=='Foo')].attrs" '["#[repr(i32)]"]' +//@ is "$.index[*][?(@.name=='Foo')].attrs" '["#[attr=\"Repr([ReprInt(SignedInt(I32))])\")]\n"]' pub enum Foo { //@ is "$.index[*][?(@.name=='Struct')].inner.variant.discriminant" null //@ count "$.index[*][?(@.name=='Struct')].inner.variant.kind.struct.fields[*]" 0 diff --git a/tests/rustdoc-json/enums/discriminant/tuple.rs b/tests/rustdoc-json/enums/discriminant/tuple.rs index fbff5aacd6741..a50ae8b9189f6 100644 --- a/tests/rustdoc-json/enums/discriminant/tuple.rs +++ b/tests/rustdoc-json/enums/discriminant/tuple.rs @@ -1,7 +1,7 @@ // ignore-tidy-linelength #[repr(u32)] -//@ is "$.index[*][?(@.name=='Foo')].attrs" '["#[repr(u32)]"]' +//@ is "$.index[*][?(@.name=='Foo')].attrs" '["#[attr=\"Repr([ReprInt(UnsignedInt(U32))])\")]\n"]' pub enum Foo { //@ is "$.index[*][?(@.name=='Tuple')].inner.variant.discriminant" null //@ count "$.index[*][?(@.name=='Tuple')].inner.variant.kind.tuple[*]" 0 diff --git a/tests/ui-fulldeps/stable-mir/check_attribute.rs b/tests/ui-fulldeps/stable-mir/check_attribute.rs index 11cb63f3f8a58..de5ba15f6ea4e 100644 --- a/tests/ui-fulldeps/stable-mir/check_attribute.rs +++ b/tests/ui-fulldeps/stable-mir/check_attribute.rs @@ -27,63 +27,23 @@ fn test_stable_mir() -> ControlFlow<()> { // Find items in the local crate. let items = stable_mir::all_local_items(); - test_builtins(&items); - test_derive(&items); test_tool(&items); - test_all_attrs(&items); ControlFlow::Continue(()) } -// Test built-in attributes. -fn test_builtins(items: &CrateItems) { - let target_fn = *get_item(&items, "builtins_fn").unwrap(); - let allow_attrs = target_fn.attrs_by_path(&["allow".to_string()]); - assert_eq!(allow_attrs[0].as_str(), "#![allow(unused_variables)]"); - - let inline_attrs = target_fn.attrs_by_path(&["inline".to_string()]); - assert_eq!(inline_attrs[0].as_str(), "#[inline]"); - - let deprecated_attrs = target_fn.attrs_by_path(&["deprecated".to_string()]); - assert_eq!(deprecated_attrs[0].as_str(), "#[deprecated(since = \"5.2.0\")]"); -} - -// Test derive attribute. -fn test_derive(items: &CrateItems) { - let target_struct = *get_item(&items, "Foo").unwrap(); - let attrs = target_struct.attrs_by_path(&["derive".to_string()]); - // No `derive` attribute since it's expanded before MIR. - assert_eq!(attrs.len(), 0); - - // Check derived trait method's attributes. - let derived_fmt = *get_item(&items, "::fmt").unwrap(); - // The Rust reference lies about this attribute. It doesn't show up in `clone` or `fmt` impl. - let _fmt_attrs = derived_fmt.attrs_by_path(&["automatically_derived".to_string()]); -} - // Test tool attributes. fn test_tool(items: &CrateItems) { let rustfmt_fn = *get_item(&items, "do_not_format").unwrap(); - let rustfmt_attrs = rustfmt_fn.attrs_by_path(&["rustfmt".to_string(), "skip".to_string()]); + let rustfmt_attrs = rustfmt_fn.tool_attrs(&["rustfmt".to_string(), "skip".to_string()]); assert_eq!(rustfmt_attrs[0].as_str(), "#[rustfmt::skip]"); let clippy_fn = *get_item(&items, "complex_fn").unwrap(); - let clippy_attrs = clippy_fn.attrs_by_path(&["clippy".to_string(), + let clippy_attrs = clippy_fn.tool_attrs(&["clippy".to_string(), "cyclomatic_complexity".to_string()]); assert_eq!(clippy_attrs[0].as_str(), "#[clippy::cyclomatic_complexity = \"100\"]"); } -fn test_all_attrs(items: &CrateItems) { - let target_fn = *get_item(&items, "many_attrs").unwrap(); - let all_attrs = target_fn.all_attrs(); - assert_eq!(all_attrs[0].as_str(), "#[inline]"); - assert_eq!(all_attrs[1].as_str(), "#[allow(unused_variables)]"); - assert_eq!(all_attrs[2].as_str(), "#[allow(dead_code)]"); - assert_eq!(all_attrs[3].as_str(), "#[allow(unused_imports)]"); - assert_eq!(all_attrs[4].as_str(), "#![allow(clippy::filter_map)]"); -} - - fn get_item<'a>( items: &'a CrateItems, name: &str, diff --git a/tests/ui/attributes/arg-error-issue-121425.stderr b/tests/ui/attributes/arg-error-issue-121425.stderr index 1beb99b1703cf..6e71f15fdc872 100644 --- a/tests/ui/attributes/arg-error-issue-121425.stderr +++ b/tests/ui/attributes/arg-error-issue-121425.stderr @@ -1,3 +1,9 @@ +error[E0693]: incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses + --> $DIR/arg-error-issue-121425.rs:16:8 + | +LL | #[repr(align())] + | ^^^^^^^ + error[E0693]: incorrect `repr(align)` attribute format: `align` expects a literal integer as argument --> $DIR/arg-error-issue-121425.rs:4:14 | @@ -16,12 +22,6 @@ error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer LL | #[repr(align("str"))] | ^^^^^ -error[E0693]: incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses - --> $DIR/arg-error-issue-121425.rs:16:8 - | -LL | #[repr(align())] - | ^^^^^^^ - error[E0552]: incorrect `repr(packed)` attribute format: `packed` expects a literal integer as argument --> $DIR/arg-error-issue-121425.rs:21:15 | diff --git a/tests/ui/attributes/issue-100631.stderr b/tests/ui/attributes/issue-100631.stderr index 6e8e4f3b418fb..b2bd0a9632513 100644 --- a/tests/ui/attributes/issue-100631.stderr +++ b/tests/ui/attributes/issue-100631.stderr @@ -1,8 +1,8 @@ error[E0084]: unsupported representation for zero-variant enum - --> $DIR/issue-100631.rs:4:1 + --> $DIR/issue-100631.rs:4:8 | LL | #[repr(C)] - | ^^^^^^^^^^ + | ^ LL | #[repr(C)] LL | enum Foo {} | -------- zero-variant enum diff --git a/tests/ui/attributes/malformed-fn-align.rs b/tests/ui/attributes/malformed-fn-align.rs new file mode 100644 index 0000000000000..4aaad01b7235e --- /dev/null +++ b/tests/ui/attributes/malformed-fn-align.rs @@ -0,0 +1,7 @@ +#![feature(fn_align)] +#![crate_type = "lib"] + +trait MyTrait { + #[repr(align)] //~ ERROR invalid `repr(align)` attribute: `align` needs an argument + fn myfun(); +} diff --git a/tests/ui/attributes/malformed-fn-align.stderr b/tests/ui/attributes/malformed-fn-align.stderr new file mode 100644 index 0000000000000..57913c48ef787 --- /dev/null +++ b/tests/ui/attributes/malformed-fn-align.stderr @@ -0,0 +1,9 @@ +error[E0589]: invalid `repr(align)` attribute: `align` needs an argument + --> $DIR/malformed-fn-align.rs:5:12 + | +LL | #[repr(align)] + | ^^^^^ help: supply an argument here: `align(...)` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0589`. diff --git a/tests/ui/attributes/mixed_export_name_and_no_mangle.fixed b/tests/ui/attributes/mixed_export_name_and_no_mangle.fixed index 7224d4289e3fa..d8b5235c52fbe 100644 --- a/tests/ui/attributes/mixed_export_name_and_no_mangle.fixed +++ b/tests/ui/attributes/mixed_export_name_and_no_mangle.fixed @@ -3,7 +3,7 @@ //@ check-pass #![warn(unused_attributes)] -//~^ WARN `#[no_mangle]` attribute may not be used in combination with `#[export_name]` [unused_attributes] +//~^ WARN `#[unsafe(no_mangle)]` attribute may not be used in combination with `#[export_name]` [unused_attributes] #[export_name = "foo"] pub fn bar() {} diff --git a/tests/ui/attributes/mixed_export_name_and_no_mangle.rs b/tests/ui/attributes/mixed_export_name_and_no_mangle.rs index 149a7904e1ea5..83a673a7d1328 100644 --- a/tests/ui/attributes/mixed_export_name_and_no_mangle.rs +++ b/tests/ui/attributes/mixed_export_name_and_no_mangle.rs @@ -4,7 +4,7 @@ #![warn(unused_attributes)] #[no_mangle] -//~^ WARN `#[no_mangle]` attribute may not be used in combination with `#[export_name]` [unused_attributes] +//~^ WARN `#[unsafe(no_mangle)]` attribute may not be used in combination with `#[export_name]` [unused_attributes] #[export_name = "foo"] pub fn bar() {} diff --git a/tests/ui/attributes/mixed_export_name_and_no_mangle.stderr b/tests/ui/attributes/mixed_export_name_and_no_mangle.stderr index ba63127ba2db8..c760d27db2513 100644 --- a/tests/ui/attributes/mixed_export_name_and_no_mangle.stderr +++ b/tests/ui/attributes/mixed_export_name_and_no_mangle.stderr @@ -1,8 +1,8 @@ -warning: `#[no_mangle]` attribute may not be used in combination with `#[export_name]` +warning: `#[unsafe(no_mangle)]` attribute may not be used in combination with `#[export_name]` --> $DIR/mixed_export_name_and_no_mangle.rs:6:1 | LL | #[no_mangle] - | ^^^^^^^^^^^^ `#[no_mangle]` is ignored + | ^^^^^^^^^^^^ `#[unsafe(no_mangle)]` is ignored | note: `#[export_name]` takes precedence --> $DIR/mixed_export_name_and_no_mangle.rs:8:1 @@ -14,7 +14,7 @@ note: the lint level is defined here | LL | #![warn(unused_attributes)] | ^^^^^^^^^^^^^^^^^ -help: remove the `#[no_mangle]` attribute +help: remove the `#[unsafe(no_mangle)]` attribute | LL - #[no_mangle] | diff --git a/tests/ui/attributes/nonterminal-expansion.rs b/tests/ui/attributes/nonterminal-expansion.rs index 1b2e92a31703c..5ea30bb8627b4 100644 --- a/tests/ui/attributes/nonterminal-expansion.rs +++ b/tests/ui/attributes/nonterminal-expansion.rs @@ -6,6 +6,7 @@ macro_rules! pass_nonterminal { ($n:expr) => { #[repr(align($n))] //~^ ERROR expected unsuffixed literal, found `n!()` + //~^^ ERROR incorrect `repr(align)` attribute format: `align` expects a literal integer as argument [E0693] struct S; }; } @@ -15,6 +16,5 @@ macro_rules! n { } pass_nonterminal!(n!()); -//~^ ERROR incorrect `repr(align)` attribute format: `align` expects a literal integer as argument [E0693] fn main() {} diff --git a/tests/ui/attributes/nonterminal-expansion.stderr b/tests/ui/attributes/nonterminal-expansion.stderr index b640575d17dc7..cce5c453d5203 100644 --- a/tests/ui/attributes/nonterminal-expansion.stderr +++ b/tests/ui/attributes/nonterminal-expansion.stderr @@ -10,10 +10,15 @@ LL | pass_nonterminal!(n!()); = note: this error originates in the macro `pass_nonterminal` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0693]: incorrect `repr(align)` attribute format: `align` expects a literal integer as argument - --> $DIR/nonterminal-expansion.rs:17:19 + --> $DIR/nonterminal-expansion.rs:7:22 | +LL | #[repr(align($n))] + | ^^ +... LL | pass_nonterminal!(n!()); - | ^ + | ----------------------- in this macro invocation + | + = note: this error originates in the macro `pass_nonterminal` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/attributes/repr-align-in-trait-issue-132391.rs b/tests/ui/attributes/repr-align-in-trait-issue-132391.rs new file mode 100644 index 0000000000000..b3b79e93e9bd8 --- /dev/null +++ b/tests/ui/attributes/repr-align-in-trait-issue-132391.rs @@ -0,0 +1,6 @@ +trait MyTrait { + #[repr(align)] //~ ERROR invalid `repr(align)` attribute: `align` needs an argument + fn myfun(); +} + +pub fn main() {} diff --git a/tests/ui/attributes/repr-align-in-trait-issue-132391.stderr b/tests/ui/attributes/repr-align-in-trait-issue-132391.stderr new file mode 100644 index 0000000000000..4208b018f52f0 --- /dev/null +++ b/tests/ui/attributes/repr-align-in-trait-issue-132391.stderr @@ -0,0 +1,9 @@ +error[E0589]: invalid `repr(align)` attribute: `align` needs an argument + --> $DIR/repr-align-in-trait-issue-132391.rs:2:12 + | +LL | #[repr(align)] + | ^^^^^ help: supply an argument here: `align(...)` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0589`. diff --git a/tests/ui/attributes/rustc_confusables.stderr b/tests/ui/attributes/rustc_confusables.stderr index dc71d974daf56..55c9219a08a84 100644 --- a/tests/ui/attributes/rustc_confusables.stderr +++ b/tests/ui/attributes/rustc_confusables.stderr @@ -4,12 +4,6 @@ error: malformed `rustc_confusables` attribute input LL | #[rustc_confusables] | ^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_confusables("name1", "name2", ...)]` -error: attribute should be applied to an inherent method - --> $DIR/rustc_confusables.rs:45:1 - | -LL | #[rustc_confusables("blah")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: expected at least one confusable name --> $DIR/rustc_confusables.rs:30:5 | @@ -27,6 +21,12 @@ help: consider surrounding this with quotes LL | #[rustc_confusables("invalid_meta_item")] | + + +error: attribute should be applied to an inherent method + --> $DIR/rustc_confusables.rs:45:1 + | +LL | #[rustc_confusables("blah")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0599]: no method named `inser` found for struct `rustc_confusables_across_crate::BTreeSet` in the current scope --> $DIR/rustc_confusables.rs:12:7 | diff --git a/tests/ui/deprecation/deprecation-sanity.rs b/tests/ui/deprecation/deprecation-sanity.rs index 9ea75b68f81ce..d5b149b18ed6a 100644 --- a/tests/ui/deprecation/deprecation-sanity.rs +++ b/tests/ui/deprecation/deprecation-sanity.rs @@ -4,16 +4,16 @@ mod bogus_attribute_types_1 { #[deprecated(since = "a", note = "a", reason)] //~ ERROR unknown meta item 'reason' fn f1() { } - #[deprecated(since = "a", note)] //~ ERROR incorrect meta item + #[deprecated(since = "a", note)] //~ ERROR expected a quoted string literal fn f2() { } - #[deprecated(since, note = "a")] //~ ERROR incorrect meta item + #[deprecated(since, note = "a")] //~ ERROR expected a quoted string literal fn f3() { } - #[deprecated(since = "a", note(b))] //~ ERROR incorrect meta item + #[deprecated(since = "a", note(b))] //~ ERROR expected a quoted string literal fn f5() { } - #[deprecated(since(b), note = "a")] //~ ERROR incorrect meta item + #[deprecated(since(b), note = "a")] //~ ERROR expected a quoted string literal fn f6() { } #[deprecated(note = b"test")] //~ ERROR literal in `deprecated` value must be a string diff --git a/tests/ui/deprecation/deprecation-sanity.stderr b/tests/ui/deprecation/deprecation-sanity.stderr index 383212ad9b4b8..53047d40cb2ff 100644 --- a/tests/ui/deprecation/deprecation-sanity.stderr +++ b/tests/ui/deprecation/deprecation-sanity.stderr @@ -1,40 +1,28 @@ -error: multiple `deprecated` attributes - --> $DIR/deprecation-sanity.rs:27:1 - | -LL | #[deprecated(since = "a", note = "b")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute - | -note: attribute also specified here - --> $DIR/deprecation-sanity.rs:26:1 - | -LL | #[deprecated(since = "a", note = "b")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error[E0541]: unknown meta item 'reason' --> $DIR/deprecation-sanity.rs:4:43 | LL | #[deprecated(since = "a", note = "a", reason)] | ^^^^^^ expected one of `since`, `note` -error[E0539]: incorrect meta item +error[E0539]: expected a quoted string literal --> $DIR/deprecation-sanity.rs:7:31 | LL | #[deprecated(since = "a", note)] | ^^^^ -error[E0539]: incorrect meta item +error[E0539]: expected a quoted string literal --> $DIR/deprecation-sanity.rs:10:18 | LL | #[deprecated(since, note = "a")] | ^^^^^ -error[E0539]: incorrect meta item +error[E0539]: expected a quoted string literal --> $DIR/deprecation-sanity.rs:13:31 | LL | #[deprecated(since = "a", note(b))] | ^^^^^^^ -error[E0539]: incorrect meta item +error[E0539]: expected a quoted string literal --> $DIR/deprecation-sanity.rs:16:18 | LL | #[deprecated(since(b), note = "a")] @@ -54,6 +42,18 @@ error[E0565]: item in `deprecated` must be a key/value pair LL | #[deprecated("test")] | ^^^^^^ +error: multiple `deprecated` attributes + --> $DIR/deprecation-sanity.rs:27:1 + | +LL | #[deprecated(since = "a", note = "b")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/deprecation-sanity.rs:26:1 + | +LL | #[deprecated(since = "a", note = "b")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0538]: multiple 'since' items --> $DIR/deprecation-sanity.rs:30:27 | diff --git a/tests/ui/deprecation/issue-66340-deprecated-attr-non-meta-grammar.rs b/tests/ui/deprecation/issue-66340-deprecated-attr-non-meta-grammar.rs index 6653bd15ddd2b..c5433151a8fd2 100644 --- a/tests/ui/deprecation/issue-66340-deprecated-attr-non-meta-grammar.rs +++ b/tests/ui/deprecation/issue-66340-deprecated-attr-non-meta-grammar.rs @@ -3,7 +3,7 @@ // was a well-formed `MetaItem`. fn main() { - foo() + foo() //~ WARNING use of deprecated function `foo` } #[deprecated(note = test)] diff --git a/tests/ui/deprecation/issue-66340-deprecated-attr-non-meta-grammar.stderr b/tests/ui/deprecation/issue-66340-deprecated-attr-non-meta-grammar.stderr index a030da5068c2d..2ff8534b276c4 100644 --- a/tests/ui/deprecation/issue-66340-deprecated-attr-non-meta-grammar.stderr +++ b/tests/ui/deprecation/issue-66340-deprecated-attr-non-meta-grammar.stderr @@ -9,5 +9,13 @@ help: surround the identifier with quotation marks to make it into a string lite LL | #[deprecated(note = "test")] | + + -error: aborting due to 1 previous error +warning: use of deprecated function `foo` + --> $DIR/issue-66340-deprecated-attr-non-meta-grammar.rs:6:5 + | +LL | foo() + | ^^^ + | + = note: `#[warn(deprecated)]` on by default + +error: aborting due to 1 previous error; 1 warning emitted diff --git a/tests/ui/error-codes/E0084.stderr b/tests/ui/error-codes/E0084.stderr index f1fbe6c2532b1..3df2e4a322b2c 100644 --- a/tests/ui/error-codes/E0084.stderr +++ b/tests/ui/error-codes/E0084.stderr @@ -1,8 +1,8 @@ error[E0084]: unsupported representation for zero-variant enum - --> $DIR/E0084.rs:1:1 + --> $DIR/E0084.rs:1:8 | LL | #[repr(i32)] - | ^^^^^^^^^^^^ + | ^^^ LL | enum Foo {} | -------- zero-variant enum diff --git a/tests/ui/error-codes/E0565.stderr b/tests/ui/error-codes/E0565.stderr index 68f4a37dcff38..6e56600133d73 100644 --- a/tests/ui/error-codes/E0565.stderr +++ b/tests/ui/error-codes/E0565.stderr @@ -1,8 +1,8 @@ error[E0565]: meta item in `repr` must be an identifier - --> $DIR/E0565.rs:2:8 + --> $DIR/E0565.rs:2:1 | LL | #[repr("C")] - | ^^^ + | ^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0789.rs b/tests/ui/error-codes/E0789.rs index 08471e1b3f3e9..4a55e1743158d 100644 --- a/tests/ui/error-codes/E0789.rs +++ b/tests/ui/error-codes/E0789.rs @@ -8,5 +8,3 @@ // #[stable(feature = "foo", since = "1.0")] struct Foo; //~^ ERROR `rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute -//~^^ ERROR `rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute -// FIXME: we shouldn't have two errors here, only occurs when using `-Zdeduplicate-diagnostics=no` diff --git a/tests/ui/error-codes/E0789.stderr b/tests/ui/error-codes/E0789.stderr index 6df9daafec76d..23631ee1b03c3 100644 --- a/tests/ui/error-codes/E0789.stderr +++ b/tests/ui/error-codes/E0789.stderr @@ -4,14 +4,6 @@ error[E0789]: `rustc_allowed_through_unstable_modules` attribute must be paired LL | struct Foo; | ^^^^^^^^^^^ -error[E0789]: `rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute - --> $DIR/E0789.rs:9:1 - | -LL | struct Foo; - | ^^^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0789`. diff --git a/tests/ui/feature-gates/feature-gate-allow-internal-unstable-struct.rs b/tests/ui/feature-gates/feature-gate-allow-internal-unstable-struct.rs index 8b13f1bf2788e..81b7fe3db2ba8 100644 --- a/tests/ui/feature-gates/feature-gate-allow-internal-unstable-struct.rs +++ b/tests/ui/feature-gates/feature-gate-allow-internal-unstable-struct.rs @@ -1,7 +1,9 @@ // checks that this attribute is caught on non-macro items. // this needs a different test since this is done after expansion -#[allow_internal_unstable()] //~ ERROR allow_internal_unstable side-steps +// FIXME(jdonszelmann): empty attributes are currently ignored, since when its empty no actual +// change is applied. This should be fixed when later moving this check to attribute parsing. +#[allow_internal_unstable(something)] //~ ERROR allow_internal_unstable side-steps //~| ERROR attribute should struct S; diff --git a/tests/ui/feature-gates/feature-gate-allow-internal-unstable-struct.stderr b/tests/ui/feature-gates/feature-gate-allow-internal-unstable-struct.stderr index 28f1a0d6ed5ab..076f2df28e3f3 100644 --- a/tests/ui/feature-gates/feature-gate-allow-internal-unstable-struct.stderr +++ b/tests/ui/feature-gates/feature-gate-allow-internal-unstable-struct.stderr @@ -1,17 +1,17 @@ error[E0658]: allow_internal_unstable side-steps feature gating and stability checks - --> $DIR/feature-gate-allow-internal-unstable-struct.rs:4:1 + --> $DIR/feature-gate-allow-internal-unstable-struct.rs:6:1 | -LL | #[allow_internal_unstable()] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[allow_internal_unstable(something)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: add `#![feature(allow_internal_unstable)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: attribute should be applied to a macro - --> $DIR/feature-gate-allow-internal-unstable-struct.rs:4:1 + --> $DIR/feature-gate-allow-internal-unstable-struct.rs:6:1 | -LL | #[allow_internal_unstable()] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[allow_internal_unstable(something)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | struct S; | --------- not a macro diff --git a/tests/ui/feature-gates/feature-gate-fn_align.rs b/tests/ui/feature-gates/feature-gate-fn_align.rs index 06784a45d76c8..744877704dd11 100644 --- a/tests/ui/feature-gates/feature-gate-fn_align.rs +++ b/tests/ui/feature-gates/feature-gate-fn_align.rs @@ -4,6 +4,6 @@ fn requires_alignment() {} trait MyTrait { - #[repr(align)] //~ ERROR `repr(align)` attributes on functions are unstable + #[repr(align)] //~ ERROR invalid `repr(align)` attribute: `align` needs an argument fn myfun(); } diff --git a/tests/ui/feature-gates/feature-gate-fn_align.stderr b/tests/ui/feature-gates/feature-gate-fn_align.stderr index cd9900c605117..ff17c29fe0296 100644 --- a/tests/ui/feature-gates/feature-gate-fn_align.stderr +++ b/tests/ui/feature-gates/feature-gate-fn_align.stderr @@ -1,3 +1,9 @@ +error[E0589]: invalid `repr(align)` attribute: `align` needs an argument + --> $DIR/feature-gate-fn_align.rs:7:12 + | +LL | #[repr(align)] + | ^^^^^ help: supply an argument here: `align(...)` + error[E0658]: `repr(align)` attributes on functions are unstable --> $DIR/feature-gate-fn_align.rs:3:8 | @@ -8,16 +14,7 @@ LL | #[repr(align(16))] = help: add `#![feature(fn_align)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: `repr(align)` attributes on functions are unstable - --> $DIR/feature-gate-fn_align.rs:7:12 - | -LL | #[repr(align)] - | ^^^^^ - | - = note: see issue #82232 for more information - = help: add `#![feature(fn_align)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0658`. +Some errors have detailed explanations: E0589, E0658. +For more information about an error, try `rustc --explain E0589`. diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr index 648bafe646094..5c2a3ae699c03 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr @@ -130,21 +130,6 @@ LL - #![rustc_main] LL + #[rustc_main] | -error: `repr` attribute cannot be used at crate level - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:17:1 - | -LL | #![repr()] - | ^^^^^^^^^^ -... -LL | mod inline { - | ------ the inner attribute doesn't annotate this module - | -help: perhaps you meant to use an outer attribute - | -LL - #![repr()] -LL + #[repr()] - | - error: `path` attribute cannot be used at crate level --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:19:1 | @@ -175,6 +160,21 @@ LL - #![automatically_derived] LL + #[automatically_derived] | +error: `repr` attribute cannot be used at crate level + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:17:1 + | +LL | #![repr()] + | ^^^^^^^^^^ +... +LL | mod inline { + | ------ the inner attribute doesn't annotate this module + | +help: perhaps you meant to use an outer attribute + | +LL - #![repr()] +LL + #[repr()] + | + error[E0518]: attribute should be applied to function or closure --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:40:17 | diff --git a/tests/ui/internal/internal-unstable.rs b/tests/ui/internal/internal-unstable.rs index 35a2941633a48..381c1337148c9 100644 --- a/tests/ui/internal/internal-unstable.rs +++ b/tests/ui/internal/internal-unstable.rs @@ -7,7 +7,7 @@ extern crate internal_unstable; struct Baz { - #[allow_internal_unstable] + #[allow_internal_unstable] //~ ERROR `allow_internal_unstable` expects a list of feature names baz: u8, } @@ -56,7 +56,7 @@ fn main() { bar!(internal_unstable::unstable()); //~ ERROR use of unstable match true { - #[allow_internal_unstable] + #[allow_internal_unstable] //~ ERROR `allow_internal_unstable` expects a list of feature names _ => {} } diff --git a/tests/ui/internal/internal-unstable.stderr b/tests/ui/internal/internal-unstable.stderr index ea74175f09b29..bbf589d3f9268 100644 --- a/tests/ui/internal/internal-unstable.stderr +++ b/tests/ui/internal/internal-unstable.stderr @@ -1,3 +1,15 @@ +error: `allow_internal_unstable` expects a list of feature names + --> $DIR/internal-unstable.rs:10:5 + | +LL | #[allow_internal_unstable] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `allow_internal_unstable` expects a list of feature names + --> $DIR/internal-unstable.rs:59:9 + | +LL | #[allow_internal_unstable] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0658]: use of unstable library feature `function` --> $DIR/internal-unstable.rs:48:25 | @@ -47,6 +59,6 @@ LL | bar!(internal_unstable::unstable()); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = note: this error originates in the macro `foo` which comes from the expansion of the macro `bar` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/issues/issue-43988.stderr b/tests/ui/issues/issue-43988.stderr index 7bbb8ed2ca94c..d629f199b223d 100644 --- a/tests/ui/issues/issue-43988.stderr +++ b/tests/ui/issues/issue-43988.stderr @@ -10,22 +10,6 @@ error: malformed `repr` attribute input LL | let _z = #[repr] 1; | ^^^^^^^ help: must be of the form: `#[repr(C)]` -error[E0518]: attribute should be applied to function or closure - --> $DIR/issue-43988.rs:5:5 - | -LL | #[inline] - | ^^^^^^^^^ -LL | let _a = 4; - | ----------- not a function or closure - -error[E0518]: attribute should be applied to function or closure - --> $DIR/issue-43988.rs:10:5 - | -LL | #[inline(XYZ)] - | ^^^^^^^^^^^^^^ -LL | let _b = 4; - | ----------- not a function or closure - error[E0552]: unrecognized representation hint --> $DIR/issue-43988.rs:14:12 | @@ -42,6 +26,22 @@ LL | #[repr(something_not_real)] | = help: valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize` +error[E0518]: attribute should be applied to function or closure + --> $DIR/issue-43988.rs:5:5 + | +LL | #[inline] + | ^^^^^^^^^ +LL | let _a = 4; + | ----------- not a function or closure + +error[E0518]: attribute should be applied to function or closure + --> $DIR/issue-43988.rs:10:5 + | +LL | #[inline(XYZ)] + | ^^^^^^^^^^^^^^ +LL | let _b = 4; + | ----------- not a function or closure + error[E0518]: attribute should be applied to function or closure --> $DIR/issue-43988.rs:30:5 | diff --git a/tests/ui/repr/invalid_repr_list_help.stderr b/tests/ui/repr/invalid_repr_list_help.stderr index e87cbd37a9930..763ad9c27958f 100644 --- a/tests/ui/repr/invalid_repr_list_help.stderr +++ b/tests/ui/repr/invalid_repr_list_help.stderr @@ -30,14 +30,6 @@ LL | #[repr(uwu, u8)] | = help: valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize` -error: unknown `doc` attribute `owo` - --> $DIR/invalid_repr_list_help.rs:20:7 - | -LL | #[doc(owo)] - | ^^^ - | - = note: `#[deny(invalid_doc_attributes)]` on by default - error[E0552]: unrecognized representation hint --> $DIR/invalid_repr_list_help.rs:19:8 | @@ -46,6 +38,14 @@ LL | #[repr(uwu)] | = help: valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize` +error: unknown `doc` attribute `owo` + --> $DIR/invalid_repr_list_help.rs:20:7 + | +LL | #[doc(owo)] + | ^^^ + | + = note: `#[deny(invalid_doc_attributes)]` on by default + error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0552`. diff --git a/tests/ui/repr/issue-83505-repr-simd.stderr b/tests/ui/repr/issue-83505-repr-simd.stderr index 44e154b4bb613..57cfbeb95de87 100644 --- a/tests/ui/repr/issue-83505-repr-simd.stderr +++ b/tests/ui/repr/issue-83505-repr-simd.stderr @@ -26,10 +26,10 @@ LL | enum Es {} | ---------- not a struct error[E0084]: unsupported representation for zero-variant enum - --> $DIR/issue-83505-repr-simd.rs:5:1 + --> $DIR/issue-83505-repr-simd.rs:5:8 | LL | #[repr(simd)] - | ^^^^^^^^^^^^^ + | ^^^^ ... LL | enum Es {} | ------- zero-variant enum diff --git a/tests/ui/repr/malformed-repr-hints.stderr b/tests/ui/repr/malformed-repr-hints.stderr index 6fb927557619f..7a6e9ccc73eb1 100644 --- a/tests/ui/repr/malformed-repr-hints.stderr +++ b/tests/ui/repr/malformed-repr-hints.stderr @@ -1,15 +1,3 @@ -error[E0552]: incorrect `repr(packed)` attribute format: `packed` takes exactly one parenthesized argument, or no parentheses at all - --> $DIR/malformed-repr-hints.rs:6:8 - | -LL | #[repr(packed())] - | ^^^^^^^^ - -error[E0589]: invalid `repr(align)` attribute: `align` needs an argument - --> $DIR/malformed-repr-hints.rs:10:8 - | -LL | #[repr(align)] - | ^^^^^ help: supply an argument here: `align(...)` - error[E0693]: incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses --> $DIR/malformed-repr-hints.rs:14:8 | @@ -22,6 +10,18 @@ error[E0693]: incorrect `repr(align)` attribute format: `align` takes exactly on LL | #[repr(align())] | ^^^^^^^ +error[E0552]: incorrect `repr(packed)` attribute format: `packed` takes exactly one parenthesized argument, or no parentheses at all + --> $DIR/malformed-repr-hints.rs:6:8 + | +LL | #[repr(packed())] + | ^^^^^^^^ + +error[E0589]: invalid `repr(align)` attribute: `align` needs an argument + --> $DIR/malformed-repr-hints.rs:10:8 + | +LL | #[repr(align)] + | ^^^^^ help: supply an argument here: `align(...)` + error[E0552]: invalid representation hint: `Rust` does not take a parenthesized argument list --> $DIR/malformed-repr-hints.rs:23:8 | diff --git a/tests/ui/repr/repr-align-assign.fixed b/tests/ui/repr/repr-align-assign.fixed index d40fcadf57b1b..96f9866611bc1 100644 --- a/tests/ui/repr/repr-align-assign.fixed +++ b/tests/ui/repr/repr-align-assign.fixed @@ -3,11 +3,9 @@ #![allow(dead_code)] #[repr(align(8))] //~ ERROR incorrect `repr(align)` attribute format - //~| ERROR incorrect `repr(align)` attribute format struct A(u64); #[repr(align(8))] //~ ERROR incorrect `repr(align)` attribute format - //~| ERROR incorrect `repr(align)` attribute format struct B(u64); fn main() {} diff --git a/tests/ui/repr/repr-align-assign.rs b/tests/ui/repr/repr-align-assign.rs index 3aff84a91f714..0b30ee6566461 100644 --- a/tests/ui/repr/repr-align-assign.rs +++ b/tests/ui/repr/repr-align-assign.rs @@ -3,11 +3,9 @@ #![allow(dead_code)] #[repr(align=8)] //~ ERROR incorrect `repr(align)` attribute format - //~| ERROR incorrect `repr(align)` attribute format struct A(u64); #[repr(align="8")] //~ ERROR incorrect `repr(align)` attribute format - //~| ERROR incorrect `repr(align)` attribute format struct B(u64); fn main() {} diff --git a/tests/ui/repr/repr-align-assign.stderr b/tests/ui/repr/repr-align-assign.stderr index 3606d02210ba0..cc046e04de542 100644 --- a/tests/ui/repr/repr-align-assign.stderr +++ b/tests/ui/repr/repr-align-assign.stderr @@ -5,27 +5,11 @@ LL | #[repr(align=8)] | ^^^^^^^ help: use parentheses instead: `align(8)` error[E0693]: incorrect `repr(align)` attribute format - --> $DIR/repr-align-assign.rs:9:8 + --> $DIR/repr-align-assign.rs:8:8 | LL | #[repr(align="8")] | ^^^^^^^^^ help: use parentheses instead: `align(8)` -error[E0693]: incorrect `repr(align)` attribute format - --> $DIR/repr-align-assign.rs:5:8 - | -LL | #[repr(align=8)] - | ^^^^^^^ help: use parentheses instead: `align(8)` - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0693]: incorrect `repr(align)` attribute format - --> $DIR/repr-align-assign.rs:9:8 - | -LL | #[repr(align="8")] - | ^^^^^^^^^ help: use parentheses instead: `align(8)` - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0693`. diff --git a/tests/ui/repr/repr-align.rs b/tests/ui/repr/repr-align.rs index 33aa727d4bd0e..6b60a765461ab 100644 --- a/tests/ui/repr/repr-align.rs +++ b/tests/ui/repr/repr-align.rs @@ -1,41 +1,33 @@ #![allow(dead_code)] #[repr(align(16.0))] //~ ERROR: invalid `repr(align)` attribute: not an unsuffixed integer - //~| ERROR: invalid `repr(align)` attribute: not an unsuffixed integer struct S0(i32); #[repr(align(15))] //~ ERROR: invalid `repr(align)` attribute: not a power of two - //~| ERROR: invalid `repr(align)` attribute: not a power of two struct S1(i32); #[repr(align(4294967296))] //~ ERROR: invalid `repr(align)` attribute: larger than 2^29 - //~| ERROR: invalid `repr(align)` attribute: larger than 2^29 struct S2(i32); #[repr(align(536870912))] // ok: this is the largest accepted alignment struct S3(i32); #[repr(align(0))] //~ ERROR: invalid `repr(align)` attribute: not a power of two - //~| ERROR: invalid `repr(align)` attribute: not a power of two struct S4(i32); #[repr(align(16.0))] //~ ERROR: invalid `repr(align)` attribute: not an unsuffixed integer - //~| ERROR: invalid `repr(align)` attribute: not an unsuffixed integer enum E0 { A, B } #[repr(align(15))] //~ ERROR: invalid `repr(align)` attribute: not a power of two - //~| ERROR: invalid `repr(align)` attribute: not a power of two enum E1 { A, B } #[repr(align(4294967296))] //~ ERROR: invalid `repr(align)` attribute: larger than 2^29 - //~| ERROR: invalid `repr(align)` attribute: larger than 2^29 enum E2 { A, B } #[repr(align(536870912))] // ok: this is the largest accepted alignment enum E3 { A, B } #[repr(align(0))] //~ ERROR: invalid `repr(align)` attribute: not a power of two - //~| ERROR: invalid `repr(align)` attribute: not a power of two enum E4 { A, B } fn main() {} diff --git a/tests/ui/repr/repr-align.stderr b/tests/ui/repr/repr-align.stderr index 660247840c48a..fe919e30b1534 100644 --- a/tests/ui/repr/repr-align.stderr +++ b/tests/ui/repr/repr-align.stderr @@ -5,111 +5,47 @@ LL | #[repr(align(16.0))] | ^^^^ error[E0589]: invalid `repr(align)` attribute: not a power of two - --> $DIR/repr-align.rs:7:14 + --> $DIR/repr-align.rs:6:14 | LL | #[repr(align(15))] | ^^ error[E0589]: invalid `repr(align)` attribute: larger than 2^29 - --> $DIR/repr-align.rs:11:14 + --> $DIR/repr-align.rs:9:14 | LL | #[repr(align(4294967296))] | ^^^^^^^^^^ error[E0589]: invalid `repr(align)` attribute: not a power of two - --> $DIR/repr-align.rs:18:14 + --> $DIR/repr-align.rs:15:14 | LL | #[repr(align(0))] | ^ error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer - --> $DIR/repr-align.rs:22:14 - | -LL | #[repr(align(16.0))] - | ^^^^ - -error[E0589]: invalid `repr(align)` attribute: not a power of two - --> $DIR/repr-align.rs:26:14 - | -LL | #[repr(align(15))] - | ^^ - -error[E0589]: invalid `repr(align)` attribute: larger than 2^29 - --> $DIR/repr-align.rs:30:14 - | -LL | #[repr(align(4294967296))] - | ^^^^^^^^^^ - -error[E0589]: invalid `repr(align)` attribute: not a power of two - --> $DIR/repr-align.rs:37:14 - | -LL | #[repr(align(0))] - | ^ - -error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer - --> $DIR/repr-align.rs:3:14 - | -LL | #[repr(align(16.0))] - | ^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0589]: invalid `repr(align)` attribute: not a power of two - --> $DIR/repr-align.rs:7:14 - | -LL | #[repr(align(15))] - | ^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0589]: invalid `repr(align)` attribute: larger than 2^29 - --> $DIR/repr-align.rs:11:14 - | -LL | #[repr(align(4294967296))] - | ^^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0589]: invalid `repr(align)` attribute: not a power of two --> $DIR/repr-align.rs:18:14 | -LL | #[repr(align(0))] - | ^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer - --> $DIR/repr-align.rs:22:14 - | LL | #[repr(align(16.0))] | ^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0589]: invalid `repr(align)` attribute: not a power of two - --> $DIR/repr-align.rs:26:14 + --> $DIR/repr-align.rs:21:14 | LL | #[repr(align(15))] | ^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0589]: invalid `repr(align)` attribute: larger than 2^29 - --> $DIR/repr-align.rs:30:14 + --> $DIR/repr-align.rs:24:14 | LL | #[repr(align(4294967296))] | ^^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0589]: invalid `repr(align)` attribute: not a power of two - --> $DIR/repr-align.rs:37:14 + --> $DIR/repr-align.rs:30:14 | LL | #[repr(align(0))] | ^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 16 previous errors +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0589`. diff --git a/tests/ui/repr/repr-transparent.stderr b/tests/ui/repr/repr-transparent.stderr index d0c78a8418a09..2cafb989bce5e 100644 --- a/tests/ui/repr/repr-transparent.stderr +++ b/tests/ui/repr/repr-transparent.stderr @@ -35,10 +35,10 @@ LL | struct GenericAlign(ZstAlign32, u32); | needs at most one field with non-trivial size or alignment, but has 2 error[E0084]: unsupported representation for zero-variant enum - --> $DIR/repr-transparent.rs:47:1 + --> $DIR/repr-transparent.rs:47:8 | LL | #[repr(transparent)] - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^ LL | enum Void {} | --------- zero-variant enum diff --git a/tests/ui/stability-attribute/stability-attribute-sanity.rs b/tests/ui/stability-attribute/stability-attribute-sanity.rs index 7857a0603bd45..f46e35e1a72af 100644 --- a/tests/ui/stability-attribute/stability-attribute-sanity.rs +++ b/tests/ui/stability-attribute/stability-attribute-sanity.rs @@ -8,16 +8,16 @@ mod bogus_attribute_types_1 { #[stable(feature = "a", since = "4.4.4", reason)] //~ ERROR unknown meta item 'reason' [E0541] fn f1() { } - #[stable(feature = "a", since)] //~ ERROR incorrect meta item [E0539] + #[stable(feature = "a", since)] //~ ERROR expected a quoted string literal [E0539] fn f2() { } - #[stable(feature, since = "3.3.3")] //~ ERROR incorrect meta item [E0539] + #[stable(feature, since = "3.3.3")] //~ ERROR expected a quoted string literal [E0539] fn f3() { } - #[stable(feature = "a", since(b))] //~ ERROR incorrect meta item [E0539] + #[stable(feature = "a", since(b))] //~ ERROR expected a quoted string literal [E0539] fn f5() { } - #[stable(feature(b), since = "3.3.3")] //~ ERROR incorrect meta item [E0539] + #[stable(feature(b), since = "3.3.3")] //~ ERROR expected a quoted string literal [E0539] fn f6() { } } diff --git a/tests/ui/stability-attribute/stability-attribute-sanity.stderr b/tests/ui/stability-attribute/stability-attribute-sanity.stderr index c614fc2b9f7fa..2e2b5b509c896 100644 --- a/tests/ui/stability-attribute/stability-attribute-sanity.stderr +++ b/tests/ui/stability-attribute/stability-attribute-sanity.stderr @@ -1,40 +1,28 @@ -error: multiple `deprecated` attributes - --> $DIR/stability-attribute-sanity.rs:62:1 - | -LL | #[deprecated(since = "5.5.5", note = "text")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute - | -note: attribute also specified here - --> $DIR/stability-attribute-sanity.rs:61:1 - | -LL | #[deprecated(since = "5.5.5", note = "text")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error[E0541]: unknown meta item 'reason' --> $DIR/stability-attribute-sanity.rs:8:46 | LL | #[stable(feature = "a", since = "4.4.4", reason)] | ^^^^^^ expected one of `feature`, `since` -error[E0539]: incorrect meta item +error[E0539]: expected a quoted string literal --> $DIR/stability-attribute-sanity.rs:11:29 | LL | #[stable(feature = "a", since)] | ^^^^^ -error[E0539]: incorrect meta item +error[E0539]: expected a quoted string literal --> $DIR/stability-attribute-sanity.rs:14:14 | LL | #[stable(feature, since = "3.3.3")] | ^^^^^^^ -error[E0539]: incorrect meta item +error[E0539]: expected a quoted string literal --> $DIR/stability-attribute-sanity.rs:17:29 | LL | #[stable(feature = "a", since(b))] | ^^^^^^^^ -error[E0539]: incorrect meta item +error[E0539]: expected a quoted string literal --> $DIR/stability-attribute-sanity.rs:20:14 | LL | #[stable(feature(b), since = "3.3.3")] @@ -100,6 +88,18 @@ error: 'since' must be a Rust version number, such as "1.31.0" LL | #[stable(feature = "e", since = "b")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: multiple `deprecated` attributes + --> $DIR/stability-attribute-sanity.rs:62:1 + | +LL | #[deprecated(since = "5.5.5", note = "text")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/stability-attribute-sanity.rs:61:1 + | +LL | #[deprecated(since = "5.5.5", note = "text")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0544]: multiple stability levels --> $DIR/stability-attribute-sanity.rs:64:1 |