Merge pull request #4206 from RalfJung/rustup

Rustup
This commit is contained in:
Ralf Jung 2025-02-25 07:43:16 +00:00 committed by GitHub
commit cfb827804f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
405 changed files with 6958 additions and 6093 deletions

View file

@ -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",
@ -4571,6 +4580,7 @@ dependencies = [
"itertools",
"minifier",
"pulldown-cmark 0.9.6",
"pulldown-cmark-escape",
"regex",
"rinja",
"rustdoc-json-types",

View file

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

View file

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

View file

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

View file

@ -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, &param.attrs);
self.lower_attrs(hir_id, &param.attrs, param.span);
hir::Param {
hir_id,
pat: self.lower_pat(&param.pat),

View file

@ -45,6 +45,7 @@ use std::sync::Arc;
use rustc_ast::node_id::NodeMap;
use rustc_ast::{self as ast, *};
use rustc_attr_parsing::{AttributeParser, OmitDoc};
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::sorted_map::SortedMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
@ -60,7 +61,8 @@ use rustc_macros::extension;
use rustc_middle::span_bug;
use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
use rustc_session::parse::{add_feature_diagnostics, feature_err};
use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym};
use rustc_span::symbol::{Ident, Symbol, kw, sym};
use rustc_span::{DUMMY_SP, DesugaringKind, Span};
use smallvec::{SmallVec, smallvec};
use thin_vec::ThinVec;
use tracing::{debug, instrument, trace};
@ -137,10 +139,13 @@ struct LoweringContext<'a, 'hir> {
allow_async_iterator: Arc<[Symbol]>,
allow_for_await: Arc<[Symbol]>,
allow_async_fn_traits: Arc<[Symbol]>,
attribute_parser: AttributeParser<'hir>,
}
impl<'a, 'hir> LoweringContext<'a, 'hir> {
fn new(tcx: TyCtxt<'hir>, resolver: &'a mut ResolverAstLowering) -> Self {
let registered_tools = tcx.registered_tools(()).iter().map(|x| x.name).collect();
Self {
// Pseudo-globals.
tcx,
@ -181,6 +186,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// FIXME(gen_blocks): how does `closure_track_caller`/`async_fn_track_caller`
// interact with `gen`/`async gen` blocks
allow_async_iterator: [sym::gen_future, sym::async_iterator].into(),
attribute_parser: AttributeParser::new(tcx.sess, tcx.features(), registered_tools),
}
}
@ -216,7 +223,6 @@ impl ResolverAstLowering {
None
}
/// Obtains resolution for a `NodeId` with a single resolution.
fn get_partial_res(&self, id: NodeId) -> Option<PartialRes> {
self.partial_res_map.get(&id).copied()
}
@ -855,45 +861,38 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
ret
}
fn lower_attrs(&mut self, id: HirId, attrs: &[Attribute]) -> &'hir [hir::Attribute] {
fn lower_attrs(
&mut self,
id: HirId,
attrs: &[Attribute],
target_span: Span,
) -> &'hir [hir::Attribute] {
if attrs.is_empty() {
&[]
} else {
let lowered_attrs = self.lower_attrs_vec(attrs, self.lower_span(target_span));
debug_assert_eq!(id.owner, self.current_hir_id_owner);
let ret = self.arena.alloc_from_iter(attrs.iter().map(|a| self.lower_attr(a)));
debug_assert!(!ret.is_empty());
self.attrs.insert(id.local_id, ret);
ret
let ret = self.arena.alloc_from_iter(lowered_attrs);
// this is possible if an item contained syntactical attribute,
// but none of them parse succesfully or all of them were ignored
// for not being built-in attributes at all. They could be remaining
// unexpanded attributes used as markers in proc-macro derives for example.
// This will have emitted some diagnostics for the misparse, but will then
// not emit the attribute making the list empty.
if ret.is_empty() {
&[]
} else {
self.attrs.insert(id.local_id, ret);
ret
}
}
}
fn lower_attr(&self, attr: &Attribute) -> hir::Attribute {
// Note that we explicitly do not walk the path. Since we don't really
// lower attributes (we use the AST version) there is nowhere to keep
// the `HirId`s. We don't actually need HIR version of attributes anyway.
// Tokens are also not needed after macro expansion and parsing.
let kind = match attr.kind {
AttrKind::Normal(ref normal) => hir::AttrKind::Normal(Box::new(hir::AttrItem {
unsafety: self.lower_safety(normal.item.unsafety, hir::Safety::Safe),
path: hir::AttrPath {
segments: normal
.item
.path
.segments
.iter()
.map(|i| i.ident)
.collect::<Vec<_>>()
.into_boxed_slice(),
span: normal.item.path.span,
},
args: self.lower_attr_args(&normal.item.args),
})),
AttrKind::DocComment(comment_kind, data) => {
hir::AttrKind::DocComment(comment_kind, data)
}
};
hir::Attribute { kind, id: attr.id, style: attr.style, span: self.lower_span(attr.span) }
fn lower_attrs_vec(&self, attrs: &[Attribute], target_span: Span) -> Vec<hir::Attribute> {
self.attribute_parser
.parse_attribute_list(attrs, target_span, OmitDoc::Lower, |s| self.lower_span(s))
}
fn alias_attrs(&mut self, id: HirId, target_id: HirId) {
@ -905,34 +904,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
}
fn lower_attr_args(&self, args: &AttrArgs) -> hir::AttrArgs {
match args {
AttrArgs::Empty => hir::AttrArgs::Empty,
AttrArgs::Delimited(args) => hir::AttrArgs::Delimited(self.lower_delim_args(args)),
// This is an inert key-value attribute - it will never be visible to macros
// after it gets lowered to HIR. Therefore, we can extract literals to handle
// nonterminals in `#[doc]` (e.g. `#[doc = $e]`).
&AttrArgs::Eq { eq_span, ref expr } => {
// In valid code the value always ends up as a single literal. Otherwise, a dummy
// literal suffices because the error is handled elsewhere.
let lit = if let ExprKind::Lit(token_lit) = expr.kind
&& let Ok(lit) = MetaItemLit::from_token_lit(token_lit, expr.span)
{
lit
} else {
let guar = self.dcx().has_errors().unwrap();
MetaItemLit {
symbol: kw::Empty,
suffix: None,
kind: LitKind::Err(guar),
span: DUMMY_SP,
}
};
hir::AttrArgs::Eq { eq_span, expr: lit }
}
}
}
fn lower_delim_args(&self, args: &DelimArgs) -> DelimArgs {
DelimArgs { dspan: args.dspan, delim: args.delim, tokens: args.tokens.flattened() }
}
@ -1845,7 +1816,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let (name, kind) = self.lower_generic_param_kind(param, source);
let hir_id = self.lower_node_id(param.id);
self.lower_attrs(hir_id, &param.attrs);
self.lower_attrs(hir_id, &param.attrs, param.span());
hir::GenericParam {
hir_id,
def_id: self.local_def_id(param.id),

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,9 +1,12 @@
use rustc_abi::Align;
use rustc_ast as ast;
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
use rustc_ast::token::CommentKind;
use rustc_ast::{self as ast, AttrStyle};
use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute};
use rustc_span::hygiene::Transparency;
use rustc_span::{Span, Symbol};
use thin_vec::ThinVec;
use crate::RustcVersion;
use crate::{DefaultBodyStability, PartialConstStability, PrintAttribute, RustcVersion, Stability};
#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
pub enum InlineAttr {
@ -54,7 +57,7 @@ impl OptimizeAttr {
}
}
#[derive(Clone, Debug, Encodable, Decodable)]
#[derive(Clone, Debug, Encodable, Decodable, HashStable_Generic, PrintAttribute)]
pub enum DiagnosticAttribute {
// tidy-alphabetical-start
DoNotRecommend,
@ -62,7 +65,7 @@ pub enum DiagnosticAttribute {
// tidy-alphabetical-end
}
#[derive(PartialEq, Debug, Encodable, Decodable, Copy, Clone)]
#[derive(PartialEq, Debug, Encodable, Decodable, Copy, Clone, HashStable_Generic, PrintAttribute)]
pub enum ReprAttr {
ReprInt(IntType),
ReprRust,
@ -71,6 +74,8 @@ pub enum ReprAttr {
ReprSimd,
ReprTransparent,
ReprAlign(Align),
// this one is just so we can emit a lint for it
ReprEmpty,
}
pub use ReprAttr::*;
@ -80,13 +85,13 @@ pub enum TransparencyError {
}
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
#[derive(Encodable, Decodable)]
#[derive(Encodable, Decodable, HashStable_Generic, PrintAttribute)]
pub enum IntType {
SignedInt(ast::IntTy),
UnsignedInt(ast::UintTy),
}
#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)]
#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic, PrintAttribute)]
pub struct Deprecation {
pub since: DeprecatedSince,
/// The note to issue a reason.
@ -98,7 +103,7 @@ pub struct Deprecation {
}
/// Release in which an API is deprecated.
#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)]
#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic, PrintAttribute)]
pub enum DeprecatedSince {
RustcVersion(RustcVersion),
/// Deprecated in the future ("to be determined").
@ -132,3 +137,61 @@ impl Deprecation {
matches!(self.since, DeprecatedSince::RustcVersion(_))
}
}
/// Attributes represent parsed, *built in*, inert attributes. That means,
/// attributes that are not actually ever expanded.
/// For more information on this, see the module docs on the rustc_attr_parsing crate.
/// They're instead used as markers, to guide the compilation process in various way in most every stage of the compiler.
/// These are kept around after the AST, into the HIR and further on.
///
/// The word parsed could be a little misleading here, because the parser already parses
/// attributes early on. However, the result, an [`ast::Attribute`]
/// is only parsed at a high level, still containing a token stream in many cases. That is
/// because the structure of the contents varies from attribute to attribute.
/// With a parsed attribute I mean that each attribute is processed individually into a
/// final structure, which on-site (the place where the attribute is useful for, think the
/// the place where `must_use` is checked) little to no extra parsing or validating needs to
/// happen.
///
/// For more docs, look in [`rustc_attr`](https://doc.rust-lang.org/stable/nightly-rustc/rustc_attr/index.html)
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
pub enum AttributeKind {
// tidy-alphabetical-start
AllowConstFnUnstable(ThinVec<Symbol>),
AllowInternalUnstable(ThinVec<(Symbol, Span)>),
BodyStability {
stability: DefaultBodyStability,
/// Span of the `#[rustc_default_body_unstable(...)]` attribute
span: Span,
},
Confusables {
symbols: ThinVec<Symbol>,
// FIXME(jdonszelmann): remove when target validation code is moved
first_span: Span,
},
ConstStability {
stability: PartialConstStability,
/// Span of the `#[rustc_const_stable(...)]` or `#[rustc_const_unstable(...)]` attribute
span: Span,
},
ConstStabilityIndirect,
Deprecation {
deprecation: Deprecation,
span: Span,
},
Diagnostic(DiagnosticAttribute),
DocComment {
style: AttrStyle,
kind: CommentKind,
span: Span,
comment: Symbol,
},
MacroTransparency(Transparency),
Repr(ThinVec<(ReprAttr, Span)>),
Stability {
stability: Stability,
/// Span of the `#[stable(...)]` or `#[unstable(...)]` attribute
span: Span,
},
// tidy-alphabetical-end
}

View file

@ -10,7 +10,142 @@ mod attributes;
mod stability;
mod version;
use std::num::NonZero;
pub use attributes::*;
pub(crate) use rustc_session::HashStableContext;
use rustc_abi::Align;
use rustc_ast::token::CommentKind;
use rustc_ast::{AttrStyle, IntTy, UintTy};
use rustc_ast_pretty::pp::Printer;
use rustc_span::hygiene::Transparency;
use rustc_span::{Span, Symbol};
pub use stability::*;
use thin_vec::ThinVec;
pub use version::*;
/// Requirements for a `StableHashingContext` to be used in this crate.
/// This is a hack to allow using the `HashStable_Generic` derive macro
/// instead of implementing everything in `rustc_middle`.
pub trait HashStableContext: rustc_ast::HashStableContext + rustc_abi::HashStableContext {}
/// This trait is used to print attributes in `rustc_hir_pretty`.
///
/// For structs and enums it can be derived using [`rustc_macros::PrintAttribute`].
/// The output will look a lot like a `Debug` implementation, but fields of several types
/// like [`Span`]s and empty tuples, are gracefully skipped so they don't clutter the
/// representation much.
pub trait PrintAttribute {
fn print_something(&self) -> bool;
fn print_attribute(&self, p: &mut Printer);
}
impl<T: PrintAttribute> PrintAttribute for &T {
fn print_something(&self) -> bool {
T::print_something(self)
}
fn print_attribute(&self, p: &mut Printer) {
T::print_attribute(self, p)
}
}
impl<T: PrintAttribute> PrintAttribute for Option<T> {
fn print_something(&self) -> bool {
self.as_ref().is_some_and(|x| x.print_something())
}
fn print_attribute(&self, p: &mut Printer) {
if let Some(i) = self {
T::print_attribute(i, p)
}
}
}
impl<T: PrintAttribute> PrintAttribute for ThinVec<T> {
fn print_something(&self) -> bool {
self.is_empty() || self[0].print_something()
}
fn print_attribute(&self, p: &mut Printer) {
let mut last_printed = false;
p.word("[");
for i in self {
if last_printed {
p.word_space(",");
}
i.print_attribute(p);
last_printed = i.print_something();
}
p.word("]");
}
}
macro_rules! print_skip {
($($t: ty),* $(,)?) => {$(
impl PrintAttribute for $t {
fn print_something(&self) -> bool { false }
fn print_attribute(&self, _: &mut Printer) { }
})*
};
}
macro_rules! print_disp {
($($t: ty),* $(,)?) => {$(
impl PrintAttribute for $t {
fn print_something(&self) -> bool { true }
fn print_attribute(&self, p: &mut Printer) {
p.word(format!("{}", self));
}
}
)*};
}
macro_rules! print_debug {
($($t: ty),* $(,)?) => {$(
impl PrintAttribute for $t {
fn print_something(&self) -> bool { true }
fn print_attribute(&self, p: &mut Printer) {
p.word(format!("{:?}", self));
}
}
)*};
}
macro_rules! print_tup {
(num_print_something $($ts: ident)*) => { 0 $(+ $ts.print_something() as usize)* };
() => {};
($t: ident $($ts: ident)*) => {
#[allow(non_snake_case, unused)]
impl<$t: PrintAttribute, $($ts: PrintAttribute),*> PrintAttribute for ($t, $($ts),*) {
fn print_something(&self) -> bool {
let ($t, $($ts),*) = self;
print_tup!(num_print_something $t $($ts)*) != 0
}
fn print_attribute(&self, p: &mut Printer) {
let ($t, $($ts),*) = self;
let parens = print_tup!(num_print_something $t $($ts)*) > 1;
if parens {
p.word("(");
}
let mut printed_anything = $t.print_something();
$t.print_attribute(p);
$(
if printed_anything && $ts.print_something() {
p.word_space(",");
printed_anything = true;
}
$ts.print_attribute(p);
)*
if parens {
p.word(")");
}
}
}
print_tup!($($ts)*);
};
}
print_tup!(A B C D E F G H);
print_skip!(Span, ());
print_disp!(Symbol, u16, bool, NonZero<u32>);
print_debug!(UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency);

View file

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

View file

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

View file

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

View file

@ -6,6 +6,8 @@ attr_parsing_deprecated_item_suggestion =
.help = add `#![feature(deprecated_suggestion)]` to the crate root
.note = see #94785 for more details
attr_parsing_empty_confusables =
expected at least one confusable name
attr_parsing_expected_one_cfg_pattern =
expected 1 cfg-pattern
@ -21,8 +23,8 @@ attr_parsing_expects_feature_list =
attr_parsing_expects_features =
`{$name}` expects feature names
attr_parsing_incorrect_meta_item =
incorrect meta item
attr_parsing_incorrect_meta_item = expected a quoted string literal
attr_parsing_incorrect_meta_item_suggestion = consider surrounding this with quotes
attr_parsing_incorrect_repr_format_align_one_arg =
incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses
@ -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

View file

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

View file

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

View file

@ -1,21 +1,58 @@
//! Parsing and validation of builtin attributes
use rustc_attr_data_structures::AttributeKind;
use rustc_span::{Span, Symbol, sym};
use thin_vec::ThinVec;
use rustc_ast::MetaItemInner;
use rustc_ast::attr::AttributeExt;
use rustc_span::Symbol;
use super::{AcceptMapping, AttributeParser};
use crate::context::FinalizeContext;
use crate::session_diagnostics;
/// Read the content of a `rustc_confusables` attribute, and return the list of candidate names.
pub fn parse_confusables(attr: &impl AttributeExt) -> Option<Vec<Symbol>> {
let metas = attr.meta_item_list()?;
let mut candidates = Vec::new();
for meta in metas {
let MetaItemInner::Lit(meta_lit) = meta else {
return None;
};
candidates.push(meta_lit.symbol);
}
Some(candidates)
#[derive(Default)]
pub(crate) struct ConfusablesParser {
confusables: ThinVec<Symbol>,
first_span: Option<Span>,
}
impl AttributeParser for ConfusablesParser {
const ATTRIBUTES: AcceptMapping<Self> = &[(&[sym::rustc_confusables], |this, cx, args| {
let Some(list) = args.list() else {
// FIXME(jdonszelmann): error when not a list? Bring validation code here.
// NOTE: currently subsequent attributes are silently ignored using
// tcx.get_attr().
return;
};
if list.is_empty() {
cx.emit_err(session_diagnostics::EmptyConfusables { span: cx.attr_span });
}
for param in list.mixed() {
let span = param.span();
let Some(lit) = param.lit() else {
cx.emit_err(session_diagnostics::IncorrectMetaItem {
span,
suggestion: Some(session_diagnostics::IncorrectMetaItemSuggestion {
lo: span.shrink_to_lo(),
hi: span.shrink_to_hi(),
}),
});
continue;
};
this.confusables.push(lit.symbol);
}
this.first_span.get_or_insert(cx.attr_span);
})];
fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
if self.confusables.is_empty() {
return None;
}
Some(AttributeKind::Confusables {
symbols: self.confusables,
first_span: self.first_span.unwrap(),
})
}
}

View file

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

View file

@ -1,17 +1,152 @@
mod allow_unstable;
mod cfg;
mod confusables;
mod deprecation;
mod repr;
mod stability;
mod transparency;
//! This module defines traits for attribute parsers, little state machines that recognize and parse
//! attributes out of a longer list of attributes. The main trait is called [`AttributeParser`].
//! You can find more docs about [`AttributeParser`]s on the trait itself.
//! However, for many types of attributes, implementing [`AttributeParser`] is not necessary.
//! It allows for a lot of flexibility you might not want.
//!
//! Specifically, you might not care about managing the state of your [`AttributeParser`]
//! state machine yourself. In this case you can choose to implement:
//!
//! - [`SingleAttributeParser`]: makes it easy to implement an attribute which should error if it
//! appears more than once in a list of attributes
//! - [`CombineAttributeParser`]: makes it easy to implement an attribute which should combine the
//! contents of attributes, if an attribute appear multiple times in a list
//!
//! Attributes should be added to [`ATTRIBUTE_MAPPING`](crate::context::ATTRIBUTE_MAPPING) to be parsed.
pub mod util;
use std::marker::PhantomData;
pub use allow_unstable::*;
pub use cfg::*;
pub use confusables::*;
pub use deprecation::*;
pub use repr::*;
pub use stability::*;
pub use transparency::*;
use rustc_attr_data_structures::AttributeKind;
use rustc_span::Span;
use thin_vec::ThinVec;
use crate::context::{AcceptContext, FinalizeContext};
use crate::parser::ArgParser;
pub(crate) mod allow_unstable;
pub(crate) mod cfg;
pub(crate) mod confusables;
pub(crate) mod deprecation;
pub(crate) mod repr;
pub(crate) mod stability;
pub(crate) mod transparency;
pub(crate) mod util;
type AcceptFn<T> = fn(&mut T, &AcceptContext<'_>, &ArgParser<'_>);
type AcceptMapping<T> = &'static [(&'static [rustc_span::Symbol], AcceptFn<T>)];
/// An [`AttributeParser`] is a type which searches for syntactic attributes.
///
/// Parsers are often tiny state machines that gets to see all syntactical attributes on an item.
/// [`Default::default`] creates a fresh instance that sits in some kind of initial state, usually that the
/// attribute it is looking for was not yet seen.
///
/// Then, it defines what paths this group will accept in [`AttributeParser::ATTRIBUTES`].
/// These are listed as pairs, of symbols and function pointers. The function pointer will
/// be called when that attribute is found on an item, which can influence the state of the little
/// state machine.
///
/// Finally, after all attributes on an item have been seen, and possibly been accepted,
/// the [`finalize`](AttributeParser::finalize) functions for all attribute parsers are called. Each can then report
/// whether it has seen the attribute it has been looking for.
///
/// The state machine is automatically reset to parse attributes on the next item.
pub(crate) trait AttributeParser: Default + 'static {
/// The symbols for the attributes that this parser is interested in.
///
/// If an attribute has this symbol, the `accept` function will be called on it.
const ATTRIBUTES: AcceptMapping<Self>;
/// The parser has gotten a chance to accept the attributes on an item,
/// here it can produce an attribute.
fn finalize(self, cx: &FinalizeContext<'_>) -> Option<AttributeKind>;
}
/// Alternative to [`AttributeParser`] that automatically handles state management.
/// A slightly simpler and more restricted way to convert attributes.
/// Assumes that an attribute can only appear a single time on an item,
/// and errors when it sees more.
///
/// [`Single<T> where T: SingleAttributeParser`](Single) implements [`AttributeParser`].
///
/// [`SingleAttributeParser`] can only convert attributes one-to-one, and cannot combine multiple
/// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example.
pub(crate) trait SingleAttributeParser: 'static {
const PATH: &'static [rustc_span::Symbol];
/// Caled when a duplicate attribute is found.
///
/// `first_span` is the span of the first occurrence of this attribute.
// FIXME(jdonszelmann): default error
fn on_duplicate(cx: &AcceptContext<'_>, first_span: Span);
/// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`]
fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind>;
}
pub(crate) struct Single<T: SingleAttributeParser>(PhantomData<T>, Option<(AttributeKind, Span)>);
impl<T: SingleAttributeParser> Default for Single<T> {
fn default() -> Self {
Self(Default::default(), Default::default())
}
}
impl<T: SingleAttributeParser> AttributeParser for Single<T> {
const ATTRIBUTES: AcceptMapping<Self> = &[(T::PATH, |group: &mut Single<T>, cx, args| {
if let Some((_, s)) = group.1 {
T::on_duplicate(cx, s);
return;
}
if let Some(pa) = T::convert(cx, args) {
group.1 = Some((pa, cx.attr_span));
}
})];
fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
Some(self.1?.0)
}
}
type ConvertFn<E> = fn(ThinVec<E>) -> AttributeKind;
/// Alternative to [`AttributeParser`] that automatically handles state management.
/// If multiple attributes appear on an element, combines the values of each into a
/// [`ThinVec`].
/// [`Combine<T> where T: CombineAttributeParser`](Combine) implements [`AttributeParser`].
///
/// [`CombineAttributeParser`] can only convert a single kind of attribute, and cannot combine multiple
/// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example.
pub(crate) trait CombineAttributeParser: 'static {
const PATH: &'static [rustc_span::Symbol];
type Item;
const CONVERT: ConvertFn<Self::Item>;
/// Converts a single syntactical attribute to a number of elements of the semantic attribute, or [`AttributeKind`]
fn extend<'a>(
cx: &'a AcceptContext<'a>,
args: &'a ArgParser<'a>,
) -> impl IntoIterator<Item = Self::Item> + 'a;
}
pub(crate) struct Combine<T: CombineAttributeParser>(
PhantomData<T>,
ThinVec<<T as CombineAttributeParser>::Item>,
);
impl<T: CombineAttributeParser> Default for Combine<T> {
fn default() -> Self {
Self(Default::default(), Default::default())
}
}
impl<T: CombineAttributeParser> AttributeParser for Combine<T> {
const ATTRIBUTES: AcceptMapping<Self> =
&[(T::PATH, |group: &mut Combine<T>, cx, args| group.1.extend(T::extend(cx, args)))];
fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
if self.1.is_empty() { None } else { Some(T::CONVERT(self.1)) }
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,348 @@
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::ops::Deref;
use std::sync::LazyLock;
use rustc_ast::{self as ast, DelimArgs};
use rustc_attr_data_structures::AttributeKind;
use rustc_errors::{DiagCtxtHandle, Diagnostic};
use rustc_feature::Features;
use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId};
use rustc_session::Session;
use rustc_span::symbol::kw;
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
use crate::attributes::confusables::ConfusablesParser;
use crate::attributes::deprecation::DeprecationParser;
use crate::attributes::repr::ReprParser;
use crate::attributes::stability::{
BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser,
};
use crate::attributes::transparency::TransparencyParser;
use crate::attributes::{AttributeParser as _, Combine, Single};
use crate::parser::{ArgParser, MetaItemParser};
macro_rules! attribute_groups {
(
pub(crate) static $name: ident = [$($names: ty),* $(,)?];
) => {
pub(crate) static $name: LazyLock<(
BTreeMap<&'static [Symbol], Vec<Box<dyn Fn(&AcceptContext<'_>, &ArgParser<'_>) + Send + Sync>>>,
Vec<Box<dyn Send + Sync + Fn(&FinalizeContext<'_>) -> Option<AttributeKind>>>
)> = LazyLock::new(|| {
let mut accepts = BTreeMap::<_, Vec<Box<dyn Fn(&AcceptContext<'_>, &ArgParser<'_>) + Send + Sync>>>::new();
let mut finalizes = Vec::<Box<dyn Send + Sync + Fn(&FinalizeContext<'_>) -> Option<AttributeKind>>>::new();
$(
{
thread_local! {
static STATE_OBJECT: RefCell<$names> = RefCell::new(<$names>::default());
};
for (k, v) in <$names>::ATTRIBUTES {
accepts.entry(*k).or_default().push(Box::new(|cx, args| {
STATE_OBJECT.with_borrow_mut(|s| {
v(s, cx, args)
})
}));
}
finalizes.push(Box::new(|cx| {
let state = STATE_OBJECT.take();
state.finalize(cx)
}));
}
)*
(accepts, finalizes)
});
};
}
attribute_groups!(
pub(crate) static ATTRIBUTE_MAPPING = [
// tidy-alphabetical-start
BodyStabilityParser,
ConfusablesParser,
ConstStabilityParser,
StabilityParser,
// tidy-alphabetical-end
// tidy-alphabetical-start
Combine<AllowConstFnUnstableParser>,
Combine<AllowInternalUnstableParser>,
Combine<ReprParser>,
// tidy-alphabetical-end
// tidy-alphabetical-start
Single<ConstStabilityIndirectParser>,
Single<DeprecationParser>,
Single<TransparencyParser>,
// tidy-alphabetical-end
];
);
/// Context given to every attribute parser when accepting
///
/// Gives [`AttributeParser`]s enough information to create errors, for example.
pub(crate) struct AcceptContext<'a> {
pub(crate) group_cx: &'a FinalizeContext<'a>,
/// The span of the attribute currently being parsed
pub(crate) attr_span: Span,
}
impl<'a> AcceptContext<'a> {
pub(crate) fn emit_err(&self, diag: impl Diagnostic<'a>) -> ErrorGuaranteed {
if self.limit_diagnostics {
self.dcx().create_err(diag).delay_as_bug()
} else {
self.dcx().emit_err(diag)
}
}
}
impl<'a> Deref for AcceptContext<'a> {
type Target = FinalizeContext<'a>;
fn deref(&self) -> &Self::Target {
&self.group_cx
}
}
/// Context given to every attribute parser during finalization.
///
/// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create errors, for example.
pub(crate) struct FinalizeContext<'a> {
/// The parse context, gives access to the session and the
/// diagnostics context.
pub(crate) cx: &'a AttributeParser<'a>,
/// The span of the syntactical component this attribute was applied to
pub(crate) target_span: Span,
}
impl<'a> Deref for FinalizeContext<'a> {
type Target = AttributeParser<'a>;
fn deref(&self) -> &Self::Target {
&self.cx
}
}
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum OmitDoc {
Lower,
Skip,
}
/// Context created once, for example as part of the ast lowering
/// context, through which all attributes can be lowered.
pub struct AttributeParser<'sess> {
#[expect(dead_code)] // FIXME(jdonszelmann): needed later to verify we parsed all attributes
tools: Vec<Symbol>,
sess: &'sess Session,
features: Option<&'sess Features>,
/// *only* parse attributes with this symbol.
///
/// Used in cases where we want the lowering infrastructure for
/// parse just a single attribute.
parse_only: Option<Symbol>,
/// Can be used to instruct parsers to reduce the number of diagnostics it emits.
/// Useful when using `parse_limited` and you know the attr will be reparsed later.
pub(crate) limit_diagnostics: bool,
}
impl<'sess> AttributeParser<'sess> {
/// This method allows you to parse attributes *before* you have access to features or tools.
/// One example where this is necessary, is to parse `feature` attributes themselves for
/// example.
///
/// Try to use this as little as possible. Attributes *should* be lowered during `rustc_ast_lowering`.
/// Some attributes require access to features to parse, which would crash if you tried to do so
/// through [`parse_limited`](Self::parse_limited).
///
/// To make sure use is limited, supply a `Symbol` you'd like to parse. Only attributes with
/// that symbol are picked out of the list of instructions and parsed. Those are returned.
pub fn parse_limited(
sess: &'sess Session,
attrs: &[ast::Attribute],
sym: Symbol,
target_span: Span,
limit_diagnostics: bool,
) -> Option<Attribute> {
let mut parsed = Self {
sess,
features: None,
tools: Vec::new(),
parse_only: Some(sym),
limit_diagnostics,
}
.parse_attribute_list(attrs, target_span, OmitDoc::Skip, std::convert::identity);
assert!(parsed.len() <= 1);
parsed.pop()
}
pub fn new(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self {
Self { sess, features: Some(features), tools, parse_only: None, limit_diagnostics: false }
}
pub(crate) fn sess(&self) -> &'sess Session {
self.sess
}
pub(crate) fn features(&self) -> &'sess Features {
self.features.expect("features not available at this point in the compiler")
}
pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> {
self.sess.dcx()
}
/// Parse a list of attributes.
///
/// `target_span` is the span of the thing this list of attributes is applied to,
/// and when `omit_doc` is set, doc attributes are filtered out.
pub fn parse_attribute_list<'a>(
&'a self,
attrs: &'a [ast::Attribute],
target_span: Span,
omit_doc: OmitDoc,
lower_span: impl Copy + Fn(Span) -> Span,
) -> Vec<Attribute> {
let mut attributes = Vec::new();
let group_cx = FinalizeContext { cx: self, target_span };
for attr in attrs {
// if we're only looking for a single attribute,
// skip all the ones we don't care about
if let Some(expected) = self.parse_only {
if attr.name_or_empty() != expected {
continue;
}
}
// sometimes, for example for `#![doc = include_str!("readme.md")]`,
// doc still contains a non-literal. You might say, when we're lowering attributes
// that's expanded right? But no, sometimes, when parsing attributes on macros,
// we already use the lowering logic and these are still there. So, when `omit_doc`
// is set we *also* want to ignore these
if omit_doc == OmitDoc::Skip && attr.name_or_empty() == sym::doc {
continue;
}
match &attr.kind {
ast::AttrKind::DocComment(comment_kind, symbol) => {
if omit_doc == OmitDoc::Skip {
continue;
}
attributes.push(Attribute::Parsed(AttributeKind::DocComment {
style: attr.style,
kind: *comment_kind,
span: lower_span(attr.span),
comment: *symbol,
}))
}
// // FIXME: make doc attributes go through a proper attribute parser
// ast::AttrKind::Normal(n) if n.name_or_empty() == sym::doc => {
// let p = GenericMetaItemParser::from_attr(&n, self.dcx());
//
// attributes.push(Attribute::Parsed(AttributeKind::DocComment {
// style: attr.style,
// kind: CommentKind::Line,
// span: attr.span,
// comment: p.args().name_value(),
// }))
// }
ast::AttrKind::Normal(n) => {
let parser = MetaItemParser::from_attr(n, self.dcx());
let (path, args) = parser.deconstruct();
let parts = path.segments().map(|i| i.name).collect::<Vec<_>>();
if let Some(accepts) = ATTRIBUTE_MAPPING.0.get(parts.as_slice()) {
for f in accepts {
let cx = AcceptContext {
group_cx: &group_cx,
attr_span: lower_span(attr.span),
};
f(&cx, &args)
}
} else {
// if we're here, we must be compiling a tool attribute... Or someone forgot to
// parse their fancy new attribute. Let's warn them in any case. If you are that
// person, and you really your attribute should remain unparsed, carefully read the
// documentation in this module and if you still think so you can add an exception
// to this assertion.
// FIXME(jdonszelmann): convert other attributes, and check with this that
// we caught em all
// const FIXME_TEMPORARY_ATTR_ALLOWLIST: &[Symbol] = &[sym::cfg];
// assert!(
// self.tools.contains(&parts[0]) || true,
// // || FIXME_TEMPORARY_ATTR_ALLOWLIST.contains(&parts[0]),
// "attribute {path} wasn't parsed and isn't a know tool attribute",
// );
attributes.push(Attribute::Unparsed(Box::new(AttrItem {
path: AttrPath::from_ast(&n.item.path),
args: self.lower_attr_args(&n.item.args, lower_span),
id: HashIgnoredAttrId { attr_id: attr.id },
style: attr.style,
span: lower_span(attr.span),
})));
}
}
}
}
let mut parsed_attributes = Vec::new();
for f in &ATTRIBUTE_MAPPING.1 {
if let Some(attr) = f(&group_cx) {
parsed_attributes.push(Attribute::Parsed(attr));
}
}
attributes.extend(parsed_attributes);
attributes
}
fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs {
match args {
ast::AttrArgs::Empty => AttrArgs::Empty,
ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(DelimArgs {
dspan: args.dspan,
delim: args.delim,
tokens: args.tokens.flattened(),
}),
// This is an inert key-value attribute - it will never be visible to macros
// after it gets lowered to HIR. Therefore, we can extract literals to handle
// nonterminals in `#[doc]` (e.g. `#[doc = $e]`).
ast::AttrArgs::Eq { eq_span, expr } => {
// In valid code the value always ends up as a single literal. Otherwise, a dummy
// literal suffices because the error is handled elsewhere.
let lit = if let ast::ExprKind::Lit(token_lit) = expr.kind
&& let Ok(lit) =
ast::MetaItemLit::from_token_lit(token_lit, lower_span(expr.span))
{
lit
} else {
let guar = self.dcx().has_errors().unwrap();
ast::MetaItemLit {
symbol: kw::Empty,
suffix: None,
kind: ast::LitKind::Err(guar),
span: DUMMY_SP,
}
};
AttrArgs::Eq { eq_span: lower_span(*eq_span), expr: lit }
}
}
}
}

View file

@ -1,8 +1,79 @@
//! Functions and types dealing with attributes and meta items.
//! Centralized logic for parsing and attributes.
//!
//! FIXME(Centril): For now being, much of the logic is still in `rustc_ast::attr`.
//! The goal is to move the definition of `MetaItem` and things that don't need to be in `syntax`
//! to this crate.
//! Part of a series of crates:
//! - rustc_attr_data_structures: contains types that the parsers parse into
//! - rustc_attr_parsing: this crate
//! - (in the future): rustc_attr_validation
//!
//! History: Check out [#131229](https://github.com/rust-lang/rust/issues/131229).
//! There used to be only one definition of attributes in the compiler: `ast::Attribute`.
//! These were then parsed or validated or both in places distributed all over the compiler.
//! This was a mess...
//!
//! Attributes are markers on items.
//! Many of them are actually attribute-like proc-macros, and are expanded to some other rust syntax.
//! This could either be a user provided proc macro, or something compiler provided.
//! `derive` is an example of one that the compiler provides.
//! These are built-in, but they have a valid expansion to Rust tokens and are thus called "active".
//! I personally like calling these *active* compiler-provided attributes, built-in *macros*,
//! because they still expand, and this helps to differentiate them from built-in *attributes*.
//! However, I'll be the first to admit that the naming here can be confusing.
//!
//! The alternative to active attributes, are inert attributes.
//! These can occur in user code (proc-macro helper attributes).
//! But what's important is, many built-in attributes are inert like this.
//! There is nothing they expand to during the macro expansion process,
//! sometimes because they literally cannot expand to something that is valid Rust.
//! They are really just markers to guide the compilation process.
//! An example is `#[inline(...)]` which changes how code for functions is generated.
//!
//! ```text
//! Active Inert
//! ┌──────────────────────┬──────────────────────┐
//! │ (mostly in) │ these are parsed │
//! │ rustc_builtin_macros │ here! │
//! │ │ │
//! │ │ │
//! │ #[derive(...)] │ #[stable()] │
//! Built-in │ #[cfg()] │ #[inline()] │
//! │ #[cfg_attr()] │ #[repr()] │
//! │ │ │
//! │ │ │
//! │ │ │
//! ├──────────────────────┼──────────────────────┤
//! │ │ │
//! │ │ │
//! │ │ `b` in │
//! │ │ #[proc_macro_derive( │
//! User created │ #[proc_macro_attr()] │ a, │
//! │ │ attributes(b) │
//! │ │ ] │
//! │ │ │
//! │ │ │
//! │ │ │
//! └──────────────────────┴──────────────────────┘
//! ```
//!
//! In this crate, syntactical attributes (sequences of tokens that look like
//! `#[something(something else)]`) are parsed into more semantic attributes, markers on items.
//! Multiple syntactic attributes might influence a single semantic attribute. For example,
//! `#[stable(...)]` and `#[unstable()]` cannot occur together, and both semantically define
//! a "stability" of an item. So, the stability attribute has an
//! [`AttributeParser`](attributes::AttributeParser) that recognizes both the `#[stable()]`
//! and `#[unstable()]` syntactic attributes, and at the end produce a single [`AttributeKind::Stability`].
//!
//! As a rule of thumb, when a syntactical attribute can be applied more than once, they should be
//! combined into a single semantic attribute. For example:
//!
//! ```
//! #[repr(C)]
//! #[repr(packed)]
//! struct Meow {}
//! ```
//!
//! should result in a single `AttributeKind::Repr` containing a list of repr annotations, in this
//! case `C` and `packed`. This is equivalent to writing `#[repr(C, packed)]` in a single
//! syntactical annotation.
// tidy-alphabetical-start
#![allow(internal_features)]
@ -12,11 +83,59 @@
#![warn(unreachable_pub)]
// tidy-alphabetical-end
#[macro_use]
mod attributes;
mod context;
pub mod parser;
mod session_diagnostics;
pub use attributes::*;
pub use attributes::cfg::*;
pub use attributes::util::{find_crate_name, is_builtin_attr, parse_version};
pub use context::{AttributeParser, OmitDoc};
pub use rustc_attr_data_structures::*;
pub use util::{find_crate_name, is_builtin_attr, parse_version};
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
/// Finds attributes in sequences of attributes by pattern matching.
///
/// A little like `matches` but for attributes.
///
/// ```rust,ignore (illustrative)
/// // finds the repr attribute
/// if let Some(r) = find_attr!(attrs, AttributeKind::Repr(r) => r) {
///
/// }
///
/// // checks if one has matched
/// if find_attr!(attrs, AttributeKind::Repr(_)) {
///
/// }
/// ```
///
/// Often this requires you to first end up with a list of attributes.
/// A common way to get those is through `tcx.get_all_attrs(did)`
#[macro_export]
macro_rules! find_attr {
($attributes_list: expr, $pattern: pat $(if $guard: expr)?) => {{
$crate::find_attr!($attributes_list, $pattern $(if $guard)? => ()).is_some()
}};
($attributes_list: expr, $pattern: pat $(if $guard: expr)? => $e: expr) => {{
fn check_attribute_iterator<'a>(_: &'_ impl IntoIterator<Item = &'a rustc_hir::Attribute>) {}
check_attribute_iterator(&$attributes_list);
let find_attribute = |iter| {
for i in $attributes_list {
match i {
rustc_hir::Attribute::Parsed($pattern) $(if $guard)? => {
return Some($e);
}
_ => {}
}
}
None
};
find_attribute($attributes_list)
}};
}

View file

@ -0,0 +1,624 @@
//! This is in essence an (improved) duplicate of `rustc_ast/attr/mod.rs`.
//! That module is intended to be deleted in its entirety.
//!
//! FIXME(jdonszelmann): delete `rustc_ast/attr/mod.rs`
use std::fmt::{Debug, Display};
use std::iter::Peekable;
use rustc_ast::token::{self, Delimiter, Token};
use rustc_ast::tokenstream::{TokenStreamIter, TokenTree};
use rustc_ast::{AttrArgs, DelimArgs, Expr, ExprKind, LitKind, MetaItemLit, NormalAttr, Path};
use rustc_ast_pretty::pprust;
use rustc_errors::DiagCtxtHandle;
use rustc_hir::{self as hir, AttrPath};
use rustc_span::symbol::{Ident, kw};
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol};
pub struct SegmentIterator<'a> {
offset: usize,
path: &'a PathParser<'a>,
}
impl<'a> Iterator for SegmentIterator<'a> {
type Item = &'a Ident;
fn next(&mut self) -> Option<Self::Item> {
if self.offset >= self.path.len() {
return None;
}
let res = match self.path {
PathParser::Ast(ast_path) => &ast_path.segments[self.offset].ident,
PathParser::Attr(attr_path) => &attr_path.segments[self.offset],
};
self.offset += 1;
Some(res)
}
}
#[derive(Clone, Debug)]
pub enum PathParser<'a> {
Ast(&'a Path),
Attr(AttrPath),
}
impl<'a> PathParser<'a> {
pub fn get_attribute_path(&self) -> hir::AttrPath {
AttrPath {
segments: self.segments().copied().collect::<Vec<_>>().into_boxed_slice(),
span: self.span(),
}
}
pub fn segments(&'a self) -> impl Iterator<Item = &'a Ident> {
SegmentIterator { offset: 0, path: self }
}
pub fn span(&self) -> Span {
match self {
PathParser::Ast(path) => path.span,
PathParser::Attr(attr_path) => attr_path.span,
}
}
pub fn len(&self) -> usize {
match self {
PathParser::Ast(path) => path.segments.len(),
PathParser::Attr(attr_path) => attr_path.segments.len(),
}
}
pub fn segments_is(&self, segments: &[Symbol]) -> bool {
self.len() == segments.len() && self.segments().zip(segments).all(|(a, b)| a.name == *b)
}
pub fn word(&self) -> Option<Ident> {
(self.len() == 1).then(|| **self.segments().next().as_ref().unwrap())
}
pub fn word_or_empty(&self) -> Ident {
self.word().unwrap_or_else(Ident::empty)
}
/// Asserts that this MetaItem is some specific word.
///
/// See [`word`](Self::word) for examples of what a word is.
pub fn word_is(&self, sym: Symbol) -> bool {
self.word().map(|i| i.name == sym).unwrap_or(false)
}
}
impl Display for PathParser<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PathParser::Ast(path) => write!(f, "{}", pprust::path_to_string(path)),
PathParser::Attr(attr_path) => write!(f, "{attr_path}"),
}
}
}
#[derive(Clone, Debug)]
#[must_use]
pub enum ArgParser<'a> {
NoArgs,
List(MetaItemListParser<'a>),
NameValue(NameValueParser),
}
impl<'a> ArgParser<'a> {
pub fn span(&self) -> Option<Span> {
match self {
Self::NoArgs => None,
Self::List(l) => Some(l.span),
Self::NameValue(n) => Some(n.value_span.with_lo(n.eq_span.lo())),
}
}
pub fn from_attr_args(value: &'a AttrArgs, dcx: DiagCtxtHandle<'a>) -> Self {
match value {
AttrArgs::Empty => Self::NoArgs,
AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => {
Self::List(MetaItemListParser::new(args, dcx))
}
AttrArgs::Delimited(args) => {
Self::List(MetaItemListParser { sub_parsers: vec![], span: args.dspan.entire() })
}
AttrArgs::Eq { eq_span, expr } => Self::NameValue(NameValueParser {
eq_span: *eq_span,
value: expr_to_lit(dcx, &expr),
value_span: expr.span,
}),
}
}
/// Asserts that this MetaItem is a list
///
/// Some examples:
///
/// - `#[allow(clippy::complexity)]`: `(clippy::complexity)` is a list
/// - `#[rustfmt::skip::macros(target_macro_name)]`: `(target_macro_name)` is a list
pub fn list(&self) -> Option<&MetaItemListParser<'a>> {
match self {
Self::List(l) => Some(l),
Self::NameValue(_) | Self::NoArgs => None,
}
}
/// Asserts that this MetaItem is a name-value pair.
///
/// Some examples:
///
/// - `#[clippy::cyclomatic_complexity = "100"]`: `clippy::cyclomatic_complexity = "100"` is a name value pair,
/// where the name is a path (`clippy::cyclomatic_complexity`). You already checked the path
/// to get an `ArgParser`, so this method will effectively only assert that the `= "100"` is
/// there
/// - `#[doc = "hello"]`: `doc = "hello` is also a name value pair
pub fn name_value(&self) -> Option<&NameValueParser> {
match self {
Self::NameValue(n) => Some(n),
Self::List(_) | Self::NoArgs => None,
}
}
/// Asserts that there are no arguments
pub fn no_args(&self) -> bool {
matches!(self, Self::NoArgs)
}
}
/// Inside lists, values could be either literals, or more deeply nested meta items.
/// This enum represents that.
///
/// Choose which one you want using the provided methods.
#[derive(Debug, Clone)]
pub enum MetaItemOrLitParser<'a> {
MetaItemParser(MetaItemParser<'a>),
Lit(MetaItemLit),
Err(Span, ErrorGuaranteed),
}
impl<'a> MetaItemOrLitParser<'a> {
pub fn span(&self) -> Span {
match self {
MetaItemOrLitParser::MetaItemParser(generic_meta_item_parser) => {
generic_meta_item_parser.span()
}
MetaItemOrLitParser::Lit(meta_item_lit) => meta_item_lit.span,
MetaItemOrLitParser::Err(span, _) => *span,
}
}
pub fn lit(&self) -> Option<&MetaItemLit> {
match self {
MetaItemOrLitParser::Lit(meta_item_lit) => Some(meta_item_lit),
_ => None,
}
}
pub fn meta_item(&self) -> Option<&MetaItemParser<'a>> {
match self {
MetaItemOrLitParser::MetaItemParser(parser) => Some(parser),
_ => None,
}
}
}
/// Utility that deconstructs a MetaItem into usable parts.
///
/// MetaItems are syntactically extremely flexible, but specific attributes want to parse
/// them in custom, more restricted ways. This can be done using this struct.
///
/// MetaItems consist of some path, and some args. The args could be empty. In other words:
///
/// - `name` -> args are empty
/// - `name(...)` -> args are a [`list`](ArgParser::list), which is the bit between the parentheses
/// - `name = value`-> arg is [`name_value`](ArgParser::name_value), where the argument is the
/// `= value` part
///
/// The syntax of MetaItems can be found at <https://doc.rust-lang.org/reference/attributes.html>
#[derive(Clone)]
pub struct MetaItemParser<'a> {
path: PathParser<'a>,
args: ArgParser<'a>,
}
impl<'a> Debug for MetaItemParser<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MetaItemParser")
.field("path", &self.path)
.field("args", &self.args)
.finish()
}
}
impl<'a> MetaItemParser<'a> {
/// Create a new parser from a [`NormalAttr`], which is stored inside of any
/// [`ast::Attribute`](rustc_ast::Attribute)
pub fn from_attr(attr: &'a NormalAttr, dcx: DiagCtxtHandle<'a>) -> Self {
Self {
path: PathParser::Ast(&attr.item.path),
args: ArgParser::from_attr_args(&attr.item.args, dcx),
}
}
}
impl<'a> MetaItemParser<'a> {
pub fn span(&self) -> Span {
if let Some(other) = self.args.span() {
self.path.span().with_hi(other.hi())
} else {
self.path.span()
}
}
/// Gets just the path, without the args.
pub fn path_without_args(&self) -> PathParser<'a> {
self.path.clone()
}
/// Gets just the args parser, without caring about the path.
pub fn args(&self) -> &ArgParser<'a> {
&self.args
}
pub fn deconstruct(&self) -> (PathParser<'a>, &ArgParser<'a>) {
(self.path_without_args(), self.args())
}
/// Asserts that this MetaItem starts with a path. Some examples:
///
/// - `#[rustfmt::skip]`: `rustfmt::skip` is a path
/// - `#[allow(clippy::complexity)]`: `clippy::complexity` is a path
/// - `#[inline]`: `inline` is a single segment path
pub fn path(&self) -> (PathParser<'a>, &ArgParser<'a>) {
self.deconstruct()
}
/// Asserts that this MetaItem starts with a word, or single segment path.
/// Doesn't return the args parser.
///
/// For examples. see [`Self::word`]
pub fn word_without_args(&self) -> Option<Ident> {
Some(self.word()?.0)
}
/// Like [`word`](Self::word), but returns an empty symbol instead of None
pub fn word_or_empty_without_args(&self) -> Ident {
self.word_or_empty().0
}
/// Asserts that this MetaItem starts with a word, or single segment path.
///
/// Some examples:
/// - `#[inline]`: `inline` is a word
/// - `#[rustfmt::skip]`: `rustfmt::skip` is a path,
/// and not a word and should instead be parsed using [`path`](Self::path)
pub fn word(&self) -> Option<(Ident, &ArgParser<'a>)> {
let (path, args) = self.deconstruct();
Some((path.word()?, args))
}
/// Like [`word`](Self::word), but returns an empty symbol instead of None
pub fn word_or_empty(&self) -> (Ident, &ArgParser<'a>) {
let (path, args) = self.deconstruct();
(path.word().unwrap_or(Ident::empty()), args)
}
/// Asserts that this MetaItem starts with some specific word.
///
/// See [`word`](Self::word) for examples of what a word is.
pub fn word_is(&self, sym: Symbol) -> Option<&ArgParser<'a>> {
self.path_without_args().word_is(sym).then(|| self.args())
}
/// Asserts that this MetaItem starts with some specific path.
///
/// See [`word`](Self::path) for examples of what a word is.
pub fn path_is(&self, segments: &[Symbol]) -> Option<&ArgParser<'a>> {
self.path_without_args().segments_is(segments).then(|| self.args())
}
}
#[derive(Clone)]
pub struct NameValueParser {
pub eq_span: Span,
value: MetaItemLit,
pub value_span: Span,
}
impl Debug for NameValueParser {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("NameValueParser")
.field("eq_span", &self.eq_span)
.field("value", &self.value)
.field("value_span", &self.value_span)
.finish()
}
}
impl NameValueParser {
pub fn value_as_lit(&self) -> &MetaItemLit {
&self.value
}
pub fn value_as_str(&self) -> Option<Symbol> {
self.value_as_lit().kind.str()
}
}
fn expr_to_lit(dcx: DiagCtxtHandle<'_>, expr: &Expr) -> MetaItemLit {
// In valid code the value always ends up as a single literal. Otherwise, a dummy
// literal suffices because the error is handled elsewhere.
if let ExprKind::Lit(token_lit) = expr.kind
&& let Ok(lit) = MetaItemLit::from_token_lit(token_lit, expr.span)
{
lit
} else {
let guar = dcx.has_errors().unwrap();
MetaItemLit { symbol: kw::Empty, suffix: None, kind: LitKind::Err(guar), span: DUMMY_SP }
}
}
struct MetaItemListParserContext<'a> {
// the tokens inside the delimiters, so `#[some::attr(a b c)]` would have `a b c` inside
inside_delimiters: Peekable<TokenStreamIter<'a>>,
dcx: DiagCtxtHandle<'a>,
}
impl<'a> MetaItemListParserContext<'a> {
fn done(&mut self) -> bool {
self.inside_delimiters.peek().is_none()
}
fn next_path(&mut self) -> Option<AttrPath> {
// FIXME: Share code with `parse_path`.
let tt = self.inside_delimiters.next().map(|tt| TokenTree::uninterpolate(tt));
match tt.as_deref()? {
&TokenTree::Token(
Token { kind: ref kind @ (token::Ident(..) | token::PathSep), span },
_,
) => {
// here we have either an ident or pathsep `::`.
let mut segments = if let &token::Ident(name, _) = kind {
// when we lookahead another pathsep, more path's coming
if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
self.inside_delimiters.peek()
{
self.inside_delimiters.next();
vec![Ident::new(name, span)]
} else {
// else we have a single identifier path, that's all
return Some(AttrPath {
segments: vec![Ident::new(name, span)].into_boxed_slice(),
span,
});
}
} else {
// if `::` is all we get, we just got a path root
vec![Ident::new(kw::PathRoot, span)]
};
// one segment accepted. accept n more
loop {
// another ident?
if let Some(&TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =
self.inside_delimiters
.next()
.map(|tt| TokenTree::uninterpolate(tt))
.as_deref()
{
segments.push(Ident::new(name, span));
} else {
return None;
}
// stop unless we see another `::`
if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
self.inside_delimiters.peek()
{
self.inside_delimiters.next();
} else {
break;
}
}
let span = span.with_hi(segments.last().unwrap().span.hi());
Some(AttrPath { segments: segments.into_boxed_slice(), span })
}
TokenTree::Token(Token { kind: token::OpenDelim(_) | token::CloseDelim(_), .. }, _) => {
None
}
_ => {
// malformed attributes can get here. We can't crash, but somewhere else should've
// already warned for this.
None
}
}
}
fn value(&mut self) -> Option<MetaItemLit> {
match self.inside_delimiters.next() {
Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => {
MetaItemListParserContext {
inside_delimiters: inner_tokens.iter().peekable(),
dcx: self.dcx,
}
.value()
}
Some(TokenTree::Token(token, _)) => MetaItemLit::from_token(token),
_ => None,
}
}
/// parses one element on the inside of a list attribute like `#[my_attr( <insides> )]`
///
/// parses a path followed be either:
/// 1. nothing (a word attr)
/// 2. a parenthesized list
/// 3. an equals sign and a literal (name-value)
///
/// Can also parse *just* a literal. This is for cases like as `#[my_attr("literal")]`
/// where no path is given before the literal
///
/// Some exceptions too for interpolated attributes which are already pre-processed
fn next(&mut self) -> Option<MetaItemOrLitParser<'a>> {
// a list element is either a literal
if let Some(TokenTree::Token(token, _)) = self.inside_delimiters.peek()
&& let Some(lit) = MetaItemLit::from_token(token)
{
self.inside_delimiters.next();
return Some(MetaItemOrLitParser::Lit(lit));
}
// or a path.
let path =
if let Some(TokenTree::Token(Token { kind: token::Interpolated(nt), span, .. }, _)) =
self.inside_delimiters.peek()
{
match &**nt {
// or maybe a full nt meta including the path but we return immediately
token::Nonterminal::NtMeta(item) => {
self.inside_delimiters.next();
return Some(MetaItemOrLitParser::MetaItemParser(MetaItemParser {
path: PathParser::Ast(&item.path),
args: ArgParser::from_attr_args(&item.args, self.dcx),
}));
}
// an already interpolated path from a macro expansion is a path, no need to parse
// one from tokens
token::Nonterminal::NtPath(path) => {
self.inside_delimiters.next();
AttrPath::from_ast(path)
}
_ => {
self.inside_delimiters.next();
// we go into this path if an expr ended up in an attribute that
// expansion did not turn into a literal. Say, `#[repr(align(macro!()))]`
// where the macro didn't expand to a literal. An error is already given
// for this at this point, and then we do continue. This makes this path
// reachable...
let e = self.dcx.span_delayed_bug(
*span,
"expr in place where literal is expected (builtin attr parsing)",
);
return Some(MetaItemOrLitParser::Err(*span, e));
}
}
} else {
self.next_path()?
};
// Paths can be followed by:
// - `(more meta items)` (another list)
// - `= lit` (a name-value)
// - nothing
Some(MetaItemOrLitParser::MetaItemParser(match self.inside_delimiters.peek() {
Some(TokenTree::Delimited(dspan, _, Delimiter::Parenthesis, inner_tokens)) => {
self.inside_delimiters.next();
MetaItemParser {
path: PathParser::Attr(path),
args: ArgParser::List(MetaItemListParser::new_tts(
inner_tokens.iter(),
dspan.entire(),
self.dcx,
)),
}
}
Some(TokenTree::Delimited(_, ..)) => {
self.inside_delimiters.next();
// self.dcx.span_delayed_bug(span.entire(), "wrong delimiters");
return None;
}
Some(TokenTree::Token(Token { kind: token::Eq, span }, _)) => {
self.inside_delimiters.next();
let value = self.value()?;
MetaItemParser {
path: PathParser::Attr(path),
args: ArgParser::NameValue(NameValueParser {
eq_span: *span,
value_span: value.span,
value,
}),
}
}
_ => MetaItemParser { path: PathParser::Attr(path), args: ArgParser::NoArgs },
}))
}
fn parse(mut self, span: Span) -> MetaItemListParser<'a> {
let mut sub_parsers = Vec::new();
while !self.done() {
let Some(n) = self.next() else {
continue;
};
sub_parsers.push(n);
match self.inside_delimiters.peek() {
None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => {
self.inside_delimiters.next();
}
Some(_) => {}
}
}
MetaItemListParser { sub_parsers, span }
}
}
#[derive(Debug, Clone)]
pub struct MetaItemListParser<'a> {
sub_parsers: Vec<MetaItemOrLitParser<'a>>,
pub span: Span,
}
impl<'a> MetaItemListParser<'a> {
fn new(delim: &'a DelimArgs, dcx: DiagCtxtHandle<'a>) -> MetaItemListParser<'a> {
MetaItemListParser::new_tts(delim.tokens.iter(), delim.dspan.entire(), dcx)
}
fn new_tts(tts: TokenStreamIter<'a>, span: Span, dcx: DiagCtxtHandle<'a>) -> Self {
MetaItemListParserContext { inside_delimiters: tts.peekable(), dcx }.parse(span)
}
/// Lets you pick and choose as what you want to parse each element in the list
pub fn mixed<'s>(&'s self) -> impl Iterator<Item = &'s MetaItemOrLitParser<'a>> + 's {
self.sub_parsers.iter()
}
pub fn len(&self) -> usize {
self.sub_parsers.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Asserts that every item in the list is another list starting with a word.
///
/// See [`MetaItemParser::word`] for examples of words.
pub fn all_word_list<'s>(&'s self) -> Option<Vec<(Ident, &'s ArgParser<'a>)>> {
self.mixed().map(|i| i.meta_item()?.word()).collect()
}
/// Asserts that every item in the list is another list starting with a full path.
///
/// See [`MetaItemParser::path`] for examples of paths.
pub fn all_path_list<'s>(&'s self) -> Option<Vec<(PathParser<'a>, &'s ArgParser<'a>)>> {
self.mixed().map(|i| Some(i.meta_item()?.path())).collect()
}
/// Returns Some if the list contains only a single element.
///
/// Inside the Some is the parser to parse this single element.
pub fn single(&self) -> Option<&MetaItemOrLitParser<'a>> {
let mut iter = self.mixed();
iter.next().filter(|_| iter.next().is_none())
}
}

View file

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

View file

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

View file

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

View file

@ -620,70 +620,31 @@ pub union MaybeUninit<T> {
pub mod intrinsics {
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub fn abort() -> ! {
loop {}
}
pub fn abort() -> !;
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub fn size_of<T>() -> usize {
loop {}
}
pub fn size_of<T>() -> usize;
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub unsafe fn size_of_val<T: ?::Sized>(_val: *const T) -> usize {
loop {}
}
pub unsafe fn size_of_val<T: ?::Sized>(_val: *const T) -> usize;
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub fn min_align_of<T>() -> usize {
loop {}
}
pub fn min_align_of<T>() -> usize;
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub unsafe fn min_align_of_val<T: ?::Sized>(_val: *const T) -> usize {
loop {}
}
pub unsafe fn min_align_of_val<T: ?::Sized>(_val: *const T) -> usize;
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub unsafe fn copy<T>(_src: *const T, _dst: *mut T, _count: usize) {
loop {}
}
pub unsafe fn copy<T>(_src: *const T, _dst: *mut T, _count: usize);
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub unsafe fn transmute<T, U>(_e: T) -> U {
loop {}
}
pub unsafe fn transmute<T, U>(_e: T) -> U;
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub unsafe fn ctlz_nonzero<T>(_x: T) -> u32 {
loop {}
}
pub unsafe fn ctlz_nonzero<T>(_x: T) -> u32;
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub fn needs_drop<T: ?::Sized>() -> bool {
loop {}
}
pub fn needs_drop<T: ?::Sized>() -> bool;
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub fn bitreverse<T>(_x: T) -> T {
loop {}
}
pub fn bitreverse<T>(_x: T) -> T;
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub fn bswap<T>(_x: T) -> T {
loop {}
}
pub fn bswap<T>(_x: T) -> T;
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub unsafe fn write_bytes<T>(_dst: *mut T, _val: u8, _count: usize) {
loop {}
}
pub unsafe fn write_bytes<T>(_dst: *mut T, _val: u8, _count: usize);
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub unsafe fn unreachable() -> ! {
loop {}
}
pub unsafe fn unreachable() -> !;
}
pub mod libc {

View file

@ -402,9 +402,13 @@ pub(crate) fn codegen_terminator_call<'tcx>(
if is_call_from_compiler_builtins_to_upstream_monomorphization(fx.tcx, instance) {
if target.is_some() {
let caller = with_no_trimmed_paths!(fx.tcx.def_path_str(fx.instance.def_id()));
let callee = with_no_trimmed_paths!(fx.tcx.def_path_str(def_id));
fx.tcx.dcx().emit_err(CompilerBuiltinsCannotCall { caller, callee });
let caller_def = fx.instance.def_id();
let e = CompilerBuiltinsCannotCall {
span: fx.tcx.def_span(caller_def),
caller: with_no_trimmed_paths!(fx.tcx.def_path_str(caller_def)),
callee: with_no_trimmed_paths!(fx.tcx.def_path_str(def_id)),
};
fx.tcx.dcx().emit_err(e);
} else {
fx.bcx.ins().trap(TrapCode::user(2).unwrap());
return;

View file

@ -116,8 +116,8 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
});
}
// simd_shuffle_generic<T, U, const I: &[u32]>(x: T, y: T) -> U
sym::simd_shuffle_generic => {
// simd_shuffle_const_generic<T, U, const I: &[u32]>(x: T, y: T) -> U
sym::simd_shuffle_const_generic => {
let [x, y] = args else {
bug!("wrong number of args for intrinsic {intrinsic}");
};

View file

@ -591,70 +591,31 @@ pub union MaybeUninit<T> {
pub mod intrinsics {
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub fn abort() -> ! {
loop {}
}
pub fn abort() -> !;
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub fn size_of<T>() -> usize {
loop {}
}
pub fn size_of<T>() -> usize;
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub unsafe fn size_of_val<T: ?::Sized>(_val: *const T) -> usize {
loop {}
}
pub unsafe fn size_of_val<T: ?::Sized>(_val: *const T) -> usize;
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub fn min_align_of<T>() -> usize {
loop {}
}
pub fn min_align_of<T>() -> usize;
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub unsafe fn min_align_of_val<T: ?::Sized>(_val: *const T) -> usize {
loop {}
}
pub unsafe fn min_align_of_val<T: ?::Sized>(_val: *const T) -> usize;
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub unsafe fn copy<T>(_src: *const T, _dst: *mut T, _count: usize) {
loop {}
}
pub unsafe fn copy<T>(_src: *const T, _dst: *mut T, _count: usize);
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub unsafe fn transmute<T, U>(_e: T) -> U {
loop {}
}
pub unsafe fn transmute<T, U>(_e: T) -> U;
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub unsafe fn ctlz_nonzero<T>(_x: T) -> u32 {
loop {}
}
pub unsafe fn ctlz_nonzero<T>(_x: T) -> u32;
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub fn needs_drop<T: ?::Sized>() -> bool {
loop {}
}
pub fn needs_drop<T: ?::Sized>() -> bool;
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub fn bitreverse<T>(_x: T) -> T {
loop {}
}
pub fn bitreverse<T>(_x: T) -> T;
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub fn bswap<T>(_x: T) -> T {
loop {}
}
pub fn bswap<T>(_x: T) -> T;
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub unsafe fn write_bytes<T>(_dst: *mut T, _val: u8, _count: usize) {
loop {}
}
pub unsafe fn write_bytes<T>(_dst: *mut T, _val: u8, _count: usize);
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub unsafe fn unreachable() -> ! {
loop {}
}
pub unsafe fn unreachable() -> !;
}
pub mod libc {

View file

@ -36,10 +36,7 @@ mod intrinsics {
#[rustc_nounwind]
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub fn abort() -> ! {
loop {}
}
pub fn abort() -> !;
}
/*

View file

@ -36,10 +36,7 @@ mod intrinsics {
#[rustc_nounwind]
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub fn abort() -> ! {
loop {}
}
pub fn abort() -> !;
}
/*

View file

@ -59,10 +59,7 @@ mod libc {
mod intrinsics {
#[rustc_nounwind]
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub fn abort() -> ! {
loop {}
}
pub fn abort() -> !;
}
#[lang = "panic"]

View file

@ -61,10 +61,7 @@ mod libc {
mod intrinsics {
#[rustc_nounwind]
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub fn abort() -> ! {
loop {}
}
pub fn abort() -> !;
}
#[lang = "panic"]

View file

@ -67,10 +67,7 @@ mod libc {
mod intrinsics {
#[rustc_nounwind]
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub fn abort() -> ! {
loop {}
}
pub fn abort() -> !;
}
#[lang = "panic"]

View file

@ -49,10 +49,7 @@ mod intrinsics {
#[rustc_nounwind]
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub fn abort() -> ! {
loop {}
}
pub fn abort() -> !;
}
mod libc {

View file

@ -1329,7 +1329,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
));
}
if name == sym::simd_shuffle_generic {
if name == sym::simd_shuffle_const_generic {
let idx = fn_args[2].expect_const().to_value().valtree.unwrap_branch();
let n = idx.len() as u64;

View file

@ -281,6 +281,8 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
("riscv32" | "riscv64", "zaamo") if get_version().0 < 19 => None,
("riscv32" | "riscv64", "zabha") if get_version().0 < 19 => None,
("riscv32" | "riscv64", "zalrsc") if get_version().0 < 19 => None,
("riscv32" | "riscv64", "zama16b") if get_version().0 < 19 => None,
("riscv32" | "riscv64", "zacas") if get_version().0 < 20 => None,
// Enable the evex512 target feature if an avx512 target feature is enabled.
("x86", s) if s.starts_with("avx512") => {
Some(LLVMFeature::with_dependency(s, TargetFeatureFoldStrength::EnableOnly("evex512")))

View file

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

View file

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

View file

@ -5,7 +5,8 @@ use rustc_ast::expand::autodiff_attrs::{
AutoDiffAttrs, DiffActivity, DiffMode, valid_input_activity, valid_ret_activity,
};
use rustc_ast::{MetaItem, MetaItemInner, attr};
use rustc_attr_parsing::{InlineAttr, InstructionSetAttr, OptimizeAttr};
use rustc_attr_parsing::ReprAttr::ReprAlign;
use rustc_attr_parsing::{AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::codes::*;
use rustc_errors::{DiagMessage, SubdiagMessage, struct_span_code_err};
@ -104,12 +105,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
if let Fn | AssocFn | Variant | Ctor(..) = def_kind {
Some(tcx.fn_sig(did))
} else {
tcx.dcx()
.span_delayed_bug(attr.span, "this attribute can only be applied to functions");
tcx.dcx().span_delayed_bug(
attr.span(),
"this attribute can only be applied to functions",
);
None
}
};
if let hir::Attribute::Parsed(p) = attr {
match p {
AttributeKind::Repr(reprs) => {
codegen_fn_attrs.alignment = reprs
.iter()
.find_map(|(r, _)| if let ReprAlign(x) = r { Some(*x) } else { None });
}
_ => {}
}
}
let Some(Ident { name, .. }) = attr.ident() else {
continue;
};
@ -130,14 +145,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
if tcx.opt_item_name(did.to_def_id()).is_some() {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
mixed_export_name_no_mangle_lint_state.track_no_mangle(
attr.span,
attr.span(),
tcx.local_def_id_to_hir_id(did),
attr,
);
} else {
tcx.dcx()
.struct_span_err(
attr.span,
attr.span(),
format!(
"`#[no_mangle]` cannot be used on {} {} as it has no name",
tcx.def_descr_article(did.to_def_id()),
@ -158,7 +173,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
feature_err(
&tcx.sess,
sym::used_with_arg,
attr.span,
attr.span(),
"`#[used(linker)]` is currently unstable",
)
.emit();
@ -170,7 +185,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
feature_err(
&tcx.sess,
sym::used_with_arg,
attr.span,
attr.span(),
"`#[used(compiler)]` is currently unstable",
)
.emit();
@ -178,7 +193,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
}
Some(_) => {
tcx.dcx().emit_err(errors::ExpectedUsedSymbol { span: attr.span });
tcx.dcx().emit_err(errors::ExpectedUsedSymbol { span: attr.span() });
}
None => {
// Unfortunately, unconditionally using `llvm.used` causes
@ -223,7 +238,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
{
struct_span_code_err!(
tcx.dcx(),
attr.span,
attr.span(),
E0737,
"`#[track_caller]` requires Rust ABI"
)
@ -231,12 +246,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
}
if is_closure
&& !tcx.features().closure_track_caller()
&& !attr.span.allows_unstable(sym::closure_track_caller)
&& !attr.span().allows_unstable(sym::closure_track_caller)
{
feature_err(
&tcx.sess,
sym::closure_track_caller,
attr.span,
attr.span(),
"`#[track_caller]` on closures is currently unstable",
)
.emit();
@ -250,19 +265,19 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
// so it may not contain any null characters.
struct_span_code_err!(
tcx.dcx(),
attr.span,
attr.span(),
E0648,
"`export_name` may not contain null characters"
)
.emit();
}
codegen_fn_attrs.export_name = Some(s);
mixed_export_name_no_mangle_lint_state.track_export_name(attr.span);
mixed_export_name_no_mangle_lint_state.track_export_name(attr.span());
}
}
sym::target_feature => {
let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else {
tcx.dcx().span_delayed_bug(attr.span, "target_feature applied to non-fn");
tcx.dcx().span_delayed_bug(attr.span(), "target_feature applied to non-fn");
continue;
};
let safe_target_features =
@ -292,7 +307,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
// `main`, `start`, and other functions that are not usually
// allowed.
} else {
check_target_feature_trait_unsafe(tcx, did, attr.span);
check_target_feature_trait_unsafe(tcx, did, attr.span());
}
}
from_target_feature_attr(
@ -310,7 +325,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
if tcx.is_mutable_static(did.into()) {
let mut diag = tcx.dcx().struct_span_err(
attr.span,
attr.span(),
"extern mutable statics are not allowed with `#[linkage]`",
);
diag.note(
@ -329,7 +344,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
if let Some(val) = attr.value_str() {
if val.as_str().bytes().any(|b| b == 0) {
let msg = format!("illegal null byte in link_section value: `{val}`");
tcx.dcx().span_err(attr.span, msg);
tcx.dcx().span_err(attr.span(), msg);
} else {
codegen_fn_attrs.link_section = Some(val);
}
@ -337,13 +352,13 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
}
sym::link_name => codegen_fn_attrs.link_name = attr.value_str(),
sym::link_ordinal => {
link_ordinal_span = Some(attr.span);
link_ordinal_span = Some(attr.span());
if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {
codegen_fn_attrs.link_ordinal = ordinal;
}
}
sym::no_sanitize => {
no_sanitize_span = Some(attr.span);
no_sanitize_span = Some(attr.span());
if let Some(list) = attr.meta_item_list() {
for item in list.iter() {
match item.name_or_empty() {
@ -381,7 +396,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
{
struct_span_code_err!(
tcx.dcx(),
attr.span,
attr.span(),
E0779,
"target does not support `#[instruction_set]`"
)
@ -393,7 +408,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
_ => {
struct_span_code_err!(
tcx.dcx(),
attr.span,
attr.span(),
E0779,
"invalid instruction set specified",
)
@ -405,7 +420,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
[] => {
struct_span_code_err!(
tcx.dcx(),
attr.span,
attr.span(),
E0778,
"`#[instruction_set]` requires an argument"
)
@ -415,7 +430,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
_ => {
struct_span_code_err!(
tcx.dcx(),
attr.span,
attr.span(),
E0779,
"cannot specify more than one instruction set"
)
@ -424,27 +439,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
}
})
}
sym::repr => {
codegen_fn_attrs.alignment = if let Some(items) = attr.meta_item_list()
&& let [item] = items.as_slice()
&& let Some((sym::align, literal)) = item.singleton_lit_list()
{
rustc_attr_parsing::parse_alignment(&literal.kind)
.inspect_err(|msg| {
struct_span_code_err!(
tcx.dcx(),
literal.span,
E0589,
"invalid `repr(align)` attribute: {}",
msg
)
.emit();
})
.ok()
} else {
None
};
}
sym::patchable_function_entry => {
codegen_fn_attrs.patchable_function_entry = attr.meta_item_list().and_then(|l| {
let mut prefix = None;
@ -510,7 +504,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
}
if let (None, None) = (prefix, entry) {
tcx.dcx().span_err(attr.span, "must specify at least one parameter");
tcx.dcx().span_err(attr.span(), "must specify at least one parameter");
}
Some(PatchableFunctionEntry::from_prefix_and_entry(
@ -536,18 +530,19 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
let Some(ref items) = attr.meta_item_list() else {
return ia;
};
inline_span = Some(attr.span());
inline_span = Some(attr.span);
let [item] = &items[..] else {
struct_span_code_err!(tcx.dcx(), attr.span, E0534, "expected one argument").emit();
struct_span_code_err!(tcx.dcx(), attr.span(), E0534, "expected one argument").emit();
return InlineAttr::None;
};
if item.has_name(sym::always) {
InlineAttr::Always
} else if item.has_name(sym::never) {
InlineAttr::Never
} else {
struct_span_code_err!(tcx.dcx(), item.span(), E0535, "invalid argument")
struct_span_code_err!(tcx.dcx(), items[0].span(), E0535, "invalid argument")
.with_help("valid inline arguments are `always` and `never`")
.emit();
@ -560,9 +555,9 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
}
if attr.is_word() {
InlineAttr::Force { attr_span: attr.span, reason: None }
InlineAttr::Force { attr_span: attr.span(), reason: None }
} else if let Some(val) = attr.value_str() {
InlineAttr::Force { attr_span: attr.span, reason: Some(val) }
InlineAttr::Force { attr_span: attr.span(), reason: Some(val) }
} else {
debug!("`rustc_force_inline` not checked by attribute validation");
ia
@ -582,16 +577,16 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
}
let err = |sp, s| struct_span_code_err!(tcx.dcx(), sp, E0722, "{}", s).emit();
if attr.is_word() {
err(attr.span, "expected one argument");
err(attr.span(), "expected one argument");
return ia;
}
let Some(ref items) = attr.meta_item_list() else {
return OptimizeAttr::Default;
};
inline_span = Some(attr.span);
inline_span = Some(attr.span());
let [item] = &items[..] else {
err(attr.span, "expected one argument");
err(attr.span(), "expected one argument");
return OptimizeAttr::Default;
};
if item.has_name(sym::size) {
@ -703,7 +698,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
let span = tcx
.get_attrs(did, sym::target_feature)
.next()
.map_or_else(|| tcx.def_span(did), |a| a.span);
.map_or_else(|| tcx.def_span(did), |a| a.span());
tcx.dcx()
.create_err(errors::TargetFeatureDisableOrEnable {
features,
@ -752,7 +747,7 @@ fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Option<u16> {
use rustc_ast::{LitIntType, LitKind, MetaItemLit};
let meta_item_list = attr.meta_item_list()?;
let [sole_meta_list] = &meta_item_list[..] else {
tcx.dcx().emit_err(errors::InvalidLinkOrdinalNargs { span: attr.span });
tcx.dcx().emit_err(errors::InvalidLinkOrdinalNargs { span: attr.span() });
return None;
};
if let Some(MetaItemLit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) =
@ -776,13 +771,13 @@ fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Option<u16> {
} else {
let msg = format!("ordinal value in `link_ordinal` is too large: `{ordinal}`");
tcx.dcx()
.struct_span_err(attr.span, msg)
.struct_span_err(attr.span(), msg)
.with_note("the value may not exceed `u16::MAX`")
.emit();
None
}
} else {
tcx.dcx().emit_err(errors::InvalidLinkOrdinalFormat { span: attr.span });
tcx.dcx().emit_err(errors::InvalidLinkOrdinalFormat { span: attr.span() });
None
}
}
@ -828,7 +823,7 @@ impl<'a> MixedExportNameAndNoMangleState<'a> {
export_name: Some(export_name),
no_mangle: Some(no_mangle),
hir_id: Some(hir_id),
no_mangle_attr: Some(no_mangle_attr),
no_mangle_attr: Some(_),
} = self
{
tcx.emit_node_span_lint(
@ -837,7 +832,7 @@ impl<'a> MixedExportNameAndNoMangleState<'a> {
no_mangle,
errors::MixedExportNameAndNoMangle {
no_mangle,
no_mangle_attr: rustc_hir_pretty::attribute_to_string(&tcx, no_mangle_attr),
no_mangle_attr: "#[unsafe(no_mangle)]".to_string(),
export_name,
removal_span: no_mangle,
},
@ -869,7 +864,7 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
_ => {
//FIXME(ZuseZ4): Once we fixed our parser, we should also prohibit the two-attribute
//branch above.
span_bug!(attrs[1].span, "cg_ssa: rustc_autodiff should only exist once per source");
span_bug!(attrs[1].span(), "cg_ssa: rustc_autodiff should only exist once per source");
}
};
@ -881,12 +876,12 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
}
let [mode, input_activities @ .., ret_activity] = &list[..] else {
span_bug!(attr.span, "rustc_autodiff attribute must contain mode and activities");
span_bug!(attr.span(), "rustc_autodiff attribute must contain mode and activities");
};
let mode = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = mode {
p1.segments.first().unwrap().ident
} else {
span_bug!(attr.span, "rustc_autodiff attribute must contain mode");
span_bug!(attr.span(), "rustc_autodiff attribute must contain mode");
};
// parse mode
@ -902,7 +897,7 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
let ret_symbol = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = ret_activity {
p1.segments.first().unwrap().ident
} else {
span_bug!(attr.span, "rustc_autodiff attribute must contain the return activity");
span_bug!(attr.span(), "rustc_autodiff attribute must contain the return activity");
};
// Then parse it into an actual DiffActivity
@ -937,11 +932,11 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
for &input in &arg_activities {
if !valid_input_activity(mode, input) {
span_bug!(attr.span, "Invalid input activity {} for {} mode", input, mode);
span_bug!(attr.span(), "Invalid input activity {} for {} mode", input, mode);
}
}
if !valid_ret_activity(mode, ret_activity) {
span_bug!(attr.span, "Invalid return activity {} for {} mode", ret_activity, mode);
span_bug!(attr.span(), "Invalid return activity {} for {} mode", ret_activity, mode);
}
Some(AutoDiffAttrs { mode, ret_activity, input_activity: arg_activities })

View file

@ -1180,6 +1180,8 @@ pub(crate) struct ErrorCreatingRemarkDir {
pub struct CompilerBuiltinsCannotCall {
pub caller: String,
pub callee: String,
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]

View file

@ -165,9 +165,13 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
if let Some(instance) = instance {
if is_call_from_compiler_builtins_to_upstream_monomorphization(tcx, instance) {
if destination.is_some() {
let caller = with_no_trimmed_paths!(tcx.def_path_str(fx.instance.def_id()));
let callee = with_no_trimmed_paths!(tcx.def_path_str(instance.def_id()));
tcx.dcx().emit_err(CompilerBuiltinsCannotCall { caller, callee });
let caller_def = fx.instance.def_id();
let e = CompilerBuiltinsCannotCall {
span: tcx.def_span(caller_def),
caller: with_no_trimmed_paths!(tcx.def_path_str(caller_def)),
callee: with_no_trimmed_paths!(tcx.def_path_str(instance.def_id())),
};
tcx.dcx().emit_err(e);
} else {
info!(
"compiler_builtins call to diverging function {:?} replaced with abort",

View file

@ -3,7 +3,7 @@ use std::iter;
use rustc_index::IndexVec;
use rustc_index::bit_set::DenseBitSet;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::{UnwindTerminateReason, traversal};
use rustc_middle::mir::{Local, UnwindTerminateReason, traversal};
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, TyAndLayout};
use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
use rustc_middle::{bug, mir, span_bug};
@ -240,7 +240,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
let local_values = {
let args = arg_local_refs(&mut start_bx, &mut fx, &memory_locals);
let mut allocate_local = |local| {
let mut allocate_local = |local: Local| {
let decl = &mir.local_decls[local];
let layout = start_bx.layout_of(fx.monomorphize(decl.ty));
assert!(!layout.ty.has_erasable_regions());

View file

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

View file

@ -147,7 +147,7 @@ impl<I: Idx, K: Ord, V> FromIterator<(K, V)> for SortedIndexMultiMap<I, K, V> {
where
J: IntoIterator<Item = (K, V)>,
{
let items = IndexVec::from_iter(iter);
let items = IndexVec::<I, _>::from_iter(iter);
let mut idx_sorted_by_item_key: Vec<_> = items.indices().collect();
// `sort_by_key` is stable, so insertion order is preserved for duplicate items.

View file

@ -7,12 +7,8 @@ Erroneous code example:
#![allow(internal_features)]
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
fn size_of<T, U>() -> usize // error: intrinsic has wrong number
// of type parameters
{
loop {}
}
fn size_of<T, U>() -> usize; // error: intrinsic has wrong number
// of type parameters
```
Please check that you provided the right number of type parameters
@ -24,9 +20,5 @@ Example:
#![allow(internal_features)]
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
fn size_of<T>() -> usize // ok!
{
loop {}
}
fn size_of<T>() -> usize; // ok!
```

View file

@ -1,5 +1,5 @@
`CoerceUnsized` was implemented on a struct which does not contain a field with
an unsized type.
`CoerceUnsized` or `DispatchFromDyn` was implemented on a struct which does not
contain a field that is being unsized.
Example of erroneous code:
@ -11,47 +11,20 @@ struct Foo<T: ?Sized> {
a: i32,
}
// error: Struct `Foo` has no unsized fields that need `CoerceUnsized`.
// error: Struct `Foo` has no unsized fields that need to be coerced.
impl<T, U> CoerceUnsized<Foo<U>> for Foo<T>
where T: CoerceUnsized<U> {}
```
An [unsized type][1] is any type where the compiler does not know the length or
alignment of at compile time. Any struct containing an unsized type is also
unsized.
`CoerceUnsized` is used to coerce structs that have a field that can be unsized,
like a custom `MyBox<T>` being unsized to `MyBox<dyn Trait>`. `DispatchFromDyn`
is used to dispatch from `MyBox<dyn Trait>` to `MyBox<Self>` in a dyn-compatible
trait.
[1]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait
If the struct doesn't have any fields of unsized types then there is no
meaningful way to implement `CoerceUnsized` or `DispatchFromDyn`, since
there is no coercion taking place.
`CoerceUnsized` is used to coerce one struct containing an unsized type
into another struct containing a different unsized type. If the struct
doesn't have any fields of unsized types then you don't need explicit
coercion to get the types you want. To fix this you can either
not try to implement `CoerceUnsized` or you can add a field that is
unsized to the struct.
Example:
```
#![feature(coerce_unsized)]
use std::ops::CoerceUnsized;
// We don't need to impl `CoerceUnsized` here.
struct Foo {
a: i32,
}
// We add the unsized type field to the struct.
struct Bar<T: ?Sized> {
a: i32,
b: T,
}
// The struct has an unsized field so we can implement
// `CoerceUnsized` for it.
impl<T, U> CoerceUnsized<Bar<U>> for Bar<T>
where T: CoerceUnsized<U> {}
```
Note that `CoerceUnsized` is mainly used by smart pointers like `Box`, `Rc`
and `Arc` to be able to mark that they can coerce unsized types that they
are pointing at.
Note that `CoerceUnsized` and `DispatchFromDyn` is mainly used by smart pointers
like `Box`, `Rc` and `Arc` to be able to mark that they can coerce unsized types
that they are pointing at.

View file

@ -1,5 +1,5 @@
`CoerceUnsized` was implemented on a struct which contains more than one field
with an unsized type.
`CoerceUnsized` or `DispatchFromDyn` was implemented on a struct which contains
more than one field that is being unsized.
Erroneous code example:
@ -17,39 +17,14 @@ struct Foo<T: ?Sized, U: ?Sized> {
impl<T, U> CoerceUnsized<Foo<U, T>> for Foo<T, U> {}
```
A struct with more than one field containing an unsized type cannot implement
`CoerceUnsized`. This only occurs when you are trying to coerce one of the
types in your struct to another type in the struct. In this case we try to
impl `CoerceUnsized` from `T` to `U` which are both types that the struct
takes. An [unsized type][1] is any type that the compiler doesn't know the
length or alignment of at compile time. Any struct containing an unsized type
is also unsized.
`CoerceUnsized` is used to coerce structs that have a field that can be unsized,
like a custom `MyBox<T>` being unsized to `MyBox<dyn Trait>`. `DispatchFromDyn`
is used to dispatch from `MyBox<dyn Trait>` to `MyBox<Self>` in a dyn-compatible
trait.
`CoerceUnsized` only allows for coercion from a structure with a single
unsized type field to another struct with a single unsized type field.
In fact Rust only allows for a struct to have one unsized type in a struct
and that unsized type must be the last field in the struct. So having two
unsized types in a single struct is not allowed by the compiler. To fix this
use only one field containing an unsized type in the struct and then use
multiple structs to manage each unsized type field you need.
If the struct has multiple fields that must be unsized, then the compiler has no
way to generate a valid implementation of `CoerceUnsized` or `DispatchFromDyn`.
Example:
```
#![feature(coerce_unsized)]
use std::ops::CoerceUnsized;
struct Foo<T: ?Sized> {
a: i32,
b: T,
}
impl <T, U> CoerceUnsized<Foo<U>> for Foo<T>
where T: CoerceUnsized<U> {}
fn coerce_foo<T: CoerceUnsized<U>, U>(t: T) -> Foo<U> {
Foo { a: 12i32, b: t } // we use coercion to get the `Foo<U>` type we need
}
```
[1]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait
Note that `CoerceUnsized` and `DispatchFromDyn` is mainly used by smart pointers
like `Box`, `Rc` and `Arc` to be able to mark that they can coerce unsized types
that they are pointing at.

View file

@ -1,8 +1,11 @@
`CoerceUnsized` was implemented on something that isn't a struct.
#### Note: this error code is no longer emitted by the compiler.
`CoerceUnsized` or `DispatchFromDyn` was implemented between two types that
are not structs.
Erroneous code example:
```compile_fail,E0376
```compile_fail,E0377
#![feature(coerce_unsized)]
use std::ops::CoerceUnsized;
@ -14,33 +17,4 @@ struct Foo<T: ?Sized> {
impl<T, U> CoerceUnsized<U> for Foo<T> {}
```
`CoerceUnsized` can only be implemented for a struct. Unsized types are
already able to be coerced without an implementation of `CoerceUnsized`
whereas a struct containing an unsized type needs to know the unsized type
field it's containing is able to be coerced. An [unsized type][1]
is any type that the compiler doesn't know the length or alignment of at
compile time. Any struct containing an unsized type is also unsized.
[1]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait
The `CoerceUnsized` trait takes a struct type. Make sure the type you are
providing to `CoerceUnsized` is a struct with only the last field containing an
unsized type.
Example:
```
#![feature(coerce_unsized)]
use std::ops::CoerceUnsized;
struct Foo<T> {
a: T,
}
// The `Foo<U>` is a struct so `CoerceUnsized` can be implemented
impl<T, U> CoerceUnsized<Foo<U>> for Foo<T> where T: CoerceUnsized<U> {}
```
Note that in Rust, structs can only contain an unsized type if the field
containing the unsized type is the last and only unsized type field in the
struct.
`CoerceUnsized` or `DispatchFromDyn` can only be implemented between structs.

View file

@ -1,5 +1,5 @@
The trait `CoerceUnsized` may only be implemented for a coercion between
structures with the same definition.
`CoerceUnsized` or `DispatchFromDyn` may only be implemented between structs
of the same type.
Example of erroneous code:
@ -20,10 +20,15 @@ pub struct Bar<T: ?Sized> {
impl<T, U> CoerceUnsized<Bar<U>> for Foo<T> where T: CoerceUnsized<U> {}
```
When attempting to implement `CoerceUnsized`, the `impl` signature must look
like: `impl CoerceUnsized<Type<U>> for Type<T> where T: CoerceUnsized<U>`;
the *implementer* and *`CoerceUnsized` type parameter* must be the same
type. In this example, `Bar` and `Foo` (even though structurally identical)
are *not* the same type and are rejected. Learn more about the `CoerceUnsized`
trait and DST coercion in
[the `CoerceUnsized` docs](../std/ops/trait.CoerceUnsized.html).
`CoerceUnsized` is used to coerce structs that have a field that can be unsized,
like a custom `MyBox<T>` being unsized to `MyBox<dyn Trait>`. `DispatchFromDyn`
is used to dispatch from `MyBox<dyn Trait>` to `MyBox<Self>` in a dyn-compatible
trait.
The compiler cannot support coercions between structs of different types, so
a valid implementation of `CoerceUnsized` or `DispatchFromDyn` should be
implemented between the same struct with different generic parameters.
Note that `CoerceUnsized` and `DispatchFromDyn` is mainly used by smart pointers
like `Box`, `Rc` and `Arc` to be able to mark that they can coerce unsized types
that they are pointing at.

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1005,10 +1005,6 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
rustc_intrinsic, Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes, intrinsics,
"the `#[rustc_intrinsic]` attribute is used to declare intrinsics as function items",
),
gated!(
rustc_intrinsic_must_be_overridden, Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes, intrinsics,
"the `#[rustc_intrinsic_must_be_overridden]` attribute is used to declare intrinsics without real bodies",
),
rustc_attr!(
rustc_no_mir_inline, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes,
"#[rustc_no_mir_inline] prevents the MIR inliner from inlining a function while not affecting codegen"

View file

@ -100,6 +100,15 @@ declare_features! (
Some("renamed to `doc_notable_trait`")),
/// Allows using `#[unsafe_destructor_blind_to_params]` (RFC 1238).
(removed, dropck_parametricity, "1.38.0", Some(28498), None),
/// Allows making `dyn Trait` well-formed even if `Trait` is not dyn compatible[^1].
/// In that case, `dyn Trait: Trait` does not hold. Moreover, coercions and
/// casts in safe Rust to `dyn Trait` for such a `Trait` is also forbidden.
///
/// Renamed from `object_safe_for_dispatch`.
///
/// [^1]: Formerly known as "object safe".
(removed, dyn_compatible_for_dispatch, "1.83.0", Some(43561),
Some("removed, not used heavily and represented additional complexity in dyn compatibility")),
/// Uses generic effect parameters for ~const bounds
(removed, effects, "1.84.0", Some(102090),
Some("removed, redundant with `#![feature(const_trait_impl)]`")),

View file

@ -270,14 +270,6 @@ declare_features! (
(unstable, doc_notable_trait, "1.52.0", Some(45040)),
/// Allows using the `may_dangle` attribute (RFC 1327).
(unstable, dropck_eyepatch, "1.10.0", Some(34761)),
/// Allows making `dyn Trait` well-formed even if `Trait` is not dyn compatible[^1].
/// In that case, `dyn Trait: Trait` does not hold. Moreover, coercions and
/// casts in safe Rust to `dyn Trait` for such a `Trait` is also forbidden.
///
/// Renamed from `object_safe_for_dispatch`.
///
/// [^1]: Formerly known as "object safe".
(unstable, dyn_compatible_for_dispatch, "1.83.0", Some(43561)),
/// Allows using the `#[fundamental]` attribute.
(unstable, fundamental, "1.0.0", Some(29635)),
/// Allows using `#[link_name="llvm.*"]`.

View file

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

View file

@ -1,18 +1,20 @@
// ignore-tidy-filelength
use std::fmt;
use rustc_abi::ExternAbi;
// ignore-tidy-filelength
use rustc_ast::attr::AttributeExt;
use rustc_ast::token::CommentKind;
use rustc_ast::util::parser::{AssocOp, ExprPrecedence};
use rustc_ast::{
self as ast, AttrId, AttrStyle, DelimArgs, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece,
IntTy, Label, LitIntType, LitKind, MetaItemInner, MetaItemLit, TraitObjectSyntax, UintTy,
self as ast, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, LitIntType,
LitKind, TraitObjectSyntax, UintTy, UnsafeBinderCastKind,
};
pub use rustc_ast::{
BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity, ByRef, CaptureBy,
ImplPolarity, IsAuto, Movability, Mutability, UnOp, UnsafeBinderCastKind,
AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity,
ByRef, CaptureBy, DelimArgs, ImplPolarity, IsAuto, MetaItemInner, MetaItemLit, Movability,
Mutability, UnOp,
};
use rustc_attr_data_structures::AttributeKind;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::sorted_map::SortedMap;
use rustc_data_structures::tagged_ptr::TaggedRef;
@ -1009,174 +1011,248 @@ pub enum AttrArgs {
},
}
#[derive(Clone, Debug, Encodable, Decodable)]
pub enum AttrKind {
/// A normal attribute.
Normal(Box<AttrItem>),
/// A doc comment (e.g. `/// ...`, `//! ...`, `/** ... */`, `/*! ... */`).
/// Doc attributes (e.g. `#[doc="..."]`) are represented with the `Normal`
/// variant (which is much less compact and thus more expensive).
DocComment(CommentKind, Symbol),
}
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable)]
pub struct AttrPath {
pub segments: Box<[Ident]>,
pub span: Span,
}
impl AttrPath {
pub fn from_ast(path: &ast::Path) -> Self {
AttrPath {
segments: path.segments.iter().map(|i| i.ident).collect::<Vec<_>>().into_boxed_slice(),
span: path.span,
}
}
}
impl fmt::Display for AttrPath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.segments.iter().map(|i| i.to_string()).collect::<Vec<_>>().join("::"))
}
}
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable)]
pub struct AttrItem {
pub unsafety: Safety,
// Not lowered to hir::Path because we have no NodeId to resolve to.
pub path: AttrPath,
pub args: AttrArgs,
}
#[derive(Clone, Debug, Encodable, Decodable)]
pub struct Attribute {
pub kind: AttrKind,
pub id: AttrId,
pub id: HashIgnoredAttrId,
/// Denotes if the attribute decorates the following construct (outer)
/// or the construct this attribute is contained within (inner).
pub style: AttrStyle,
/// Span of the entire attribute
pub span: Span,
}
/// The derived implementation of [`HashStable_Generic`] on [`Attribute`]s shouldn't hash
/// [`AttrId`]s. By wrapping them in this, we make sure we never do.
#[derive(Copy, Debug, Encodable, Decodable, Clone)]
pub struct HashIgnoredAttrId {
pub attr_id: AttrId,
}
#[derive(Clone, Debug, Encodable, Decodable, HashStable_Generic)]
pub enum Attribute {
/// A parsed built-in attribute.
///
/// Each attribute has a span connected to it. However, you must be somewhat careful using it.
/// That's because sometimes we merge multiple attributes together, like when an item has
/// multiple `repr` attributes. In this case the span might not be very useful.
Parsed(AttributeKind),
/// An attribute that could not be parsed, out of a token-like representation.
/// This is the case for custom tool attributes.
Unparsed(Box<AttrItem>),
}
impl Attribute {
pub fn get_normal_item(&self) -> &AttrItem {
match &self.kind {
AttrKind::Normal(normal) => &normal,
AttrKind::DocComment(..) => panic!("unexpected doc comment"),
match &self {
Attribute::Unparsed(normal) => &normal,
_ => panic!("unexpected parsed attribute"),
}
}
pub fn unwrap_normal_item(self) -> AttrItem {
match self.kind {
AttrKind::Normal(normal) => *normal,
AttrKind::DocComment(..) => panic!("unexpected doc comment"),
match self {
Attribute::Unparsed(normal) => *normal,
_ => panic!("unexpected parsed attribute"),
}
}
pub fn value_lit(&self) -> Option<&MetaItemLit> {
match &self.kind {
AttrKind::Normal(box AttrItem { args: AttrArgs::Eq { expr, .. }, .. }) => Some(expr),
match &self {
Attribute::Unparsed(n) => match n.as_ref() {
AttrItem { args: AttrArgs::Eq { eq_span: _, expr }, .. } => Some(expr),
_ => None,
},
_ => None,
}
}
}
impl AttributeExt for Attribute {
#[inline]
fn id(&self) -> AttrId {
self.id
match &self {
Attribute::Unparsed(u) => u.id.attr_id,
_ => panic!(),
}
}
#[inline]
fn meta_item_list(&self) -> Option<ThinVec<ast::MetaItemInner>> {
match &self.kind {
AttrKind::Normal(box AttrItem { args: AttrArgs::Delimited(d), .. }) => {
ast::MetaItemKind::list_from_tokens(d.tokens.clone())
}
match &self {
Attribute::Unparsed(n) => match n.as_ref() {
AttrItem { args: AttrArgs::Delimited(d), .. } => {
ast::MetaItemKind::list_from_tokens(d.tokens.clone())
}
_ => None,
},
_ => None,
}
}
#[inline]
fn value_str(&self) -> Option<Symbol> {
self.value_lit().and_then(|x| x.value_str())
}
#[inline]
fn value_span(&self) -> Option<Span> {
self.value_lit().map(|i| i.span)
}
/// For a single-segment attribute, returns its name; otherwise, returns `None`.
#[inline]
fn ident(&self) -> Option<Ident> {
match &self.kind {
AttrKind::Normal(box AttrItem {
path: AttrPath { segments: box [ident], .. }, ..
}) => Some(*ident),
match &self {
Attribute::Unparsed(n) => {
if let [ident] = n.path.segments.as_ref() {
Some(*ident)
} else {
None
}
}
_ => None,
}
}
#[inline]
fn path_matches(&self, name: &[Symbol]) -> bool {
match &self.kind {
AttrKind::Normal(n) => n.path.segments.iter().map(|segment| &segment.name).eq(name),
AttrKind::DocComment(..) => false,
match &self {
Attribute::Unparsed(n) => {
n.path.segments.len() == name.len()
&& n.path.segments.iter().zip(name).all(|(s, n)| s.name == *n)
}
_ => false,
}
}
#[inline]
fn is_doc_comment(&self) -> bool {
matches!(self.kind, AttrKind::DocComment(..))
matches!(self, Attribute::Parsed(AttributeKind::DocComment { .. }))
}
#[inline]
fn span(&self) -> Span {
self.span
}
fn is_word(&self) -> bool {
matches!(self.kind, AttrKind::Normal(box AttrItem { args: AttrArgs::Empty, .. }))
}
fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> {
match &self.kind {
AttrKind::Normal(n) => Some(n.path.segments.iter().copied().collect()),
AttrKind::DocComment(..) => None,
match &self {
Attribute::Unparsed(u) => u.span,
// FIXME: should not be needed anymore when all attrs are parsed
Attribute::Parsed(AttributeKind::Deprecation { span, .. }) => *span,
Attribute::Parsed(AttributeKind::DocComment { span, .. }) => *span,
a => panic!("can't get the span of an arbitrary parsed attribute: {a:?}"),
}
}
fn doc_str(&self) -> Option<Symbol> {
match &self.kind {
AttrKind::DocComment(.., data) => Some(*data),
AttrKind::Normal(_) if self.has_name(sym::doc) => self.value_str(),
#[inline]
fn is_word(&self) -> bool {
match &self {
Attribute::Unparsed(n) => {
matches!(n.args, AttrArgs::Empty)
}
_ => false,
}
}
#[inline]
fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> {
match &self {
Attribute::Unparsed(n) => Some(n.path.segments.iter().copied().collect()),
_ => None,
}
}
#[inline]
fn doc_str(&self) -> Option<Symbol> {
match &self {
Attribute::Parsed(AttributeKind::DocComment { comment, .. }) => Some(*comment),
Attribute::Unparsed(_) if self.has_name(sym::doc) => self.value_str(),
_ => None,
}
}
#[inline]
fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
match &self.kind {
AttrKind::DocComment(kind, data) => Some((*data, *kind)),
AttrKind::Normal(_) if self.name_or_empty() == sym::doc => {
match &self {
Attribute::Parsed(AttributeKind::DocComment { kind, comment, .. }) => {
Some((*comment, *kind))
}
Attribute::Unparsed(_) if self.name_or_empty() == sym::doc => {
self.value_str().map(|s| (s, CommentKind::Line))
}
_ => None,
}
}
#[inline]
fn style(&self) -> AttrStyle {
self.style
match &self {
Attribute::Unparsed(u) => u.style,
Attribute::Parsed(AttributeKind::DocComment { style, .. }) => *style,
_ => panic!(),
}
}
}
// FIXME(fn_delegation): use function delegation instead of manually forwarding
impl Attribute {
#[inline]
pub fn id(&self) -> AttrId {
AttributeExt::id(self)
}
#[inline]
pub fn name_or_empty(&self) -> Symbol {
AttributeExt::name_or_empty(self)
}
#[inline]
pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
AttributeExt::meta_item_list(self)
}
#[inline]
pub fn value_str(&self) -> Option<Symbol> {
AttributeExt::value_str(self)
}
#[inline]
pub fn value_span(&self) -> Option<Span> {
AttributeExt::value_span(self)
}
#[inline]
pub fn ident(&self) -> Option<Ident> {
AttributeExt::ident(self)
}
#[inline]
pub fn path_matches(&self, name: &[Symbol]) -> bool {
AttributeExt::path_matches(self, name)
}
#[inline]
pub fn is_doc_comment(&self) -> bool {
AttributeExt::is_doc_comment(self)
}
@ -1186,34 +1262,42 @@ impl Attribute {
AttributeExt::has_name(self, name)
}
#[inline]
pub fn span(&self) -> Span {
AttributeExt::span(self)
}
#[inline]
pub fn is_word(&self) -> bool {
AttributeExt::is_word(self)
}
#[inline]
pub fn path(&self) -> SmallVec<[Symbol; 1]> {
AttributeExt::path(self)
}
#[inline]
pub fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> {
AttributeExt::ident_path(self)
}
#[inline]
pub fn doc_str(&self) -> Option<Symbol> {
AttributeExt::doc_str(self)
}
#[inline]
pub fn is_proc_macro_attr(&self) -> bool {
AttributeExt::is_proc_macro_attr(self)
}
#[inline]
pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
AttributeExt::doc_str_and_comment_kind(self)
}
#[inline]
pub fn style(&self) -> AttrStyle {
AttributeExt::style(self)
}

View file

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

View file

@ -85,6 +85,10 @@ hir_analysis_cmse_output_stack_spill =
.note1 = functions with the `{$abi}` ABI must pass their result via the available return registers
.note2 = the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
hir_analysis_coerce_multi = implementing `{$trait_name}` does not allow multiple fields to be coerced
.note = the trait `{$trait_name}` may only be implemented when a single field is being coerced
.label = these fields must be coerced for `{$trait_name}` to be valid
hir_analysis_coerce_pointee_no_field = `CoercePointee` can only be derived on `struct`s with at least one field
hir_analysis_coerce_pointee_no_user_validity_assertion = asserting applicability of `derive(CoercePointee)` on a target data is forbidden
@ -95,12 +99,12 @@ hir_analysis_coerce_pointee_not_struct = `derive(CoercePointee)` is only applica
hir_analysis_coerce_pointee_not_transparent = `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout
hir_analysis_coerce_unsized_field_validity = for `{$ty}` to have a valid implementation of `{$trait_name}`, it must be possible to coerce the field of type `{$field_ty}`
.label = `{$field_ty}` must be a pointer, reference, or smart pointer that is allowed to be unsized
hir_analysis_coerce_unsized_may = the trait `{$trait_name}` may only be implemented for a coercion between structures
hir_analysis_coerce_unsized_multi = implementing the trait `CoerceUnsized` requires multiple coercions
.note = `CoerceUnsized` may only be implemented for a coercion between structures with one field being coerced
.coercions_note = currently, {$number} fields need coercions: {$coercions}
.label = requires multiple coercions
hir_analysis_coerce_zero = implementing `{$trait_name}` requires a field to be coerced
hir_analysis_coercion_between_struct_same_note = expected coercion between the same definition; expected `{$source_path}`, found `{$target_path}`
@ -139,10 +143,6 @@ hir_analysis_cross_crate_traits = cross-crate traits with a default impl, like `
hir_analysis_cross_crate_traits_defined = cross-crate traits with a default impl, like `{$traits}`, can only be implemented for a struct/enum type defined in the current crate
.label = can't implement cross-crate trait for type in another crate
hir_analysis_dispatch_from_dyn_multi = implementing the `DispatchFromDyn` trait requires multiple coercions
.note = the trait `DispatchFromDyn` may only be implemented for a coercion between structures with a single field being coerced
.coercions_note = currently, {$number} fields need coercions: {$coercions}
hir_analysis_dispatch_from_dyn_repr = structs implementing `DispatchFromDyn` may not have `#[repr(packed)]` or `#[repr(C)]`
hir_analysis_dispatch_from_dyn_zst = the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment that don't mention type/const generics, and nothing else

View file

@ -2,6 +2,8 @@ use std::cell::LazyCell;
use std::ops::ControlFlow;
use rustc_abi::FieldIdx;
use rustc_attr_parsing::AttributeKind;
use rustc_attr_parsing::ReprAttr::ReprPacked;
use rustc_data_structures::unord::{UnordMap, UnordSet};
use rustc_errors::MultiSpan;
use rustc_errors::codes::*;
@ -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);

View file

@ -99,7 +99,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
}
for attr in tcx.get_attrs(main_def_id, sym::track_caller) {
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;
}

View file

@ -699,7 +699,7 @@ pub fn check_intrinsic_type(
| sym::simd_reduce_min
| sym::simd_reduce_max => (2, 0, vec![param(0)], param(1)),
sym::simd_shuffle => (3, 0, vec![param(0), param(0), param(1)], param(2)),
sym::simd_shuffle_generic => (2, 1, vec![param(0), param(0)], param(1)),
sym::simd_shuffle_const_generic => (2, 1, vec![param(0), param(0)], param(1)),
other => {
tcx.dcx().emit_err(UnrecognizedIntrinsicFunction { span, name: other });

View file

@ -27,20 +27,19 @@ enum NonAsmTypeReason<'tcx> {
UnevaluatedSIMDArrayLength(DefId, ty::Const<'tcx>),
Invalid(Ty<'tcx>),
InvalidElement(DefId, Ty<'tcx>),
NotSizedPtr(Ty<'tcx>),
}
impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
pub fn new(
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
typing_env: ty::TypingEnv<'tcx>,
get_operand_ty: impl Fn(&hir::Expr<'tcx>) -> Ty<'tcx> + 'a,
) -> Self {
InlineAsmCtxt {
tcx,
typing_env: ty::TypingEnv {
typing_mode: ty::TypingMode::non_body_analysis(),
param_env: ty::ParamEnv::empty(),
},
typing_env,
target_features: tcx.asm_target_features(def_id),
expr_ty: Box::new(get_operand_ty),
}
@ -83,7 +82,13 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
ty::Float(FloatTy::F64) => Ok(InlineAsmType::F64),
ty::Float(FloatTy::F128) => Ok(InlineAsmType::F128),
ty::FnPtr(..) => Ok(asm_ty_isize),
ty::RawPtr(ty, _) if self.is_thin_ptr_ty(ty) => Ok(asm_ty_isize),
ty::RawPtr(elem_ty, _) => {
if self.is_thin_ptr_ty(elem_ty) {
Ok(asm_ty_isize)
} else {
Err(NonAsmTypeReason::NotSizedPtr(ty))
}
}
ty::Adt(adt, args) if adt.repr().simd() => {
let fields = &adt.non_enum_variant().fields;
let field = &fields[FieldIdx::ZERO];
@ -189,6 +194,16 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
can be used as arguments for inline assembly",
).emit();
}
NonAsmTypeReason::NotSizedPtr(ty) => {
let msg = format!(
"cannot use value of unsized pointer type `{ty}` for inline assembly"
);
self.tcx
.dcx()
.struct_span_err(expr.span, msg)
.with_note("only sized pointers can be used in inline assembly")
.emit();
}
NonAsmTypeReason::InvalidElement(did, ty) => {
let msg = format!(
"cannot use SIMD vector with element type `{ty}` for inline assembly"

View file

@ -17,7 +17,7 @@ use rustc_middle::ty::print::PrintTraitRefExt as _;
use rustc_middle::ty::{
self, Ty, TyCtxt, TypeVisitableExt, TypingMode, suggest_constraining_type_params,
};
use rustc_span::{DUMMY_SP, Span};
use rustc_span::{DUMMY_SP, Span, sym};
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
use rustc_trait_selection::traits::misc::{
ConstParamTyImplementationError, CopyImplementationError, InfringingFieldsReason,
@ -195,8 +195,14 @@ fn visit_implementation_of_coerce_unsized(checker: &Checker<'_>) -> Result<(), E
// Just compute this for the side-effects, in particular reporting
// errors; other parts of the code may demand it for the info of
// course.
let span = tcx.def_span(impl_did);
tcx.at(span).ensure_ok().coerce_unsized_info(impl_did)
tcx.ensure_ok().coerce_unsized_info(impl_did)
}
fn is_from_coerce_pointee_derive(tcx: TyCtxt<'_>, span: Span) -> bool {
span.ctxt()
.outer_expn_data()
.macro_def_id
.is_some_and(|def_id| tcx.is_diagnostic_item(sym::CoercePointee, def_id))
}
fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {
@ -206,17 +212,29 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
debug!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did);
let span = tcx.def_span(impl_did);
let trait_name = "DispatchFromDyn";
let dispatch_from_dyn_trait = tcx.require_lang_item(LangItem::DispatchFromDyn, Some(span));
let source = trait_ref.self_ty();
assert!(!source.has_escaping_bound_vars());
let target = {
assert_eq!(trait_ref.def_id, dispatch_from_dyn_trait);
trait_ref.args.type_at(1)
};
// Check `CoercePointee` impl is WF -- if not, then there's no reason to report
// redundant errors for `DispatchFromDyn`. This is best effort, though.
let mut res = Ok(());
tcx.for_each_relevant_impl(
tcx.require_lang_item(LangItem::CoerceUnsized, Some(span)),
source,
|impl_def_id| {
res = res.and(tcx.ensure_ok().coerce_unsized_info(impl_def_id));
},
);
res?;
debug!("visit_implementation_of_dispatch_from_dyn: {:?} -> {:?}", source, target);
let param_env = tcx.param_env(impl_did);
@ -242,26 +260,25 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
if def_a != def_b {
let source_path = tcx.def_path_str(def_a.did());
let target_path = tcx.def_path_str(def_b.did());
return Err(tcx.dcx().emit_err(errors::DispatchFromDynCoercion {
return Err(tcx.dcx().emit_err(errors::CoerceSameStruct {
span,
trait_name: "DispatchFromDyn",
trait_name,
note: true,
source_path,
target_path,
}));
}
let mut res = Ok(());
if def_a.repr().c() || def_a.repr().packed() {
res = Err(tcx.dcx().emit_err(errors::DispatchFromDynRepr { span }));
return Err(tcx.dcx().emit_err(errors::DispatchFromDynRepr { span }));
}
let fields = &def_a.non_enum_variant().fields;
let mut res = Ok(());
let coerced_fields = fields
.iter()
.filter(|field| {
.iter_enumerated()
.filter_map(|(i, field)| {
// Ignore PhantomData fields
let unnormalized_ty = tcx.type_of(field.did).instantiate_identity();
if tcx
@ -272,7 +289,7 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
.unwrap_or(unnormalized_ty)
.is_phantom_data()
{
return false;
return None;
}
let ty_a = field.ty(tcx, args_a);
@ -290,7 +307,7 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
&& !ty_a.has_non_region_param()
{
// ignore 1-ZST fields
return false;
return None;
}
res = Err(tcx.dcx().emit_err(errors::DispatchFromDynZST {
@ -299,64 +316,57 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
ty: ty_a,
}));
return false;
None
} else {
Some((i, ty_a, ty_b, tcx.def_span(field.did)))
}
true
})
.collect::<Vec<_>>();
res?;
if coerced_fields.is_empty() {
res = Err(tcx.dcx().emit_err(errors::DispatchFromDynSingle {
return Err(tcx.dcx().emit_err(errors::CoerceNoField {
span,
trait_name: "DispatchFromDyn",
trait_name,
note: true,
}));
} else if coerced_fields.len() > 1 {
res = Err(tcx.dcx().emit_err(errors::DispatchFromDynMulti {
span,
coercions_note: true,
number: coerced_fields.len(),
coercions: coerced_fields
.iter()
.map(|field| {
format!(
"`{}` (`{}` to `{}`)",
field.name,
field.ty(tcx, args_a),
field.ty(tcx, args_b),
)
})
.collect::<Vec<_>>()
.join(", "),
}));
} else {
} else if let &[(_, ty_a, ty_b, field_span)] = &coerced_fields[..] {
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
for field in coerced_fields {
ocx.register_obligation(Obligation::new(
tcx,
cause.clone(),
param_env,
ty::TraitRef::new(
tcx,
dispatch_from_dyn_trait,
[field.ty(tcx, args_a), field.ty(tcx, args_b)],
),
));
}
ocx.register_obligation(Obligation::new(
tcx,
cause.clone(),
param_env,
ty::TraitRef::new(tcx, dispatch_from_dyn_trait, [ty_a, ty_b]),
));
let errors = ocx.select_all_or_error();
if !errors.is_empty() {
res = Err(infcx.err_ctxt().report_fulfillment_errors(errors));
if is_from_coerce_pointee_derive(tcx, span) {
return Err(tcx.dcx().emit_err(errors::CoerceFieldValidity {
span,
trait_name,
ty: trait_ref.self_ty(),
field_span,
field_ty: ty_a,
}));
} else {
return Err(infcx.err_ctxt().report_fulfillment_errors(errors));
}
}
// Finally, resolve all regions.
res = res.and(ocx.resolve_regions_and_report_errors(impl_did, param_env, []));
ocx.resolve_regions_and_report_errors(impl_did, param_env, [])?;
Ok(())
} else {
return Err(tcx.dcx().emit_err(errors::CoerceMulti {
span,
trait_name,
number: coerced_fields.len(),
fields: coerced_fields.iter().map(|(_, _, _, s)| *s).collect::<Vec<_>>().into(),
}));
}
res
}
_ => Err(tcx
.dcx()
.emit_err(errors::CoerceUnsizedMay { span, trait_name: "DispatchFromDyn" })),
_ => Err(tcx.dcx().emit_err(errors::CoerceUnsizedNonStruct { span, trait_name })),
}
}
@ -366,13 +376,14 @@ pub(crate) fn coerce_unsized_info<'tcx>(
) -> Result<CoerceUnsizedInfo, ErrorGuaranteed> {
debug!("compute_coerce_unsized_info(impl_did={:?})", impl_did);
let span = tcx.def_span(impl_did);
let trait_name = "CoerceUnsized";
let coerce_unsized_trait = tcx.require_lang_item(LangItem::CoerceUnsized, Some(span));
let unsize_trait = tcx.require_lang_item(LangItem::Unsize, Some(span));
let source = tcx.type_of(impl_did).instantiate_identity();
let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity();
assert_eq!(trait_ref.def_id, coerce_unsized_trait);
let target = trait_ref.args.type_at(1);
debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)", source, target);
@ -399,9 +410,9 @@ pub(crate) fn coerce_unsized_info<'tcx>(
)
.emit();
}
(mt_a.ty, mt_b.ty, unsize_trait, None)
(mt_a.ty, mt_b.ty, unsize_trait, None, span)
};
let (source, target, trait_def_id, kind) = match (source.kind(), target.kind()) {
let (source, target, trait_def_id, kind, field_span) = match (source.kind(), target.kind()) {
(&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => {
infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a);
let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };
@ -422,9 +433,9 @@ pub(crate) fn coerce_unsized_info<'tcx>(
if def_a != def_b {
let source_path = tcx.def_path_str(def_a.did());
let target_path = tcx.def_path_str(def_b.did());
return Err(tcx.dcx().emit_err(errors::DispatchFromDynSame {
return Err(tcx.dcx().emit_err(errors::CoerceSameStruct {
span,
trait_name: "CoerceUnsized",
trait_name,
note: true,
source_path,
target_path,
@ -504,14 +515,14 @@ pub(crate) fn coerce_unsized_info<'tcx>(
// Collect up all fields that were significantly changed
// i.e., those that contain T in coerce_unsized T -> U
Some((i, a, b))
Some((i, a, b, tcx.def_span(f.did)))
})
.collect::<Vec<_>>();
if diff_fields.is_empty() {
return Err(tcx.dcx().emit_err(errors::CoerceUnsizedOneField {
return Err(tcx.dcx().emit_err(errors::CoerceNoField {
span,
trait_name: "CoerceUnsized",
trait_name,
note: true,
}));
} else if diff_fields.len() > 1 {
@ -522,27 +533,21 @@ pub(crate) fn coerce_unsized_info<'tcx>(
tcx.def_span(impl_did)
};
return Err(tcx.dcx().emit_err(errors::CoerceUnsizedMulti {
return Err(tcx.dcx().emit_err(errors::CoerceMulti {
span,
coercions_note: true,
trait_name,
number: diff_fields.len(),
coercions: diff_fields
.iter()
.map(|&(i, a, b)| format!("`{}` (`{}` to `{}`)", fields[i].name, a, b))
.collect::<Vec<_>>()
.join(", "),
fields: diff_fields.iter().map(|(_, _, _, s)| *s).collect::<Vec<_>>().into(),
}));
}
let (i, a, b) = diff_fields[0];
let (i, a, b, field_span) = diff_fields[0];
let kind = ty::adjustment::CustomCoerceUnsized::Struct(i);
(a, b, coerce_unsized_trait, Some(kind))
(a, b, coerce_unsized_trait, Some(kind), field_span)
}
_ => {
return Err(tcx
.dcx()
.emit_err(errors::DispatchFromDynStruct { span, trait_name: "CoerceUnsized" }));
return Err(tcx.dcx().emit_err(errors::CoerceUnsizedNonStruct { span, trait_name }));
}
};
@ -557,12 +562,23 @@ pub(crate) fn coerce_unsized_info<'tcx>(
);
ocx.register_obligation(obligation);
let errors = ocx.select_all_or_error();
if !errors.is_empty() {
infcx.err_ctxt().report_fulfillment_errors(errors);
if is_from_coerce_pointee_derive(tcx, span) {
return Err(tcx.dcx().emit_err(errors::CoerceFieldValidity {
span,
trait_name,
ty: trait_ref.self_ty(),
field_span,
field_ty: source,
}));
} else {
return Err(infcx.err_ctxt().report_fulfillment_errors(errors));
}
}
// Finally, resolve all regions.
let _ = ocx.resolve_regions_and_report_errors(impl_did, param_env, []);
ocx.resolve_regions_and_report_errors(impl_did, param_env, [])?;
Ok(CoerceUnsizedInfo { custom_kind: kind })
}

View file

@ -199,11 +199,7 @@ fn check_object_overlap<'tcx>(
for component_def_id in component_def_ids {
if !tcx.is_dyn_compatible(component_def_id) {
// Without the 'dyn_compatible_for_dispatch' feature this is an error
// which will be reported by wfcheck. Ignore it here.
// This is tested by `coherence-impl-trait-for-trait-dyn-compatible.rs`.
// With the feature enabled, the trait is not implemented automatically,
// so this is valid.
// This is a WF error tested by `coherence-impl-trait-for-trait-dyn-compatible.rs`.
} else {
let mut supertrait_def_ids = elaborate::supertrait_def_ids(tcx, component_def_id);
if supertrait_def_ids

View file

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

View file

@ -111,14 +111,14 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) {
let trait_ref = tcx.impl_trait_ref(def_id).unwrap().instantiate_identity();
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;

View file

@ -1164,18 +1164,6 @@ pub(crate) struct InherentTyOutside {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_coerce_unsized_may, code = E0378)]
pub(crate) struct DispatchFromDynCoercion<'a> {
#[primary_span]
pub span: Span,
pub trait_name: &'a str,
#[note(hir_analysis_coercion_between_struct_same_note)]
pub note: bool,
pub source_path: String,
pub target_path: String,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_dispatch_from_dyn_repr, code = E0378)]
pub(crate) struct DispatchFromDynRepr {
@ -1293,41 +1281,40 @@ pub(crate) struct DispatchFromDynZST<'a> {
}
#[derive(Diagnostic)]
#[diag(hir_analysis_coerce_unsized_may, code = E0378)]
pub(crate) struct DispatchFromDynSingle<'a> {
#[diag(hir_analysis_coerce_zero, code = E0374)]
pub(crate) struct CoerceNoField {
#[primary_span]
pub span: Span,
pub trait_name: &'a str,
pub trait_name: &'static str,
#[note(hir_analysis_coercion_between_struct_single_note)]
pub note: bool,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_dispatch_from_dyn_multi, code = E0378)]
#[note]
pub(crate) struct DispatchFromDynMulti {
#[diag(hir_analysis_coerce_multi, code = E0375)]
pub(crate) struct CoerceMulti {
pub trait_name: &'static str,
#[primary_span]
pub span: Span,
#[note(hir_analysis_coercions_note)]
pub coercions_note: bool,
pub number: usize,
pub coercions: String,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_coerce_unsized_may, code = E0376)]
pub(crate) struct DispatchFromDynStruct<'a> {
#[primary_span]
pub span: Span,
pub trait_name: &'a str,
#[note]
pub fields: MultiSpan,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_coerce_unsized_may, code = E0377)]
pub(crate) struct DispatchFromDynSame<'a> {
pub(crate) struct CoerceUnsizedNonStruct {
#[primary_span]
pub span: Span,
pub trait_name: &'a str,
pub trait_name: &'static str,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_coerce_unsized_may, code = E0377)]
pub(crate) struct CoerceSameStruct {
#[primary_span]
pub span: Span,
pub trait_name: &'static str,
#[note(hir_analysis_coercion_between_struct_same_note)]
pub note: bool,
pub source_path: String,
@ -1335,34 +1322,15 @@ pub(crate) struct DispatchFromDynSame<'a> {
}
#[derive(Diagnostic)]
#[diag(hir_analysis_coerce_unsized_may, code = E0374)]
pub(crate) struct CoerceUnsizedOneField<'a> {
#[diag(hir_analysis_coerce_unsized_field_validity)]
pub(crate) struct CoerceFieldValidity<'tcx> {
#[primary_span]
pub span: Span,
pub trait_name: &'a str,
#[note(hir_analysis_coercion_between_struct_single_note)]
pub note: bool,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_coerce_unsized_multi, code = E0375)]
#[note]
pub(crate) struct CoerceUnsizedMulti {
#[primary_span]
pub ty: Ty<'tcx>,
pub trait_name: &'static str,
#[label]
pub span: Span,
#[note(hir_analysis_coercions_note)]
pub coercions_note: bool,
pub number: usize,
pub coercions: String,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_coerce_unsized_may, code = E0378)]
pub(crate) struct CoerceUnsizedMay<'a> {
#[primary_span]
pub span: Span,
pub trait_name: &'a str,
pub field_span: Span,
pub field_ty: Ty<'tcx>,
}
#[derive(Diagnostic)]

View file

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

View file

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

View file

@ -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));

View file

@ -22,6 +22,12 @@ impl ExpectedIdx {
}
}
impl ProvidedIdx {
pub(crate) fn to_expected_idx(self) -> ExpectedIdx {
ExpectedIdx::from_u32(self.as_u32())
}
}
// An issue that might be found in the compatibility matrix
#[derive(Debug)]
enum Issue {

View file

@ -110,7 +110,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx.erase_regions(ty)
}
};
InlineAsmCtxt::new(self.tcx, enclosing_id, expr_ty).check_asm(asm);
InlineAsmCtxt::new(self.tcx, enclosing_id, self.typing_env(self.param_env), expr_ty)
.check_asm(asm);
}
}
@ -775,7 +776,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// First, check if we just need to wrap some arguments in a tuple.
if let Some((mismatch_idx, terr)) =
compatibility_diagonal.iter().enumerate().find_map(|(i, c)| {
compatibility_diagonal.iter_enumerated().find_map(|(i, c)| {
if let Compatibility::Incompatible(Some(terr)) = c {
Some((i, *terr))
} else {
@ -787,24 +788,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Do we have as many extra provided arguments as the tuple's length?
// If so, we might have just forgotten to wrap some args in a tuple.
if let Some(ty::Tuple(tys)) =
formal_and_expected_inputs.get(mismatch_idx.into()).map(|tys| tys.1.kind())
formal_and_expected_inputs.get(mismatch_idx.to_expected_idx()).map(|tys| tys.1.kind())
// If the tuple is unit, we're not actually wrapping any arguments.
&& !tys.is_empty()
&& provided_arg_tys.len() == formal_and_expected_inputs.len() - 1 + tys.len()
{
// Wrap up the N provided arguments starting at this position in a tuple.
let provided_as_tuple = Ty::new_tup_from_iter(
tcx,
provided_arg_tys.iter().map(|(ty, _)| *ty).skip(mismatch_idx).take(tys.len()),
);
let provided_args_to_tuple = &provided_arg_tys[mismatch_idx..];
let (provided_args_to_tuple, provided_args_after_tuple) =
provided_args_to_tuple.split_at(tys.len());
let provided_as_tuple =
Ty::new_tup_from_iter(tcx, provided_args_to_tuple.iter().map(|&(ty, _)| ty));
let mut satisfied = true;
// Check if the newly wrapped tuple + rest of the arguments are compatible.
for ((_, expected_ty), provided_ty) in std::iter::zip(
formal_and_expected_inputs.iter().skip(mismatch_idx),
[provided_as_tuple].into_iter().chain(
provided_arg_tys.iter().map(|(ty, _)| *ty).skip(mismatch_idx + tys.len()),
),
formal_and_expected_inputs[mismatch_idx.to_expected_idx()..].iter(),
[provided_as_tuple]
.into_iter()
.chain(provided_args_after_tuple.iter().map(|&(ty, _)| ty)),
) {
if !self.may_coerce(provided_ty, *expected_ty) {
satisfied = false;
@ -816,10 +818,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Take some care with spans, so we don't suggest wrapping a macro's
// innards in parenthesis, for example.
if satisfied
&& let Some((_, lo)) =
provided_arg_tys.get(ProvidedIdx::from_usize(mismatch_idx))
&& let Some((_, hi)) =
provided_arg_tys.get(ProvidedIdx::from_usize(mismatch_idx + tys.len() - 1))
&& let &[(_, hi @ lo)] | &[(_, lo), .., (_, hi)] = provided_args_to_tuple
{
let mut err;
if tys.len() == 1 {
@ -827,9 +826,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// so don't do anything special here.
err = self.err_ctxt().report_and_explain_type_error(
mk_trace(
*lo,
formal_and_expected_inputs[mismatch_idx.into()],
provided_arg_tys[mismatch_idx.into()].0,
lo,
formal_and_expected_inputs[mismatch_idx.to_expected_idx()],
provided_arg_tys[mismatch_idx].0,
),
self.param_env,
terr,
@ -868,7 +867,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
callee_ty,
call_expr,
None,
Some(mismatch_idx),
Some(mismatch_idx.as_usize()),
&matched_inputs,
&formal_and_expected_inputs,
is_method,
@ -2615,7 +2614,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
let expected_display_type = self
.resolve_vars_if_possible(formal_and_expected_inputs[idx.into()].1)
.resolve_vars_if_possible(formal_and_expected_inputs[idx].1)
.sort_string(self.tcx);
let label = if idxs_matched == params_with_generics.len() - 1 {
format!(

View file

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

View file

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

View file

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

View file

@ -1,5 +1,7 @@
use std::fmt::Debug;
use std::hash::Hash;
use std::ops;
use std::slice::SliceIndex;
/// Represents some newtyped `usize` wrapper.
///
@ -43,3 +45,92 @@ impl Idx for u32 {
self as usize
}
}
/// Helper trait for indexing operations with a custom index type.
pub trait IntoSliceIdx<I, T: ?Sized> {
type Output: SliceIndex<T>;
fn into_slice_idx(self) -> Self::Output;
}
impl<I: Idx, T> IntoSliceIdx<I, [T]> for I {
type Output = usize;
#[inline]
fn into_slice_idx(self) -> Self::Output {
self.index()
}
}
impl<I, T> IntoSliceIdx<I, [T]> for ops::RangeFull {
type Output = ops::RangeFull;
#[inline]
fn into_slice_idx(self) -> Self::Output {
self
}
}
impl<I: Idx, T> IntoSliceIdx<I, [T]> for ops::Range<I> {
type Output = ops::Range<usize>;
#[inline]
fn into_slice_idx(self) -> Self::Output {
ops::Range { start: self.start.index(), end: self.end.index() }
}
}
impl<I: Idx, T> IntoSliceIdx<I, [T]> for ops::RangeFrom<I> {
type Output = ops::RangeFrom<usize>;
#[inline]
fn into_slice_idx(self) -> Self::Output {
ops::RangeFrom { start: self.start.index() }
}
}
impl<I: Idx, T> IntoSliceIdx<I, [T]> for ops::RangeTo<I> {
type Output = ops::RangeTo<usize>;
#[inline]
fn into_slice_idx(self) -> Self::Output {
..self.end.index()
}
}
impl<I: Idx, T> IntoSliceIdx<I, [T]> for ops::RangeInclusive<I> {
type Output = ops::RangeInclusive<usize>;
#[inline]
fn into_slice_idx(self) -> Self::Output {
ops::RangeInclusive::new(self.start().index(), self.end().index())
}
}
impl<I: Idx, T> IntoSliceIdx<I, [T]> for ops::RangeToInclusive<I> {
type Output = ops::RangeToInclusive<usize>;
#[inline]
fn into_slice_idx(self) -> Self::Output {
..=self.end.index()
}
}
#[cfg(feature = "nightly")]
impl<I: Idx, T> IntoSliceIdx<I, [T]> for core::range::Range<I> {
type Output = core::range::Range<usize>;
#[inline]
fn into_slice_idx(self) -> Self::Output {
core::range::Range { start: self.start.index(), end: self.end.index() }
}
}
#[cfg(feature = "nightly")]
impl<I: Idx, T> IntoSliceIdx<I, [T]> for core::range::RangeFrom<I> {
type Output = core::range::RangeFrom<usize>;
#[inline]
fn into_slice_idx(self) -> Self::Output {
core::range::RangeFrom { start: self.start.index() }
}
}
#[cfg(feature = "nightly")]
impl<I: Idx, T> IntoSliceIdx<I, [T]> for core::range::RangeInclusive<I> {
type Output = core::range::RangeInclusive<usize>;
#[inline]
fn into_slice_idx(self) -> Self::Output {
core::range::RangeInclusive { start: self.start.index(), end: self.end.index() }
}
}

View file

@ -2,6 +2,7 @@
#![cfg_attr(all(feature = "nightly", test), feature(stmt_expr_attributes))]
#![cfg_attr(feature = "nightly", allow(internal_features))]
#![cfg_attr(feature = "nightly", feature(extend_one, step_trait, test))]
#![cfg_attr(feature = "nightly", feature(new_range_api))]
#![cfg_attr(feature = "nightly", feature(new_zeroed_alloc))]
#![warn(unreachable_pub)]
// tidy-alphabetical-end
@ -14,7 +15,7 @@ mod idx;
mod slice;
mod vec;
pub use idx::Idx;
pub use idx::{Idx, IntoSliceIdx};
pub use rustc_index_macros::newtype_index;
pub use slice::IndexSlice;
#[doc(no_inline)]

View file

@ -1,8 +1,9 @@
use std::fmt;
use std::marker::PhantomData;
use std::ops::{Index, IndexMut};
use std::{fmt, slice};
use std::slice::{self, SliceIndex};
use crate::{Idx, IndexVec};
use crate::{Idx, IndexVec, IntoSliceIdx};
/// A view into contiguous `T`s, indexed by `I` rather than by `usize`.
///
@ -97,13 +98,19 @@ impl<I: Idx, T> IndexSlice<I, T> {
}
#[inline]
pub fn get(&self, index: I) -> Option<&T> {
self.raw.get(index.index())
pub fn get<R: IntoSliceIdx<I, [T]>>(
&self,
index: R,
) -> Option<&<R::Output as SliceIndex<[T]>>::Output> {
self.raw.get(index.into_slice_idx())
}
#[inline]
pub fn get_mut(&mut self, index: I) -> Option<&mut T> {
self.raw.get_mut(index.index())
pub fn get_mut<R: IntoSliceIdx<I, [T]>>(
&mut self,
index: R,
) -> Option<&mut <R::Output as SliceIndex<[T]>>::Output> {
self.raw.get_mut(index.into_slice_idx())
}
/// Returns mutable references to two distinct elements, `a` and `b`.
@ -184,19 +191,19 @@ impl<I: Idx, T: fmt::Debug> fmt::Debug for IndexSlice<I, T> {
}
}
impl<I: Idx, T> Index<I> for IndexSlice<I, T> {
type Output = T;
impl<I: Idx, T, R: IntoSliceIdx<I, [T]>> Index<R> for IndexSlice<I, T> {
type Output = <R::Output as SliceIndex<[T]>>::Output;
#[inline]
fn index(&self, index: I) -> &T {
&self.raw[index.index()]
fn index(&self, index: R) -> &Self::Output {
&self.raw[index.into_slice_idx()]
}
}
impl<I: Idx, T> IndexMut<I> for IndexSlice<I, T> {
impl<I: Idx, T, R: IntoSliceIdx<I, [T]>> IndexMut<R> for IndexSlice<I, T> {
#[inline]
fn index_mut(&mut self, index: I) -> &mut T {
&mut self.raw[index.index()]
fn index_mut(&mut self, index: R) -> &mut Self::Output {
&mut self.raw[index.into_slice_idx()]
}
}

View file

@ -160,7 +160,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
/// empty region. The `expansion` phase will grow this larger.
fn construct_var_data(&self) -> LexicalRegionResolutions<'tcx> {
LexicalRegionResolutions {
values: IndexVec::from_fn_n(
values: IndexVec::<RegionVid, _>::from_fn_n(
|vid| {
let vid_universe = self.var_infos[vid].universe;
VarValue::Empty(vid_universe)

View file

@ -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 },
);
}

View file

@ -38,7 +38,8 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) {
}
LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => {
// 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"),

View file

@ -182,7 +182,7 @@ fn name_of_extern_decl(tcx: TyCtxt<'_>, fi: hir::OwnerId) -> SymbolName {
// information, we could have codegen_fn_attrs also give span information back for
// 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)

View file

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

View file

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

View file

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

View file

@ -0,0 +1,145 @@
use proc_macro2::TokenStream;
use quote::{format_ident, quote, quote_spanned};
use syn::spanned::Spanned;
use syn::{Data, Fields, Ident};
use synstructure::Structure;
fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream, TokenStream) {
let string_name = name.to_string();
let mut disps = vec![quote! {let mut __printed_anything = false;}];
match fields {
Fields::Named(fields_named) => {
let mut field_names = Vec::new();
for field in &fields_named.named {
let name = field.ident.as_ref().unwrap();
let string_name = name.to_string();
disps.push(quote! {
if __printed_anything && #name.print_something() {
__p.word_space(",");
__printed_anything = true;
}
__p.word(#string_name);
__p.word_space(":");
#name.print_attribute(__p);
});
field_names.push(name);
}
(
quote! { {#(#field_names),*} },
quote! {
__p.word(#string_name);
if true #(&& !#field_names.print_something())* {
return;
}
__p.word("{");
#(#disps)*
__p.word("}");
},
quote! { true },
)
}
Fields::Unnamed(fields_unnamed) => {
let mut field_names = Vec::new();
for idx in 0..fields_unnamed.unnamed.len() {
let name = format_ident!("f{idx}");
disps.push(quote! {
if __printed_anything && #name.print_something() {
__p.word_space(",");
__printed_anything = true;
}
#name.print_attribute(__p);
});
field_names.push(name);
}
(
quote! { (#(#field_names),*) },
quote! {
__p.word(#string_name);
if true #(&& !#field_names.print_something())* {
return;
}
__p.word("(");
#(#disps)*
__p.word(")");
},
quote! { true },
)
}
Fields::Unit => (quote! {}, quote! { __p.word(#string_name) }, quote! { true }),
}
}
pub(crate) fn print_attribute(input: Structure<'_>) -> TokenStream {
let span_error = |span, message: &str| {
quote_spanned! { span => const _: () = ::core::compile_error!(#message); }
};
// Must be applied to an enum type.
let (code, printed) = match &input.ast().data {
Data::Enum(e) => {
let (arms, printed) = e
.variants
.iter()
.map(|x| {
let ident = &x.ident;
let (pat, code, printed) = print_fields(ident, &x.fields);
(
quote! {
Self::#ident #pat => {#code}
},
quote! {
Self::#ident #pat => {#printed}
},
)
})
.unzip::<_, _, Vec<_>, Vec<_>>();
(
quote! {
match self {
#(#arms)*
}
},
quote! {
match self {
#(#printed)*
}
},
)
}
Data::Struct(s) => {
let (pat, code, printed) = print_fields(&input.ast().ident, &s.fields);
(
quote! {
let Self #pat = self;
#code
},
quote! {
let Self #pat = self;
#printed
},
)
}
Data::Union(u) => {
return span_error(u.union_token.span(), "can't derive PrintAttribute on unions");
}
};
#[allow(keyword_idents_2024)]
input.gen_impl(quote! {
#[allow(unused)]
gen impl PrintAttribute for @Self {
fn print_something(&self) -> bool { #printed }
fn print_attribute(&self, __p: &mut rustc_ast_pretty::pp::Printer) { #code }
}
})
}

View file

@ -450,7 +450,7 @@ impl<'tcx> Collector<'tcx> {
(name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule));
}
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(),
});
}
}

View file

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

View file

@ -96,6 +96,17 @@ impl<'tcx> HasLocalDecls<'tcx> for Body<'tcx> {
}
impl MirPhase {
pub fn name(&self) -> &'static str {
match *self {
MirPhase::Built => "built",
MirPhase::Analysis(AnalysisPhase::Initial) => "analysis",
MirPhase::Analysis(AnalysisPhase::PostCleanup) => "analysis-post-cleanup",
MirPhase::Runtime(RuntimePhase::Initial) => "runtime",
MirPhase::Runtime(RuntimePhase::PostCleanup) => "runtime-post-cleanup",
MirPhase::Runtime(RuntimePhase::Optimized) => "runtime-optimized",
}
}
/// Gets the (dialect, phase) index of the current `MirPhase`. Both numbers
/// are 1-indexed.
pub fn index(&self) -> (usize, usize) {

Some files were not shown because too many files have changed in this diff Show more