commit
cfb827804f
405 changed files with 6958 additions and 6093 deletions
20
Cargo.lock
20
Cargo.lock
|
|
@ -3207,6 +3207,7 @@ dependencies = [
|
|||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_ast_pretty",
|
||||
"rustc_attr_parsing",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_feature",
|
||||
|
|
@ -3215,6 +3216,7 @@ dependencies = [
|
|||
"rustc_index",
|
||||
"rustc_macros",
|
||||
"rustc_middle",
|
||||
"rustc_parse",
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
"rustc_target",
|
||||
|
|
@ -3263,14 +3265,10 @@ dependencies = [
|
|||
"rustc_ast",
|
||||
"rustc_ast_pretty",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_feature",
|
||||
"rustc_fluent_macro",
|
||||
"rustc_lexer",
|
||||
"rustc_macros",
|
||||
"rustc_serialize",
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
"thin-vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3285,11 +3283,13 @@ dependencies = [
|
|||
"rustc_errors",
|
||||
"rustc_feature",
|
||||
"rustc_fluent_macro",
|
||||
"rustc_hir",
|
||||
"rustc_lexer",
|
||||
"rustc_macros",
|
||||
"rustc_serialize",
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
"thin-vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3342,6 +3342,7 @@ dependencies = [
|
|||
"rustc_expand",
|
||||
"rustc_feature",
|
||||
"rustc_fluent_macro",
|
||||
"rustc_hir",
|
||||
"rustc_index",
|
||||
"rustc_lexer",
|
||||
"rustc_lint_defs",
|
||||
|
|
@ -3598,6 +3599,7 @@ dependencies = [
|
|||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_ast_pretty",
|
||||
"rustc_attr_data_structures",
|
||||
"rustc_data_structures",
|
||||
"rustc_error_codes",
|
||||
"rustc_error_messages",
|
||||
|
|
@ -3632,6 +3634,7 @@ dependencies = [
|
|||
"rustc_errors",
|
||||
"rustc_feature",
|
||||
"rustc_fluent_macro",
|
||||
"rustc_hir",
|
||||
"rustc_lexer",
|
||||
"rustc_lint_defs",
|
||||
"rustc_macros",
|
||||
|
|
@ -3690,6 +3693,7 @@ dependencies = [
|
|||
"rustc_abi",
|
||||
"rustc_arena",
|
||||
"rustc_ast",
|
||||
"rustc_attr_data_structures",
|
||||
"rustc_data_structures",
|
||||
"rustc_hashes",
|
||||
"rustc_index",
|
||||
|
|
@ -3737,6 +3741,7 @@ dependencies = [
|
|||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_ast_pretty",
|
||||
"rustc_attr_parsing",
|
||||
"rustc_hir",
|
||||
"rustc_span",
|
||||
]
|
||||
|
|
@ -4244,6 +4249,7 @@ name = "rustc_query_impl"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"measureme",
|
||||
"rustc_attr_data_structures",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_hashes",
|
||||
|
|
@ -4266,6 +4272,7 @@ dependencies = [
|
|||
"rustc-rayon-core",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_attr_data_structures",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_feature",
|
||||
|
|
@ -4316,6 +4323,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"bitflags",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_data_structures",
|
||||
"rustc_hir",
|
||||
"rustc_middle",
|
||||
|
|
@ -4412,6 +4420,7 @@ dependencies = [
|
|||
"punycode",
|
||||
"rustc-demangle",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_hashes",
|
||||
|
|
@ -4571,6 +4580,7 @@ dependencies = [
|
|||
"itertools",
|
||||
"minifier",
|
||||
"pulldown-cmark 0.9.6",
|
||||
"pulldown-cmark-escape",
|
||||
"regex",
|
||||
"rinja",
|
||||
"rustdoc-json-types",
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ doctest = false
|
|||
rustc_abi = { path = "../rustc_abi" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
|
||||
rustc_attr_parsing = { path = "../rustc_attr_parsing" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
rustc_feature = { path = "../rustc_feature" }
|
||||
|
|
@ -19,6 +20,7 @@ rustc_hir = { path = "../rustc_hir" }
|
|||
rustc_index = { path = "../rustc_index" }
|
||||
rustc_macros = { path = "../rustc_macros" }
|
||||
rustc_middle = { path = "../rustc_middle" }
|
||||
rustc_parse = { path = "../rustc_parse" }
|
||||
rustc_session = { path = "../rustc_session" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_target = { path = "../rustc_target" }
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
};
|
||||
let span = self.lower_span(l.span);
|
||||
let source = hir::LocalSource::Normal;
|
||||
self.lower_attrs(hir_id, &l.attrs);
|
||||
self.lower_attrs(hir_id, &l.attrs, l.span);
|
||||
self.arena.alloc(hir::LetStmt { hir_id, ty, pat, init, els, span, source })
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,9 +77,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
self.attrs.insert(
|
||||
ex.hir_id.local_id,
|
||||
&*self.arena.alloc_from_iter(
|
||||
e.attrs
|
||||
.iter()
|
||||
.map(|a| self.lower_attr(a))
|
||||
self.lower_attrs_vec(&e.attrs, e.span)
|
||||
.into_iter()
|
||||
.chain(old_attrs.iter().cloned()),
|
||||
),
|
||||
);
|
||||
|
|
@ -98,7 +97,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
}
|
||||
|
||||
let expr_hir_id = self.lower_node_id(e.id);
|
||||
self.lower_attrs(expr_hir_id, &e.attrs);
|
||||
self.lower_attrs(expr_hir_id, &e.attrs, e.span);
|
||||
|
||||
let kind = match &e.kind {
|
||||
ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)),
|
||||
|
|
@ -670,7 +669,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let guard = arm.guard.as_ref().map(|cond| self.lower_expr(cond));
|
||||
let hir_id = self.next_id();
|
||||
let span = self.lower_span(arm.span);
|
||||
self.lower_attrs(hir_id, &arm.attrs);
|
||||
self.lower_attrs(hir_id, &arm.attrs, arm.span);
|
||||
let is_never_pattern = pat.is_never_pattern();
|
||||
let body = if let Some(body) = &arm.body
|
||||
&& !is_never_pattern
|
||||
|
|
@ -839,6 +838,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
style: AttrStyle::Outer,
|
||||
span: unstable_span,
|
||||
}],
|
||||
span,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1673,7 +1673,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
|
||||
fn lower_expr_field(&mut self, f: &ExprField) -> hir::ExprField<'hir> {
|
||||
let hir_id = self.lower_node_id(f.id);
|
||||
self.lower_attrs(hir_id, &f.attrs);
|
||||
self.lower_attrs(hir_id, &f.attrs, f.span);
|
||||
hir::ExprField {
|
||||
hir_id,
|
||||
ident: self.lower_ident(f.ident),
|
||||
|
|
@ -1936,7 +1936,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
//
|
||||
// Also, add the attributes to the outer returned expr node.
|
||||
let expr = self.expr_drop_temps_mut(for_span, match_expr);
|
||||
self.lower_attrs(expr.hir_id, &e.attrs);
|
||||
self.lower_attrs(expr.hir_id, &e.attrs, e.span);
|
||||
expr
|
||||
}
|
||||
|
||||
|
|
@ -1993,7 +1993,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let val_ident = Ident::with_dummy_span(sym::val);
|
||||
let (val_pat, val_pat_nid) = self.pat_ident(span, val_ident);
|
||||
let val_expr = self.expr_ident(span, val_ident, val_pat_nid);
|
||||
self.lower_attrs(val_expr.hir_id, &attrs);
|
||||
self.lower_attrs(val_expr.hir_id, &attrs, span);
|
||||
let continue_pat = self.pat_cf_continue(unstable_span, val_pat);
|
||||
self.arm(continue_pat, val_expr)
|
||||
};
|
||||
|
|
@ -2024,7 +2024,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let ret_expr = self.checked_return(Some(from_residual_expr));
|
||||
self.arena.alloc(self.expr(try_span, ret_expr))
|
||||
};
|
||||
self.lower_attrs(ret_expr.hir_id, &attrs);
|
||||
self.lower_attrs(ret_expr.hir_id, &attrs, ret_expr.span);
|
||||
|
||||
let break_pat = self.pat_cf_break(try_span, residual_local);
|
||||
self.arm(break_pat, ret_expr)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use rustc_index::{IndexSlice, IndexVec};
|
|||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
|
||||
use rustc_span::edit_distance::find_best_match_for_name;
|
||||
use rustc_span::{DesugaringKind, Ident, Span, Symbol, kw, sym};
|
||||
use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym};
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use thin_vec::ThinVec;
|
||||
use tracing::instrument;
|
||||
|
|
@ -93,7 +93,8 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
|
|||
debug_assert_eq!(self.resolver.node_id_to_def_id[&CRATE_NODE_ID], CRATE_DEF_ID);
|
||||
self.with_lctx(CRATE_NODE_ID, |lctx| {
|
||||
let module = lctx.lower_mod(&c.items, &c.spans);
|
||||
lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs);
|
||||
// FIXME(jdonszelman): is dummy span ever a problem here?
|
||||
lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs, DUMMY_SP);
|
||||
hir::OwnerNode::Crate(module)
|
||||
})
|
||||
}
|
||||
|
|
@ -157,7 +158,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let mut ident = i.ident;
|
||||
let vis_span = self.lower_span(i.vis.span);
|
||||
let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
|
||||
let attrs = self.lower_attrs(hir_id, &i.attrs);
|
||||
let attrs = self.lower_attrs(hir_id, &i.attrs, i.span);
|
||||
let kind = self.lower_item_kind(i.span, i.id, hir_id, &mut ident, attrs, vis_span, &i.kind);
|
||||
let item = hir::Item {
|
||||
owner_id: hir_id.expect_owner(),
|
||||
|
|
@ -620,7 +621,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
fn lower_foreign_item(&mut self, i: &ForeignItem) -> &'hir hir::ForeignItem<'hir> {
|
||||
let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
|
||||
let owner_id = hir_id.expect_owner();
|
||||
let attrs = self.lower_attrs(hir_id, &i.attrs);
|
||||
let attrs = self.lower_attrs(hir_id, &i.attrs, i.span);
|
||||
let item = hir::ForeignItem {
|
||||
owner_id,
|
||||
ident: self.lower_ident(i.ident),
|
||||
|
|
@ -678,7 +679,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
|
||||
fn lower_variant(&mut self, v: &Variant) -> hir::Variant<'hir> {
|
||||
let hir_id = self.lower_node_id(v.id);
|
||||
self.lower_attrs(hir_id, &v.attrs);
|
||||
self.lower_attrs(hir_id, &v.attrs, v.span);
|
||||
hir::Variant {
|
||||
hir_id,
|
||||
def_id: self.local_def_id(v.id),
|
||||
|
|
@ -740,7 +741,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
) -> hir::FieldDef<'hir> {
|
||||
let ty = self.lower_ty(&f.ty, ImplTraitContext::Disallowed(ImplTraitPosition::FieldTy));
|
||||
let hir_id = self.lower_node_id(f.id);
|
||||
self.lower_attrs(hir_id, &f.attrs);
|
||||
self.lower_attrs(hir_id, &f.attrs, f.span);
|
||||
hir::FieldDef {
|
||||
span: self.lower_span(f.span),
|
||||
hir_id,
|
||||
|
|
@ -759,7 +760,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
|
||||
fn lower_trait_item(&mut self, i: &AssocItem) -> &'hir hir::TraitItem<'hir> {
|
||||
let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
|
||||
let attrs = self.lower_attrs(hir_id, &i.attrs);
|
||||
let attrs = self.lower_attrs(hir_id, &i.attrs, i.span);
|
||||
let trait_item_def_id = hir_id.expect_owner();
|
||||
|
||||
let (generics, kind, has_default) = match &i.kind {
|
||||
|
|
@ -895,7 +896,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let has_value = true;
|
||||
let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value);
|
||||
let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
|
||||
let attrs = self.lower_attrs(hir_id, &i.attrs);
|
||||
let attrs = self.lower_attrs(hir_id, &i.attrs, i.span);
|
||||
|
||||
let (generics, kind) = match &i.kind {
|
||||
AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => self.lower_generics(
|
||||
|
|
@ -1056,7 +1057,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
|
||||
fn lower_param(&mut self, param: &Param) -> hir::Param<'hir> {
|
||||
let hir_id = self.lower_node_id(param.id);
|
||||
self.lower_attrs(hir_id, ¶m.attrs);
|
||||
self.lower_attrs(hir_id, ¶m.attrs, param.span);
|
||||
hir::Param {
|
||||
hir_id,
|
||||
pat: self.lower_pat(¶m.pat),
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ use std::sync::Arc;
|
|||
|
||||
use rustc_ast::node_id::NodeMap;
|
||||
use rustc_ast::{self as ast, *};
|
||||
use rustc_attr_parsing::{AttributeParser, OmitDoc};
|
||||
use rustc_data_structures::fingerprint::Fingerprint;
|
||||
use rustc_data_structures::sorted_map::SortedMap;
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
|
|
@ -60,7 +61,8 @@ use rustc_macros::extension;
|
|||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
|
||||
use rustc_session::parse::{add_feature_diagnostics, feature_err};
|
||||
use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym};
|
||||
use rustc_span::symbol::{Ident, Symbol, kw, sym};
|
||||
use rustc_span::{DUMMY_SP, DesugaringKind, Span};
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use thin_vec::ThinVec;
|
||||
use tracing::{debug, instrument, trace};
|
||||
|
|
@ -137,10 +139,13 @@ struct LoweringContext<'a, 'hir> {
|
|||
allow_async_iterator: Arc<[Symbol]>,
|
||||
allow_for_await: Arc<[Symbol]>,
|
||||
allow_async_fn_traits: Arc<[Symbol]>,
|
||||
|
||||
attribute_parser: AttributeParser<'hir>,
|
||||
}
|
||||
|
||||
impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
fn new(tcx: TyCtxt<'hir>, resolver: &'a mut ResolverAstLowering) -> Self {
|
||||
let registered_tools = tcx.registered_tools(()).iter().map(|x| x.name).collect();
|
||||
Self {
|
||||
// Pseudo-globals.
|
||||
tcx,
|
||||
|
|
@ -181,6 +186,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
// FIXME(gen_blocks): how does `closure_track_caller`/`async_fn_track_caller`
|
||||
// interact with `gen`/`async gen` blocks
|
||||
allow_async_iterator: [sym::gen_future, sym::async_iterator].into(),
|
||||
|
||||
attribute_parser: AttributeParser::new(tcx.sess, tcx.features(), registered_tools),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -216,7 +223,6 @@ impl ResolverAstLowering {
|
|||
None
|
||||
}
|
||||
|
||||
/// Obtains resolution for a `NodeId` with a single resolution.
|
||||
fn get_partial_res(&self, id: NodeId) -> Option<PartialRes> {
|
||||
self.partial_res_map.get(&id).copied()
|
||||
}
|
||||
|
|
@ -855,45 +861,38 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
ret
|
||||
}
|
||||
|
||||
fn lower_attrs(&mut self, id: HirId, attrs: &[Attribute]) -> &'hir [hir::Attribute] {
|
||||
fn lower_attrs(
|
||||
&mut self,
|
||||
id: HirId,
|
||||
attrs: &[Attribute],
|
||||
target_span: Span,
|
||||
) -> &'hir [hir::Attribute] {
|
||||
if attrs.is_empty() {
|
||||
&[]
|
||||
} else {
|
||||
let lowered_attrs = self.lower_attrs_vec(attrs, self.lower_span(target_span));
|
||||
|
||||
debug_assert_eq!(id.owner, self.current_hir_id_owner);
|
||||
let ret = self.arena.alloc_from_iter(attrs.iter().map(|a| self.lower_attr(a)));
|
||||
debug_assert!(!ret.is_empty());
|
||||
self.attrs.insert(id.local_id, ret);
|
||||
ret
|
||||
let ret = self.arena.alloc_from_iter(lowered_attrs);
|
||||
|
||||
// this is possible if an item contained syntactical attribute,
|
||||
// but none of them parse succesfully or all of them were ignored
|
||||
// for not being built-in attributes at all. They could be remaining
|
||||
// unexpanded attributes used as markers in proc-macro derives for example.
|
||||
// This will have emitted some diagnostics for the misparse, but will then
|
||||
// not emit the attribute making the list empty.
|
||||
if ret.is_empty() {
|
||||
&[]
|
||||
} else {
|
||||
self.attrs.insert(id.local_id, ret);
|
||||
ret
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_attr(&self, attr: &Attribute) -> hir::Attribute {
|
||||
// Note that we explicitly do not walk the path. Since we don't really
|
||||
// lower attributes (we use the AST version) there is nowhere to keep
|
||||
// the `HirId`s. We don't actually need HIR version of attributes anyway.
|
||||
// Tokens are also not needed after macro expansion and parsing.
|
||||
let kind = match attr.kind {
|
||||
AttrKind::Normal(ref normal) => hir::AttrKind::Normal(Box::new(hir::AttrItem {
|
||||
unsafety: self.lower_safety(normal.item.unsafety, hir::Safety::Safe),
|
||||
path: hir::AttrPath {
|
||||
segments: normal
|
||||
.item
|
||||
.path
|
||||
.segments
|
||||
.iter()
|
||||
.map(|i| i.ident)
|
||||
.collect::<Vec<_>>()
|
||||
.into_boxed_slice(),
|
||||
span: normal.item.path.span,
|
||||
},
|
||||
args: self.lower_attr_args(&normal.item.args),
|
||||
})),
|
||||
AttrKind::DocComment(comment_kind, data) => {
|
||||
hir::AttrKind::DocComment(comment_kind, data)
|
||||
}
|
||||
};
|
||||
|
||||
hir::Attribute { kind, id: attr.id, style: attr.style, span: self.lower_span(attr.span) }
|
||||
fn lower_attrs_vec(&self, attrs: &[Attribute], target_span: Span) -> Vec<hir::Attribute> {
|
||||
self.attribute_parser
|
||||
.parse_attribute_list(attrs, target_span, OmitDoc::Lower, |s| self.lower_span(s))
|
||||
}
|
||||
|
||||
fn alias_attrs(&mut self, id: HirId, target_id: HirId) {
|
||||
|
|
@ -905,34 +904,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
}
|
||||
}
|
||||
|
||||
fn lower_attr_args(&self, args: &AttrArgs) -> hir::AttrArgs {
|
||||
match args {
|
||||
AttrArgs::Empty => hir::AttrArgs::Empty,
|
||||
AttrArgs::Delimited(args) => hir::AttrArgs::Delimited(self.lower_delim_args(args)),
|
||||
// This is an inert key-value attribute - it will never be visible to macros
|
||||
// after it gets lowered to HIR. Therefore, we can extract literals to handle
|
||||
// nonterminals in `#[doc]` (e.g. `#[doc = $e]`).
|
||||
&AttrArgs::Eq { eq_span, ref expr } => {
|
||||
// In valid code the value always ends up as a single literal. Otherwise, a dummy
|
||||
// literal suffices because the error is handled elsewhere.
|
||||
let lit = if let ExprKind::Lit(token_lit) = expr.kind
|
||||
&& let Ok(lit) = MetaItemLit::from_token_lit(token_lit, expr.span)
|
||||
{
|
||||
lit
|
||||
} else {
|
||||
let guar = self.dcx().has_errors().unwrap();
|
||||
MetaItemLit {
|
||||
symbol: kw::Empty,
|
||||
suffix: None,
|
||||
kind: LitKind::Err(guar),
|
||||
span: DUMMY_SP,
|
||||
}
|
||||
};
|
||||
hir::AttrArgs::Eq { eq_span, expr: lit }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_delim_args(&self, args: &DelimArgs) -> DelimArgs {
|
||||
DelimArgs { dspan: args.dspan, delim: args.delim, tokens: args.tokens.flattened() }
|
||||
}
|
||||
|
|
@ -1845,7 +1816,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
let (name, kind) = self.lower_generic_param_kind(param, source);
|
||||
|
||||
let hir_id = self.lower_node_id(param.id);
|
||||
self.lower_attrs(hir_id, ¶m.attrs);
|
||||
self.lower_attrs(hir_id, ¶m.attrs, param.span());
|
||||
hir::GenericParam {
|
||||
hir_id,
|
||||
def_id: self.local_def_id(param.id),
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
|
||||
let fs = self.arena.alloc_from_iter(fields.iter().map(|f| {
|
||||
let hir_id = self.lower_node_id(f.id);
|
||||
self.lower_attrs(hir_id, &f.attrs);
|
||||
self.lower_attrs(hir_id, &f.attrs, f.span);
|
||||
|
||||
hir::PatField {
|
||||
hir_id,
|
||||
|
|
|
|||
|
|
@ -207,8 +207,6 @@ ast_passes_precise_capturing_duplicated = duplicate `use<...>` precise capturing
|
|||
|
||||
ast_passes_precise_capturing_not_allowed_here = `use<...>` precise capturing syntax not allowed in {$loc}
|
||||
|
||||
ast_passes_stability_outside_std = stability attributes may not be used outside of the standard library
|
||||
|
||||
ast_passes_static_without_body =
|
||||
free static item without body
|
||||
.suggestion = provide a definition for the static
|
||||
|
|
|
|||
|
|
@ -732,13 +732,6 @@ pub(crate) struct AssociatedSuggestion2 {
|
|||
pub potential_assoc: Ident,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_stability_outside_std, code = E0734)]
|
||||
pub(crate) struct StabilityOutsideStd {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_feature_on_non_nightly, code = E0554)]
|
||||
pub(crate) struct FeatureOnNonNightly {
|
||||
|
|
|
|||
|
|
@ -178,18 +178,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Emit errors for non-staged-api crates.
|
||||
if !self.features.staged_api() {
|
||||
if attr.has_name(sym::unstable)
|
||||
|| attr.has_name(sym::stable)
|
||||
|| attr.has_name(sym::rustc_const_unstable)
|
||||
|| attr.has_name(sym::rustc_const_stable)
|
||||
|| attr.has_name(sym::rustc_default_body_unstable)
|
||||
{
|
||||
self.sess.dcx().emit_err(errors::StabilityOutsideStd { span: attr.span });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_item(&mut self, i: &'a ast::Item) {
|
||||
|
|
|
|||
|
|
@ -5,16 +5,12 @@ edition = "2024"
|
|||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
rustc_abi = { path = "../rustc_abi" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
rustc_feature = { path = "../rustc_feature" }
|
||||
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
|
||||
rustc_lexer = { path = "../rustc_lexer" }
|
||||
rustc_macros = { path = "../rustc_macros" }
|
||||
rustc_serialize = { path = "../rustc_serialize" }
|
||||
rustc_session = { path = "../rustc_session" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_abi = {path = "../rustc_abi"}
|
||||
rustc_ast = {path = "../rustc_ast"}
|
||||
rustc_ast_pretty = {path = "../rustc_ast_pretty"}
|
||||
rustc_data_structures = {path = "../rustc_data_structures"}
|
||||
rustc_macros = {path = "../rustc_macros"}
|
||||
rustc_serialize = {path = "../rustc_serialize"}
|
||||
rustc_span = {path = "../rustc_span"}
|
||||
thin-vec = "0.2.12"
|
||||
# tidy-alphabetical-end
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
use rustc_abi::Align;
|
||||
use rustc_ast as ast;
|
||||
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
|
||||
use rustc_ast::token::CommentKind;
|
||||
use rustc_ast::{self as ast, AttrStyle};
|
||||
use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute};
|
||||
use rustc_span::hygiene::Transparency;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use crate::RustcVersion;
|
||||
use crate::{DefaultBodyStability, PartialConstStability, PrintAttribute, RustcVersion, Stability};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
|
||||
pub enum InlineAttr {
|
||||
|
|
@ -54,7 +57,7 @@ impl OptimizeAttr {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Encodable, Decodable)]
|
||||
#[derive(Clone, Debug, Encodable, Decodable, HashStable_Generic, PrintAttribute)]
|
||||
pub enum DiagnosticAttribute {
|
||||
// tidy-alphabetical-start
|
||||
DoNotRecommend,
|
||||
|
|
@ -62,7 +65,7 @@ pub enum DiagnosticAttribute {
|
|||
// tidy-alphabetical-end
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Encodable, Decodable, Copy, Clone)]
|
||||
#[derive(PartialEq, Debug, Encodable, Decodable, Copy, Clone, HashStable_Generic, PrintAttribute)]
|
||||
pub enum ReprAttr {
|
||||
ReprInt(IntType),
|
||||
ReprRust,
|
||||
|
|
@ -71,6 +74,8 @@ pub enum ReprAttr {
|
|||
ReprSimd,
|
||||
ReprTransparent,
|
||||
ReprAlign(Align),
|
||||
// this one is just so we can emit a lint for it
|
||||
ReprEmpty,
|
||||
}
|
||||
pub use ReprAttr::*;
|
||||
|
||||
|
|
@ -80,13 +85,13 @@ pub enum TransparencyError {
|
|||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
|
||||
#[derive(Encodable, Decodable)]
|
||||
#[derive(Encodable, Decodable, HashStable_Generic, PrintAttribute)]
|
||||
pub enum IntType {
|
||||
SignedInt(ast::IntTy),
|
||||
UnsignedInt(ast::UintTy),
|
||||
}
|
||||
|
||||
#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)]
|
||||
#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic, PrintAttribute)]
|
||||
pub struct Deprecation {
|
||||
pub since: DeprecatedSince,
|
||||
/// The note to issue a reason.
|
||||
|
|
@ -98,7 +103,7 @@ pub struct Deprecation {
|
|||
}
|
||||
|
||||
/// Release in which an API is deprecated.
|
||||
#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)]
|
||||
#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic, PrintAttribute)]
|
||||
pub enum DeprecatedSince {
|
||||
RustcVersion(RustcVersion),
|
||||
/// Deprecated in the future ("to be determined").
|
||||
|
|
@ -132,3 +137,61 @@ impl Deprecation {
|
|||
matches!(self.since, DeprecatedSince::RustcVersion(_))
|
||||
}
|
||||
}
|
||||
|
||||
/// Attributes represent parsed, *built in*, inert attributes. That means,
|
||||
/// attributes that are not actually ever expanded.
|
||||
/// For more information on this, see the module docs on the rustc_attr_parsing crate.
|
||||
/// They're instead used as markers, to guide the compilation process in various way in most every stage of the compiler.
|
||||
/// These are kept around after the AST, into the HIR and further on.
|
||||
///
|
||||
/// The word parsed could be a little misleading here, because the parser already parses
|
||||
/// attributes early on. However, the result, an [`ast::Attribute`]
|
||||
/// is only parsed at a high level, still containing a token stream in many cases. That is
|
||||
/// because the structure of the contents varies from attribute to attribute.
|
||||
/// With a parsed attribute I mean that each attribute is processed individually into a
|
||||
/// final structure, which on-site (the place where the attribute is useful for, think the
|
||||
/// the place where `must_use` is checked) little to no extra parsing or validating needs to
|
||||
/// happen.
|
||||
///
|
||||
/// For more docs, look in [`rustc_attr`](https://doc.rust-lang.org/stable/nightly-rustc/rustc_attr/index.html)
|
||||
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
|
||||
pub enum AttributeKind {
|
||||
// tidy-alphabetical-start
|
||||
AllowConstFnUnstable(ThinVec<Symbol>),
|
||||
AllowInternalUnstable(ThinVec<(Symbol, Span)>),
|
||||
BodyStability {
|
||||
stability: DefaultBodyStability,
|
||||
/// Span of the `#[rustc_default_body_unstable(...)]` attribute
|
||||
span: Span,
|
||||
},
|
||||
Confusables {
|
||||
symbols: ThinVec<Symbol>,
|
||||
// FIXME(jdonszelmann): remove when target validation code is moved
|
||||
first_span: Span,
|
||||
},
|
||||
ConstStability {
|
||||
stability: PartialConstStability,
|
||||
/// Span of the `#[rustc_const_stable(...)]` or `#[rustc_const_unstable(...)]` attribute
|
||||
span: Span,
|
||||
},
|
||||
ConstStabilityIndirect,
|
||||
Deprecation {
|
||||
deprecation: Deprecation,
|
||||
span: Span,
|
||||
},
|
||||
Diagnostic(DiagnosticAttribute),
|
||||
DocComment {
|
||||
style: AttrStyle,
|
||||
kind: CommentKind,
|
||||
span: Span,
|
||||
comment: Symbol,
|
||||
},
|
||||
MacroTransparency(Transparency),
|
||||
Repr(ThinVec<(ReprAttr, Span)>),
|
||||
Stability {
|
||||
stability: Stability,
|
||||
/// Span of the `#[stable(...)]` or `#[unstable(...)]` attribute
|
||||
span: Span,
|
||||
},
|
||||
// tidy-alphabetical-end
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,142 @@ mod attributes;
|
|||
mod stability;
|
||||
mod version;
|
||||
|
||||
use std::num::NonZero;
|
||||
|
||||
pub use attributes::*;
|
||||
pub(crate) use rustc_session::HashStableContext;
|
||||
use rustc_abi::Align;
|
||||
use rustc_ast::token::CommentKind;
|
||||
use rustc_ast::{AttrStyle, IntTy, UintTy};
|
||||
use rustc_ast_pretty::pp::Printer;
|
||||
use rustc_span::hygiene::Transparency;
|
||||
use rustc_span::{Span, Symbol};
|
||||
pub use stability::*;
|
||||
use thin_vec::ThinVec;
|
||||
pub use version::*;
|
||||
|
||||
/// Requirements for a `StableHashingContext` to be used in this crate.
|
||||
/// This is a hack to allow using the `HashStable_Generic` derive macro
|
||||
/// instead of implementing everything in `rustc_middle`.
|
||||
pub trait HashStableContext: rustc_ast::HashStableContext + rustc_abi::HashStableContext {}
|
||||
|
||||
/// This trait is used to print attributes in `rustc_hir_pretty`.
|
||||
///
|
||||
/// For structs and enums it can be derived using [`rustc_macros::PrintAttribute`].
|
||||
/// The output will look a lot like a `Debug` implementation, but fields of several types
|
||||
/// like [`Span`]s and empty tuples, are gracefully skipped so they don't clutter the
|
||||
/// representation much.
|
||||
pub trait PrintAttribute {
|
||||
fn print_something(&self) -> bool;
|
||||
fn print_attribute(&self, p: &mut Printer);
|
||||
}
|
||||
|
||||
impl<T: PrintAttribute> PrintAttribute for &T {
|
||||
fn print_something(&self) -> bool {
|
||||
T::print_something(self)
|
||||
}
|
||||
|
||||
fn print_attribute(&self, p: &mut Printer) {
|
||||
T::print_attribute(self, p)
|
||||
}
|
||||
}
|
||||
impl<T: PrintAttribute> PrintAttribute for Option<T> {
|
||||
fn print_something(&self) -> bool {
|
||||
self.as_ref().is_some_and(|x| x.print_something())
|
||||
}
|
||||
fn print_attribute(&self, p: &mut Printer) {
|
||||
if let Some(i) = self {
|
||||
T::print_attribute(i, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T: PrintAttribute> PrintAttribute for ThinVec<T> {
|
||||
fn print_something(&self) -> bool {
|
||||
self.is_empty() || self[0].print_something()
|
||||
}
|
||||
fn print_attribute(&self, p: &mut Printer) {
|
||||
let mut last_printed = false;
|
||||
p.word("[");
|
||||
for i in self {
|
||||
if last_printed {
|
||||
p.word_space(",");
|
||||
}
|
||||
i.print_attribute(p);
|
||||
last_printed = i.print_something();
|
||||
}
|
||||
p.word("]");
|
||||
}
|
||||
}
|
||||
macro_rules! print_skip {
|
||||
($($t: ty),* $(,)?) => {$(
|
||||
impl PrintAttribute for $t {
|
||||
fn print_something(&self) -> bool { false }
|
||||
fn print_attribute(&self, _: &mut Printer) { }
|
||||
})*
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! print_disp {
|
||||
($($t: ty),* $(,)?) => {$(
|
||||
impl PrintAttribute for $t {
|
||||
fn print_something(&self) -> bool { true }
|
||||
fn print_attribute(&self, p: &mut Printer) {
|
||||
p.word(format!("{}", self));
|
||||
}
|
||||
}
|
||||
)*};
|
||||
}
|
||||
macro_rules! print_debug {
|
||||
($($t: ty),* $(,)?) => {$(
|
||||
impl PrintAttribute for $t {
|
||||
fn print_something(&self) -> bool { true }
|
||||
fn print_attribute(&self, p: &mut Printer) {
|
||||
p.word(format!("{:?}", self));
|
||||
}
|
||||
}
|
||||
)*};
|
||||
}
|
||||
|
||||
macro_rules! print_tup {
|
||||
(num_print_something $($ts: ident)*) => { 0 $(+ $ts.print_something() as usize)* };
|
||||
() => {};
|
||||
($t: ident $($ts: ident)*) => {
|
||||
#[allow(non_snake_case, unused)]
|
||||
impl<$t: PrintAttribute, $($ts: PrintAttribute),*> PrintAttribute for ($t, $($ts),*) {
|
||||
fn print_something(&self) -> bool {
|
||||
let ($t, $($ts),*) = self;
|
||||
print_tup!(num_print_something $t $($ts)*) != 0
|
||||
}
|
||||
|
||||
fn print_attribute(&self, p: &mut Printer) {
|
||||
let ($t, $($ts),*) = self;
|
||||
let parens = print_tup!(num_print_something $t $($ts)*) > 1;
|
||||
if parens {
|
||||
p.word("(");
|
||||
}
|
||||
|
||||
let mut printed_anything = $t.print_something();
|
||||
|
||||
$t.print_attribute(p);
|
||||
|
||||
$(
|
||||
if printed_anything && $ts.print_something() {
|
||||
p.word_space(",");
|
||||
printed_anything = true;
|
||||
}
|
||||
$ts.print_attribute(p);
|
||||
)*
|
||||
|
||||
if parens {
|
||||
p.word(")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print_tup!($($ts)*);
|
||||
};
|
||||
}
|
||||
|
||||
print_tup!(A B C D E F G H);
|
||||
print_skip!(Span, ());
|
||||
print_disp!(Symbol, u16, bool, NonZero<u32>);
|
||||
print_debug!(UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use std::num::NonZero;
|
||||
|
||||
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
|
||||
use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute};
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
||||
use crate::RustcVersion;
|
||||
use crate::{PrintAttribute, RustcVersion};
|
||||
|
||||
/// The version placeholder that recently stabilized features contain inside the
|
||||
/// `since` field of the `#[stable]` attribute.
|
||||
|
|
@ -21,7 +21,7 @@ pub const VERSION_PLACEHOLDER: &str = concat!("CURRENT_RUSTC_VERSIO", "N");
|
|||
/// - `#[stable]`
|
||||
/// - `#[unstable]`
|
||||
#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[derive(HashStable_Generic)]
|
||||
#[derive(HashStable_Generic, PrintAttribute)]
|
||||
pub struct Stability {
|
||||
pub level: StabilityLevel,
|
||||
pub feature: Symbol,
|
||||
|
|
@ -43,7 +43,7 @@ impl Stability {
|
|||
|
||||
/// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes.
|
||||
#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[derive(HashStable_Generic)]
|
||||
#[derive(HashStable_Generic, PrintAttribute)]
|
||||
pub struct ConstStability {
|
||||
pub level: StabilityLevel,
|
||||
pub feature: Symbol,
|
||||
|
|
@ -83,7 +83,7 @@ impl ConstStability {
|
|||
/// Excludes `const_stable_indirect`. This is necessary because when `-Zforce-unstable-if-unmarked`
|
||||
/// is set, we need to encode standalone `#[rustc_const_stable_indirect]` attributes
|
||||
#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[derive(HashStable_Generic)]
|
||||
#[derive(HashStable_Generic, PrintAttribute)]
|
||||
pub struct PartialConstStability {
|
||||
pub level: StabilityLevel,
|
||||
pub feature: Symbol,
|
||||
|
|
@ -103,7 +103,7 @@ impl PartialConstStability {
|
|||
|
||||
/// The available stability levels.
|
||||
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
|
||||
#[derive(HashStable_Generic)]
|
||||
#[derive(HashStable_Generic, PrintAttribute)]
|
||||
pub enum StabilityLevel {
|
||||
/// `#[unstable]`
|
||||
Unstable {
|
||||
|
|
@ -145,7 +145,7 @@ pub enum StabilityLevel {
|
|||
|
||||
/// Rust release in which a feature is stabilized.
|
||||
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, PartialOrd, Ord, Hash)]
|
||||
#[derive(HashStable_Generic)]
|
||||
#[derive(HashStable_Generic, PrintAttribute)]
|
||||
pub enum StableSince {
|
||||
/// also stores the original symbol for printing
|
||||
Version(RustcVersion),
|
||||
|
|
@ -171,7 +171,7 @@ impl StabilityLevel {
|
|||
}
|
||||
|
||||
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
|
||||
#[derive(HashStable_Generic)]
|
||||
#[derive(HashStable_Generic, PrintAttribute)]
|
||||
pub enum UnstableReason {
|
||||
None,
|
||||
Default,
|
||||
|
|
@ -180,7 +180,7 @@ pub enum UnstableReason {
|
|||
|
||||
/// Represents the `#[rustc_default_body_unstable]` attribute.
|
||||
#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[derive(HashStable_Generic)]
|
||||
#[derive(HashStable_Generic, PrintAttribute)]
|
||||
pub struct DefaultBodyStability {
|
||||
pub level: StabilityLevel,
|
||||
pub feature: Symbol,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
use std::fmt::{self, Display};
|
||||
|
||||
use rustc_macros::{Decodable, Encodable, HashStable_Generic, current_rustc_version};
|
||||
use rustc_macros::{
|
||||
Decodable, Encodable, HashStable_Generic, PrintAttribute, current_rustc_version,
|
||||
};
|
||||
|
||||
use crate::PrintAttribute;
|
||||
|
||||
#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[derive(HashStable_Generic)]
|
||||
#[derive(HashStable_Generic, PrintAttribute)]
|
||||
pub struct RustcVersion {
|
||||
pub major: u16,
|
||||
pub minor: u16,
|
||||
|
|
|
|||
|
|
@ -13,9 +13,11 @@ rustc_data_structures = { path = "../rustc_data_structures" }
|
|||
rustc_errors = { path = "../rustc_errors" }
|
||||
rustc_feature = { path = "../rustc_feature" }
|
||||
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
|
||||
rustc_hir = { path = "../rustc_hir" }
|
||||
rustc_lexer = { path = "../rustc_lexer" }
|
||||
rustc_macros = { path = "../rustc_macros" }
|
||||
rustc_serialize = { path = "../rustc_serialize" }
|
||||
rustc_session = { path = "../rustc_session" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
thin-vec = "0.2.12"
|
||||
# tidy-alphabetical-end
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ attr_parsing_deprecated_item_suggestion =
|
|||
.help = add `#![feature(deprecated_suggestion)]` to the crate root
|
||||
.note = see #94785 for more details
|
||||
|
||||
attr_parsing_empty_confusables =
|
||||
expected at least one confusable name
|
||||
attr_parsing_expected_one_cfg_pattern =
|
||||
expected 1 cfg-pattern
|
||||
|
||||
|
|
@ -21,8 +23,8 @@ attr_parsing_expects_feature_list =
|
|||
attr_parsing_expects_features =
|
||||
`{$name}` expects feature names
|
||||
|
||||
attr_parsing_incorrect_meta_item =
|
||||
incorrect meta item
|
||||
attr_parsing_incorrect_meta_item = expected a quoted string literal
|
||||
attr_parsing_incorrect_meta_item_suggestion = consider surrounding this with quotes
|
||||
|
||||
attr_parsing_incorrect_repr_format_align_one_arg =
|
||||
incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses
|
||||
|
|
@ -88,18 +90,20 @@ attr_parsing_multiple_stability_levels =
|
|||
attr_parsing_non_ident_feature =
|
||||
'feature' is not an identifier
|
||||
|
||||
attr_parsing_repr_ident =
|
||||
meta item in `repr` must be an identifier
|
||||
|
||||
attr_parsing_rustc_allowed_unstable_pairing =
|
||||
`rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute
|
||||
|
||||
attr_parsing_rustc_const_stable_indirect_pairing =
|
||||
`const_stable_indirect` attribute does not make sense on `rustc_const_stable` function, its behavior is already implied
|
||||
|
||||
attr_parsing_rustc_promotable_pairing =
|
||||
`rustc_promotable` attribute must be paired with either a `rustc_const_unstable` or a `rustc_const_stable` attribute
|
||||
|
||||
attr_parsing_soft_no_args =
|
||||
`soft` should not have any arguments
|
||||
|
||||
attr_parsing_stability_outside_std = stability attributes may not be used outside of the standard library
|
||||
|
||||
attr_parsing_unknown_meta_item =
|
||||
unknown meta item '{$item}'
|
||||
.label = expected one of {$expected}
|
||||
|
|
@ -107,6 +111,10 @@ attr_parsing_unknown_meta_item =
|
|||
attr_parsing_unknown_version_literal =
|
||||
unknown version literal format, assuming it refers to a future version
|
||||
|
||||
attr_parsing_unrecognized_repr_hint =
|
||||
unrecognized representation hint
|
||||
.help = valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`
|
||||
|
||||
attr_parsing_unstable_cfg_target_compact =
|
||||
compact `cfg(target(..))` is experimental and subject to change
|
||||
|
||||
|
|
@ -122,3 +130,8 @@ attr_parsing_unsupported_literal_generic =
|
|||
unsupported literal
|
||||
attr_parsing_unsupported_literal_suggestion =
|
||||
consider removing the prefix
|
||||
|
||||
attr_parsing_unused_multiple =
|
||||
multiple `{$name}` attributes
|
||||
.suggestion = remove this attribute
|
||||
.note = attribute also specified here
|
||||
|
|
|
|||
|
|
@ -1,49 +1,67 @@
|
|||
use rustc_ast::attr::{AttributeExt, filter_by_name};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::{Symbol, sym};
|
||||
use std::iter;
|
||||
|
||||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use super::{CombineAttributeParser, ConvertFn};
|
||||
use crate::context::AcceptContext;
|
||||
use crate::parser::ArgParser;
|
||||
use crate::session_diagnostics;
|
||||
|
||||
pub fn allow_internal_unstable(
|
||||
sess: &Session,
|
||||
attrs: &[impl AttributeExt],
|
||||
) -> impl Iterator<Item = Symbol> {
|
||||
allow_unstable(sess, attrs, sym::allow_internal_unstable)
|
||||
pub(crate) struct AllowInternalUnstableParser;
|
||||
impl CombineAttributeParser for AllowInternalUnstableParser {
|
||||
const PATH: &'static [rustc_span::Symbol] = &[sym::allow_internal_unstable];
|
||||
type Item = (Symbol, Span);
|
||||
const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowInternalUnstable;
|
||||
|
||||
fn extend<'a>(
|
||||
cx: &'a AcceptContext<'a>,
|
||||
args: &'a ArgParser<'a>,
|
||||
) -> impl IntoIterator<Item = Self::Item> + 'a {
|
||||
parse_unstable(cx, args, Self::PATH[0]).into_iter().zip(iter::repeat(cx.attr_span))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rustc_allow_const_fn_unstable(
|
||||
sess: &Session,
|
||||
attrs: &[impl AttributeExt],
|
||||
) -> impl Iterator<Item = Symbol> {
|
||||
allow_unstable(sess, attrs, sym::rustc_allow_const_fn_unstable)
|
||||
pub(crate) struct AllowConstFnUnstableParser;
|
||||
impl CombineAttributeParser for AllowConstFnUnstableParser {
|
||||
const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_allow_const_fn_unstable];
|
||||
type Item = Symbol;
|
||||
const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowConstFnUnstable;
|
||||
|
||||
fn extend<'a>(
|
||||
cx: &'a AcceptContext<'a>,
|
||||
args: &'a ArgParser<'a>,
|
||||
) -> impl IntoIterator<Item = Self::Item> + 'a {
|
||||
parse_unstable(cx, args, Self::PATH[0])
|
||||
}
|
||||
}
|
||||
|
||||
fn allow_unstable(
|
||||
sess: &Session,
|
||||
attrs: &[impl AttributeExt],
|
||||
fn parse_unstable<'a>(
|
||||
cx: &AcceptContext<'_>,
|
||||
args: &'a ArgParser<'a>,
|
||||
symbol: Symbol,
|
||||
) -> impl Iterator<Item = Symbol> {
|
||||
let attrs = filter_by_name(attrs, symbol);
|
||||
let list = attrs
|
||||
.filter_map(move |attr| {
|
||||
attr.meta_item_list().or_else(|| {
|
||||
sess.dcx().emit_err(session_diagnostics::ExpectsFeatureList {
|
||||
span: attr.span(),
|
||||
name: symbol.to_ident_string(),
|
||||
});
|
||||
None
|
||||
})
|
||||
})
|
||||
.flatten();
|
||||
) -> impl IntoIterator<Item = Symbol> {
|
||||
let mut res = Vec::new();
|
||||
|
||||
list.into_iter().filter_map(move |it| {
|
||||
let name = it.ident().map(|ident| ident.name);
|
||||
if name.is_none() {
|
||||
sess.dcx().emit_err(session_diagnostics::ExpectsFeatures {
|
||||
span: it.span(),
|
||||
let Some(list) = args.list() else {
|
||||
cx.emit_err(session_diagnostics::ExpectsFeatureList {
|
||||
span: cx.attr_span,
|
||||
name: symbol.to_ident_string(),
|
||||
});
|
||||
return res;
|
||||
};
|
||||
|
||||
for param in list.mixed() {
|
||||
let param_span = param.span();
|
||||
if let Some(ident) = param.meta_item().and_then(|i| i.word_without_args()) {
|
||||
res.push(ident.name);
|
||||
} else {
|
||||
cx.emit_err(session_diagnostics::ExpectsFeatures {
|
||||
span: param_span,
|
||||
name: symbol.to_ident_string(),
|
||||
});
|
||||
}
|
||||
name
|
||||
})
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
//! Parsing and validation of builtin attributes
|
||||
|
||||
use rustc_ast::{self as ast, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId};
|
||||
use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_attr_data_structures::RustcVersion;
|
||||
use rustc_feature::{Features, GatedCfg, find_gated_cfg};
|
||||
|
|
@ -9,10 +7,11 @@ use rustc_session::config::ExpectedValues;
|
|||
use rustc_session::lint::BuiltinLintDiag;
|
||||
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_span::{Span, Symbol, kw, sym};
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use crate::util::UnsupportedLiteralReason;
|
||||
use crate::{fluent_generated, parse_version, session_diagnostics};
|
||||
use crate::session_diagnostics::{self, UnsupportedLiteralReason};
|
||||
use crate::{fluent_generated, parse_version};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Condition {
|
||||
|
|
@ -25,7 +24,7 @@ pub struct Condition {
|
|||
|
||||
/// Tests if a cfg-pattern matches the cfg set
|
||||
pub fn cfg_matches(
|
||||
cfg: &ast::MetaItemInner,
|
||||
cfg: &MetaItemInner,
|
||||
sess: &Session,
|
||||
lint_node_id: NodeId,
|
||||
features: Option<&Features>,
|
||||
|
|
@ -80,7 +79,7 @@ fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Fea
|
|||
/// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
|
||||
/// evaluate individual items.
|
||||
pub fn eval_condition(
|
||||
cfg: &ast::MetaItemInner,
|
||||
cfg: &MetaItemInner,
|
||||
sess: &Session,
|
||||
features: Option<&Features>,
|
||||
eval: &mut impl FnMut(Condition) -> bool,
|
||||
|
|
@ -88,8 +87,8 @@ pub fn eval_condition(
|
|||
let dcx = sess.dcx();
|
||||
|
||||
let cfg = match cfg {
|
||||
ast::MetaItemInner::MetaItem(meta_item) => meta_item,
|
||||
ast::MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => {
|
||||
MetaItemInner::MetaItem(meta_item) => meta_item,
|
||||
MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => {
|
||||
if let Some(features) = features {
|
||||
// we can't use `try_gate_cfg` as symbols don't differentiate between `r#true`
|
||||
// and `true`, and we want to keep the former working without feature gate
|
||||
|
|
@ -118,7 +117,7 @@ pub fn eval_condition(
|
|||
};
|
||||
|
||||
match &cfg.kind {
|
||||
ast::MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => {
|
||||
MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => {
|
||||
try_gate_cfg(sym::version, cfg.span, sess, features);
|
||||
let (min_version, span) = match &mis[..] {
|
||||
[MetaItemInner::Lit(MetaItemLit { kind: LitKind::Str(sym, ..), span, .. })] => {
|
||||
|
|
@ -150,7 +149,7 @@ pub fn eval_condition(
|
|||
RustcVersion::CURRENT >= min_version
|
||||
}
|
||||
}
|
||||
ast::MetaItemKind::List(mis) => {
|
||||
MetaItemKind::List(mis) => {
|
||||
for mi in mis.iter() {
|
||||
if mi.meta_item_or_bool().is_none() {
|
||||
dcx.emit_err(session_diagnostics::UnsupportedLiteral {
|
||||
|
|
@ -209,12 +208,7 @@ pub fn eval_condition(
|
|||
seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name));
|
||||
}
|
||||
|
||||
res & eval_condition(
|
||||
&ast::MetaItemInner::MetaItem(mi),
|
||||
sess,
|
||||
features,
|
||||
eval,
|
||||
)
|
||||
res & eval_condition(&MetaItemInner::MetaItem(mi), sess, features, eval)
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
|
|
@ -226,7 +220,7 @@ pub fn eval_condition(
|
|||
}
|
||||
}
|
||||
}
|
||||
ast::MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => {
|
||||
MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => {
|
||||
dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span });
|
||||
true
|
||||
}
|
||||
|
|
@ -239,7 +233,7 @@ pub fn eval_condition(
|
|||
});
|
||||
true
|
||||
}
|
||||
ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => {
|
||||
MetaItemKind::Word | MetaItemKind::NameValue(..) => {
|
||||
let ident = cfg.ident().expect("multi-segment cfg predicate");
|
||||
eval(Condition {
|
||||
name: ident.name,
|
||||
|
|
|
|||
|
|
@ -1,21 +1,58 @@
|
|||
//! Parsing and validation of builtin attributes
|
||||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use rustc_ast::MetaItemInner;
|
||||
use rustc_ast::attr::AttributeExt;
|
||||
use rustc_span::Symbol;
|
||||
use super::{AcceptMapping, AttributeParser};
|
||||
use crate::context::FinalizeContext;
|
||||
use crate::session_diagnostics;
|
||||
|
||||
/// Read the content of a `rustc_confusables` attribute, and return the list of candidate names.
|
||||
pub fn parse_confusables(attr: &impl AttributeExt) -> Option<Vec<Symbol>> {
|
||||
let metas = attr.meta_item_list()?;
|
||||
|
||||
let mut candidates = Vec::new();
|
||||
|
||||
for meta in metas {
|
||||
let MetaItemInner::Lit(meta_lit) = meta else {
|
||||
return None;
|
||||
};
|
||||
candidates.push(meta_lit.symbol);
|
||||
}
|
||||
|
||||
Some(candidates)
|
||||
#[derive(Default)]
|
||||
pub(crate) struct ConfusablesParser {
|
||||
confusables: ThinVec<Symbol>,
|
||||
first_span: Option<Span>,
|
||||
}
|
||||
|
||||
impl AttributeParser for ConfusablesParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self> = &[(&[sym::rustc_confusables], |this, cx, args| {
|
||||
let Some(list) = args.list() else {
|
||||
// FIXME(jdonszelmann): error when not a list? Bring validation code here.
|
||||
// NOTE: currently subsequent attributes are silently ignored using
|
||||
// tcx.get_attr().
|
||||
return;
|
||||
};
|
||||
|
||||
if list.is_empty() {
|
||||
cx.emit_err(session_diagnostics::EmptyConfusables { span: cx.attr_span });
|
||||
}
|
||||
|
||||
for param in list.mixed() {
|
||||
let span = param.span();
|
||||
|
||||
let Some(lit) = param.lit() else {
|
||||
cx.emit_err(session_diagnostics::IncorrectMetaItem {
|
||||
span,
|
||||
suggestion: Some(session_diagnostics::IncorrectMetaItemSuggestion {
|
||||
lo: span.shrink_to_lo(),
|
||||
hi: span.shrink_to_hi(),
|
||||
}),
|
||||
});
|
||||
continue;
|
||||
};
|
||||
|
||||
this.confusables.push(lit.symbol);
|
||||
}
|
||||
|
||||
this.first_span.get_or_insert(cx.attr_span);
|
||||
})];
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
|
||||
if self.confusables.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(AttributeKind::Confusables {
|
||||
symbols: self.confusables,
|
||||
first_span: self.first_span.unwrap(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,121 +1,122 @@
|
|||
//! Parsing and validation of builtin attributes
|
||||
|
||||
use rustc_ast::attr::AttributeExt;
|
||||
use rustc_ast::{MetaItem, MetaItemInner};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_attr_data_structures::{DeprecatedSince, Deprecation};
|
||||
use rustc_feature::Features;
|
||||
use rustc_session::Session;
|
||||
use rustc_attr_data_structures::{AttributeKind, DeprecatedSince, Deprecation};
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use super::util::UnsupportedLiteralReason;
|
||||
use crate::{parse_version, session_diagnostics};
|
||||
use super::SingleAttributeParser;
|
||||
use super::util::parse_version;
|
||||
use crate::context::AcceptContext;
|
||||
use crate::parser::ArgParser;
|
||||
use crate::session_diagnostics;
|
||||
use crate::session_diagnostics::UnsupportedLiteralReason;
|
||||
|
||||
/// Finds the deprecation attribute. `None` if none exists.
|
||||
pub fn find_deprecation(
|
||||
sess: &Session,
|
||||
features: &Features,
|
||||
attrs: &[impl AttributeExt],
|
||||
) -> Option<(Deprecation, Span)> {
|
||||
let mut depr: Option<(Deprecation, Span)> = None;
|
||||
let is_rustc = features.staged_api();
|
||||
pub(crate) struct DeprecationParser;
|
||||
|
||||
'outer: for attr in attrs {
|
||||
if !attr.has_name(sym::deprecated) {
|
||||
continue;
|
||||
fn get(
|
||||
cx: &AcceptContext<'_>,
|
||||
ident: Ident,
|
||||
param_span: Span,
|
||||
arg: &ArgParser<'_>,
|
||||
item: &Option<Symbol>,
|
||||
) -> Option<Symbol> {
|
||||
if item.is_some() {
|
||||
cx.emit_err(session_diagnostics::MultipleItem {
|
||||
span: param_span,
|
||||
item: ident.to_string(),
|
||||
});
|
||||
return None;
|
||||
}
|
||||
if let Some(v) = arg.name_value() {
|
||||
if let Some(value_str) = v.value_as_str() {
|
||||
Some(value_str)
|
||||
} else {
|
||||
let lit = v.value_as_lit();
|
||||
cx.emit_err(session_diagnostics::UnsupportedLiteral {
|
||||
span: v.value_span,
|
||||
reason: UnsupportedLiteralReason::DeprecatedString,
|
||||
is_bytestr: lit.kind.is_bytestr(),
|
||||
start_point_span: cx.sess().source_map().start_point(lit.span),
|
||||
});
|
||||
None
|
||||
}
|
||||
} else {
|
||||
// FIXME(jdonszelmann): suggestion?
|
||||
cx.emit_err(session_diagnostics::IncorrectMetaItem { span: param_span, suggestion: None });
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl SingleAttributeParser for DeprecationParser {
|
||||
const PATH: &'static [rustc_span::Symbol] = &[sym::deprecated];
|
||||
|
||||
fn on_duplicate(cx: &AcceptContext<'_>, first_span: rustc_span::Span) {
|
||||
// FIXME(jdonszelmann): merge with errors from check_attrs.rs
|
||||
cx.emit_err(session_diagnostics::UnusedMultiple {
|
||||
this: cx.attr_span,
|
||||
other: first_span,
|
||||
name: sym::deprecated,
|
||||
});
|
||||
}
|
||||
|
||||
fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let features = cx.features();
|
||||
|
||||
let mut since = None;
|
||||
let mut note = None;
|
||||
let mut suggestion = None;
|
||||
|
||||
if attr.is_doc_comment() {
|
||||
continue;
|
||||
} else if attr.is_word() {
|
||||
} else if let Some(value) = attr.value_str() {
|
||||
note = Some(value)
|
||||
} else if let Some(list) = attr.meta_item_list() {
|
||||
let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
|
||||
if item.is_some() {
|
||||
sess.dcx().emit_err(session_diagnostics::MultipleItem {
|
||||
span: meta.span,
|
||||
item: pprust::path_to_string(&meta.path),
|
||||
let is_rustc = features.staged_api();
|
||||
|
||||
if let Some(value) = args.name_value()
|
||||
&& let Some(value_str) = value.value_as_str()
|
||||
{
|
||||
note = Some(value_str)
|
||||
} else if let Some(list) = args.list() {
|
||||
for param in list.mixed() {
|
||||
let param_span = param.span();
|
||||
let Some(param) = param.meta_item() else {
|
||||
cx.emit_err(session_diagnostics::UnsupportedLiteral {
|
||||
span: param_span,
|
||||
reason: UnsupportedLiteralReason::DeprecatedKvPair,
|
||||
is_bytestr: false,
|
||||
start_point_span: cx.sess().source_map().start_point(param_span),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
if let Some(v) = meta.value_str() {
|
||||
*item = Some(v);
|
||||
true
|
||||
} else {
|
||||
if let Some(lit) = meta.name_value_literal() {
|
||||
sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral {
|
||||
span: lit.span,
|
||||
reason: UnsupportedLiteralReason::DeprecatedString,
|
||||
is_bytestr: lit.kind.is_bytestr(),
|
||||
start_point_span: sess.source_map().start_point(lit.span),
|
||||
});
|
||||
} else {
|
||||
sess.dcx()
|
||||
.emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span });
|
||||
return None;
|
||||
};
|
||||
|
||||
let (ident, arg) = param.word_or_empty();
|
||||
|
||||
match ident.name {
|
||||
sym::since => {
|
||||
since = Some(get(cx, ident, param_span, arg, &since)?);
|
||||
}
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
for meta in &list {
|
||||
match meta {
|
||||
MetaItemInner::MetaItem(mi) => match mi.name_or_empty() {
|
||||
sym::since => {
|
||||
if !get(mi, &mut since) {
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
sym::note => {
|
||||
if !get(mi, &mut note) {
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
sym::suggestion => {
|
||||
if !features.deprecated_suggestion() {
|
||||
sess.dcx().emit_err(
|
||||
session_diagnostics::DeprecatedItemSuggestion {
|
||||
span: mi.span,
|
||||
is_nightly: sess.is_nightly_build(),
|
||||
details: (),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if !get(mi, &mut suggestion) {
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
sess.dcx().emit_err(session_diagnostics::UnknownMetaItem {
|
||||
span: meta.span(),
|
||||
item: pprust::path_to_string(&mi.path),
|
||||
expected: if features.deprecated_suggestion() {
|
||||
&["since", "note", "suggestion"]
|
||||
} else {
|
||||
&["since", "note"]
|
||||
},
|
||||
sym::note => {
|
||||
note = Some(get(cx, ident, param_span, arg, ¬e)?);
|
||||
}
|
||||
sym::suggestion => {
|
||||
if !features.deprecated_suggestion() {
|
||||
cx.emit_err(session_diagnostics::DeprecatedItemSuggestion {
|
||||
span: param_span,
|
||||
is_nightly: cx.sess().is_nightly_build(),
|
||||
details: (),
|
||||
});
|
||||
continue 'outer;
|
||||
}
|
||||
},
|
||||
MetaItemInner::Lit(lit) => {
|
||||
sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral {
|
||||
span: lit.span,
|
||||
reason: UnsupportedLiteralReason::DeprecatedKvPair,
|
||||
is_bytestr: false,
|
||||
start_point_span: sess.source_map().start_point(lit.span),
|
||||
|
||||
suggestion = Some(get(cx, ident, param_span, arg, &suggestion)?);
|
||||
}
|
||||
_ => {
|
||||
cx.emit_err(session_diagnostics::UnknownMetaItem {
|
||||
span: param_span,
|
||||
item: ident.to_string(),
|
||||
expected: if features.deprecated_suggestion() {
|
||||
&["since", "note", "suggestion"]
|
||||
} else {
|
||||
&["since", "note"]
|
||||
},
|
||||
});
|
||||
continue 'outer;
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
let since = if let Some(since) = since {
|
||||
|
|
@ -126,23 +127,24 @@ pub fn find_deprecation(
|
|||
} else if let Some(version) = parse_version(since) {
|
||||
DeprecatedSince::RustcVersion(version)
|
||||
} else {
|
||||
sess.dcx().emit_err(session_diagnostics::InvalidSince { span: attr.span() });
|
||||
cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span });
|
||||
DeprecatedSince::Err
|
||||
}
|
||||
} else if is_rustc {
|
||||
sess.dcx().emit_err(session_diagnostics::MissingSince { span: attr.span() });
|
||||
cx.emit_err(session_diagnostics::MissingSince { span: cx.attr_span });
|
||||
DeprecatedSince::Err
|
||||
} else {
|
||||
DeprecatedSince::Unspecified
|
||||
};
|
||||
|
||||
if is_rustc && note.is_none() {
|
||||
sess.dcx().emit_err(session_diagnostics::MissingNote { span: attr.span() });
|
||||
continue;
|
||||
cx.emit_err(session_diagnostics::MissingNote { span: cx.attr_span });
|
||||
return None;
|
||||
}
|
||||
|
||||
depr = Some((Deprecation { since, note, suggestion }, attr.span()));
|
||||
Some(AttributeKind::Deprecation {
|
||||
deprecation: Deprecation { since, note, suggestion },
|
||||
span: cx.attr_span,
|
||||
})
|
||||
}
|
||||
|
||||
depr
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,152 @@
|
|||
mod allow_unstable;
|
||||
mod cfg;
|
||||
mod confusables;
|
||||
mod deprecation;
|
||||
mod repr;
|
||||
mod stability;
|
||||
mod transparency;
|
||||
//! This module defines traits for attribute parsers, little state machines that recognize and parse
|
||||
//! attributes out of a longer list of attributes. The main trait is called [`AttributeParser`].
|
||||
//! You can find more docs about [`AttributeParser`]s on the trait itself.
|
||||
//! However, for many types of attributes, implementing [`AttributeParser`] is not necessary.
|
||||
//! It allows for a lot of flexibility you might not want.
|
||||
//!
|
||||
//! Specifically, you might not care about managing the state of your [`AttributeParser`]
|
||||
//! state machine yourself. In this case you can choose to implement:
|
||||
//!
|
||||
//! - [`SingleAttributeParser`]: makes it easy to implement an attribute which should error if it
|
||||
//! appears more than once in a list of attributes
|
||||
//! - [`CombineAttributeParser`]: makes it easy to implement an attribute which should combine the
|
||||
//! contents of attributes, if an attribute appear multiple times in a list
|
||||
//!
|
||||
//! Attributes should be added to [`ATTRIBUTE_MAPPING`](crate::context::ATTRIBUTE_MAPPING) to be parsed.
|
||||
|
||||
pub mod util;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub use allow_unstable::*;
|
||||
pub use cfg::*;
|
||||
pub use confusables::*;
|
||||
pub use deprecation::*;
|
||||
pub use repr::*;
|
||||
pub use stability::*;
|
||||
pub use transparency::*;
|
||||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_span::Span;
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use crate::context::{AcceptContext, FinalizeContext};
|
||||
use crate::parser::ArgParser;
|
||||
|
||||
pub(crate) mod allow_unstable;
|
||||
pub(crate) mod cfg;
|
||||
pub(crate) mod confusables;
|
||||
pub(crate) mod deprecation;
|
||||
pub(crate) mod repr;
|
||||
pub(crate) mod stability;
|
||||
pub(crate) mod transparency;
|
||||
pub(crate) mod util;
|
||||
|
||||
type AcceptFn<T> = fn(&mut T, &AcceptContext<'_>, &ArgParser<'_>);
|
||||
type AcceptMapping<T> = &'static [(&'static [rustc_span::Symbol], AcceptFn<T>)];
|
||||
|
||||
/// An [`AttributeParser`] is a type which searches for syntactic attributes.
|
||||
///
|
||||
/// Parsers are often tiny state machines that gets to see all syntactical attributes on an item.
|
||||
/// [`Default::default`] creates a fresh instance that sits in some kind of initial state, usually that the
|
||||
/// attribute it is looking for was not yet seen.
|
||||
///
|
||||
/// Then, it defines what paths this group will accept in [`AttributeParser::ATTRIBUTES`].
|
||||
/// These are listed as pairs, of symbols and function pointers. The function pointer will
|
||||
/// be called when that attribute is found on an item, which can influence the state of the little
|
||||
/// state machine.
|
||||
///
|
||||
/// Finally, after all attributes on an item have been seen, and possibly been accepted,
|
||||
/// the [`finalize`](AttributeParser::finalize) functions for all attribute parsers are called. Each can then report
|
||||
/// whether it has seen the attribute it has been looking for.
|
||||
///
|
||||
/// The state machine is automatically reset to parse attributes on the next item.
|
||||
pub(crate) trait AttributeParser: Default + 'static {
|
||||
/// The symbols for the attributes that this parser is interested in.
|
||||
///
|
||||
/// If an attribute has this symbol, the `accept` function will be called on it.
|
||||
const ATTRIBUTES: AcceptMapping<Self>;
|
||||
|
||||
/// The parser has gotten a chance to accept the attributes on an item,
|
||||
/// here it can produce an attribute.
|
||||
fn finalize(self, cx: &FinalizeContext<'_>) -> Option<AttributeKind>;
|
||||
}
|
||||
|
||||
/// Alternative to [`AttributeParser`] that automatically handles state management.
|
||||
/// A slightly simpler and more restricted way to convert attributes.
|
||||
/// Assumes that an attribute can only appear a single time on an item,
|
||||
/// and errors when it sees more.
|
||||
///
|
||||
/// [`Single<T> where T: SingleAttributeParser`](Single) implements [`AttributeParser`].
|
||||
///
|
||||
/// [`SingleAttributeParser`] can only convert attributes one-to-one, and cannot combine multiple
|
||||
/// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example.
|
||||
pub(crate) trait SingleAttributeParser: 'static {
|
||||
const PATH: &'static [rustc_span::Symbol];
|
||||
|
||||
/// Caled when a duplicate attribute is found.
|
||||
///
|
||||
/// `first_span` is the span of the first occurrence of this attribute.
|
||||
// FIXME(jdonszelmann): default error
|
||||
fn on_duplicate(cx: &AcceptContext<'_>, first_span: Span);
|
||||
|
||||
/// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`]
|
||||
fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind>;
|
||||
}
|
||||
|
||||
pub(crate) struct Single<T: SingleAttributeParser>(PhantomData<T>, Option<(AttributeKind, Span)>);
|
||||
|
||||
impl<T: SingleAttributeParser> Default for Single<T> {
|
||||
fn default() -> Self {
|
||||
Self(Default::default(), Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SingleAttributeParser> AttributeParser for Single<T> {
|
||||
const ATTRIBUTES: AcceptMapping<Self> = &[(T::PATH, |group: &mut Single<T>, cx, args| {
|
||||
if let Some((_, s)) = group.1 {
|
||||
T::on_duplicate(cx, s);
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(pa) = T::convert(cx, args) {
|
||||
group.1 = Some((pa, cx.attr_span));
|
||||
}
|
||||
})];
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
|
||||
Some(self.1?.0)
|
||||
}
|
||||
}
|
||||
|
||||
type ConvertFn<E> = fn(ThinVec<E>) -> AttributeKind;
|
||||
|
||||
/// Alternative to [`AttributeParser`] that automatically handles state management.
|
||||
/// If multiple attributes appear on an element, combines the values of each into a
|
||||
/// [`ThinVec`].
|
||||
/// [`Combine<T> where T: CombineAttributeParser`](Combine) implements [`AttributeParser`].
|
||||
///
|
||||
/// [`CombineAttributeParser`] can only convert a single kind of attribute, and cannot combine multiple
|
||||
/// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example.
|
||||
pub(crate) trait CombineAttributeParser: 'static {
|
||||
const PATH: &'static [rustc_span::Symbol];
|
||||
|
||||
type Item;
|
||||
const CONVERT: ConvertFn<Self::Item>;
|
||||
|
||||
/// Converts a single syntactical attribute to a number of elements of the semantic attribute, or [`AttributeKind`]
|
||||
fn extend<'a>(
|
||||
cx: &'a AcceptContext<'a>,
|
||||
args: &'a ArgParser<'a>,
|
||||
) -> impl IntoIterator<Item = Self::Item> + 'a;
|
||||
}
|
||||
|
||||
pub(crate) struct Combine<T: CombineAttributeParser>(
|
||||
PhantomData<T>,
|
||||
ThinVec<<T as CombineAttributeParser>::Item>,
|
||||
);
|
||||
|
||||
impl<T: CombineAttributeParser> Default for Combine<T> {
|
||||
fn default() -> Self {
|
||||
Self(Default::default(), Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: CombineAttributeParser> AttributeParser for Combine<T> {
|
||||
const ATTRIBUTES: AcceptMapping<Self> =
|
||||
&[(T::PATH, |group: &mut Combine<T>, cx, args| group.1.extend(T::extend(cx, args)))];
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
|
||||
if self.1.is_empty() { None } else { Some(T::CONVERT(self.1)) }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
//! Parsing and validation of builtin attributes
|
||||
|
||||
use rustc_abi::Align;
|
||||
use rustc_ast::attr::AttributeExt;
|
||||
use rustc_ast::{self as ast, MetaItemKind};
|
||||
use rustc_attr_data_structures::IntType;
|
||||
use rustc_attr_data_structures::ReprAttr::*;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::{Symbol, sym};
|
||||
use rustc_ast::{IntTy, LitIntType, LitKind, UintTy};
|
||||
use rustc_attr_data_structures::{AttributeKind, IntType, ReprAttr};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use crate::ReprAttr;
|
||||
use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
|
||||
use super::{CombineAttributeParser, ConvertFn};
|
||||
use crate::context::AcceptContext;
|
||||
use crate::parser::{ArgParser, MetaItemListParser, MetaItemParser};
|
||||
use crate::session_diagnostics;
|
||||
use crate::session_diagnostics::IncorrectReprFormatGenericCause;
|
||||
|
||||
/// Parse #[repr(...)] forms.
|
||||
///
|
||||
|
|
@ -18,185 +16,216 @@ use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
|
|||
/// the same discriminant size that the corresponding C enum would or C
|
||||
/// structure layout, `packed` to remove padding, and `transparent` to delegate representation
|
||||
/// concerns to the only non-ZST field.
|
||||
pub fn find_repr_attrs(sess: &Session, attr: &impl AttributeExt) -> Vec<ReprAttr> {
|
||||
if attr.has_name(sym::repr) { parse_repr_attr(sess, attr) } else { Vec::new() }
|
||||
// FIXME(jdonszelmann): is a vec the right representation here even? isn't it just a struct?
|
||||
pub(crate) struct ReprParser;
|
||||
|
||||
impl CombineAttributeParser for ReprParser {
|
||||
type Item = (ReprAttr, Span);
|
||||
const PATH: &'static [rustc_span::Symbol] = &[sym::repr];
|
||||
const CONVERT: ConvertFn<Self::Item> = AttributeKind::Repr;
|
||||
|
||||
fn extend<'a>(
|
||||
cx: &'a AcceptContext<'a>,
|
||||
args: &'a ArgParser<'a>,
|
||||
) -> impl IntoIterator<Item = Self::Item> + 'a {
|
||||
let mut reprs = Vec::new();
|
||||
|
||||
let Some(list) = args.list() else {
|
||||
return reprs;
|
||||
};
|
||||
|
||||
if list.is_empty() {
|
||||
// this is so validation can emit a lint
|
||||
reprs.push((ReprAttr::ReprEmpty, cx.attr_span));
|
||||
}
|
||||
|
||||
for param in list.mixed() {
|
||||
if let Some(_) = param.lit() {
|
||||
cx.emit_err(session_diagnostics::ReprIdent { span: cx.attr_span });
|
||||
continue;
|
||||
}
|
||||
|
||||
reprs.extend(
|
||||
param.meta_item().and_then(|mi| parse_repr(cx, &mi)).map(|r| (r, param.span())),
|
||||
);
|
||||
}
|
||||
|
||||
reprs
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_repr_attr(sess: &Session, attr: &impl AttributeExt) -> Vec<ReprAttr> {
|
||||
assert!(attr.has_name(sym::repr), "expected `#[repr(..)]`, found: {attr:?}");
|
||||
let mut acc = Vec::new();
|
||||
let dcx = sess.dcx();
|
||||
|
||||
if let Some(items) = attr.meta_item_list() {
|
||||
for item in items {
|
||||
let mut recognised = false;
|
||||
if item.is_word() {
|
||||
let hint = match item.name_or_empty() {
|
||||
sym::Rust => Some(ReprRust),
|
||||
sym::C => Some(ReprC),
|
||||
sym::packed => Some(ReprPacked(Align::ONE)),
|
||||
sym::simd => Some(ReprSimd),
|
||||
sym::transparent => Some(ReprTransparent),
|
||||
sym::align => {
|
||||
sess.dcx().emit_err(session_diagnostics::InvalidReprAlignNeedArg {
|
||||
span: item.span(),
|
||||
});
|
||||
recognised = true;
|
||||
None
|
||||
}
|
||||
name => int_type_of_word(name).map(ReprInt),
|
||||
};
|
||||
|
||||
if let Some(h) = hint {
|
||||
recognised = true;
|
||||
acc.push(h);
|
||||
}
|
||||
} else if let Some((name, value)) = item.singleton_lit_list() {
|
||||
let mut literal_error = None;
|
||||
let mut err_span = item.span();
|
||||
if name == sym::align {
|
||||
recognised = true;
|
||||
match parse_alignment(&value.kind) {
|
||||
Ok(literal) => acc.push(ReprAlign(literal)),
|
||||
Err(message) => {
|
||||
err_span = value.span;
|
||||
literal_error = Some(message)
|
||||
}
|
||||
};
|
||||
} else if name == sym::packed {
|
||||
recognised = true;
|
||||
match parse_alignment(&value.kind) {
|
||||
Ok(literal) => acc.push(ReprPacked(literal)),
|
||||
Err(message) => {
|
||||
err_span = value.span;
|
||||
literal_error = Some(message)
|
||||
}
|
||||
};
|
||||
} else if matches!(name, sym::Rust | sym::C | sym::simd | sym::transparent)
|
||||
|| int_type_of_word(name).is_some()
|
||||
{
|
||||
recognised = true;
|
||||
sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen {
|
||||
span: item.span(),
|
||||
name: name.to_ident_string(),
|
||||
});
|
||||
}
|
||||
if let Some(literal_error) = literal_error {
|
||||
sess.dcx().emit_err(session_diagnostics::InvalidReprGeneric {
|
||||
span: err_span,
|
||||
repr_arg: name.to_ident_string(),
|
||||
error_part: literal_error,
|
||||
});
|
||||
}
|
||||
} else if let Some(meta_item) = item.meta_item() {
|
||||
match &meta_item.kind {
|
||||
MetaItemKind::NameValue(value) => {
|
||||
if meta_item.has_name(sym::align) || meta_item.has_name(sym::packed) {
|
||||
let name = meta_item.name_or_empty().to_ident_string();
|
||||
recognised = true;
|
||||
sess.dcx().emit_err(session_diagnostics::IncorrectReprFormatGeneric {
|
||||
span: item.span(),
|
||||
repr_arg: &name,
|
||||
cause: IncorrectReprFormatGenericCause::from_lit_kind(
|
||||
item.span(),
|
||||
&value.kind,
|
||||
&name,
|
||||
),
|
||||
});
|
||||
} else if matches!(
|
||||
meta_item.name_or_empty(),
|
||||
sym::Rust | sym::C | sym::simd | sym::transparent
|
||||
) || int_type_of_word(meta_item.name_or_empty()).is_some()
|
||||
{
|
||||
recognised = true;
|
||||
sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoValue {
|
||||
span: meta_item.span,
|
||||
name: meta_item.name_or_empty().to_ident_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
MetaItemKind::List(nested_items) => {
|
||||
if meta_item.has_name(sym::align) {
|
||||
recognised = true;
|
||||
if let [nested_item] = nested_items.as_slice() {
|
||||
sess.dcx().emit_err(
|
||||
session_diagnostics::IncorrectReprFormatExpectInteger {
|
||||
span: nested_item.span(),
|
||||
},
|
||||
);
|
||||
} else {
|
||||
sess.dcx().emit_err(
|
||||
session_diagnostics::IncorrectReprFormatAlignOneArg {
|
||||
span: meta_item.span,
|
||||
},
|
||||
);
|
||||
}
|
||||
} else if meta_item.has_name(sym::packed) {
|
||||
recognised = true;
|
||||
if let [nested_item] = nested_items.as_slice() {
|
||||
sess.dcx().emit_err(
|
||||
session_diagnostics::IncorrectReprFormatPackedExpectInteger {
|
||||
span: nested_item.span(),
|
||||
},
|
||||
);
|
||||
} else {
|
||||
sess.dcx().emit_err(
|
||||
session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg {
|
||||
span: meta_item.span,
|
||||
},
|
||||
);
|
||||
}
|
||||
} else if matches!(
|
||||
meta_item.name_or_empty(),
|
||||
sym::Rust | sym::C | sym::simd | sym::transparent
|
||||
) || int_type_of_word(meta_item.name_or_empty()).is_some()
|
||||
{
|
||||
recognised = true;
|
||||
sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen {
|
||||
span: meta_item.span,
|
||||
name: meta_item.name_or_empty().to_ident_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
if !recognised {
|
||||
// Not a word we recognize. This will be caught and reported by
|
||||
// the `check_mod_attrs` pass, but this pass doesn't always run
|
||||
// (e.g. if we only pretty-print the source), so we have to gate
|
||||
// the `span_delayed_bug` call as follows:
|
||||
if sess.opts.pretty.is_none_or(|pp| pp.needs_analysis()) {
|
||||
dcx.span_delayed_bug(item.span(), "unrecognized representation hint");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
acc
|
||||
macro_rules! int_pat {
|
||||
() => {
|
||||
sym::i8
|
||||
| sym::u8
|
||||
| sym::i16
|
||||
| sym::u16
|
||||
| sym::i32
|
||||
| sym::u32
|
||||
| sym::i64
|
||||
| sym::u64
|
||||
| sym::i128
|
||||
| sym::u128
|
||||
| sym::isize
|
||||
| sym::usize
|
||||
};
|
||||
}
|
||||
|
||||
fn int_type_of_word(s: Symbol) -> Option<IntType> {
|
||||
use rustc_attr_data_structures::IntType::*;
|
||||
use IntType::*;
|
||||
|
||||
match s {
|
||||
sym::i8 => Some(SignedInt(ast::IntTy::I8)),
|
||||
sym::u8 => Some(UnsignedInt(ast::UintTy::U8)),
|
||||
sym::i16 => Some(SignedInt(ast::IntTy::I16)),
|
||||
sym::u16 => Some(UnsignedInt(ast::UintTy::U16)),
|
||||
sym::i32 => Some(SignedInt(ast::IntTy::I32)),
|
||||
sym::u32 => Some(UnsignedInt(ast::UintTy::U32)),
|
||||
sym::i64 => Some(SignedInt(ast::IntTy::I64)),
|
||||
sym::u64 => Some(UnsignedInt(ast::UintTy::U64)),
|
||||
sym::i128 => Some(SignedInt(ast::IntTy::I128)),
|
||||
sym::u128 => Some(UnsignedInt(ast::UintTy::U128)),
|
||||
sym::isize => Some(SignedInt(ast::IntTy::Isize)),
|
||||
sym::usize => Some(UnsignedInt(ast::UintTy::Usize)),
|
||||
sym::i8 => Some(SignedInt(IntTy::I8)),
|
||||
sym::u8 => Some(UnsignedInt(UintTy::U8)),
|
||||
sym::i16 => Some(SignedInt(IntTy::I16)),
|
||||
sym::u16 => Some(UnsignedInt(UintTy::U16)),
|
||||
sym::i32 => Some(SignedInt(IntTy::I32)),
|
||||
sym::u32 => Some(UnsignedInt(UintTy::U32)),
|
||||
sym::i64 => Some(SignedInt(IntTy::I64)),
|
||||
sym::u64 => Some(UnsignedInt(UintTy::U64)),
|
||||
sym::i128 => Some(SignedInt(IntTy::I128)),
|
||||
sym::u128 => Some(UnsignedInt(UintTy::U128)),
|
||||
sym::isize => Some(SignedInt(IntTy::Isize)),
|
||||
sym::usize => Some(UnsignedInt(UintTy::Usize)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_alignment(node: &ast::LitKind) -> Result<Align, &'static str> {
|
||||
if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node {
|
||||
fn parse_repr(cx: &AcceptContext<'_>, param: &MetaItemParser<'_>) -> Option<ReprAttr> {
|
||||
use ReprAttr::*;
|
||||
|
||||
// FIXME(jdonszelmann): invert the parsing here to match on the word first and then the
|
||||
// structure.
|
||||
let (ident, args) = param.word_or_empty();
|
||||
|
||||
match (ident.name, args) {
|
||||
(sym::align, ArgParser::NoArgs) => {
|
||||
cx.emit_err(session_diagnostics::InvalidReprAlignNeedArg { span: ident.span });
|
||||
None
|
||||
}
|
||||
(sym::align, ArgParser::List(l)) => parse_repr_align(cx, l, param.span(), AlignKind::Align),
|
||||
|
||||
(sym::packed, ArgParser::NoArgs) => Some(ReprPacked(Align::ONE)),
|
||||
(sym::packed, ArgParser::List(l)) => {
|
||||
parse_repr_align(cx, l, param.span(), AlignKind::Packed)
|
||||
}
|
||||
|
||||
(sym::align | sym::packed, ArgParser::NameValue(l)) => {
|
||||
cx.emit_err(session_diagnostics::IncorrectReprFormatGeneric {
|
||||
span: param.span(),
|
||||
// FIXME(jdonszelmann) can just be a string in the diag type
|
||||
repr_arg: &ident.to_string(),
|
||||
cause: IncorrectReprFormatGenericCause::from_lit_kind(
|
||||
param.span(),
|
||||
&l.value_as_lit().kind,
|
||||
ident.name.as_str(),
|
||||
),
|
||||
});
|
||||
None
|
||||
}
|
||||
|
||||
(sym::Rust, ArgParser::NoArgs) => Some(ReprRust),
|
||||
(sym::C, ArgParser::NoArgs) => Some(ReprC),
|
||||
(sym::simd, ArgParser::NoArgs) => Some(ReprSimd),
|
||||
(sym::transparent, ArgParser::NoArgs) => Some(ReprTransparent),
|
||||
(i @ int_pat!(), ArgParser::NoArgs) => {
|
||||
// int_pat!() should make sure it always parses
|
||||
Some(ReprInt(int_type_of_word(i).unwrap()))
|
||||
}
|
||||
|
||||
(
|
||||
sym::Rust | sym::C | sym::simd | sym::transparent | int_pat!(),
|
||||
ArgParser::NameValue(_),
|
||||
) => {
|
||||
cx.emit_err(session_diagnostics::InvalidReprHintNoValue {
|
||||
span: param.span(),
|
||||
name: ident.to_string(),
|
||||
});
|
||||
None
|
||||
}
|
||||
(sym::Rust | sym::C | sym::simd | sym::transparent | int_pat!(), ArgParser::List(_)) => {
|
||||
cx.emit_err(session_diagnostics::InvalidReprHintNoParen {
|
||||
span: param.span(),
|
||||
name: ident.to_string(),
|
||||
});
|
||||
None
|
||||
}
|
||||
|
||||
_ => {
|
||||
cx.emit_err(session_diagnostics::UnrecognizedReprHint { span: param.span() });
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum AlignKind {
|
||||
Packed,
|
||||
Align,
|
||||
}
|
||||
|
||||
fn parse_repr_align(
|
||||
cx: &AcceptContext<'_>,
|
||||
list: &MetaItemListParser<'_>,
|
||||
param_span: Span,
|
||||
align_kind: AlignKind,
|
||||
) -> Option<ReprAttr> {
|
||||
use AlignKind::*;
|
||||
|
||||
let Some(align) = list.single() else {
|
||||
match align_kind {
|
||||
Packed => {
|
||||
cx.emit_err(session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg {
|
||||
span: param_span,
|
||||
});
|
||||
}
|
||||
Align => {
|
||||
cx.dcx().emit_err(session_diagnostics::IncorrectReprFormatAlignOneArg {
|
||||
span: param_span,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return None;
|
||||
};
|
||||
|
||||
let Some(lit) = align.lit() else {
|
||||
match align_kind {
|
||||
Packed => {
|
||||
cx.emit_err(session_diagnostics::IncorrectReprFormatPackedExpectInteger {
|
||||
span: align.span(),
|
||||
});
|
||||
}
|
||||
Align => {
|
||||
cx.emit_err(session_diagnostics::IncorrectReprFormatExpectInteger {
|
||||
span: align.span(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return None;
|
||||
};
|
||||
|
||||
match parse_alignment(&lit.kind) {
|
||||
Ok(literal) => Some(match align_kind {
|
||||
AlignKind::Packed => ReprAttr::ReprPacked(literal),
|
||||
AlignKind::Align => ReprAttr::ReprAlign(literal),
|
||||
}),
|
||||
Err(message) => {
|
||||
cx.emit_err(session_diagnostics::InvalidReprGeneric {
|
||||
span: lit.span,
|
||||
repr_arg: match align_kind {
|
||||
Packed => "packed".to_string(),
|
||||
Align => "align".to_string(),
|
||||
},
|
||||
error_part: message,
|
||||
});
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_alignment(node: &LitKind) -> Result<Align, &'static str> {
|
||||
if let LitKind::Int(literal, LitIntType::Unsuffixed) = node {
|
||||
// `Align::from_bytes` accepts 0 as an input, check is_power_of_two() first
|
||||
if literal.get().is_power_of_two() {
|
||||
// Only possible error is larger than 2^29
|
||||
|
|
|
|||
|
|
@ -1,266 +1,258 @@
|
|||
//! Parsing and validation of builtin attributes
|
||||
|
||||
use std::num::NonZero;
|
||||
|
||||
use rustc_ast::MetaItem;
|
||||
use rustc_ast::attr::AttributeExt;
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_attr_data_structures::{
|
||||
ConstStability, DefaultBodyStability, Stability, StabilityLevel, StableSince, UnstableReason,
|
||||
VERSION_PLACEHOLDER,
|
||||
AttributeKind, DefaultBodyStability, PartialConstStability, Stability, StabilityLevel,
|
||||
StableSince, UnstableReason, VERSION_PLACEHOLDER,
|
||||
};
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::{Span, Symbol, kw, sym};
|
||||
|
||||
use crate::attributes::util::UnsupportedLiteralReason;
|
||||
use crate::{parse_version, session_diagnostics};
|
||||
use super::util::parse_version;
|
||||
use super::{AcceptMapping, AttributeParser, SingleAttributeParser};
|
||||
use crate::context::{AcceptContext, FinalizeContext};
|
||||
use crate::parser::{ArgParser, MetaItemParser};
|
||||
use crate::session_diagnostics::{self, UnsupportedLiteralReason};
|
||||
|
||||
/// Collects stability info from `stable`/`unstable`/`rustc_allowed_through_unstable_modules`
|
||||
/// attributes in `attrs`. Returns `None` if no stability attributes are found.
|
||||
pub fn find_stability(
|
||||
sess: &Session,
|
||||
attrs: &[impl AttributeExt],
|
||||
item_sp: Span,
|
||||
) -> Option<(Stability, Span)> {
|
||||
let mut stab: Option<(Stability, Span)> = None;
|
||||
let mut allowed_through_unstable_modules = None;
|
||||
macro_rules! reject_outside_std {
|
||||
($cx: ident) => {
|
||||
// Emit errors for non-staged-api crates.
|
||||
if !$cx.features().staged_api() {
|
||||
$cx.emit_err(session_diagnostics::StabilityOutsideStd { span: $cx.attr_span });
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
for attr in attrs {
|
||||
match attr.name_or_empty() {
|
||||
sym::rustc_allowed_through_unstable_modules => {
|
||||
// The value is mandatory, but avoid ICEs in case such code reaches this function.
|
||||
allowed_through_unstable_modules = Some(attr.value_str().unwrap_or_else(|| {
|
||||
sess.dcx().span_delayed_bug(
|
||||
item_sp,
|
||||
"`#[rustc_allowed_through_unstable_modules]` without deprecation message",
|
||||
);
|
||||
kw::Empty
|
||||
}))
|
||||
}
|
||||
sym::unstable => {
|
||||
if stab.is_some() {
|
||||
sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
|
||||
span: attr.span(),
|
||||
});
|
||||
break;
|
||||
}
|
||||
#[derive(Default)]
|
||||
pub(crate) struct StabilityParser {
|
||||
allowed_through_unstable_modules: Option<Symbol>,
|
||||
stability: Option<(Stability, Span)>,
|
||||
}
|
||||
|
||||
if let Some((feature, level)) = parse_unstability(sess, attr) {
|
||||
stab = Some((Stability { level, feature }, attr.span()));
|
||||
}
|
||||
}
|
||||
sym::stable => {
|
||||
if stab.is_some() {
|
||||
sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
|
||||
span: attr.span(),
|
||||
});
|
||||
break;
|
||||
}
|
||||
if let Some((feature, level)) = parse_stability(sess, attr) {
|
||||
stab = Some((Stability { level, feature }, attr.span()));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
impl StabilityParser {
|
||||
/// Checks, and emits an error when a stability (or unstability) was already set, which would be a duplicate.
|
||||
fn check_duplicate(&self, cx: &AcceptContext<'_>) -> bool {
|
||||
if let Some((_, _)) = self.stability {
|
||||
cx.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span });
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(allowed_through_unstable_modules) = allowed_through_unstable_modules {
|
||||
match &mut stab {
|
||||
Some((
|
||||
impl AttributeParser for StabilityParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self> = &[
|
||||
(&[sym::stable], |this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
if !this.check_duplicate(cx)
|
||||
&& let Some((feature, level)) = parse_stability(cx, args)
|
||||
{
|
||||
this.stability = Some((Stability { level, feature }, cx.attr_span));
|
||||
}
|
||||
}),
|
||||
(&[sym::unstable], |this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
if !this.check_duplicate(cx)
|
||||
&& let Some((feature, level)) = parse_unstability(cx, args)
|
||||
{
|
||||
this.stability = Some((Stability { level, feature }, cx.attr_span));
|
||||
}
|
||||
}),
|
||||
(&[sym::rustc_allowed_through_unstable_modules], |this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
this.allowed_through_unstable_modules =
|
||||
Some(match args.name_value().and_then(|i| i.value_as_str()) {
|
||||
Some(msg) => msg,
|
||||
None => kw::Empty,
|
||||
});
|
||||
}),
|
||||
];
|
||||
|
||||
fn finalize(mut self, cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
|
||||
if let Some(atum) = self.allowed_through_unstable_modules {
|
||||
if let Some((
|
||||
Stability {
|
||||
level: StabilityLevel::Stable { allowed_through_unstable_modules: in_stab, .. },
|
||||
level: StabilityLevel::Stable { ref mut allowed_through_unstable_modules, .. },
|
||||
..
|
||||
},
|
||||
_,
|
||||
)) => *in_stab = Some(allowed_through_unstable_modules),
|
||||
_ => {
|
||||
sess.dcx()
|
||||
.emit_err(session_diagnostics::RustcAllowedUnstablePairing { span: item_sp });
|
||||
)) = self.stability
|
||||
{
|
||||
*allowed_through_unstable_modules = Some(atum);
|
||||
} else {
|
||||
cx.dcx().emit_err(session_diagnostics::RustcAllowedUnstablePairing {
|
||||
span: cx.target_span,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stab
|
||||
}
|
||||
let (stability, span) = self.stability?;
|
||||
|
||||
/// Collects stability info from `rustc_const_stable`/`rustc_const_unstable`/`rustc_promotable`
|
||||
/// attributes in `attrs`. Returns `None` if no stability attributes are found.
|
||||
pub fn find_const_stability(
|
||||
sess: &Session,
|
||||
attrs: &[impl AttributeExt],
|
||||
item_sp: Span,
|
||||
) -> Option<(ConstStability, Span)> {
|
||||
let mut const_stab: Option<(ConstStability, Span)> = None;
|
||||
let mut promotable = false;
|
||||
let mut const_stable_indirect = false;
|
||||
|
||||
for attr in attrs {
|
||||
match attr.name_or_empty() {
|
||||
sym::rustc_promotable => promotable = true,
|
||||
sym::rustc_const_stable_indirect => const_stable_indirect = true,
|
||||
sym::rustc_const_unstable => {
|
||||
if const_stab.is_some() {
|
||||
sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
|
||||
span: attr.span(),
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some((feature, level)) = parse_unstability(sess, attr) {
|
||||
const_stab = Some((
|
||||
ConstStability {
|
||||
level,
|
||||
feature,
|
||||
const_stable_indirect: false,
|
||||
promotable: false,
|
||||
},
|
||||
attr.span(),
|
||||
));
|
||||
}
|
||||
}
|
||||
sym::rustc_const_stable => {
|
||||
if const_stab.is_some() {
|
||||
sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
|
||||
span: attr.span(),
|
||||
});
|
||||
break;
|
||||
}
|
||||
if let Some((feature, level)) = parse_stability(sess, attr) {
|
||||
const_stab = Some((
|
||||
ConstStability {
|
||||
level,
|
||||
feature,
|
||||
const_stable_indirect: false,
|
||||
promotable: false,
|
||||
},
|
||||
attr.span(),
|
||||
));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Merge promotable and const_stable_indirect into stability info
|
||||
if promotable {
|
||||
match &mut const_stab {
|
||||
Some((stab, _)) => stab.promotable = promotable,
|
||||
_ => {
|
||||
_ = sess
|
||||
.dcx()
|
||||
.emit_err(session_diagnostics::RustcPromotablePairing { span: item_sp })
|
||||
}
|
||||
}
|
||||
}
|
||||
if const_stable_indirect {
|
||||
match &mut const_stab {
|
||||
Some((stab, _)) => {
|
||||
if stab.is_const_unstable() {
|
||||
stab.const_stable_indirect = true;
|
||||
} else {
|
||||
_ = sess.dcx().emit_err(session_diagnostics::RustcConstStableIndirectPairing {
|
||||
span: item_sp,
|
||||
})
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// This function has no const stability attribute, but has `const_stable_indirect`.
|
||||
// We ignore that; unmarked functions are subject to recursive const stability
|
||||
// checks by default so we do carry out the user's intent.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const_stab
|
||||
}
|
||||
|
||||
/// Calculates the const stability for a const function in a `-Zforce-unstable-if-unmarked` crate
|
||||
/// without the `staged_api` feature.
|
||||
pub fn unmarked_crate_const_stab(
|
||||
_sess: &Session,
|
||||
attrs: &[impl AttributeExt],
|
||||
regular_stab: Stability,
|
||||
) -> ConstStability {
|
||||
assert!(regular_stab.level.is_unstable());
|
||||
// The only attribute that matters here is `rustc_const_stable_indirect`.
|
||||
// We enforce recursive const stability rules for those functions.
|
||||
let const_stable_indirect =
|
||||
attrs.iter().any(|a| a.name_or_empty() == sym::rustc_const_stable_indirect);
|
||||
ConstStability {
|
||||
feature: regular_stab.feature,
|
||||
const_stable_indirect,
|
||||
promotable: false,
|
||||
level: regular_stab.level,
|
||||
Some(AttributeKind::Stability { stability, span })
|
||||
}
|
||||
}
|
||||
|
||||
/// Collects stability info from `rustc_default_body_unstable` attributes in `attrs`.
|
||||
/// Returns `None` if no stability attributes are found.
|
||||
pub fn find_body_stability(
|
||||
sess: &Session,
|
||||
attrs: &[impl AttributeExt],
|
||||
) -> Option<(DefaultBodyStability, Span)> {
|
||||
let mut body_stab: Option<(DefaultBodyStability, Span)> = None;
|
||||
|
||||
for attr in attrs {
|
||||
if attr.has_name(sym::rustc_default_body_unstable) {
|
||||
if body_stab.is_some() {
|
||||
sess.dcx()
|
||||
.emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span() });
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some((feature, level)) = parse_unstability(sess, attr) {
|
||||
body_stab = Some((DefaultBodyStability { level, feature }, attr.span()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body_stab
|
||||
// FIXME(jdonszelmann) change to Single
|
||||
#[derive(Default)]
|
||||
pub(crate) struct BodyStabilityParser {
|
||||
stability: Option<(DefaultBodyStability, Span)>,
|
||||
}
|
||||
|
||||
fn insert_or_error(sess: &Session, meta: &MetaItem, item: &mut Option<Symbol>) -> Option<()> {
|
||||
impl AttributeParser for BodyStabilityParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self> =
|
||||
&[(&[sym::rustc_default_body_unstable], |this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
if this.stability.is_some() {
|
||||
cx.dcx()
|
||||
.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span });
|
||||
} else if let Some((feature, level)) = parse_unstability(cx, args) {
|
||||
this.stability = Some((DefaultBodyStability { level, feature }, cx.attr_span));
|
||||
}
|
||||
})];
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
|
||||
let (stability, span) = self.stability?;
|
||||
|
||||
Some(AttributeKind::BodyStability { stability, span })
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ConstStabilityIndirectParser;
|
||||
// FIXME(jdonszelmann): single word attribute group when we have these
|
||||
impl SingleAttributeParser for ConstStabilityIndirectParser {
|
||||
const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_const_stable_indirect];
|
||||
|
||||
// ignore
|
||||
fn on_duplicate(_cx: &AcceptContext<'_>, _first_span: Span) {}
|
||||
|
||||
fn convert(_cx: &AcceptContext<'_>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
Some(AttributeKind::ConstStabilityIndirect)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct ConstStabilityParser {
|
||||
promotable: bool,
|
||||
stability: Option<(PartialConstStability, Span)>,
|
||||
}
|
||||
|
||||
impl ConstStabilityParser {
|
||||
/// Checks, and emits an error when a stability (or unstability) was already set, which would be a duplicate.
|
||||
fn check_duplicate(&self, cx: &AcceptContext<'_>) -> bool {
|
||||
if let Some((_, _)) = self.stability {
|
||||
cx.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span });
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AttributeParser for ConstStabilityParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self> = &[
|
||||
(&[sym::rustc_const_stable], |this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
|
||||
if !this.check_duplicate(cx)
|
||||
&& let Some((feature, level)) = parse_stability(cx, args)
|
||||
{
|
||||
this.stability = Some((
|
||||
PartialConstStability { level, feature, promotable: false },
|
||||
cx.attr_span,
|
||||
));
|
||||
}
|
||||
}),
|
||||
(&[sym::rustc_const_unstable], |this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
if !this.check_duplicate(cx)
|
||||
&& let Some((feature, level)) = parse_unstability(cx, args)
|
||||
{
|
||||
this.stability = Some((
|
||||
PartialConstStability { level, feature, promotable: false },
|
||||
cx.attr_span,
|
||||
));
|
||||
}
|
||||
}),
|
||||
(&[sym::rustc_promotable], |this, cx, _| {
|
||||
reject_outside_std!(cx);
|
||||
this.promotable = true;
|
||||
}),
|
||||
];
|
||||
|
||||
fn finalize(mut self, cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
|
||||
if self.promotable {
|
||||
if let Some((ref mut stab, _)) = self.stability {
|
||||
stab.promotable = true;
|
||||
} else {
|
||||
cx.dcx()
|
||||
.emit_err(session_diagnostics::RustcPromotablePairing { span: cx.target_span });
|
||||
}
|
||||
}
|
||||
|
||||
let (stability, span) = self.stability?;
|
||||
|
||||
Some(AttributeKind::ConstStability { stability, span })
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to insert the value of a `key = value` meta item into an option.
|
||||
///
|
||||
/// Emits an error when either the option was already Some, or the arguments weren't of form
|
||||
/// `name = value`
|
||||
fn insert_value_into_option_or_error(
|
||||
cx: &AcceptContext<'_>,
|
||||
param: &MetaItemParser<'_>,
|
||||
item: &mut Option<Symbol>,
|
||||
) -> Option<()> {
|
||||
if item.is_some() {
|
||||
sess.dcx().emit_err(session_diagnostics::MultipleItem {
|
||||
span: meta.span,
|
||||
item: pprust::path_to_string(&meta.path),
|
||||
cx.emit_err(session_diagnostics::MultipleItem {
|
||||
span: param.span(),
|
||||
item: param.path_without_args().to_string(),
|
||||
});
|
||||
None
|
||||
} else if let Some(v) = meta.value_str() {
|
||||
*item = Some(v);
|
||||
} else if let Some(v) = param.args().name_value()
|
||||
&& let Some(s) = v.value_as_str()
|
||||
{
|
||||
*item = Some(s);
|
||||
Some(())
|
||||
} else {
|
||||
sess.dcx().emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span });
|
||||
cx.emit_err(session_diagnostics::IncorrectMetaItem {
|
||||
span: param.span(),
|
||||
suggestion: None,
|
||||
});
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the content of a `stable`/`rustc_const_stable` attribute, and return the feature name and
|
||||
/// its stability information.
|
||||
fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol, StabilityLevel)> {
|
||||
let metas = attr.meta_item_list()?;
|
||||
|
||||
pub(crate) fn parse_stability(
|
||||
cx: &AcceptContext<'_>,
|
||||
args: &ArgParser<'_>,
|
||||
) -> Option<(Symbol, StabilityLevel)> {
|
||||
let mut feature = None;
|
||||
let mut since = None;
|
||||
for meta in metas {
|
||||
let Some(mi) = meta.meta_item() else {
|
||||
sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral {
|
||||
span: meta.span(),
|
||||
|
||||
for param in args.list()?.mixed() {
|
||||
let param_span = param.span();
|
||||
let Some(param) = param.meta_item() else {
|
||||
cx.emit_err(session_diagnostics::UnsupportedLiteral {
|
||||
span: param_span,
|
||||
reason: UnsupportedLiteralReason::Generic,
|
||||
is_bytestr: false,
|
||||
start_point_span: sess.source_map().start_point(meta.span()),
|
||||
start_point_span: cx.sess().source_map().start_point(param_span),
|
||||
});
|
||||
return None;
|
||||
};
|
||||
|
||||
match mi.name_or_empty() {
|
||||
sym::feature => insert_or_error(sess, mi, &mut feature)?,
|
||||
sym::since => insert_or_error(sess, mi, &mut since)?,
|
||||
match param.word_or_empty_without_args().name {
|
||||
sym::feature => insert_value_into_option_or_error(cx, ¶m, &mut feature)?,
|
||||
sym::since => insert_value_into_option_or_error(cx, ¶m, &mut since)?,
|
||||
_ => {
|
||||
sess.dcx().emit_err(session_diagnostics::UnknownMetaItem {
|
||||
span: meta.span(),
|
||||
item: pprust::path_to_string(&mi.path),
|
||||
cx.emit_err(session_diagnostics::UnknownMetaItem {
|
||||
span: param_span,
|
||||
item: param.path_without_args().to_string(),
|
||||
expected: &["feature", "since"],
|
||||
});
|
||||
return None;
|
||||
|
|
@ -271,9 +263,9 @@ fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol,
|
|||
let feature = match feature {
|
||||
Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature),
|
||||
Some(_bad_feature) => {
|
||||
Err(sess.dcx().emit_err(session_diagnostics::NonIdentFeature { span: attr.span() }))
|
||||
Err(cx.emit_err(session_diagnostics::NonIdentFeature { span: cx.attr_span }))
|
||||
}
|
||||
None => Err(sess.dcx().emit_err(session_diagnostics::MissingFeature { span: attr.span() })),
|
||||
None => Err(cx.emit_err(session_diagnostics::MissingFeature { span: cx.attr_span })),
|
||||
};
|
||||
|
||||
let since = if let Some(since) = since {
|
||||
|
|
@ -282,11 +274,11 @@ fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol,
|
|||
} else if let Some(version) = parse_version(since) {
|
||||
StableSince::Version(version)
|
||||
} else {
|
||||
sess.dcx().emit_err(session_diagnostics::InvalidSince { span: attr.span() });
|
||||
cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span });
|
||||
StableSince::Err
|
||||
}
|
||||
} else {
|
||||
sess.dcx().emit_err(session_diagnostics::MissingSince { span: attr.span() });
|
||||
cx.emit_err(session_diagnostics::MissingSince { span: cx.attr_span });
|
||||
StableSince::Err
|
||||
};
|
||||
|
||||
|
|
@ -299,46 +291,48 @@ fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol,
|
|||
}
|
||||
}
|
||||
|
||||
/// Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable`
|
||||
// Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable`
|
||||
/// attribute, and return the feature name and its stability information.
|
||||
fn parse_unstability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol, StabilityLevel)> {
|
||||
let metas = attr.meta_item_list()?;
|
||||
|
||||
pub(crate) fn parse_unstability(
|
||||
cx: &AcceptContext<'_>,
|
||||
args: &ArgParser<'_>,
|
||||
) -> Option<(Symbol, StabilityLevel)> {
|
||||
let mut feature = None;
|
||||
let mut reason = None;
|
||||
let mut issue = None;
|
||||
let mut issue_num = None;
|
||||
let mut is_soft = false;
|
||||
let mut implied_by = None;
|
||||
for meta in metas {
|
||||
let Some(mi) = meta.meta_item() else {
|
||||
sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral {
|
||||
span: meta.span(),
|
||||
for param in args.list()?.mixed() {
|
||||
let Some(param) = param.meta_item() else {
|
||||
cx.emit_err(session_diagnostics::UnsupportedLiteral {
|
||||
span: param.span(),
|
||||
reason: UnsupportedLiteralReason::Generic,
|
||||
is_bytestr: false,
|
||||
start_point_span: sess.source_map().start_point(meta.span()),
|
||||
start_point_span: cx.sess().source_map().start_point(param.span()),
|
||||
});
|
||||
return None;
|
||||
};
|
||||
|
||||
match mi.name_or_empty() {
|
||||
sym::feature => insert_or_error(sess, mi, &mut feature)?,
|
||||
sym::reason => insert_or_error(sess, mi, &mut reason)?,
|
||||
let (word, args) = param.word_or_empty();
|
||||
match word.name {
|
||||
sym::feature => insert_value_into_option_or_error(cx, ¶m, &mut feature)?,
|
||||
sym::reason => insert_value_into_option_or_error(cx, ¶m, &mut reason)?,
|
||||
sym::issue => {
|
||||
insert_or_error(sess, mi, &mut issue)?;
|
||||
insert_value_into_option_or_error(cx, ¶m, &mut issue)?;
|
||||
|
||||
// These unwraps are safe because `insert_or_error` ensures the meta item
|
||||
// These unwraps are safe because `insert_value_into_option_or_error` ensures the meta item
|
||||
// is a name/value pair string literal.
|
||||
issue_num = match issue.unwrap().as_str() {
|
||||
"none" => None,
|
||||
issue => match issue.parse::<NonZero<u32>>() {
|
||||
issue_str => match issue_str.parse::<NonZero<u32>>() {
|
||||
Ok(num) => Some(num),
|
||||
Err(err) => {
|
||||
sess.dcx().emit_err(
|
||||
cx.emit_err(
|
||||
session_diagnostics::InvalidIssueString {
|
||||
span: mi.span,
|
||||
span: param.span(),
|
||||
cause: session_diagnostics::InvalidIssueStringCause::from_int_error_kind(
|
||||
mi.name_value_literal_span().unwrap(),
|
||||
args.name_value().unwrap().value_span,
|
||||
err.kind(),
|
||||
),
|
||||
},
|
||||
|
|
@ -349,16 +343,16 @@ fn parse_unstability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol
|
|||
};
|
||||
}
|
||||
sym::soft => {
|
||||
if !mi.is_word() {
|
||||
sess.dcx().emit_err(session_diagnostics::SoftNoArgs { span: mi.span });
|
||||
if !args.no_args() {
|
||||
cx.emit_err(session_diagnostics::SoftNoArgs { span: param.span() });
|
||||
}
|
||||
is_soft = true;
|
||||
}
|
||||
sym::implied_by => insert_or_error(sess, mi, &mut implied_by)?,
|
||||
sym::implied_by => insert_value_into_option_or_error(cx, ¶m, &mut implied_by)?,
|
||||
_ => {
|
||||
sess.dcx().emit_err(session_diagnostics::UnknownMetaItem {
|
||||
span: meta.span(),
|
||||
item: pprust::path_to_string(&mi.path),
|
||||
cx.emit_err(session_diagnostics::UnknownMetaItem {
|
||||
span: param.span(),
|
||||
item: param.path_without_args().to_string(),
|
||||
expected: &["feature", "reason", "issue", "soft", "implied_by"],
|
||||
});
|
||||
return None;
|
||||
|
|
@ -369,14 +363,13 @@ fn parse_unstability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol
|
|||
let feature = match feature {
|
||||
Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature),
|
||||
Some(_bad_feature) => {
|
||||
Err(sess.dcx().emit_err(session_diagnostics::NonIdentFeature { span: attr.span() }))
|
||||
Err(cx.emit_err(session_diagnostics::NonIdentFeature { span: cx.attr_span }))
|
||||
}
|
||||
None => Err(sess.dcx().emit_err(session_diagnostics::MissingFeature { span: attr.span() })),
|
||||
None => Err(cx.emit_err(session_diagnostics::MissingFeature { span: cx.attr_span })),
|
||||
};
|
||||
|
||||
let issue = issue.ok_or_else(|| {
|
||||
sess.dcx().emit_err(session_diagnostics::MissingIssue { span: attr.span() })
|
||||
});
|
||||
let issue =
|
||||
issue.ok_or_else(|| cx.emit_err(session_diagnostics::MissingIssue { span: cx.attr_span }));
|
||||
|
||||
match (feature, issue) {
|
||||
(Ok(feature), Ok(_)) => {
|
||||
|
|
|
|||
|
|
@ -1,36 +1,33 @@
|
|||
use rustc_ast::attr::AttributeExt;
|
||||
use rustc_attr_data_structures::TransparencyError;
|
||||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_span::hygiene::Transparency;
|
||||
use rustc_span::sym;
|
||||
|
||||
pub fn find_transparency(
|
||||
attrs: &[impl AttributeExt],
|
||||
macro_rules: bool,
|
||||
) -> (Transparency, Option<TransparencyError>) {
|
||||
let mut transparency = None;
|
||||
let mut error = None;
|
||||
for attr in attrs {
|
||||
if attr.has_name(sym::rustc_macro_transparency) {
|
||||
if let Some((_, old_span)) = transparency {
|
||||
error = Some(TransparencyError::MultipleTransparencyAttrs(old_span, attr.span()));
|
||||
break;
|
||||
} else if let Some(value) = attr.value_str() {
|
||||
transparency = Some((
|
||||
match value {
|
||||
sym::transparent => Transparency::Transparent,
|
||||
sym::semitransparent => Transparency::SemiTransparent,
|
||||
sym::opaque => Transparency::Opaque,
|
||||
_ => {
|
||||
error =
|
||||
Some(TransparencyError::UnknownTransparency(value, attr.span()));
|
||||
continue;
|
||||
}
|
||||
},
|
||||
attr.span(),
|
||||
));
|
||||
}
|
||||
}
|
||||
use super::{AcceptContext, SingleAttributeParser};
|
||||
use crate::parser::ArgParser;
|
||||
|
||||
pub(crate) struct TransparencyParser;
|
||||
|
||||
// FIXME(jdonszelmann): make these proper diagnostics
|
||||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
#[allow(rustc::diagnostic_outside_of_impl)]
|
||||
impl SingleAttributeParser for TransparencyParser {
|
||||
const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_macro_transparency];
|
||||
|
||||
fn on_duplicate(cx: &crate::context::AcceptContext<'_>, first_span: rustc_span::Span) {
|
||||
cx.dcx().span_err(vec![first_span, cx.attr_span], "multiple macro transparency attributes");
|
||||
}
|
||||
|
||||
fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
match args.name_value().and_then(|nv| nv.value_as_str()) {
|
||||
Some(sym::transparent) => Some(Transparency::Transparent),
|
||||
Some(sym::semitransparent) => Some(Transparency::SemiTransparent),
|
||||
Some(sym::opaque) => Some(Transparency::Opaque),
|
||||
Some(other) => {
|
||||
cx.dcx().span_err(cx.attr_span, format!("unknown macro transparency: `{other}`"));
|
||||
None
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
.map(AttributeKind::MacroTransparency)
|
||||
}
|
||||
let fallback = if macro_rules { Transparency::SemiTransparent } else { Transparency::Opaque };
|
||||
(transparency.map_or(fallback, |t| t.0), error)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,22 +3,6 @@ use rustc_attr_data_structures::RustcVersion;
|
|||
use rustc_feature::is_builtin_attr_name;
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
||||
pub(crate) enum UnsupportedLiteralReason {
|
||||
Generic,
|
||||
CfgString,
|
||||
CfgBoolean,
|
||||
DeprecatedString,
|
||||
DeprecatedKvPair,
|
||||
}
|
||||
|
||||
pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool {
|
||||
attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
|
||||
}
|
||||
|
||||
pub fn find_crate_name(attrs: &[impl AttributeExt]) -> Option<Symbol> {
|
||||
first_attr_value_str_by_name(attrs, sym::crate_name)
|
||||
}
|
||||
|
||||
/// Parse a rustc version number written inside string literal in an attribute,
|
||||
/// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are
|
||||
/// not accepted in this position, unlike when parsing CFG_RELEASE.
|
||||
|
|
@ -34,3 +18,11 @@ pub fn parse_version(s: Symbol) -> Option<RustcVersion> {
|
|||
let patch = digits.next().unwrap_or("0").parse().ok()?;
|
||||
Some(RustcVersion { major, minor, patch })
|
||||
}
|
||||
|
||||
pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool {
|
||||
attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
|
||||
}
|
||||
|
||||
pub fn find_crate_name(attrs: &[impl AttributeExt]) -> Option<Symbol> {
|
||||
first_attr_value_str_by_name(attrs, sym::crate_name)
|
||||
}
|
||||
|
|
|
|||
348
compiler/rustc_attr_parsing/src/context.rs
Normal file
348
compiler/rustc_attr_parsing/src/context.rs
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
use std::cell::RefCell;
|
||||
use std::collections::BTreeMap;
|
||||
use std::ops::Deref;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use rustc_ast::{self as ast, DelimArgs};
|
||||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_errors::{DiagCtxtHandle, Diagnostic};
|
||||
use rustc_feature::Features;
|
||||
use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
|
||||
|
||||
use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
|
||||
use crate::attributes::confusables::ConfusablesParser;
|
||||
use crate::attributes::deprecation::DeprecationParser;
|
||||
use crate::attributes::repr::ReprParser;
|
||||
use crate::attributes::stability::{
|
||||
BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser,
|
||||
};
|
||||
use crate::attributes::transparency::TransparencyParser;
|
||||
use crate::attributes::{AttributeParser as _, Combine, Single};
|
||||
use crate::parser::{ArgParser, MetaItemParser};
|
||||
|
||||
macro_rules! attribute_groups {
|
||||
(
|
||||
pub(crate) static $name: ident = [$($names: ty),* $(,)?];
|
||||
) => {
|
||||
pub(crate) static $name: LazyLock<(
|
||||
BTreeMap<&'static [Symbol], Vec<Box<dyn Fn(&AcceptContext<'_>, &ArgParser<'_>) + Send + Sync>>>,
|
||||
Vec<Box<dyn Send + Sync + Fn(&FinalizeContext<'_>) -> Option<AttributeKind>>>
|
||||
)> = LazyLock::new(|| {
|
||||
let mut accepts = BTreeMap::<_, Vec<Box<dyn Fn(&AcceptContext<'_>, &ArgParser<'_>) + Send + Sync>>>::new();
|
||||
let mut finalizes = Vec::<Box<dyn Send + Sync + Fn(&FinalizeContext<'_>) -> Option<AttributeKind>>>::new();
|
||||
$(
|
||||
{
|
||||
thread_local! {
|
||||
static STATE_OBJECT: RefCell<$names> = RefCell::new(<$names>::default());
|
||||
};
|
||||
|
||||
for (k, v) in <$names>::ATTRIBUTES {
|
||||
accepts.entry(*k).or_default().push(Box::new(|cx, args| {
|
||||
STATE_OBJECT.with_borrow_mut(|s| {
|
||||
v(s, cx, args)
|
||||
})
|
||||
}));
|
||||
}
|
||||
|
||||
finalizes.push(Box::new(|cx| {
|
||||
let state = STATE_OBJECT.take();
|
||||
state.finalize(cx)
|
||||
}));
|
||||
}
|
||||
)*
|
||||
|
||||
(accepts, finalizes)
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
attribute_groups!(
|
||||
pub(crate) static ATTRIBUTE_MAPPING = [
|
||||
// tidy-alphabetical-start
|
||||
BodyStabilityParser,
|
||||
ConfusablesParser,
|
||||
ConstStabilityParser,
|
||||
StabilityParser,
|
||||
// tidy-alphabetical-end
|
||||
|
||||
// tidy-alphabetical-start
|
||||
Combine<AllowConstFnUnstableParser>,
|
||||
Combine<AllowInternalUnstableParser>,
|
||||
Combine<ReprParser>,
|
||||
// tidy-alphabetical-end
|
||||
|
||||
// tidy-alphabetical-start
|
||||
Single<ConstStabilityIndirectParser>,
|
||||
Single<DeprecationParser>,
|
||||
Single<TransparencyParser>,
|
||||
// tidy-alphabetical-end
|
||||
];
|
||||
);
|
||||
|
||||
/// Context given to every attribute parser when accepting
|
||||
///
|
||||
/// Gives [`AttributeParser`]s enough information to create errors, for example.
|
||||
pub(crate) struct AcceptContext<'a> {
|
||||
pub(crate) group_cx: &'a FinalizeContext<'a>,
|
||||
/// The span of the attribute currently being parsed
|
||||
pub(crate) attr_span: Span,
|
||||
}
|
||||
|
||||
impl<'a> AcceptContext<'a> {
|
||||
pub(crate) fn emit_err(&self, diag: impl Diagnostic<'a>) -> ErrorGuaranteed {
|
||||
if self.limit_diagnostics {
|
||||
self.dcx().create_err(diag).delay_as_bug()
|
||||
} else {
|
||||
self.dcx().emit_err(diag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for AcceptContext<'a> {
|
||||
type Target = FinalizeContext<'a>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.group_cx
|
||||
}
|
||||
}
|
||||
|
||||
/// Context given to every attribute parser during finalization.
|
||||
///
|
||||
/// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create errors, for example.
|
||||
pub(crate) struct FinalizeContext<'a> {
|
||||
/// The parse context, gives access to the session and the
|
||||
/// diagnostics context.
|
||||
pub(crate) cx: &'a AttributeParser<'a>,
|
||||
/// The span of the syntactical component this attribute was applied to
|
||||
pub(crate) target_span: Span,
|
||||
}
|
||||
|
||||
impl<'a> Deref for FinalizeContext<'a> {
|
||||
type Target = AttributeParser<'a>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.cx
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Copy, Debug)]
|
||||
pub enum OmitDoc {
|
||||
Lower,
|
||||
Skip,
|
||||
}
|
||||
|
||||
/// Context created once, for example as part of the ast lowering
|
||||
/// context, through which all attributes can be lowered.
|
||||
pub struct AttributeParser<'sess> {
|
||||
#[expect(dead_code)] // FIXME(jdonszelmann): needed later to verify we parsed all attributes
|
||||
tools: Vec<Symbol>,
|
||||
sess: &'sess Session,
|
||||
features: Option<&'sess Features>,
|
||||
|
||||
/// *only* parse attributes with this symbol.
|
||||
///
|
||||
/// Used in cases where we want the lowering infrastructure for
|
||||
/// parse just a single attribute.
|
||||
parse_only: Option<Symbol>,
|
||||
|
||||
/// Can be used to instruct parsers to reduce the number of diagnostics it emits.
|
||||
/// Useful when using `parse_limited` and you know the attr will be reparsed later.
|
||||
pub(crate) limit_diagnostics: bool,
|
||||
}
|
||||
|
||||
impl<'sess> AttributeParser<'sess> {
|
||||
/// This method allows you to parse attributes *before* you have access to features or tools.
|
||||
/// One example where this is necessary, is to parse `feature` attributes themselves for
|
||||
/// example.
|
||||
///
|
||||
/// Try to use this as little as possible. Attributes *should* be lowered during `rustc_ast_lowering`.
|
||||
/// Some attributes require access to features to parse, which would crash if you tried to do so
|
||||
/// through [`parse_limited`](Self::parse_limited).
|
||||
///
|
||||
/// To make sure use is limited, supply a `Symbol` you'd like to parse. Only attributes with
|
||||
/// that symbol are picked out of the list of instructions and parsed. Those are returned.
|
||||
pub fn parse_limited(
|
||||
sess: &'sess Session,
|
||||
attrs: &[ast::Attribute],
|
||||
sym: Symbol,
|
||||
target_span: Span,
|
||||
limit_diagnostics: bool,
|
||||
) -> Option<Attribute> {
|
||||
let mut parsed = Self {
|
||||
sess,
|
||||
features: None,
|
||||
tools: Vec::new(),
|
||||
parse_only: Some(sym),
|
||||
limit_diagnostics,
|
||||
}
|
||||
.parse_attribute_list(attrs, target_span, OmitDoc::Skip, std::convert::identity);
|
||||
|
||||
assert!(parsed.len() <= 1);
|
||||
|
||||
parsed.pop()
|
||||
}
|
||||
|
||||
pub fn new(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self {
|
||||
Self { sess, features: Some(features), tools, parse_only: None, limit_diagnostics: false }
|
||||
}
|
||||
|
||||
pub(crate) fn sess(&self) -> &'sess Session {
|
||||
self.sess
|
||||
}
|
||||
|
||||
pub(crate) fn features(&self) -> &'sess Features {
|
||||
self.features.expect("features not available at this point in the compiler")
|
||||
}
|
||||
|
||||
pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> {
|
||||
self.sess.dcx()
|
||||
}
|
||||
|
||||
/// Parse a list of attributes.
|
||||
///
|
||||
/// `target_span` is the span of the thing this list of attributes is applied to,
|
||||
/// and when `omit_doc` is set, doc attributes are filtered out.
|
||||
pub fn parse_attribute_list<'a>(
|
||||
&'a self,
|
||||
attrs: &'a [ast::Attribute],
|
||||
target_span: Span,
|
||||
omit_doc: OmitDoc,
|
||||
|
||||
lower_span: impl Copy + Fn(Span) -> Span,
|
||||
) -> Vec<Attribute> {
|
||||
let mut attributes = Vec::new();
|
||||
|
||||
let group_cx = FinalizeContext { cx: self, target_span };
|
||||
|
||||
for attr in attrs {
|
||||
// if we're only looking for a single attribute,
|
||||
// skip all the ones we don't care about
|
||||
if let Some(expected) = self.parse_only {
|
||||
if attr.name_or_empty() != expected {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// sometimes, for example for `#![doc = include_str!("readme.md")]`,
|
||||
// doc still contains a non-literal. You might say, when we're lowering attributes
|
||||
// that's expanded right? But no, sometimes, when parsing attributes on macros,
|
||||
// we already use the lowering logic and these are still there. So, when `omit_doc`
|
||||
// is set we *also* want to ignore these
|
||||
if omit_doc == OmitDoc::Skip && attr.name_or_empty() == sym::doc {
|
||||
continue;
|
||||
}
|
||||
|
||||
match &attr.kind {
|
||||
ast::AttrKind::DocComment(comment_kind, symbol) => {
|
||||
if omit_doc == OmitDoc::Skip {
|
||||
continue;
|
||||
}
|
||||
|
||||
attributes.push(Attribute::Parsed(AttributeKind::DocComment {
|
||||
style: attr.style,
|
||||
kind: *comment_kind,
|
||||
span: lower_span(attr.span),
|
||||
comment: *symbol,
|
||||
}))
|
||||
}
|
||||
// // FIXME: make doc attributes go through a proper attribute parser
|
||||
// ast::AttrKind::Normal(n) if n.name_or_empty() == sym::doc => {
|
||||
// let p = GenericMetaItemParser::from_attr(&n, self.dcx());
|
||||
//
|
||||
// attributes.push(Attribute::Parsed(AttributeKind::DocComment {
|
||||
// style: attr.style,
|
||||
// kind: CommentKind::Line,
|
||||
// span: attr.span,
|
||||
// comment: p.args().name_value(),
|
||||
// }))
|
||||
// }
|
||||
ast::AttrKind::Normal(n) => {
|
||||
let parser = MetaItemParser::from_attr(n, self.dcx());
|
||||
let (path, args) = parser.deconstruct();
|
||||
let parts = path.segments().map(|i| i.name).collect::<Vec<_>>();
|
||||
|
||||
if let Some(accepts) = ATTRIBUTE_MAPPING.0.get(parts.as_slice()) {
|
||||
for f in accepts {
|
||||
let cx = AcceptContext {
|
||||
group_cx: &group_cx,
|
||||
attr_span: lower_span(attr.span),
|
||||
};
|
||||
|
||||
f(&cx, &args)
|
||||
}
|
||||
} else {
|
||||
// if we're here, we must be compiling a tool attribute... Or someone forgot to
|
||||
// parse their fancy new attribute. Let's warn them in any case. If you are that
|
||||
// person, and you really your attribute should remain unparsed, carefully read the
|
||||
// documentation in this module and if you still think so you can add an exception
|
||||
// to this assertion.
|
||||
|
||||
// FIXME(jdonszelmann): convert other attributes, and check with this that
|
||||
// we caught em all
|
||||
// const FIXME_TEMPORARY_ATTR_ALLOWLIST: &[Symbol] = &[sym::cfg];
|
||||
// assert!(
|
||||
// self.tools.contains(&parts[0]) || true,
|
||||
// // || FIXME_TEMPORARY_ATTR_ALLOWLIST.contains(&parts[0]),
|
||||
// "attribute {path} wasn't parsed and isn't a know tool attribute",
|
||||
// );
|
||||
|
||||
attributes.push(Attribute::Unparsed(Box::new(AttrItem {
|
||||
path: AttrPath::from_ast(&n.item.path),
|
||||
args: self.lower_attr_args(&n.item.args, lower_span),
|
||||
id: HashIgnoredAttrId { attr_id: attr.id },
|
||||
style: attr.style,
|
||||
span: lower_span(attr.span),
|
||||
})));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut parsed_attributes = Vec::new();
|
||||
for f in &ATTRIBUTE_MAPPING.1 {
|
||||
if let Some(attr) = f(&group_cx) {
|
||||
parsed_attributes.push(Attribute::Parsed(attr));
|
||||
}
|
||||
}
|
||||
|
||||
attributes.extend(parsed_attributes);
|
||||
|
||||
attributes
|
||||
}
|
||||
|
||||
fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs {
|
||||
match args {
|
||||
ast::AttrArgs::Empty => AttrArgs::Empty,
|
||||
ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(DelimArgs {
|
||||
dspan: args.dspan,
|
||||
delim: args.delim,
|
||||
tokens: args.tokens.flattened(),
|
||||
}),
|
||||
// This is an inert key-value attribute - it will never be visible to macros
|
||||
// after it gets lowered to HIR. Therefore, we can extract literals to handle
|
||||
// nonterminals in `#[doc]` (e.g. `#[doc = $e]`).
|
||||
ast::AttrArgs::Eq { eq_span, expr } => {
|
||||
// In valid code the value always ends up as a single literal. Otherwise, a dummy
|
||||
// literal suffices because the error is handled elsewhere.
|
||||
let lit = if let ast::ExprKind::Lit(token_lit) = expr.kind
|
||||
&& let Ok(lit) =
|
||||
ast::MetaItemLit::from_token_lit(token_lit, lower_span(expr.span))
|
||||
{
|
||||
lit
|
||||
} else {
|
||||
let guar = self.dcx().has_errors().unwrap();
|
||||
ast::MetaItemLit {
|
||||
symbol: kw::Empty,
|
||||
suffix: None,
|
||||
kind: ast::LitKind::Err(guar),
|
||||
span: DUMMY_SP,
|
||||
}
|
||||
};
|
||||
AttrArgs::Eq { eq_span: lower_span(*eq_span), expr: lit }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,79 @@
|
|||
//! Functions and types dealing with attributes and meta items.
|
||||
//! Centralized logic for parsing and attributes.
|
||||
//!
|
||||
//! FIXME(Centril): For now being, much of the logic is still in `rustc_ast::attr`.
|
||||
//! The goal is to move the definition of `MetaItem` and things that don't need to be in `syntax`
|
||||
//! to this crate.
|
||||
//! Part of a series of crates:
|
||||
//! - rustc_attr_data_structures: contains types that the parsers parse into
|
||||
//! - rustc_attr_parsing: this crate
|
||||
//! - (in the future): rustc_attr_validation
|
||||
//!
|
||||
//! History: Check out [#131229](https://github.com/rust-lang/rust/issues/131229).
|
||||
//! There used to be only one definition of attributes in the compiler: `ast::Attribute`.
|
||||
//! These were then parsed or validated or both in places distributed all over the compiler.
|
||||
//! This was a mess...
|
||||
//!
|
||||
//! Attributes are markers on items.
|
||||
//! Many of them are actually attribute-like proc-macros, and are expanded to some other rust syntax.
|
||||
//! This could either be a user provided proc macro, or something compiler provided.
|
||||
//! `derive` is an example of one that the compiler provides.
|
||||
//! These are built-in, but they have a valid expansion to Rust tokens and are thus called "active".
|
||||
//! I personally like calling these *active* compiler-provided attributes, built-in *macros*,
|
||||
//! because they still expand, and this helps to differentiate them from built-in *attributes*.
|
||||
//! However, I'll be the first to admit that the naming here can be confusing.
|
||||
//!
|
||||
//! The alternative to active attributes, are inert attributes.
|
||||
//! These can occur in user code (proc-macro helper attributes).
|
||||
//! But what's important is, many built-in attributes are inert like this.
|
||||
//! There is nothing they expand to during the macro expansion process,
|
||||
//! sometimes because they literally cannot expand to something that is valid Rust.
|
||||
//! They are really just markers to guide the compilation process.
|
||||
//! An example is `#[inline(...)]` which changes how code for functions is generated.
|
||||
//!
|
||||
//! ```text
|
||||
//! Active Inert
|
||||
//! ┌──────────────────────┬──────────────────────┐
|
||||
//! │ (mostly in) │ these are parsed │
|
||||
//! │ rustc_builtin_macros │ here! │
|
||||
//! │ │ │
|
||||
//! │ │ │
|
||||
//! │ #[derive(...)] │ #[stable()] │
|
||||
//! Built-in │ #[cfg()] │ #[inline()] │
|
||||
//! │ #[cfg_attr()] │ #[repr()] │
|
||||
//! │ │ │
|
||||
//! │ │ │
|
||||
//! │ │ │
|
||||
//! ├──────────────────────┼──────────────────────┤
|
||||
//! │ │ │
|
||||
//! │ │ │
|
||||
//! │ │ `b` in │
|
||||
//! │ │ #[proc_macro_derive( │
|
||||
//! User created │ #[proc_macro_attr()] │ a, │
|
||||
//! │ │ attributes(b) │
|
||||
//! │ │ ] │
|
||||
//! │ │ │
|
||||
//! │ │ │
|
||||
//! │ │ │
|
||||
//! └──────────────────────┴──────────────────────┘
|
||||
//! ```
|
||||
//!
|
||||
//! In this crate, syntactical attributes (sequences of tokens that look like
|
||||
//! `#[something(something else)]`) are parsed into more semantic attributes, markers on items.
|
||||
//! Multiple syntactic attributes might influence a single semantic attribute. For example,
|
||||
//! `#[stable(...)]` and `#[unstable()]` cannot occur together, and both semantically define
|
||||
//! a "stability" of an item. So, the stability attribute has an
|
||||
//! [`AttributeParser`](attributes::AttributeParser) that recognizes both the `#[stable()]`
|
||||
//! and `#[unstable()]` syntactic attributes, and at the end produce a single [`AttributeKind::Stability`].
|
||||
//!
|
||||
//! As a rule of thumb, when a syntactical attribute can be applied more than once, they should be
|
||||
//! combined into a single semantic attribute. For example:
|
||||
//!
|
||||
//! ```
|
||||
//! #[repr(C)]
|
||||
//! #[repr(packed)]
|
||||
//! struct Meow {}
|
||||
//! ```
|
||||
//!
|
||||
//! should result in a single `AttributeKind::Repr` containing a list of repr annotations, in this
|
||||
//! case `C` and `packed`. This is equivalent to writing `#[repr(C, packed)]` in a single
|
||||
//! syntactical annotation.
|
||||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
|
|
@ -12,11 +83,59 @@
|
|||
#![warn(unreachable_pub)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
#[macro_use]
|
||||
mod attributes;
|
||||
mod context;
|
||||
pub mod parser;
|
||||
mod session_diagnostics;
|
||||
|
||||
pub use attributes::*;
|
||||
pub use attributes::cfg::*;
|
||||
pub use attributes::util::{find_crate_name, is_builtin_attr, parse_version};
|
||||
pub use context::{AttributeParser, OmitDoc};
|
||||
pub use rustc_attr_data_structures::*;
|
||||
pub use util::{find_crate_name, is_builtin_attr, parse_version};
|
||||
|
||||
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
|
||||
|
||||
/// Finds attributes in sequences of attributes by pattern matching.
|
||||
///
|
||||
/// A little like `matches` but for attributes.
|
||||
///
|
||||
/// ```rust,ignore (illustrative)
|
||||
/// // finds the repr attribute
|
||||
/// if let Some(r) = find_attr!(attrs, AttributeKind::Repr(r) => r) {
|
||||
///
|
||||
/// }
|
||||
///
|
||||
/// // checks if one has matched
|
||||
/// if find_attr!(attrs, AttributeKind::Repr(_)) {
|
||||
///
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Often this requires you to first end up with a list of attributes.
|
||||
/// A common way to get those is through `tcx.get_all_attrs(did)`
|
||||
#[macro_export]
|
||||
macro_rules! find_attr {
|
||||
($attributes_list: expr, $pattern: pat $(if $guard: expr)?) => {{
|
||||
$crate::find_attr!($attributes_list, $pattern $(if $guard)? => ()).is_some()
|
||||
}};
|
||||
|
||||
($attributes_list: expr, $pattern: pat $(if $guard: expr)? => $e: expr) => {{
|
||||
fn check_attribute_iterator<'a>(_: &'_ impl IntoIterator<Item = &'a rustc_hir::Attribute>) {}
|
||||
check_attribute_iterator(&$attributes_list);
|
||||
|
||||
let find_attribute = |iter| {
|
||||
for i in $attributes_list {
|
||||
match i {
|
||||
rustc_hir::Attribute::Parsed($pattern) $(if $guard)? => {
|
||||
return Some($e);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
};
|
||||
find_attribute($attributes_list)
|
||||
}};
|
||||
}
|
||||
|
|
|
|||
624
compiler/rustc_attr_parsing/src/parser.rs
Normal file
624
compiler/rustc_attr_parsing/src/parser.rs
Normal file
|
|
@ -0,0 +1,624 @@
|
|||
//! This is in essence an (improved) duplicate of `rustc_ast/attr/mod.rs`.
|
||||
//! That module is intended to be deleted in its entirety.
|
||||
//!
|
||||
//! FIXME(jdonszelmann): delete `rustc_ast/attr/mod.rs`
|
||||
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::iter::Peekable;
|
||||
|
||||
use rustc_ast::token::{self, Delimiter, Token};
|
||||
use rustc_ast::tokenstream::{TokenStreamIter, TokenTree};
|
||||
use rustc_ast::{AttrArgs, DelimArgs, Expr, ExprKind, LitKind, MetaItemLit, NormalAttr, Path};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_errors::DiagCtxtHandle;
|
||||
use rustc_hir::{self as hir, AttrPath};
|
||||
use rustc_span::symbol::{Ident, kw};
|
||||
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol};
|
||||
|
||||
pub struct SegmentIterator<'a> {
|
||||
offset: usize,
|
||||
path: &'a PathParser<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for SegmentIterator<'a> {
|
||||
type Item = &'a Ident;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.offset >= self.path.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let res = match self.path {
|
||||
PathParser::Ast(ast_path) => &ast_path.segments[self.offset].ident,
|
||||
PathParser::Attr(attr_path) => &attr_path.segments[self.offset],
|
||||
};
|
||||
|
||||
self.offset += 1;
|
||||
Some(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum PathParser<'a> {
|
||||
Ast(&'a Path),
|
||||
Attr(AttrPath),
|
||||
}
|
||||
|
||||
impl<'a> PathParser<'a> {
|
||||
pub fn get_attribute_path(&self) -> hir::AttrPath {
|
||||
AttrPath {
|
||||
segments: self.segments().copied().collect::<Vec<_>>().into_boxed_slice(),
|
||||
span: self.span(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn segments(&'a self) -> impl Iterator<Item = &'a Ident> {
|
||||
SegmentIterator { offset: 0, path: self }
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
PathParser::Ast(path) => path.span,
|
||||
PathParser::Attr(attr_path) => attr_path.span,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
match self {
|
||||
PathParser::Ast(path) => path.segments.len(),
|
||||
PathParser::Attr(attr_path) => attr_path.segments.len(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn segments_is(&self, segments: &[Symbol]) -> bool {
|
||||
self.len() == segments.len() && self.segments().zip(segments).all(|(a, b)| a.name == *b)
|
||||
}
|
||||
|
||||
pub fn word(&self) -> Option<Ident> {
|
||||
(self.len() == 1).then(|| **self.segments().next().as_ref().unwrap())
|
||||
}
|
||||
|
||||
pub fn word_or_empty(&self) -> Ident {
|
||||
self.word().unwrap_or_else(Ident::empty)
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem is some specific word.
|
||||
///
|
||||
/// See [`word`](Self::word) for examples of what a word is.
|
||||
pub fn word_is(&self, sym: Symbol) -> bool {
|
||||
self.word().map(|i| i.name == sym).unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PathParser<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
PathParser::Ast(path) => write!(f, "{}", pprust::path_to_string(path)),
|
||||
PathParser::Attr(attr_path) => write!(f, "{attr_path}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[must_use]
|
||||
pub enum ArgParser<'a> {
|
||||
NoArgs,
|
||||
List(MetaItemListParser<'a>),
|
||||
NameValue(NameValueParser),
|
||||
}
|
||||
|
||||
impl<'a> ArgParser<'a> {
|
||||
pub fn span(&self) -> Option<Span> {
|
||||
match self {
|
||||
Self::NoArgs => None,
|
||||
Self::List(l) => Some(l.span),
|
||||
Self::NameValue(n) => Some(n.value_span.with_lo(n.eq_span.lo())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_attr_args(value: &'a AttrArgs, dcx: DiagCtxtHandle<'a>) -> Self {
|
||||
match value {
|
||||
AttrArgs::Empty => Self::NoArgs,
|
||||
AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => {
|
||||
Self::List(MetaItemListParser::new(args, dcx))
|
||||
}
|
||||
AttrArgs::Delimited(args) => {
|
||||
Self::List(MetaItemListParser { sub_parsers: vec![], span: args.dspan.entire() })
|
||||
}
|
||||
AttrArgs::Eq { eq_span, expr } => Self::NameValue(NameValueParser {
|
||||
eq_span: *eq_span,
|
||||
value: expr_to_lit(dcx, &expr),
|
||||
value_span: expr.span,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem is a list
|
||||
///
|
||||
/// Some examples:
|
||||
///
|
||||
/// - `#[allow(clippy::complexity)]`: `(clippy::complexity)` is a list
|
||||
/// - `#[rustfmt::skip::macros(target_macro_name)]`: `(target_macro_name)` is a list
|
||||
pub fn list(&self) -> Option<&MetaItemListParser<'a>> {
|
||||
match self {
|
||||
Self::List(l) => Some(l),
|
||||
Self::NameValue(_) | Self::NoArgs => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem is a name-value pair.
|
||||
///
|
||||
/// Some examples:
|
||||
///
|
||||
/// - `#[clippy::cyclomatic_complexity = "100"]`: `clippy::cyclomatic_complexity = "100"` is a name value pair,
|
||||
/// where the name is a path (`clippy::cyclomatic_complexity`). You already checked the path
|
||||
/// to get an `ArgParser`, so this method will effectively only assert that the `= "100"` is
|
||||
/// there
|
||||
/// - `#[doc = "hello"]`: `doc = "hello` is also a name value pair
|
||||
pub fn name_value(&self) -> Option<&NameValueParser> {
|
||||
match self {
|
||||
Self::NameValue(n) => Some(n),
|
||||
Self::List(_) | Self::NoArgs => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Asserts that there are no arguments
|
||||
pub fn no_args(&self) -> bool {
|
||||
matches!(self, Self::NoArgs)
|
||||
}
|
||||
}
|
||||
|
||||
/// Inside lists, values could be either literals, or more deeply nested meta items.
|
||||
/// This enum represents that.
|
||||
///
|
||||
/// Choose which one you want using the provided methods.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum MetaItemOrLitParser<'a> {
|
||||
MetaItemParser(MetaItemParser<'a>),
|
||||
Lit(MetaItemLit),
|
||||
Err(Span, ErrorGuaranteed),
|
||||
}
|
||||
|
||||
impl<'a> MetaItemOrLitParser<'a> {
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
MetaItemOrLitParser::MetaItemParser(generic_meta_item_parser) => {
|
||||
generic_meta_item_parser.span()
|
||||
}
|
||||
MetaItemOrLitParser::Lit(meta_item_lit) => meta_item_lit.span,
|
||||
MetaItemOrLitParser::Err(span, _) => *span,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lit(&self) -> Option<&MetaItemLit> {
|
||||
match self {
|
||||
MetaItemOrLitParser::Lit(meta_item_lit) => Some(meta_item_lit),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn meta_item(&self) -> Option<&MetaItemParser<'a>> {
|
||||
match self {
|
||||
MetaItemOrLitParser::MetaItemParser(parser) => Some(parser),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Utility that deconstructs a MetaItem into usable parts.
|
||||
///
|
||||
/// MetaItems are syntactically extremely flexible, but specific attributes want to parse
|
||||
/// them in custom, more restricted ways. This can be done using this struct.
|
||||
///
|
||||
/// MetaItems consist of some path, and some args. The args could be empty. In other words:
|
||||
///
|
||||
/// - `name` -> args are empty
|
||||
/// - `name(...)` -> args are a [`list`](ArgParser::list), which is the bit between the parentheses
|
||||
/// - `name = value`-> arg is [`name_value`](ArgParser::name_value), where the argument is the
|
||||
/// `= value` part
|
||||
///
|
||||
/// The syntax of MetaItems can be found at <https://doc.rust-lang.org/reference/attributes.html>
|
||||
#[derive(Clone)]
|
||||
pub struct MetaItemParser<'a> {
|
||||
path: PathParser<'a>,
|
||||
args: ArgParser<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Debug for MetaItemParser<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("MetaItemParser")
|
||||
.field("path", &self.path)
|
||||
.field("args", &self.args)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MetaItemParser<'a> {
|
||||
/// Create a new parser from a [`NormalAttr`], which is stored inside of any
|
||||
/// [`ast::Attribute`](rustc_ast::Attribute)
|
||||
pub fn from_attr(attr: &'a NormalAttr, dcx: DiagCtxtHandle<'a>) -> Self {
|
||||
Self {
|
||||
path: PathParser::Ast(&attr.item.path),
|
||||
args: ArgParser::from_attr_args(&attr.item.args, dcx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MetaItemParser<'a> {
|
||||
pub fn span(&self) -> Span {
|
||||
if let Some(other) = self.args.span() {
|
||||
self.path.span().with_hi(other.hi())
|
||||
} else {
|
||||
self.path.span()
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets just the path, without the args.
|
||||
pub fn path_without_args(&self) -> PathParser<'a> {
|
||||
self.path.clone()
|
||||
}
|
||||
|
||||
/// Gets just the args parser, without caring about the path.
|
||||
pub fn args(&self) -> &ArgParser<'a> {
|
||||
&self.args
|
||||
}
|
||||
|
||||
pub fn deconstruct(&self) -> (PathParser<'a>, &ArgParser<'a>) {
|
||||
(self.path_without_args(), self.args())
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem starts with a path. Some examples:
|
||||
///
|
||||
/// - `#[rustfmt::skip]`: `rustfmt::skip` is a path
|
||||
/// - `#[allow(clippy::complexity)]`: `clippy::complexity` is a path
|
||||
/// - `#[inline]`: `inline` is a single segment path
|
||||
pub fn path(&self) -> (PathParser<'a>, &ArgParser<'a>) {
|
||||
self.deconstruct()
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem starts with a word, or single segment path.
|
||||
/// Doesn't return the args parser.
|
||||
///
|
||||
/// For examples. see [`Self::word`]
|
||||
pub fn word_without_args(&self) -> Option<Ident> {
|
||||
Some(self.word()?.0)
|
||||
}
|
||||
|
||||
/// Like [`word`](Self::word), but returns an empty symbol instead of None
|
||||
pub fn word_or_empty_without_args(&self) -> Ident {
|
||||
self.word_or_empty().0
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem starts with a word, or single segment path.
|
||||
///
|
||||
/// Some examples:
|
||||
/// - `#[inline]`: `inline` is a word
|
||||
/// - `#[rustfmt::skip]`: `rustfmt::skip` is a path,
|
||||
/// and not a word and should instead be parsed using [`path`](Self::path)
|
||||
pub fn word(&self) -> Option<(Ident, &ArgParser<'a>)> {
|
||||
let (path, args) = self.deconstruct();
|
||||
Some((path.word()?, args))
|
||||
}
|
||||
|
||||
/// Like [`word`](Self::word), but returns an empty symbol instead of None
|
||||
pub fn word_or_empty(&self) -> (Ident, &ArgParser<'a>) {
|
||||
let (path, args) = self.deconstruct();
|
||||
(path.word().unwrap_or(Ident::empty()), args)
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem starts with some specific word.
|
||||
///
|
||||
/// See [`word`](Self::word) for examples of what a word is.
|
||||
pub fn word_is(&self, sym: Symbol) -> Option<&ArgParser<'a>> {
|
||||
self.path_without_args().word_is(sym).then(|| self.args())
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem starts with some specific path.
|
||||
///
|
||||
/// See [`word`](Self::path) for examples of what a word is.
|
||||
pub fn path_is(&self, segments: &[Symbol]) -> Option<&ArgParser<'a>> {
|
||||
self.path_without_args().segments_is(segments).then(|| self.args())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NameValueParser {
|
||||
pub eq_span: Span,
|
||||
value: MetaItemLit,
|
||||
pub value_span: Span,
|
||||
}
|
||||
|
||||
impl Debug for NameValueParser {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("NameValueParser")
|
||||
.field("eq_span", &self.eq_span)
|
||||
.field("value", &self.value)
|
||||
.field("value_span", &self.value_span)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl NameValueParser {
|
||||
pub fn value_as_lit(&self) -> &MetaItemLit {
|
||||
&self.value
|
||||
}
|
||||
|
||||
pub fn value_as_str(&self) -> Option<Symbol> {
|
||||
self.value_as_lit().kind.str()
|
||||
}
|
||||
}
|
||||
|
||||
fn expr_to_lit(dcx: DiagCtxtHandle<'_>, expr: &Expr) -> MetaItemLit {
|
||||
// In valid code the value always ends up as a single literal. Otherwise, a dummy
|
||||
// literal suffices because the error is handled elsewhere.
|
||||
if let ExprKind::Lit(token_lit) = expr.kind
|
||||
&& let Ok(lit) = MetaItemLit::from_token_lit(token_lit, expr.span)
|
||||
{
|
||||
lit
|
||||
} else {
|
||||
let guar = dcx.has_errors().unwrap();
|
||||
MetaItemLit { symbol: kw::Empty, suffix: None, kind: LitKind::Err(guar), span: DUMMY_SP }
|
||||
}
|
||||
}
|
||||
|
||||
struct MetaItemListParserContext<'a> {
|
||||
// the tokens inside the delimiters, so `#[some::attr(a b c)]` would have `a b c` inside
|
||||
inside_delimiters: Peekable<TokenStreamIter<'a>>,
|
||||
dcx: DiagCtxtHandle<'a>,
|
||||
}
|
||||
|
||||
impl<'a> MetaItemListParserContext<'a> {
|
||||
fn done(&mut self) -> bool {
|
||||
self.inside_delimiters.peek().is_none()
|
||||
}
|
||||
|
||||
fn next_path(&mut self) -> Option<AttrPath> {
|
||||
// FIXME: Share code with `parse_path`.
|
||||
let tt = self.inside_delimiters.next().map(|tt| TokenTree::uninterpolate(tt));
|
||||
|
||||
match tt.as_deref()? {
|
||||
&TokenTree::Token(
|
||||
Token { kind: ref kind @ (token::Ident(..) | token::PathSep), span },
|
||||
_,
|
||||
) => {
|
||||
// here we have either an ident or pathsep `::`.
|
||||
|
||||
let mut segments = if let &token::Ident(name, _) = kind {
|
||||
// when we lookahead another pathsep, more path's coming
|
||||
if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
|
||||
self.inside_delimiters.peek()
|
||||
{
|
||||
self.inside_delimiters.next();
|
||||
vec![Ident::new(name, span)]
|
||||
} else {
|
||||
// else we have a single identifier path, that's all
|
||||
return Some(AttrPath {
|
||||
segments: vec![Ident::new(name, span)].into_boxed_slice(),
|
||||
span,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// if `::` is all we get, we just got a path root
|
||||
vec![Ident::new(kw::PathRoot, span)]
|
||||
};
|
||||
|
||||
// one segment accepted. accept n more
|
||||
loop {
|
||||
// another ident?
|
||||
if let Some(&TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =
|
||||
self.inside_delimiters
|
||||
.next()
|
||||
.map(|tt| TokenTree::uninterpolate(tt))
|
||||
.as_deref()
|
||||
{
|
||||
segments.push(Ident::new(name, span));
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
// stop unless we see another `::`
|
||||
if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
|
||||
self.inside_delimiters.peek()
|
||||
{
|
||||
self.inside_delimiters.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let span = span.with_hi(segments.last().unwrap().span.hi());
|
||||
Some(AttrPath { segments: segments.into_boxed_slice(), span })
|
||||
}
|
||||
TokenTree::Token(Token { kind: token::OpenDelim(_) | token::CloseDelim(_), .. }, _) => {
|
||||
None
|
||||
}
|
||||
_ => {
|
||||
// malformed attributes can get here. We can't crash, but somewhere else should've
|
||||
// already warned for this.
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn value(&mut self) -> Option<MetaItemLit> {
|
||||
match self.inside_delimiters.next() {
|
||||
Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => {
|
||||
MetaItemListParserContext {
|
||||
inside_delimiters: inner_tokens.iter().peekable(),
|
||||
dcx: self.dcx,
|
||||
}
|
||||
.value()
|
||||
}
|
||||
Some(TokenTree::Token(token, _)) => MetaItemLit::from_token(token),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// parses one element on the inside of a list attribute like `#[my_attr( <insides> )]`
|
||||
///
|
||||
/// parses a path followed be either:
|
||||
/// 1. nothing (a word attr)
|
||||
/// 2. a parenthesized list
|
||||
/// 3. an equals sign and a literal (name-value)
|
||||
///
|
||||
/// Can also parse *just* a literal. This is for cases like as `#[my_attr("literal")]`
|
||||
/// where no path is given before the literal
|
||||
///
|
||||
/// Some exceptions too for interpolated attributes which are already pre-processed
|
||||
fn next(&mut self) -> Option<MetaItemOrLitParser<'a>> {
|
||||
// a list element is either a literal
|
||||
if let Some(TokenTree::Token(token, _)) = self.inside_delimiters.peek()
|
||||
&& let Some(lit) = MetaItemLit::from_token(token)
|
||||
{
|
||||
self.inside_delimiters.next();
|
||||
return Some(MetaItemOrLitParser::Lit(lit));
|
||||
}
|
||||
|
||||
// or a path.
|
||||
let path =
|
||||
if let Some(TokenTree::Token(Token { kind: token::Interpolated(nt), span, .. }, _)) =
|
||||
self.inside_delimiters.peek()
|
||||
{
|
||||
match &**nt {
|
||||
// or maybe a full nt meta including the path but we return immediately
|
||||
token::Nonterminal::NtMeta(item) => {
|
||||
self.inside_delimiters.next();
|
||||
|
||||
return Some(MetaItemOrLitParser::MetaItemParser(MetaItemParser {
|
||||
path: PathParser::Ast(&item.path),
|
||||
args: ArgParser::from_attr_args(&item.args, self.dcx),
|
||||
}));
|
||||
}
|
||||
// an already interpolated path from a macro expansion is a path, no need to parse
|
||||
// one from tokens
|
||||
token::Nonterminal::NtPath(path) => {
|
||||
self.inside_delimiters.next();
|
||||
|
||||
AttrPath::from_ast(path)
|
||||
}
|
||||
_ => {
|
||||
self.inside_delimiters.next();
|
||||
// we go into this path if an expr ended up in an attribute that
|
||||
// expansion did not turn into a literal. Say, `#[repr(align(macro!()))]`
|
||||
// where the macro didn't expand to a literal. An error is already given
|
||||
// for this at this point, and then we do continue. This makes this path
|
||||
// reachable...
|
||||
let e = self.dcx.span_delayed_bug(
|
||||
*span,
|
||||
"expr in place where literal is expected (builtin attr parsing)",
|
||||
);
|
||||
|
||||
return Some(MetaItemOrLitParser::Err(*span, e));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.next_path()?
|
||||
};
|
||||
|
||||
// Paths can be followed by:
|
||||
// - `(more meta items)` (another list)
|
||||
// - `= lit` (a name-value)
|
||||
// - nothing
|
||||
Some(MetaItemOrLitParser::MetaItemParser(match self.inside_delimiters.peek() {
|
||||
Some(TokenTree::Delimited(dspan, _, Delimiter::Parenthesis, inner_tokens)) => {
|
||||
self.inside_delimiters.next();
|
||||
|
||||
MetaItemParser {
|
||||
path: PathParser::Attr(path),
|
||||
args: ArgParser::List(MetaItemListParser::new_tts(
|
||||
inner_tokens.iter(),
|
||||
dspan.entire(),
|
||||
self.dcx,
|
||||
)),
|
||||
}
|
||||
}
|
||||
Some(TokenTree::Delimited(_, ..)) => {
|
||||
self.inside_delimiters.next();
|
||||
// self.dcx.span_delayed_bug(span.entire(), "wrong delimiters");
|
||||
return None;
|
||||
}
|
||||
Some(TokenTree::Token(Token { kind: token::Eq, span }, _)) => {
|
||||
self.inside_delimiters.next();
|
||||
let value = self.value()?;
|
||||
MetaItemParser {
|
||||
path: PathParser::Attr(path),
|
||||
args: ArgParser::NameValue(NameValueParser {
|
||||
eq_span: *span,
|
||||
value_span: value.span,
|
||||
value,
|
||||
}),
|
||||
}
|
||||
}
|
||||
_ => MetaItemParser { path: PathParser::Attr(path), args: ArgParser::NoArgs },
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse(mut self, span: Span) -> MetaItemListParser<'a> {
|
||||
let mut sub_parsers = Vec::new();
|
||||
|
||||
while !self.done() {
|
||||
let Some(n) = self.next() else {
|
||||
continue;
|
||||
};
|
||||
sub_parsers.push(n);
|
||||
|
||||
match self.inside_delimiters.peek() {
|
||||
None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => {
|
||||
self.inside_delimiters.next();
|
||||
}
|
||||
Some(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
MetaItemListParser { sub_parsers, span }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MetaItemListParser<'a> {
|
||||
sub_parsers: Vec<MetaItemOrLitParser<'a>>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl<'a> MetaItemListParser<'a> {
|
||||
fn new(delim: &'a DelimArgs, dcx: DiagCtxtHandle<'a>) -> MetaItemListParser<'a> {
|
||||
MetaItemListParser::new_tts(delim.tokens.iter(), delim.dspan.entire(), dcx)
|
||||
}
|
||||
|
||||
fn new_tts(tts: TokenStreamIter<'a>, span: Span, dcx: DiagCtxtHandle<'a>) -> Self {
|
||||
MetaItemListParserContext { inside_delimiters: tts.peekable(), dcx }.parse(span)
|
||||
}
|
||||
|
||||
/// Lets you pick and choose as what you want to parse each element in the list
|
||||
pub fn mixed<'s>(&'s self) -> impl Iterator<Item = &'s MetaItemOrLitParser<'a>> + 's {
|
||||
self.sub_parsers.iter()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.sub_parsers.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Asserts that every item in the list is another list starting with a word.
|
||||
///
|
||||
/// See [`MetaItemParser::word`] for examples of words.
|
||||
pub fn all_word_list<'s>(&'s self) -> Option<Vec<(Ident, &'s ArgParser<'a>)>> {
|
||||
self.mixed().map(|i| i.meta_item()?.word()).collect()
|
||||
}
|
||||
|
||||
/// Asserts that every item in the list is another list starting with a full path.
|
||||
///
|
||||
/// See [`MetaItemParser::path`] for examples of paths.
|
||||
pub fn all_path_list<'s>(&'s self) -> Option<Vec<(PathParser<'a>, &'s ArgParser<'a>)>> {
|
||||
self.mixed().map(|i| Some(i.meta_item()?.path())).collect()
|
||||
}
|
||||
|
||||
/// Returns Some if the list contains only a single element.
|
||||
///
|
||||
/// Inside the Some is the parser to parse this single element.
|
||||
pub fn single(&self) -> Option<&MetaItemOrLitParser<'a>> {
|
||||
let mut iter = self.mixed();
|
||||
iter.next().filter(|_| iter.next().is_none())
|
||||
}
|
||||
}
|
||||
|
|
@ -6,9 +6,16 @@ use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuar
|
|||
use rustc_macros::{Diagnostic, Subdiagnostic};
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
||||
use crate::attributes::util::UnsupportedLiteralReason;
|
||||
use crate::fluent_generated as fluent;
|
||||
|
||||
pub(crate) enum UnsupportedLiteralReason {
|
||||
Generic,
|
||||
CfgString,
|
||||
CfgBoolean,
|
||||
DeprecatedString,
|
||||
DeprecatedKvPair,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_expected_one_cfg_pattern, code = E0536)]
|
||||
pub(crate) struct ExpectedOneCfgPattern {
|
||||
|
|
@ -39,6 +46,21 @@ pub(crate) struct MultipleItem {
|
|||
pub(crate) struct IncorrectMetaItem {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
|
||||
#[subdiagnostic]
|
||||
pub suggestion: Option<IncorrectMetaItemSuggestion>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[multipart_suggestion(
|
||||
attr_parsing_incorrect_meta_item_suggestion,
|
||||
applicability = "maybe-incorrect"
|
||||
)]
|
||||
pub(crate) struct IncorrectMetaItemSuggestion {
|
||||
#[suggestion_part(code = "\"")]
|
||||
pub lo: Span,
|
||||
#[suggestion_part(code = "\"")]
|
||||
pub hi: Span,
|
||||
}
|
||||
|
||||
/// Error code: E0541
|
||||
|
|
@ -337,13 +359,6 @@ pub(crate) struct RustcPromotablePairing {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_rustc_const_stable_indirect_pairing)]
|
||||
pub(crate) struct RustcConstStableIndirectPairing {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_rustc_allowed_unstable_pairing, code = E0789)]
|
||||
pub(crate) struct RustcAllowedUnstablePairing {
|
||||
|
|
@ -423,3 +438,44 @@ pub(crate) struct UnknownVersionLiteral {
|
|||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
// FIXME(jdonszelmann) duplicated from `rustc_passes`, remove once `check_attr` is integrated.
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_unused_multiple)]
|
||||
pub(crate) struct UnusedMultiple {
|
||||
#[primary_span]
|
||||
#[suggestion(code = "", applicability = "machine-applicable")]
|
||||
pub this: Span,
|
||||
#[note]
|
||||
pub other: Span,
|
||||
pub name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_stability_outside_std, code = E0734)]
|
||||
pub(crate) struct StabilityOutsideStd {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_empty_confusables)]
|
||||
pub(crate) struct EmptyConfusables {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_repr_ident, code = E0565)]
|
||||
pub(crate) struct ReprIdent {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_unrecognized_repr_hint, code = E0552)]
|
||||
#[help]
|
||||
pub(crate) struct UnrecognizedReprHint {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ rustc_errors = { path = "../rustc_errors" }
|
|||
rustc_expand = { path = "../rustc_expand" }
|
||||
rustc_feature = { path = "../rustc_feature" }
|
||||
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
|
||||
rustc_hir = { path = "../rustc_hir" }
|
||||
rustc_index = { path = "../rustc_index" }
|
||||
rustc_lexer = { path = "../rustc_lexer" }
|
||||
rustc_lint_defs = { path = "../rustc_lint_defs" }
|
||||
|
|
|
|||
|
|
@ -185,8 +185,9 @@ use rustc_ast::{
|
|||
self as ast, AnonConst, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind,
|
||||
Generics, Mutability, PatKind, VariantData,
|
||||
};
|
||||
use rustc_attr_parsing as attr;
|
||||
use rustc_attr_parsing::{AttributeKind, AttributeParser, ReprPacked};
|
||||
use rustc_expand::base::{Annotatable, ExtCtxt};
|
||||
use rustc_hir::Attribute;
|
||||
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
|
||||
use thin_vec::{ThinVec, thin_vec};
|
||||
use ty::{Bounds, Path, Ref, Self_, Ty};
|
||||
|
|
@ -480,14 +481,10 @@ impl<'a> TraitDef<'a> {
|
|||
) {
|
||||
match item {
|
||||
Annotatable::Item(item) => {
|
||||
let is_packed = item.attrs.iter().any(|attr| {
|
||||
for r in attr::find_repr_attrs(cx.sess, attr) {
|
||||
if let attr::ReprPacked(_) = r {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
});
|
||||
let is_packed = matches!(
|
||||
AttributeParser::parse_limited(cx.sess, &item.attrs, sym::repr, item.span, true),
|
||||
Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.iter().any(|(x, _)| matches!(x, ReprPacked(..)))
|
||||
);
|
||||
|
||||
let newitem = match &item.kind {
|
||||
ast::ItemKind::Struct(struct_def, generics) => self.expand_struct_def(
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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}");
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -36,10 +36,7 @@ mod intrinsics {
|
|||
|
||||
#[rustc_nounwind]
|
||||
#[rustc_intrinsic]
|
||||
#[rustc_intrinsic_must_be_overridden]
|
||||
pub fn abort() -> ! {
|
||||
loop {}
|
||||
}
|
||||
pub fn abort() -> !;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -36,10 +36,7 @@ mod intrinsics {
|
|||
|
||||
#[rustc_nounwind]
|
||||
#[rustc_intrinsic]
|
||||
#[rustc_intrinsic_must_be_overridden]
|
||||
pub fn abort() -> ! {
|
||||
loop {}
|
||||
}
|
||||
pub fn abort() -> !;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -49,10 +49,7 @@ mod intrinsics {
|
|||
|
||||
#[rustc_nounwind]
|
||||
#[rustc_intrinsic]
|
||||
#[rustc_intrinsic_must_be_overridden]
|
||||
pub fn abort() -> ! {
|
||||
loop {}
|
||||
}
|
||||
pub fn abort() -> !;
|
||||
}
|
||||
|
||||
mod libc {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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")))
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ impl<'tcx> AssertModuleSource<'tcx> {
|
|||
other => {
|
||||
self.tcx
|
||||
.dcx()
|
||||
.emit_fatal(errors::UnknownReuseKind { span: attr.span, kind: other });
|
||||
.emit_fatal(errors::UnknownReuseKind { span: attr.span(), kind: other });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -102,7 +102,7 @@ impl<'tcx> AssertModuleSource<'tcx> {
|
|||
};
|
||||
|
||||
if !self.tcx.sess.opts.unstable_opts.query_dep_graph {
|
||||
self.tcx.dcx().emit_fatal(errors::MissingQueryDepGraph { span: attr.span });
|
||||
self.tcx.dcx().emit_fatal(errors::MissingQueryDepGraph { span: attr.span() });
|
||||
}
|
||||
|
||||
if !self.check_config(attr) {
|
||||
|
|
@ -115,7 +115,7 @@ impl<'tcx> AssertModuleSource<'tcx> {
|
|||
|
||||
if !user_path.starts_with(&crate_name) {
|
||||
self.tcx.dcx().emit_fatal(errors::MalformedCguName {
|
||||
span: attr.span,
|
||||
span: attr.span(),
|
||||
user_path,
|
||||
crate_name,
|
||||
});
|
||||
|
|
@ -145,7 +145,7 @@ impl<'tcx> AssertModuleSource<'tcx> {
|
|||
let cgu_names: Vec<&str> =
|
||||
self.available_cgus.items().map(|cgu| cgu.as_str()).into_sorted_stable_ord();
|
||||
self.tcx.dcx().emit_err(errors::NoModuleNamed {
|
||||
span: attr.span,
|
||||
span: attr.span(),
|
||||
user_path,
|
||||
cgu_name,
|
||||
cgu_names: cgu_names.join(", "),
|
||||
|
|
@ -155,7 +155,7 @@ impl<'tcx> AssertModuleSource<'tcx> {
|
|||
self.cgu_reuse_tracker.set_expectation(
|
||||
cgu_name,
|
||||
user_path,
|
||||
attr.span,
|
||||
attr.span(),
|
||||
expected_reuse,
|
||||
comp_kind,
|
||||
);
|
||||
|
|
@ -175,7 +175,7 @@ impl<'tcx> AssertModuleSource<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
self.tcx.dcx().emit_fatal(errors::NoField { span: attr.span, name });
|
||||
self.tcx.dcx().emit_fatal(errors::NoField { span: attr.span(), name });
|
||||
}
|
||||
|
||||
/// Scan for a `cfg="foo"` attribute and check whether we have a
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ use std::time::{Duration, Instant};
|
|||
|
||||
use itertools::Itertools;
|
||||
use rustc_abi::FIRST_VARIANT;
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, AllocatorKind, global_fn_name};
|
||||
use rustc_attr_parsing::OptimizeAttr;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
|
||||
use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry};
|
||||
use rustc_data_structures::sync::par_map;
|
||||
|
|
@ -29,7 +31,6 @@ use rustc_span::{DUMMY_SP, Symbol, sym};
|
|||
use rustc_trait_selection::infer::{BoundRegionConversionTime, TyCtxtInferExt};
|
||||
use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt};
|
||||
use tracing::{debug, info};
|
||||
use {rustc_ast as ast, rustc_attr_parsing as attr};
|
||||
|
||||
use crate::assert_module_sources::CguReuse;
|
||||
use crate::back::link::are_upstream_rust_objects_already_included;
|
||||
|
|
@ -1061,7 +1062,7 @@ pub(crate) fn provide(providers: &mut Providers) {
|
|||
|
||||
let any_for_speed = defids.items().any(|id| {
|
||||
let CodegenFnAttrs { optimize, .. } = tcx.codegen_fn_attrs(*id);
|
||||
matches!(optimize, attr::OptimizeAttr::Speed)
|
||||
matches!(optimize, OptimizeAttr::Speed)
|
||||
});
|
||||
|
||||
if any_for_speed {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ use rustc_ast::expand::autodiff_attrs::{
|
|||
AutoDiffAttrs, DiffActivity, DiffMode, valid_input_activity, valid_ret_activity,
|
||||
};
|
||||
use rustc_ast::{MetaItem, MetaItemInner, attr};
|
||||
use rustc_attr_parsing::{InlineAttr, InstructionSetAttr, OptimizeAttr};
|
||||
use rustc_attr_parsing::ReprAttr::ReprAlign;
|
||||
use rustc_attr_parsing::{AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{DiagMessage, SubdiagMessage, struct_span_code_err};
|
||||
|
|
@ -104,12 +105,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
if let Fn | AssocFn | Variant | Ctor(..) = def_kind {
|
||||
Some(tcx.fn_sig(did))
|
||||
} else {
|
||||
tcx.dcx()
|
||||
.span_delayed_bug(attr.span, "this attribute can only be applied to functions");
|
||||
tcx.dcx().span_delayed_bug(
|
||||
attr.span(),
|
||||
"this attribute can only be applied to functions",
|
||||
);
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
if let hir::Attribute::Parsed(p) = attr {
|
||||
match p {
|
||||
AttributeKind::Repr(reprs) => {
|
||||
codegen_fn_attrs.alignment = reprs
|
||||
.iter()
|
||||
.find_map(|(r, _)| if let ReprAlign(x) = r { Some(*x) } else { None });
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let Some(Ident { name, .. }) = attr.ident() else {
|
||||
continue;
|
||||
};
|
||||
|
|
@ -130,14 +145,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
if tcx.opt_item_name(did.to_def_id()).is_some() {
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
|
||||
mixed_export_name_no_mangle_lint_state.track_no_mangle(
|
||||
attr.span,
|
||||
attr.span(),
|
||||
tcx.local_def_id_to_hir_id(did),
|
||||
attr,
|
||||
);
|
||||
} else {
|
||||
tcx.dcx()
|
||||
.struct_span_err(
|
||||
attr.span,
|
||||
attr.span(),
|
||||
format!(
|
||||
"`#[no_mangle]` cannot be used on {} {} as it has no name",
|
||||
tcx.def_descr_article(did.to_def_id()),
|
||||
|
|
@ -158,7 +173,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
feature_err(
|
||||
&tcx.sess,
|
||||
sym::used_with_arg,
|
||||
attr.span,
|
||||
attr.span(),
|
||||
"`#[used(linker)]` is currently unstable",
|
||||
)
|
||||
.emit();
|
||||
|
|
@ -170,7 +185,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
feature_err(
|
||||
&tcx.sess,
|
||||
sym::used_with_arg,
|
||||
attr.span,
|
||||
attr.span(),
|
||||
"`#[used(compiler)]` is currently unstable",
|
||||
)
|
||||
.emit();
|
||||
|
|
@ -178,7 +193,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
|
||||
}
|
||||
Some(_) => {
|
||||
tcx.dcx().emit_err(errors::ExpectedUsedSymbol { span: attr.span });
|
||||
tcx.dcx().emit_err(errors::ExpectedUsedSymbol { span: attr.span() });
|
||||
}
|
||||
None => {
|
||||
// Unfortunately, unconditionally using `llvm.used` causes
|
||||
|
|
@ -223,7 +238,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
{
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
attr.span,
|
||||
attr.span(),
|
||||
E0737,
|
||||
"`#[track_caller]` requires Rust ABI"
|
||||
)
|
||||
|
|
@ -231,12 +246,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
}
|
||||
if is_closure
|
||||
&& !tcx.features().closure_track_caller()
|
||||
&& !attr.span.allows_unstable(sym::closure_track_caller)
|
||||
&& !attr.span().allows_unstable(sym::closure_track_caller)
|
||||
{
|
||||
feature_err(
|
||||
&tcx.sess,
|
||||
sym::closure_track_caller,
|
||||
attr.span,
|
||||
attr.span(),
|
||||
"`#[track_caller]` on closures is currently unstable",
|
||||
)
|
||||
.emit();
|
||||
|
|
@ -250,19 +265,19 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
// so it may not contain any null characters.
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
attr.span,
|
||||
attr.span(),
|
||||
E0648,
|
||||
"`export_name` may not contain null characters"
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
codegen_fn_attrs.export_name = Some(s);
|
||||
mixed_export_name_no_mangle_lint_state.track_export_name(attr.span);
|
||||
mixed_export_name_no_mangle_lint_state.track_export_name(attr.span());
|
||||
}
|
||||
}
|
||||
sym::target_feature => {
|
||||
let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else {
|
||||
tcx.dcx().span_delayed_bug(attr.span, "target_feature applied to non-fn");
|
||||
tcx.dcx().span_delayed_bug(attr.span(), "target_feature applied to non-fn");
|
||||
continue;
|
||||
};
|
||||
let safe_target_features =
|
||||
|
|
@ -292,7 +307,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
// `main`, `start`, and other functions that are not usually
|
||||
// allowed.
|
||||
} else {
|
||||
check_target_feature_trait_unsafe(tcx, did, attr.span);
|
||||
check_target_feature_trait_unsafe(tcx, did, attr.span());
|
||||
}
|
||||
}
|
||||
from_target_feature_attr(
|
||||
|
|
@ -310,7 +325,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
|
||||
if tcx.is_mutable_static(did.into()) {
|
||||
let mut diag = tcx.dcx().struct_span_err(
|
||||
attr.span,
|
||||
attr.span(),
|
||||
"extern mutable statics are not allowed with `#[linkage]`",
|
||||
);
|
||||
diag.note(
|
||||
|
|
@ -329,7 +344,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
if let Some(val) = attr.value_str() {
|
||||
if val.as_str().bytes().any(|b| b == 0) {
|
||||
let msg = format!("illegal null byte in link_section value: `{val}`");
|
||||
tcx.dcx().span_err(attr.span, msg);
|
||||
tcx.dcx().span_err(attr.span(), msg);
|
||||
} else {
|
||||
codegen_fn_attrs.link_section = Some(val);
|
||||
}
|
||||
|
|
@ -337,13 +352,13 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
}
|
||||
sym::link_name => codegen_fn_attrs.link_name = attr.value_str(),
|
||||
sym::link_ordinal => {
|
||||
link_ordinal_span = Some(attr.span);
|
||||
link_ordinal_span = Some(attr.span());
|
||||
if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {
|
||||
codegen_fn_attrs.link_ordinal = ordinal;
|
||||
}
|
||||
}
|
||||
sym::no_sanitize => {
|
||||
no_sanitize_span = Some(attr.span);
|
||||
no_sanitize_span = Some(attr.span());
|
||||
if let Some(list) = attr.meta_item_list() {
|
||||
for item in list.iter() {
|
||||
match item.name_or_empty() {
|
||||
|
|
@ -381,7 +396,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
{
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
attr.span,
|
||||
attr.span(),
|
||||
E0779,
|
||||
"target does not support `#[instruction_set]`"
|
||||
)
|
||||
|
|
@ -393,7 +408,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
_ => {
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
attr.span,
|
||||
attr.span(),
|
||||
E0779,
|
||||
"invalid instruction set specified",
|
||||
)
|
||||
|
|
@ -405,7 +420,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
[] => {
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
attr.span,
|
||||
attr.span(),
|
||||
E0778,
|
||||
"`#[instruction_set]` requires an argument"
|
||||
)
|
||||
|
|
@ -415,7 +430,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
_ => {
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
attr.span,
|
||||
attr.span(),
|
||||
E0779,
|
||||
"cannot specify more than one instruction set"
|
||||
)
|
||||
|
|
@ -424,27 +439,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
}
|
||||
})
|
||||
}
|
||||
sym::repr => {
|
||||
codegen_fn_attrs.alignment = if let Some(items) = attr.meta_item_list()
|
||||
&& let [item] = items.as_slice()
|
||||
&& let Some((sym::align, literal)) = item.singleton_lit_list()
|
||||
{
|
||||
rustc_attr_parsing::parse_alignment(&literal.kind)
|
||||
.inspect_err(|msg| {
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
literal.span,
|
||||
E0589,
|
||||
"invalid `repr(align)` attribute: {}",
|
||||
msg
|
||||
)
|
||||
.emit();
|
||||
})
|
||||
.ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
sym::patchable_function_entry => {
|
||||
codegen_fn_attrs.patchable_function_entry = attr.meta_item_list().and_then(|l| {
|
||||
let mut prefix = None;
|
||||
|
|
@ -510,7 +504,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
}
|
||||
|
||||
if let (None, None) = (prefix, entry) {
|
||||
tcx.dcx().span_err(attr.span, "must specify at least one parameter");
|
||||
tcx.dcx().span_err(attr.span(), "must specify at least one parameter");
|
||||
}
|
||||
|
||||
Some(PatchableFunctionEntry::from_prefix_and_entry(
|
||||
|
|
@ -536,18 +530,19 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
let Some(ref items) = attr.meta_item_list() else {
|
||||
return ia;
|
||||
};
|
||||
inline_span = Some(attr.span());
|
||||
|
||||
inline_span = Some(attr.span);
|
||||
let [item] = &items[..] else {
|
||||
struct_span_code_err!(tcx.dcx(), attr.span, E0534, "expected one argument").emit();
|
||||
struct_span_code_err!(tcx.dcx(), attr.span(), E0534, "expected one argument").emit();
|
||||
return InlineAttr::None;
|
||||
};
|
||||
|
||||
if item.has_name(sym::always) {
|
||||
InlineAttr::Always
|
||||
} else if item.has_name(sym::never) {
|
||||
InlineAttr::Never
|
||||
} else {
|
||||
struct_span_code_err!(tcx.dcx(), item.span(), E0535, "invalid argument")
|
||||
struct_span_code_err!(tcx.dcx(), items[0].span(), E0535, "invalid argument")
|
||||
.with_help("valid inline arguments are `always` and `never`")
|
||||
.emit();
|
||||
|
||||
|
|
@ -560,9 +555,9 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
}
|
||||
|
||||
if attr.is_word() {
|
||||
InlineAttr::Force { attr_span: attr.span, reason: None }
|
||||
InlineAttr::Force { attr_span: attr.span(), reason: None }
|
||||
} else if let Some(val) = attr.value_str() {
|
||||
InlineAttr::Force { attr_span: attr.span, reason: Some(val) }
|
||||
InlineAttr::Force { attr_span: attr.span(), reason: Some(val) }
|
||||
} else {
|
||||
debug!("`rustc_force_inline` not checked by attribute validation");
|
||||
ia
|
||||
|
|
@ -582,16 +577,16 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
}
|
||||
let err = |sp, s| struct_span_code_err!(tcx.dcx(), sp, E0722, "{}", s).emit();
|
||||
if attr.is_word() {
|
||||
err(attr.span, "expected one argument");
|
||||
err(attr.span(), "expected one argument");
|
||||
return ia;
|
||||
}
|
||||
let Some(ref items) = attr.meta_item_list() else {
|
||||
return OptimizeAttr::Default;
|
||||
};
|
||||
|
||||
inline_span = Some(attr.span);
|
||||
inline_span = Some(attr.span());
|
||||
let [item] = &items[..] else {
|
||||
err(attr.span, "expected one argument");
|
||||
err(attr.span(), "expected one argument");
|
||||
return OptimizeAttr::Default;
|
||||
};
|
||||
if item.has_name(sym::size) {
|
||||
|
|
@ -703,7 +698,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
let span = tcx
|
||||
.get_attrs(did, sym::target_feature)
|
||||
.next()
|
||||
.map_or_else(|| tcx.def_span(did), |a| a.span);
|
||||
.map_or_else(|| tcx.def_span(did), |a| a.span());
|
||||
tcx.dcx()
|
||||
.create_err(errors::TargetFeatureDisableOrEnable {
|
||||
features,
|
||||
|
|
@ -752,7 +747,7 @@ fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Option<u16> {
|
|||
use rustc_ast::{LitIntType, LitKind, MetaItemLit};
|
||||
let meta_item_list = attr.meta_item_list()?;
|
||||
let [sole_meta_list] = &meta_item_list[..] else {
|
||||
tcx.dcx().emit_err(errors::InvalidLinkOrdinalNargs { span: attr.span });
|
||||
tcx.dcx().emit_err(errors::InvalidLinkOrdinalNargs { span: attr.span() });
|
||||
return None;
|
||||
};
|
||||
if let Some(MetaItemLit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) =
|
||||
|
|
@ -776,13 +771,13 @@ fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Option<u16> {
|
|||
} else {
|
||||
let msg = format!("ordinal value in `link_ordinal` is too large: `{ordinal}`");
|
||||
tcx.dcx()
|
||||
.struct_span_err(attr.span, msg)
|
||||
.struct_span_err(attr.span(), msg)
|
||||
.with_note("the value may not exceed `u16::MAX`")
|
||||
.emit();
|
||||
None
|
||||
}
|
||||
} else {
|
||||
tcx.dcx().emit_err(errors::InvalidLinkOrdinalFormat { span: attr.span });
|
||||
tcx.dcx().emit_err(errors::InvalidLinkOrdinalFormat { span: attr.span() });
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
@ -828,7 +823,7 @@ impl<'a> MixedExportNameAndNoMangleState<'a> {
|
|||
export_name: Some(export_name),
|
||||
no_mangle: Some(no_mangle),
|
||||
hir_id: Some(hir_id),
|
||||
no_mangle_attr: Some(no_mangle_attr),
|
||||
no_mangle_attr: Some(_),
|
||||
} = self
|
||||
{
|
||||
tcx.emit_node_span_lint(
|
||||
|
|
@ -837,7 +832,7 @@ impl<'a> MixedExportNameAndNoMangleState<'a> {
|
|||
no_mangle,
|
||||
errors::MixedExportNameAndNoMangle {
|
||||
no_mangle,
|
||||
no_mangle_attr: rustc_hir_pretty::attribute_to_string(&tcx, no_mangle_attr),
|
||||
no_mangle_attr: "#[unsafe(no_mangle)]".to_string(),
|
||||
export_name,
|
||||
removal_span: no_mangle,
|
||||
},
|
||||
|
|
@ -869,7 +864,7 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
|
|||
_ => {
|
||||
//FIXME(ZuseZ4): Once we fixed our parser, we should also prohibit the two-attribute
|
||||
//branch above.
|
||||
span_bug!(attrs[1].span, "cg_ssa: rustc_autodiff should only exist once per source");
|
||||
span_bug!(attrs[1].span(), "cg_ssa: rustc_autodiff should only exist once per source");
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -881,12 +876,12 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
|
|||
}
|
||||
|
||||
let [mode, input_activities @ .., ret_activity] = &list[..] else {
|
||||
span_bug!(attr.span, "rustc_autodiff attribute must contain mode and activities");
|
||||
span_bug!(attr.span(), "rustc_autodiff attribute must contain mode and activities");
|
||||
};
|
||||
let mode = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = mode {
|
||||
p1.segments.first().unwrap().ident
|
||||
} else {
|
||||
span_bug!(attr.span, "rustc_autodiff attribute must contain mode");
|
||||
span_bug!(attr.span(), "rustc_autodiff attribute must contain mode");
|
||||
};
|
||||
|
||||
// parse mode
|
||||
|
|
@ -902,7 +897,7 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
|
|||
let ret_symbol = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = ret_activity {
|
||||
p1.segments.first().unwrap().ident
|
||||
} else {
|
||||
span_bug!(attr.span, "rustc_autodiff attribute must contain the return activity");
|
||||
span_bug!(attr.span(), "rustc_autodiff attribute must contain the return activity");
|
||||
};
|
||||
|
||||
// Then parse it into an actual DiffActivity
|
||||
|
|
@ -937,11 +932,11 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
|
|||
|
||||
for &input in &arg_activities {
|
||||
if !valid_input_activity(mode, input) {
|
||||
span_bug!(attr.span, "Invalid input activity {} for {} mode", input, mode);
|
||||
span_bug!(attr.span(), "Invalid input activity {} for {} mode", input, mode);
|
||||
}
|
||||
}
|
||||
if !valid_ret_activity(mode, ret_activity) {
|
||||
span_bug!(attr.span, "Invalid return activity {} for {} mode", ret_activity, mode);
|
||||
span_bug!(attr.span(), "Invalid return activity {} for {} mode", ret_activity, mode);
|
||||
}
|
||||
|
||||
Some(AutoDiffAttrs { mode, ret_activity, input_activity: arg_activities })
|
||||
|
|
|
|||
|
|
@ -1180,6 +1180,8 @@ pub(crate) struct ErrorCreatingRemarkDir {
|
|||
pub struct CompilerBuiltinsCannotCall {
|
||||
pub caller: String,
|
||||
pub callee: String,
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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".
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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!
|
||||
```
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ derive_setters = "0.1.6"
|
|||
rustc_abi = { path = "../rustc_abi" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
|
||||
rustc_attr_data_structures = { path = "../rustc_attr_data_structures" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_error_codes = { path = "../rustc_error_codes" }
|
||||
rustc_error_messages = { path = "../rustc_error_messages" }
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use std::process::ExitStatus;
|
|||
use rustc_abi::TargetDataLayoutErrors;
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_attr_data_structures::RustcVersion;
|
||||
use rustc_macros::Subdiagnostic;
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol};
|
||||
|
|
@ -96,6 +97,12 @@ into_diag_arg_using_display!(
|
|||
rustc_abi::ExternAbi,
|
||||
);
|
||||
|
||||
impl IntoDiagArg for RustcVersion {
|
||||
fn into_diag_arg(self) -> DiagArgValue {
|
||||
DiagArgValue::Str(Cow::Owned(self.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: rustc_type_ir::Interner> IntoDiagArg for rustc_type_ir::TraitRef<I> {
|
||||
fn into_diag_arg(self) -> DiagArgValue {
|
||||
self.to_string().into_diag_arg()
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ rustc_data_structures = { path = "../rustc_data_structures" }
|
|||
rustc_errors = { path = "../rustc_errors" }
|
||||
rustc_feature = { path = "../rustc_feature" }
|
||||
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
|
||||
rustc_hir = { path = "../rustc_hir" }
|
||||
rustc_lexer = { path = "../rustc_lexer" }
|
||||
rustc_lint_defs = { path = "../rustc_lint_defs" }
|
||||
rustc_macros = { path = "../rustc_macros" }
|
||||
|
|
|
|||
|
|
@ -11,11 +11,12 @@ use rustc_ast::token::Nonterminal;
|
|||
use rustc_ast::tokenstream::TokenStream;
|
||||
use rustc_ast::visit::{AssocCtxt, Visitor};
|
||||
use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind};
|
||||
use rustc_attr_parsing::{self as attr, Deprecation, Stability};
|
||||
use rustc_attr_parsing::{AttributeKind, Deprecation, Stability, find_attr};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_data_structures::sync;
|
||||
use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult};
|
||||
use rustc_feature::Features;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint_defs::{BufferedEarlyLint, RegisteredTools};
|
||||
use rustc_parse::MACRO_ARGUMENTS;
|
||||
use rustc_parse::parser::Parser;
|
||||
|
|
@ -838,19 +839,23 @@ impl SyntaxExtension {
|
|||
/// and other properties converted from attributes.
|
||||
pub fn new(
|
||||
sess: &Session,
|
||||
features: &Features,
|
||||
kind: SyntaxExtensionKind,
|
||||
span: Span,
|
||||
helper_attrs: Vec<Symbol>,
|
||||
edition: Edition,
|
||||
name: Symbol,
|
||||
attrs: &[impl AttributeExt],
|
||||
attrs: &[hir::Attribute],
|
||||
is_local: bool,
|
||||
) -> SyntaxExtension {
|
||||
let allow_internal_unstable =
|
||||
rustc_attr_parsing::allow_internal_unstable(sess, attrs).collect::<Vec<Symbol>>();
|
||||
find_attr!(attrs, AttributeKind::AllowInternalUnstable(i) => i)
|
||||
.map(|i| i.as_slice())
|
||||
.unwrap_or_default();
|
||||
// FIXME(jdonszelman): allow_internal_unsafe isn't yet new-style
|
||||
// let allow_internal_unsafe = find_attr!(attrs, AttributeKind::AllowInternalUnsafe);
|
||||
let allow_internal_unsafe =
|
||||
ast::attr::find_by_name(attrs, sym::allow_internal_unsafe).is_some();
|
||||
|
||||
let allow_internal_unsafe = ast::attr::contains_name(attrs, sym::allow_internal_unsafe);
|
||||
let local_inner_macros = ast::attr::find_by_name(attrs, sym::macro_export)
|
||||
.and_then(|macro_export| macro_export.meta_item_list())
|
||||
.is_some_and(|l| ast::attr::list_contains_name(&l, sym::local_inner_macros));
|
||||
|
|
@ -867,16 +872,17 @@ impl SyntaxExtension {
|
|||
)
|
||||
})
|
||||
.unwrap_or_else(|| (None, helper_attrs));
|
||||
let stability = attr::find_stability(sess, attrs, span);
|
||||
let const_stability = attr::find_const_stability(sess, attrs, span);
|
||||
let body_stability = attr::find_body_stability(sess, attrs);
|
||||
if let Some((_, sp)) = const_stability {
|
||||
|
||||
let stability = find_attr!(attrs, AttributeKind::Stability{stability, ..} => *stability);
|
||||
|
||||
// FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem
|
||||
if let Some(sp) = find_attr!(attrs, AttributeKind::ConstStability{span, ..} => *span) {
|
||||
sess.dcx().emit_err(errors::MacroConstStability {
|
||||
span: sp,
|
||||
head_span: sess.source_map().guess_head_span(span),
|
||||
});
|
||||
}
|
||||
if let Some((_, sp)) = body_stability {
|
||||
if let Some(sp) = find_attr!(attrs, AttributeKind::BodyStability{span, ..} => *span) {
|
||||
sess.dcx().emit_err(errors::MacroBodyStability {
|
||||
span: sp,
|
||||
head_span: sess.source_map().guess_head_span(span),
|
||||
|
|
@ -887,9 +893,10 @@ impl SyntaxExtension {
|
|||
kind,
|
||||
span,
|
||||
allow_internal_unstable: (!allow_internal_unstable.is_empty())
|
||||
.then(|| allow_internal_unstable.into()),
|
||||
stability: stability.map(|(s, _)| s),
|
||||
deprecation: attr::find_deprecation(sess, features, attrs).map(|(d, _)| d),
|
||||
// FIXME(jdonszelmann): avoid the into_iter/collect?
|
||||
.then(|| allow_internal_unstable.iter().map(|i| i.0).collect::<Vec<_>>().into()),
|
||||
stability,
|
||||
deprecation: find_attr!(attrs, AttributeKind::Deprecation{deprecation, ..} => *deprecation),
|
||||
helper_attrs,
|
||||
edition,
|
||||
builtin_name,
|
||||
|
|
|
|||
|
|
@ -3,17 +3,17 @@ use std::collections::hash_map::Entry;
|
|||
use std::{mem, slice};
|
||||
|
||||
use ast::token::IdentIsRaw;
|
||||
use rustc_ast::attr::AttributeExt;
|
||||
use rustc_ast::token::NtPatKind::*;
|
||||
use rustc_ast::token::TokenKind::*;
|
||||
use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind};
|
||||
use rustc_ast::tokenstream::{DelimSpan, TokenStream};
|
||||
use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_attr_parsing::{self as attr, TransparencyError};
|
||||
use rustc_attr_parsing::{AttributeKind, find_attr};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
||||
use rustc_errors::{Applicability, ErrorGuaranteed};
|
||||
use rustc_feature::Features;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint_defs::BuiltinLintDiag;
|
||||
use rustc_lint_defs::builtin::{
|
||||
RUST_2021_INCOMPATIBLE_OR_PATTERNS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
|
||||
|
|
@ -371,7 +371,7 @@ pub fn compile_declarative_macro(
|
|||
features: &Features,
|
||||
macro_def: &ast::MacroDef,
|
||||
ident: Ident,
|
||||
attrs: &[impl AttributeExt],
|
||||
attrs: &[hir::Attribute],
|
||||
span: Span,
|
||||
node_id: NodeId,
|
||||
edition: Edition,
|
||||
|
|
@ -379,7 +379,6 @@ pub fn compile_declarative_macro(
|
|||
let mk_syn_ext = |expander| {
|
||||
SyntaxExtension::new(
|
||||
sess,
|
||||
features,
|
||||
SyntaxExtensionKind::LegacyBang(expander),
|
||||
span,
|
||||
Vec::new(),
|
||||
|
|
@ -391,7 +390,6 @@ pub fn compile_declarative_macro(
|
|||
};
|
||||
let dummy_syn_ext = |guar| (mk_syn_ext(Box::new(DummyExpander(guar))), Vec::new());
|
||||
|
||||
let dcx = sess.dcx();
|
||||
let lhs_nm = Ident::new(sym::lhs, span);
|
||||
let rhs_nm = Ident::new(sym::rhs, span);
|
||||
let tt_spec = Some(NonterminalKind::TT);
|
||||
|
|
@ -542,16 +540,8 @@ pub fn compile_declarative_macro(
|
|||
|
||||
check_emission(macro_check::check_meta_variables(&sess.psess, node_id, span, &lhses, &rhses));
|
||||
|
||||
let (transparency, transparency_error) = attr::find_transparency(attrs, macro_rules);
|
||||
match transparency_error {
|
||||
Some(TransparencyError::UnknownTransparency(value, span)) => {
|
||||
dcx.span_err(span, format!("unknown macro transparency: `{value}`"));
|
||||
}
|
||||
Some(TransparencyError::MultipleTransparencyAttrs(old_span, new_span)) => {
|
||||
dcx.span_err(vec![old_span, new_span], "multiple macro transparency attributes");
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
let transparency = find_attr!(attrs, AttributeKind::MacroTransparency(x) => *x)
|
||||
.unwrap_or(Transparency::fallback(macro_rules));
|
||||
|
||||
if let Some(guar) = guar {
|
||||
// To avoid warning noise, only consider the rules of this
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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)]`")),
|
||||
|
|
|
|||
|
|
@ -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.*"]`.
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ odht = { version = "0.3.1", features = ["nightly"] }
|
|||
rustc_abi = { path = "../rustc_abi" }
|
||||
rustc_arena = { path = "../rustc_arena" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_attr_data_structures = { path = "../rustc_attr_data_structures" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_hashes = { path = "../rustc_hashes" }
|
||||
rustc_index = { path = "../rustc_index" }
|
||||
|
|
|
|||
|
|
@ -1,18 +1,20 @@
|
|||
// ignore-tidy-filelength
|
||||
use std::fmt;
|
||||
|
||||
use rustc_abi::ExternAbi;
|
||||
// ignore-tidy-filelength
|
||||
use rustc_ast::attr::AttributeExt;
|
||||
use rustc_ast::token::CommentKind;
|
||||
use rustc_ast::util::parser::{AssocOp, ExprPrecedence};
|
||||
use rustc_ast::{
|
||||
self as ast, AttrId, AttrStyle, DelimArgs, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece,
|
||||
IntTy, Label, LitIntType, LitKind, MetaItemInner, MetaItemLit, TraitObjectSyntax, UintTy,
|
||||
self as ast, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, LitIntType,
|
||||
LitKind, TraitObjectSyntax, UintTy, UnsafeBinderCastKind,
|
||||
};
|
||||
pub use rustc_ast::{
|
||||
BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity, ByRef, CaptureBy,
|
||||
ImplPolarity, IsAuto, Movability, Mutability, UnOp, UnsafeBinderCastKind,
|
||||
AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity,
|
||||
ByRef, CaptureBy, DelimArgs, ImplPolarity, IsAuto, MetaItemInner, MetaItemLit, Movability,
|
||||
Mutability, UnOp,
|
||||
};
|
||||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_data_structures::fingerprint::Fingerprint;
|
||||
use rustc_data_structures::sorted_map::SortedMap;
|
||||
use rustc_data_structures::tagged_ptr::TaggedRef;
|
||||
|
|
@ -1009,174 +1011,248 @@ pub enum AttrArgs {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Encodable, Decodable)]
|
||||
pub enum AttrKind {
|
||||
/// A normal attribute.
|
||||
Normal(Box<AttrItem>),
|
||||
|
||||
/// A doc comment (e.g. `/// ...`, `//! ...`, `/** ... */`, `/*! ... */`).
|
||||
/// Doc attributes (e.g. `#[doc="..."]`) are represented with the `Normal`
|
||||
/// variant (which is much less compact and thus more expensive).
|
||||
DocComment(CommentKind, Symbol),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable)]
|
||||
pub struct AttrPath {
|
||||
pub segments: Box<[Ident]>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl AttrPath {
|
||||
pub fn from_ast(path: &ast::Path) -> Self {
|
||||
AttrPath {
|
||||
segments: path.segments.iter().map(|i| i.ident).collect::<Vec<_>>().into_boxed_slice(),
|
||||
span: path.span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for AttrPath {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.segments.iter().map(|i| i.to_string()).collect::<Vec<_>>().join("::"))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable)]
|
||||
pub struct AttrItem {
|
||||
pub unsafety: Safety,
|
||||
// Not lowered to hir::Path because we have no NodeId to resolve to.
|
||||
pub path: AttrPath,
|
||||
pub args: AttrArgs,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Encodable, Decodable)]
|
||||
pub struct Attribute {
|
||||
pub kind: AttrKind,
|
||||
pub id: AttrId,
|
||||
pub id: HashIgnoredAttrId,
|
||||
/// Denotes if the attribute decorates the following construct (outer)
|
||||
/// or the construct this attribute is contained within (inner).
|
||||
pub style: AttrStyle,
|
||||
/// Span of the entire attribute
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
/// The derived implementation of [`HashStable_Generic`] on [`Attribute`]s shouldn't hash
|
||||
/// [`AttrId`]s. By wrapping them in this, we make sure we never do.
|
||||
#[derive(Copy, Debug, Encodable, Decodable, Clone)]
|
||||
pub struct HashIgnoredAttrId {
|
||||
pub attr_id: AttrId,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Encodable, Decodable, HashStable_Generic)]
|
||||
pub enum Attribute {
|
||||
/// A parsed built-in attribute.
|
||||
///
|
||||
/// Each attribute has a span connected to it. However, you must be somewhat careful using it.
|
||||
/// That's because sometimes we merge multiple attributes together, like when an item has
|
||||
/// multiple `repr` attributes. In this case the span might not be very useful.
|
||||
Parsed(AttributeKind),
|
||||
|
||||
/// An attribute that could not be parsed, out of a token-like representation.
|
||||
/// This is the case for custom tool attributes.
|
||||
Unparsed(Box<AttrItem>),
|
||||
}
|
||||
|
||||
impl Attribute {
|
||||
pub fn get_normal_item(&self) -> &AttrItem {
|
||||
match &self.kind {
|
||||
AttrKind::Normal(normal) => &normal,
|
||||
AttrKind::DocComment(..) => panic!("unexpected doc comment"),
|
||||
match &self {
|
||||
Attribute::Unparsed(normal) => &normal,
|
||||
_ => panic!("unexpected parsed attribute"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap_normal_item(self) -> AttrItem {
|
||||
match self.kind {
|
||||
AttrKind::Normal(normal) => *normal,
|
||||
AttrKind::DocComment(..) => panic!("unexpected doc comment"),
|
||||
match self {
|
||||
Attribute::Unparsed(normal) => *normal,
|
||||
_ => panic!("unexpected parsed attribute"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value_lit(&self) -> Option<&MetaItemLit> {
|
||||
match &self.kind {
|
||||
AttrKind::Normal(box AttrItem { args: AttrArgs::Eq { expr, .. }, .. }) => Some(expr),
|
||||
match &self {
|
||||
Attribute::Unparsed(n) => match n.as_ref() {
|
||||
AttrItem { args: AttrArgs::Eq { eq_span: _, expr }, .. } => Some(expr),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AttributeExt for Attribute {
|
||||
#[inline]
|
||||
fn id(&self) -> AttrId {
|
||||
self.id
|
||||
match &self {
|
||||
Attribute::Unparsed(u) => u.id.attr_id,
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn meta_item_list(&self) -> Option<ThinVec<ast::MetaItemInner>> {
|
||||
match &self.kind {
|
||||
AttrKind::Normal(box AttrItem { args: AttrArgs::Delimited(d), .. }) => {
|
||||
ast::MetaItemKind::list_from_tokens(d.tokens.clone())
|
||||
}
|
||||
match &self {
|
||||
Attribute::Unparsed(n) => match n.as_ref() {
|
||||
AttrItem { args: AttrArgs::Delimited(d), .. } => {
|
||||
ast::MetaItemKind::list_from_tokens(d.tokens.clone())
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_str(&self) -> Option<Symbol> {
|
||||
self.value_lit().and_then(|x| x.value_str())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_span(&self) -> Option<Span> {
|
||||
self.value_lit().map(|i| i.span)
|
||||
}
|
||||
|
||||
/// For a single-segment attribute, returns its name; otherwise, returns `None`.
|
||||
#[inline]
|
||||
fn ident(&self) -> Option<Ident> {
|
||||
match &self.kind {
|
||||
AttrKind::Normal(box AttrItem {
|
||||
path: AttrPath { segments: box [ident], .. }, ..
|
||||
}) => Some(*ident),
|
||||
match &self {
|
||||
Attribute::Unparsed(n) => {
|
||||
if let [ident] = n.path.segments.as_ref() {
|
||||
Some(*ident)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn path_matches(&self, name: &[Symbol]) -> bool {
|
||||
match &self.kind {
|
||||
AttrKind::Normal(n) => n.path.segments.iter().map(|segment| &segment.name).eq(name),
|
||||
AttrKind::DocComment(..) => false,
|
||||
match &self {
|
||||
Attribute::Unparsed(n) => {
|
||||
n.path.segments.len() == name.len()
|
||||
&& n.path.segments.iter().zip(name).all(|(s, n)| s.name == *n)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_doc_comment(&self) -> bool {
|
||||
matches!(self.kind, AttrKind::DocComment(..))
|
||||
matches!(self, Attribute::Parsed(AttributeKind::DocComment { .. }))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn span(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
|
||||
fn is_word(&self) -> bool {
|
||||
matches!(self.kind, AttrKind::Normal(box AttrItem { args: AttrArgs::Empty, .. }))
|
||||
}
|
||||
|
||||
fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> {
|
||||
match &self.kind {
|
||||
AttrKind::Normal(n) => Some(n.path.segments.iter().copied().collect()),
|
||||
AttrKind::DocComment(..) => None,
|
||||
match &self {
|
||||
Attribute::Unparsed(u) => u.span,
|
||||
// FIXME: should not be needed anymore when all attrs are parsed
|
||||
Attribute::Parsed(AttributeKind::Deprecation { span, .. }) => *span,
|
||||
Attribute::Parsed(AttributeKind::DocComment { span, .. }) => *span,
|
||||
a => panic!("can't get the span of an arbitrary parsed attribute: {a:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn doc_str(&self) -> Option<Symbol> {
|
||||
match &self.kind {
|
||||
AttrKind::DocComment(.., data) => Some(*data),
|
||||
AttrKind::Normal(_) if self.has_name(sym::doc) => self.value_str(),
|
||||
#[inline]
|
||||
fn is_word(&self) -> bool {
|
||||
match &self {
|
||||
Attribute::Unparsed(n) => {
|
||||
matches!(n.args, AttrArgs::Empty)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> {
|
||||
match &self {
|
||||
Attribute::Unparsed(n) => Some(n.path.segments.iter().copied().collect()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn doc_str(&self) -> Option<Symbol> {
|
||||
match &self {
|
||||
Attribute::Parsed(AttributeKind::DocComment { comment, .. }) => Some(*comment),
|
||||
Attribute::Unparsed(_) if self.has_name(sym::doc) => self.value_str(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
|
||||
match &self.kind {
|
||||
AttrKind::DocComment(kind, data) => Some((*data, *kind)),
|
||||
AttrKind::Normal(_) if self.name_or_empty() == sym::doc => {
|
||||
match &self {
|
||||
Attribute::Parsed(AttributeKind::DocComment { kind, comment, .. }) => {
|
||||
Some((*comment, *kind))
|
||||
}
|
||||
Attribute::Unparsed(_) if self.name_or_empty() == sym::doc => {
|
||||
self.value_str().map(|s| (s, CommentKind::Line))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn style(&self) -> AttrStyle {
|
||||
self.style
|
||||
match &self {
|
||||
Attribute::Unparsed(u) => u.style,
|
||||
Attribute::Parsed(AttributeKind::DocComment { style, .. }) => *style,
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(fn_delegation): use function delegation instead of manually forwarding
|
||||
impl Attribute {
|
||||
#[inline]
|
||||
pub fn id(&self) -> AttrId {
|
||||
AttributeExt::id(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn name_or_empty(&self) -> Symbol {
|
||||
AttributeExt::name_or_empty(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
|
||||
AttributeExt::meta_item_list(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn value_str(&self) -> Option<Symbol> {
|
||||
AttributeExt::value_str(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn value_span(&self) -> Option<Span> {
|
||||
AttributeExt::value_span(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ident(&self) -> Option<Ident> {
|
||||
AttributeExt::ident(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn path_matches(&self, name: &[Symbol]) -> bool {
|
||||
AttributeExt::path_matches(self, name)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_doc_comment(&self) -> bool {
|
||||
AttributeExt::is_doc_comment(self)
|
||||
}
|
||||
|
|
@ -1186,34 +1262,42 @@ impl Attribute {
|
|||
AttributeExt::has_name(self, name)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn span(&self) -> Span {
|
||||
AttributeExt::span(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_word(&self) -> bool {
|
||||
AttributeExt::is_word(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn path(&self) -> SmallVec<[Symbol; 1]> {
|
||||
AttributeExt::path(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> {
|
||||
AttributeExt::ident_path(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn doc_str(&self) -> Option<Symbol> {
|
||||
AttributeExt::doc_str(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_proc_macro_attr(&self) -> bool {
|
||||
AttributeExt::is_proc_macro_attr(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
|
||||
AttributeExt::doc_str_and_comment_kind(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn style(&self) -> AttrStyle {
|
||||
AttributeExt::style(self)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,21 @@
|
|||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
|
||||
use rustc_span::def_id::DefPathHash;
|
||||
|
||||
use crate::HashIgnoredAttrId;
|
||||
use crate::hir::{
|
||||
Attribute, AttributeMap, BodyId, Crate, ForeignItemId, ImplItemId, ItemId, OwnerNodes,
|
||||
TraitItemId,
|
||||
AttributeMap, BodyId, Crate, ForeignItemId, ImplItemId, ItemId, OwnerNodes, TraitItemId,
|
||||
};
|
||||
use crate::hir_id::{HirId, ItemLocalId};
|
||||
|
||||
/// Requirements for a `StableHashingContext` to be used in this crate.
|
||||
/// This is a hack to allow using the `HashStable_Generic` derive macro
|
||||
/// instead of implementing everything in `rustc_middle`.
|
||||
pub trait HashStableContext: rustc_ast::HashStableContext + rustc_abi::HashStableContext {
|
||||
fn hash_attr(&mut self, _: &Attribute, hasher: &mut StableHasher);
|
||||
pub trait HashStableContext:
|
||||
rustc_attr_data_structures::HashStableContext
|
||||
+ rustc_ast::HashStableContext
|
||||
+ rustc_abi::HashStableContext
|
||||
{
|
||||
fn hash_attr_id(&mut self, id: &HashIgnoredAttrId, hasher: &mut StableHasher);
|
||||
}
|
||||
|
||||
impl<HirCtx: crate::HashStableContext> ToStableHashKey<HirCtx> for HirId {
|
||||
|
|
@ -114,8 +118,8 @@ impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for Crate<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for Attribute {
|
||||
impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for HashIgnoredAttrId {
|
||||
fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) {
|
||||
hcx.hash_attr(self, hasher)
|
||||
hcx.hash_attr_id(self, hasher)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ use std::cell::LazyCell;
|
|||
use std::ops::ControlFlow;
|
||||
|
||||
use rustc_abi::FieldIdx;
|
||||
use rustc_attr_parsing::AttributeKind;
|
||||
use rustc_attr_parsing::ReprAttr::ReprPacked;
|
||||
use rustc_data_structures::unord::{UnordMap, UnordSet};
|
||||
use rustc_errors::MultiSpan;
|
||||
use rustc_errors::codes::*;
|
||||
|
|
@ -1114,7 +1116,7 @@ fn check_impl_items_against_trait<'tcx>(
|
|||
if let Some(missing_items) = must_implement_one_of {
|
||||
let attr_span = tcx
|
||||
.get_attr(trait_ref.def_id, sym::rustc_must_implement_one_of)
|
||||
.map(|attr| attr.span);
|
||||
.map(|attr| attr.span());
|
||||
|
||||
missing_items_must_implement_one_of_err(
|
||||
tcx,
|
||||
|
|
@ -1203,11 +1205,13 @@ fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) {
|
|||
pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) {
|
||||
let repr = def.repr();
|
||||
if repr.packed() {
|
||||
for attr in tcx.get_attrs(def.did(), sym::repr) {
|
||||
for r in attr::parse_repr_attr(tcx.sess, attr) {
|
||||
if let attr::ReprPacked(pack) = r
|
||||
if let Some(reprs) =
|
||||
attr::find_attr!(tcx.get_all_attrs(def.did()), AttributeKind::Repr(r) => r)
|
||||
{
|
||||
for (r, _) in reprs {
|
||||
if let ReprPacked(pack) = r
|
||||
&& let Some(repr_pack) = repr.pack
|
||||
&& pack != repr_pack
|
||||
&& pack != &repr_pack
|
||||
{
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
|
|
@ -1419,16 +1423,19 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) {
|
|||
def.destructor(tcx); // force the destructor to be evaluated
|
||||
|
||||
if def.variants().is_empty() {
|
||||
if let Some(attr) = tcx.get_attrs(def_id, sym::repr).next() {
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
attr.span,
|
||||
E0084,
|
||||
"unsupported representation for zero-variant enum"
|
||||
)
|
||||
.with_span_label(tcx.def_span(def_id), "zero-variant enum")
|
||||
.emit();
|
||||
}
|
||||
attr::find_attr!(
|
||||
tcx.get_all_attrs(def_id),
|
||||
AttributeKind::Repr(rs) => {
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
rs.first().unwrap().1,
|
||||
E0084,
|
||||
"unsupported representation for zero-variant enum"
|
||||
)
|
||||
.with_span_label(tcx.def_span(def_id), "zero-variant enum")
|
||||
.emit();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
let repr_type_ty = def.repr().discr_type().to_ty(tcx);
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
|
|||
}
|
||||
|
||||
for attr in tcx.get_attrs(main_def_id, sym::track_caller) {
|
||||
tcx.dcx().emit_err(errors::TrackCallerOnMain { span: attr.span, annotated: main_span });
|
||||
tcx.dcx().emit_err(errors::TrackCallerOnMain { span: attr.span(), annotated: main_span });
|
||||
error = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1202,7 +1202,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
|
|||
// and that they are all identifiers
|
||||
.and_then(|attr| match attr.meta_item_list() {
|
||||
Some(items) if items.len() < 2 => {
|
||||
tcx.dcx().emit_err(errors::MustImplementOneOfAttribute { span: attr.span });
|
||||
tcx.dcx().emit_err(errors::MustImplementOneOfAttribute { span: attr.span() });
|
||||
|
||||
None
|
||||
}
|
||||
|
|
@ -1214,7 +1214,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
|
|||
tcx.dcx().emit_err(errors::MustBeNameOfAssociatedFunction { span });
|
||||
})
|
||||
.ok()
|
||||
.zip(Some(attr.span)),
|
||||
.zip(Some(attr.span())),
|
||||
// Error is reported by `rustc_attr!`
|
||||
None => None,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -111,14 +111,14 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) {
|
|||
let trait_ref = tcx.impl_trait_ref(def_id).unwrap().instantiate_identity();
|
||||
if trait_ref.has_non_region_param() {
|
||||
tcx.dcx().span_err(
|
||||
attr.span,
|
||||
attr.span(),
|
||||
"`rustc_dump_vtable` must be applied to non-generic impl",
|
||||
);
|
||||
continue;
|
||||
}
|
||||
if !tcx.is_dyn_compatible(trait_ref.def_id) {
|
||||
tcx.dcx().span_err(
|
||||
attr.span,
|
||||
attr.span(),
|
||||
"`rustc_dump_vtable` must be applied to dyn-compatible trait",
|
||||
);
|
||||
continue;
|
||||
|
|
@ -127,7 +127,7 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) {
|
|||
.try_normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref)
|
||||
else {
|
||||
tcx.dcx().span_err(
|
||||
attr.span,
|
||||
attr.span(),
|
||||
"`rustc_dump_vtable` applied to impl header that cannot be normalized",
|
||||
);
|
||||
continue;
|
||||
|
|
@ -138,7 +138,7 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) {
|
|||
let ty = tcx.type_of(def_id).instantiate_identity();
|
||||
if ty.has_non_region_param() {
|
||||
tcx.dcx().span_err(
|
||||
attr.span,
|
||||
attr.span(),
|
||||
"`rustc_dump_vtable` must be applied to non-generic type",
|
||||
);
|
||||
continue;
|
||||
|
|
@ -147,13 +147,14 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) {
|
|||
tcx.try_normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), ty)
|
||||
else {
|
||||
tcx.dcx().span_err(
|
||||
attr.span,
|
||||
attr.span(),
|
||||
"`rustc_dump_vtable` applied to type alias that cannot be normalized",
|
||||
);
|
||||
continue;
|
||||
};
|
||||
let ty::Dynamic(data, _, _) = *ty.kind() else {
|
||||
tcx.dcx().span_err(attr.span, "`rustc_dump_vtable` to type alias of dyn type");
|
||||
tcx.dcx()
|
||||
.span_err(attr.span(), "`rustc_dump_vtable` to type alias of dyn type");
|
||||
continue;
|
||||
};
|
||||
if let Some(principal) = data.principal() {
|
||||
|
|
@ -166,7 +167,7 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) {
|
|||
}
|
||||
_ => {
|
||||
tcx.dcx().span_err(
|
||||
attr.span,
|
||||
attr.span(),
|
||||
"`rustc_dump_vtable` only applies to impl, or type alias of dyn type",
|
||||
);
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ edition = "2024"
|
|||
rustc_abi = { path = "../rustc_abi" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
|
||||
rustc_attr_parsing = { path = "../rustc_attr_parsing" }
|
||||
rustc_hir = { path = "../rustc_hir" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
# tidy-alphabetical-end
|
||||
|
|
|
|||
|
|
@ -11,11 +11,12 @@ use std::vec;
|
|||
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_ast::util::parser::{self, AssocOp, ExprPrecedence, Fixity};
|
||||
use rustc_ast::{DUMMY_NODE_ID, DelimArgs};
|
||||
use rustc_ast::{AttrStyle, DUMMY_NODE_ID, DelimArgs};
|
||||
use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent};
|
||||
use rustc_ast_pretty::pp::{self, Breaks};
|
||||
use rustc_ast_pretty::pprust::state::MacHeader;
|
||||
use rustc_ast_pretty::pprust::{Comments, PrintState};
|
||||
use rustc_attr_parsing::{AttributeKind, PrintAttribute};
|
||||
use rustc_hir::{
|
||||
BindingMode, ByRef, ConstArgKind, GenericArg, GenericBound, GenericParam, GenericParamKind,
|
||||
HirId, ImplicitSelfKind, LifetimeParamKind, Node, PatKind, PreciseCapturingArg, RangeEnd, Term,
|
||||
|
|
@ -80,65 +81,48 @@ impl<'a> State<'a> {
|
|||
(self.attrs)(id)
|
||||
}
|
||||
|
||||
fn print_inner_attributes(&mut self, attrs: &[hir::Attribute]) -> bool {
|
||||
self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, true)
|
||||
fn print_attrs_as_inner(&mut self, attrs: &[hir::Attribute]) {
|
||||
self.print_either_attributes(attrs, ast::AttrStyle::Inner)
|
||||
}
|
||||
|
||||
fn print_outer_attributes(&mut self, attrs: &[hir::Attribute]) -> bool {
|
||||
self.print_either_attributes(attrs, ast::AttrStyle::Outer, false, true)
|
||||
fn print_attrs_as_outer(&mut self, attrs: &[hir::Attribute]) {
|
||||
self.print_either_attributes(attrs, ast::AttrStyle::Outer)
|
||||
}
|
||||
|
||||
fn print_either_attributes(
|
||||
&mut self,
|
||||
attrs: &[hir::Attribute],
|
||||
kind: ast::AttrStyle,
|
||||
is_inline: bool,
|
||||
trailing_hardbreak: bool,
|
||||
) -> bool {
|
||||
let mut printed = false;
|
||||
fn print_either_attributes(&mut self, attrs: &[hir::Attribute], style: ast::AttrStyle) {
|
||||
if attrs.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
for attr in attrs {
|
||||
if attr.style == kind {
|
||||
self.print_attribute_inline(attr, is_inline);
|
||||
if is_inline {
|
||||
self.nbsp();
|
||||
}
|
||||
printed = true;
|
||||
}
|
||||
self.print_attribute_inline(attr, style);
|
||||
}
|
||||
if printed && trailing_hardbreak && !is_inline {
|
||||
self.hardbreak_if_not_bol();
|
||||
}
|
||||
printed
|
||||
self.hardbreak_if_not_bol();
|
||||
}
|
||||
|
||||
fn print_attribute_inline(&mut self, attr: &hir::Attribute, is_inline: bool) {
|
||||
if !is_inline {
|
||||
self.hardbreak_if_not_bol();
|
||||
}
|
||||
self.maybe_print_comment(attr.span.lo());
|
||||
match &attr.kind {
|
||||
hir::AttrKind::Normal(normal) => {
|
||||
match attr.style {
|
||||
fn print_attribute_inline(&mut self, attr: &hir::Attribute, style: AttrStyle) {
|
||||
match &attr {
|
||||
hir::Attribute::Unparsed(unparsed) => {
|
||||
self.maybe_print_comment(unparsed.span.lo());
|
||||
match style {
|
||||
ast::AttrStyle::Inner => self.word("#!["),
|
||||
ast::AttrStyle::Outer => self.word("#["),
|
||||
}
|
||||
if normal.unsafety == hir::Safety::Unsafe {
|
||||
self.word("unsafe(");
|
||||
}
|
||||
self.print_attr_item(&normal, attr.span);
|
||||
if normal.unsafety == hir::Safety::Unsafe {
|
||||
self.word(")");
|
||||
}
|
||||
self.print_attr_item(&unparsed, unparsed.span);
|
||||
self.word("]");
|
||||
}
|
||||
hir::AttrKind::DocComment(comment_kind, data) => {
|
||||
hir::Attribute::Parsed(AttributeKind::DocComment { style, kind, comment, .. }) => {
|
||||
self.word(rustc_ast_pretty::pprust::state::doc_comment_to_string(
|
||||
*comment_kind,
|
||||
attr.style,
|
||||
*data,
|
||||
*kind, *style, *comment,
|
||||
));
|
||||
self.hardbreak()
|
||||
}
|
||||
hir::Attribute::Parsed(pa) => {
|
||||
self.word("#[attr=\"");
|
||||
pa.print_attribute(self);
|
||||
self.word("\")]");
|
||||
self.hardbreak()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -162,7 +146,7 @@ impl<'a> State<'a> {
|
|||
false,
|
||||
None,
|
||||
*delim,
|
||||
tokens,
|
||||
&tokens,
|
||||
true,
|
||||
span,
|
||||
),
|
||||
|
|
@ -307,7 +291,7 @@ where
|
|||
}
|
||||
|
||||
pub fn attribute_to_string(ann: &dyn PpAnn, attr: &hir::Attribute) -> String {
|
||||
to_string(ann, |s| s.print_attribute_inline(attr, false))
|
||||
to_string(ann, |s| s.print_attribute_inline(attr, AttrStyle::Outer))
|
||||
}
|
||||
|
||||
pub fn ty_to_string(ann: &dyn PpAnn, ty: &hir::Ty<'_>) -> String {
|
||||
|
|
@ -370,7 +354,7 @@ impl<'a> State<'a> {
|
|||
}
|
||||
|
||||
fn print_mod(&mut self, _mod: &hir::Mod<'_>, attrs: &[hir::Attribute]) {
|
||||
self.print_inner_attributes(attrs);
|
||||
self.print_attrs_as_inner(attrs);
|
||||
for &item_id in _mod.item_ids {
|
||||
self.ann.nested(self, Nested::Item(item_id));
|
||||
}
|
||||
|
|
@ -487,7 +471,7 @@ impl<'a> State<'a> {
|
|||
fn print_foreign_item(&mut self, item: &hir::ForeignItem<'_>) {
|
||||
self.hardbreak_if_not_bol();
|
||||
self.maybe_print_comment(item.span.lo());
|
||||
self.print_outer_attributes(self.attrs(item.hir_id()));
|
||||
self.print_attrs_as_outer(self.attrs(item.hir_id()));
|
||||
match item.kind {
|
||||
hir::ForeignItemKind::Fn(sig, arg_names, generics) => {
|
||||
self.head("");
|
||||
|
|
@ -591,7 +575,7 @@ impl<'a> State<'a> {
|
|||
self.hardbreak_if_not_bol();
|
||||
self.maybe_print_comment(item.span.lo());
|
||||
let attrs = self.attrs(item.hir_id());
|
||||
self.print_outer_attributes(attrs);
|
||||
self.print_attrs_as_outer(attrs);
|
||||
self.ann.pre(self, AnnNode::Item(item));
|
||||
match item.kind {
|
||||
hir::ItemKind::ExternCrate(orig_name) => {
|
||||
|
|
@ -687,7 +671,7 @@ impl<'a> State<'a> {
|
|||
self.head("extern");
|
||||
self.word_nbsp(abi.to_string());
|
||||
self.bopen();
|
||||
self.print_inner_attributes(self.attrs(item.hir_id()));
|
||||
self.print_attrs_as_inner(self.attrs(item.hir_id()));
|
||||
for item in items {
|
||||
self.ann.nested(self, Nested::ForeignItem(item.id));
|
||||
}
|
||||
|
|
@ -755,7 +739,7 @@ impl<'a> State<'a> {
|
|||
|
||||
self.space();
|
||||
self.bopen();
|
||||
self.print_inner_attributes(attrs);
|
||||
self.print_attrs_as_inner(attrs);
|
||||
for impl_item in items {
|
||||
self.ann.nested(self, Nested::ImplItem(impl_item.id));
|
||||
}
|
||||
|
|
@ -847,7 +831,7 @@ impl<'a> State<'a> {
|
|||
for v in variants {
|
||||
self.space_if_not_bol();
|
||||
self.maybe_print_comment(v.span.lo());
|
||||
self.print_outer_attributes(self.attrs(v.hir_id));
|
||||
self.print_attrs_as_outer(self.attrs(v.hir_id));
|
||||
self.ibox(INDENT_UNIT);
|
||||
self.print_variant(v);
|
||||
self.word(",");
|
||||
|
|
@ -880,7 +864,7 @@ impl<'a> State<'a> {
|
|||
self.popen();
|
||||
self.commasep(Inconsistent, struct_def.fields(), |s, field| {
|
||||
s.maybe_print_comment(field.span.lo());
|
||||
s.print_outer_attributes(s.attrs(field.hir_id));
|
||||
s.print_attrs_as_outer(s.attrs(field.hir_id));
|
||||
s.print_type(field.ty);
|
||||
});
|
||||
self.pclose();
|
||||
|
|
@ -907,7 +891,7 @@ impl<'a> State<'a> {
|
|||
for field in fields {
|
||||
self.hardbreak_if_not_bol();
|
||||
self.maybe_print_comment(field.span.lo());
|
||||
self.print_outer_attributes(self.attrs(field.hir_id));
|
||||
self.print_attrs_as_outer(self.attrs(field.hir_id));
|
||||
self.print_ident(field.ident);
|
||||
self.word_nbsp(":");
|
||||
self.print_type(field.ty);
|
||||
|
|
@ -943,7 +927,7 @@ impl<'a> State<'a> {
|
|||
self.ann.pre(self, AnnNode::SubItem(ti.hir_id()));
|
||||
self.hardbreak_if_not_bol();
|
||||
self.maybe_print_comment(ti.span.lo());
|
||||
self.print_outer_attributes(self.attrs(ti.hir_id()));
|
||||
self.print_attrs_as_outer(self.attrs(ti.hir_id()));
|
||||
match ti.kind {
|
||||
hir::TraitItemKind::Const(ty, default) => {
|
||||
self.print_associated_const(ti.ident, ti.generics, ty, default);
|
||||
|
|
@ -971,7 +955,7 @@ impl<'a> State<'a> {
|
|||
self.ann.pre(self, AnnNode::SubItem(ii.hir_id()));
|
||||
self.hardbreak_if_not_bol();
|
||||
self.maybe_print_comment(ii.span.lo());
|
||||
self.print_outer_attributes(self.attrs(ii.hir_id()));
|
||||
self.print_attrs_as_outer(self.attrs(ii.hir_id()));
|
||||
|
||||
match ii.kind {
|
||||
hir::ImplItemKind::Const(ty, expr) => {
|
||||
|
|
@ -1074,7 +1058,7 @@ impl<'a> State<'a> {
|
|||
self.ann.pre(self, AnnNode::Block(blk));
|
||||
self.bopen();
|
||||
|
||||
self.print_inner_attributes(attrs);
|
||||
self.print_attrs_as_inner(attrs);
|
||||
|
||||
for st in blk.stmts {
|
||||
self.print_stmt(st);
|
||||
|
|
@ -1264,7 +1248,7 @@ impl<'a> State<'a> {
|
|||
self.space();
|
||||
}
|
||||
self.cbox(INDENT_UNIT);
|
||||
self.print_outer_attributes(self.attrs(field.hir_id));
|
||||
self.print_attrs_as_outer(self.attrs(field.hir_id));
|
||||
if !field.is_shorthand {
|
||||
self.print_ident(field.ident);
|
||||
self.word_space(":");
|
||||
|
|
@ -1461,7 +1445,7 @@ impl<'a> State<'a> {
|
|||
|
||||
fn print_expr(&mut self, expr: &hir::Expr<'_>) {
|
||||
self.maybe_print_comment(expr.span.lo());
|
||||
self.print_outer_attributes(self.attrs(expr.hir_id));
|
||||
self.print_attrs_as_outer(self.attrs(expr.hir_id));
|
||||
self.ibox(INDENT_UNIT);
|
||||
self.ann.pre(self, AnnNode::Expr(expr));
|
||||
match expr.kind {
|
||||
|
|
@ -1677,8 +1661,8 @@ impl<'a> State<'a> {
|
|||
}
|
||||
hir::ExprKind::UnsafeBinderCast(kind, expr, ty) => {
|
||||
match kind {
|
||||
hir::UnsafeBinderCastKind::Wrap => self.word("wrap_binder!("),
|
||||
hir::UnsafeBinderCastKind::Unwrap => self.word("unwrap_binder!("),
|
||||
ast::UnsafeBinderCastKind::Wrap => self.word("wrap_binder!("),
|
||||
ast::UnsafeBinderCastKind::Unwrap => self.word("unwrap_binder!("),
|
||||
}
|
||||
self.print_expr(expr);
|
||||
if let Some(ty) = ty {
|
||||
|
|
@ -2073,7 +2057,7 @@ impl<'a> State<'a> {
|
|||
self.space();
|
||||
}
|
||||
self.cbox(INDENT_UNIT);
|
||||
self.print_outer_attributes(self.attrs(field.hir_id));
|
||||
self.print_attrs_as_outer(self.attrs(field.hir_id));
|
||||
if !field.is_shorthand {
|
||||
self.print_ident(field.ident);
|
||||
self.word_nbsp(":");
|
||||
|
|
@ -2083,7 +2067,7 @@ impl<'a> State<'a> {
|
|||
}
|
||||
|
||||
fn print_param(&mut self, arg: &hir::Param<'_>) {
|
||||
self.print_outer_attributes(self.attrs(arg.hir_id));
|
||||
self.print_attrs_as_outer(self.attrs(arg.hir_id));
|
||||
self.print_pat(arg.pat);
|
||||
}
|
||||
|
||||
|
|
@ -2118,7 +2102,7 @@ impl<'a> State<'a> {
|
|||
self.cbox(INDENT_UNIT);
|
||||
self.ann.pre(self, AnnNode::Arm(arm));
|
||||
self.ibox(0);
|
||||
self.print_outer_attributes(self.attrs(arm.hir_id));
|
||||
self.print_attrs_as_outer(self.attrs(arm.hir_id));
|
||||
self.print_pat(arm.pat);
|
||||
self.space();
|
||||
if let Some(ref g) = arm.guard {
|
||||
|
|
|
|||
|
|
@ -1656,13 +1656,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
fn check_expr_unsafe_binder_cast(
|
||||
&self,
|
||||
span: Span,
|
||||
kind: hir::UnsafeBinderCastKind,
|
||||
kind: ast::UnsafeBinderCastKind,
|
||||
inner_expr: &'tcx hir::Expr<'tcx>,
|
||||
hir_ty: Option<&'tcx hir::Ty<'tcx>>,
|
||||
expected: Expectation<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
match kind {
|
||||
hir::UnsafeBinderCastKind::Wrap => {
|
||||
ast::UnsafeBinderCastKind::Wrap => {
|
||||
let ascribed_ty =
|
||||
hir_ty.map(|hir_ty| self.lower_ty_saving_user_provided_ty(hir_ty));
|
||||
let expected_ty = expected.only_has_type(self);
|
||||
|
|
@ -1706,7 +1706,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
binder_ty
|
||||
}
|
||||
hir::UnsafeBinderCastKind::Unwrap => {
|
||||
ast::UnsafeBinderCastKind::Unwrap => {
|
||||
let ascribed_ty =
|
||||
hir_ty.map(|hir_ty| self.lower_ty_saving_user_provided_ty(hir_ty));
|
||||
let hint_ty = ascribed_ty.unwrap_or_else(|| self.next_ty_var(inner_expr.span));
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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!(
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use std::path::PathBuf;
|
|||
|
||||
use hir::Expr;
|
||||
use rustc_ast::ast::Mutability;
|
||||
use rustc_attr_parsing::parse_confusables;
|
||||
use rustc_attr_parsing::{AttributeKind, find_attr};
|
||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::sorted_map::SortedMap;
|
||||
use rustc_data_structures::unord::UnordSet;
|
||||
|
|
@ -1884,9 +1884,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
for inherent_method in
|
||||
self.tcx.associated_items(inherent_impl_did).in_definition_order()
|
||||
{
|
||||
if let Some(attr) =
|
||||
self.tcx.get_attr(inherent_method.def_id, sym::rustc_confusables)
|
||||
&& let Some(candidates) = parse_confusables(attr)
|
||||
if let Some(candidates) = find_attr!(self.tcx.get_all_attrs(inherent_method.def_id), AttributeKind::Confusables{symbols, ..} => symbols)
|
||||
&& candidates.contains(&item_name.name)
|
||||
&& let ty::AssocKind::Fn = inherent_method.kind
|
||||
{
|
||||
|
|
|
|||
|
|
@ -137,13 +137,13 @@ impl<'tcx> IfThisChanged<'tcx> {
|
|||
match DepNode::from_label_string(self.tcx, n.as_str(), def_path_hash) {
|
||||
Ok(n) => n,
|
||||
Err(()) => self.tcx.dcx().emit_fatal(errors::UnrecognizedDepNode {
|
||||
span: attr.span,
|
||||
span: attr.span(),
|
||||
name: n,
|
||||
}),
|
||||
}
|
||||
}
|
||||
};
|
||||
self.if_this_changed.push((attr.span, def_id.to_def_id(), dep_node));
|
||||
self.if_this_changed.push((attr.span(), def_id.to_def_id(), dep_node));
|
||||
} else if attr.has_name(sym::rustc_then_this_would_need) {
|
||||
let dep_node_interned = self.argument(attr);
|
||||
let dep_node = match dep_node_interned {
|
||||
|
|
@ -151,17 +151,17 @@ impl<'tcx> IfThisChanged<'tcx> {
|
|||
match DepNode::from_label_string(self.tcx, n.as_str(), def_path_hash) {
|
||||
Ok(n) => n,
|
||||
Err(()) => self.tcx.dcx().emit_fatal(errors::UnrecognizedDepNode {
|
||||
span: attr.span,
|
||||
span: attr.span(),
|
||||
name: n,
|
||||
}),
|
||||
}
|
||||
}
|
||||
None => {
|
||||
self.tcx.dcx().emit_fatal(errors::MissingDepNode { span: attr.span });
|
||||
self.tcx.dcx().emit_fatal(errors::MissingDepNode { span: attr.span() });
|
||||
}
|
||||
};
|
||||
self.then_this_would_need.push((
|
||||
attr.span,
|
||||
attr.span(),
|
||||
dep_node_interned.unwrap(),
|
||||
hir_id,
|
||||
dep_node,
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
|
|||
let loaded_from_disk = self.loaded_from_disk(attr);
|
||||
for e in except.items().into_sorted_stable_ord() {
|
||||
if !auto.remove(e) {
|
||||
self.tcx.dcx().emit_fatal(errors::AssertionAuto { span: attr.span, name, e });
|
||||
self.tcx.dcx().emit_fatal(errors::AssertionAuto { span: attr.span(), name, e });
|
||||
}
|
||||
}
|
||||
Assertion { clean: auto, dirty: except, loaded_from_disk }
|
||||
|
|
@ -282,7 +282,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
|
|||
HirItem::Impl { .. } => ("ItemKind::Impl", LABELS_IMPL),
|
||||
|
||||
_ => self.tcx.dcx().emit_fatal(errors::UndefinedCleanDirtyItem {
|
||||
span: attr.span,
|
||||
span: attr.span(),
|
||||
kind: format!("{:?}", item.kind),
|
||||
}),
|
||||
}
|
||||
|
|
@ -298,7 +298,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
|
|||
ImplItemKind::Type(..) => ("NodeImplType", LABELS_CONST_IN_IMPL),
|
||||
},
|
||||
_ => self.tcx.dcx().emit_fatal(errors::UndefinedCleanDirty {
|
||||
span: attr.span,
|
||||
span: attr.span(),
|
||||
kind: format!("{node:?}"),
|
||||
}),
|
||||
};
|
||||
|
|
@ -375,7 +375,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
|
|||
let Some(assertion) = self.assertion_maybe(item_id, attr) else {
|
||||
continue;
|
||||
};
|
||||
self.checked_attrs.insert(attr.id);
|
||||
self.checked_attrs.insert(attr.id());
|
||||
for label in assertion.clean.items().into_sorted_stable_ord() {
|
||||
let dep_node = DepNode::from_label_string(self.tcx, label, def_path_hash).unwrap();
|
||||
self.assert_clean(item_span, dep_node);
|
||||
|
|
@ -405,12 +405,13 @@ fn check_config(tcx: TyCtxt<'_>, attr: &Attribute) -> bool {
|
|||
debug!("check_config: searching for cfg {:?}", value);
|
||||
cfg = Some(config.contains(&(value, None)));
|
||||
} else if !(item.has_name(EXCEPT) || item.has_name(LOADED_FROM_DISK)) {
|
||||
tcx.dcx().emit_err(errors::UnknownItem { span: attr.span, name: item.name_or_empty() });
|
||||
tcx.dcx()
|
||||
.emit_err(errors::UnknownItem { span: attr.span(), name: item.name_or_empty() });
|
||||
}
|
||||
}
|
||||
|
||||
match cfg {
|
||||
None => tcx.dcx().emit_fatal(errors::NoCfg { span: attr.span }),
|
||||
None => tcx.dcx().emit_fatal(errors::NoCfg { span: attr.span() }),
|
||||
Some(c) => c,
|
||||
}
|
||||
}
|
||||
|
|
@ -444,9 +445,9 @@ impl<'tcx> FindAllAttrs<'tcx> {
|
|||
|
||||
fn report_unchecked_attrs(&self, mut checked_attrs: FxHashSet<ast::AttrId>) {
|
||||
for attr in &self.found_attrs {
|
||||
if !checked_attrs.contains(&attr.id) {
|
||||
self.tcx.dcx().emit_err(errors::UncheckedClean { span: attr.span });
|
||||
checked_attrs.insert(attr.id);
|
||||
if !checked_attrs.contains(&attr.id()) {
|
||||
self.tcx.dcx().emit_err(errors::UncheckedClean { span: attr.span() });
|
||||
checked_attrs.insert(attr.id());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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()]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -1011,7 +1011,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
|
|||
cx.emit_span_lint(
|
||||
NO_MANGLE_GENERIC_ITEMS,
|
||||
span,
|
||||
BuiltinNoMangleGeneric { suggestion: no_mangle_attr.span },
|
||||
BuiltinNoMangleGeneric { suggestion: no_mangle_attr.span() },
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
|
@ -1226,7 +1226,7 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller {
|
|||
{
|
||||
cx.emit_span_lint(
|
||||
UNGATED_ASYNC_FN_TRACK_CALLER,
|
||||
attr.span,
|
||||
attr.span(),
|
||||
BuiltinUngatedAsyncFnTrackCaller { label: span, session: &cx.tcx.sess },
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) {
|
|||
}
|
||||
LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => {
|
||||
// We are an `eval_always` query, so looking at the attribute's `AttrId` is ok.
|
||||
let attr_id = tcx.hir().attrs(hir_id)[attr_index as usize].id;
|
||||
let attr_id = tcx.hir().attrs(hir_id)[attr_index as usize].id();
|
||||
|
||||
(attr_id, lint_index)
|
||||
}
|
||||
_ => panic!("fulfilled expectations must have a lint index"),
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ fn name_of_extern_decl(tcx: TyCtxt<'_>, fi: hir::OwnerId) -> SymbolName {
|
|||
// information, we could have codegen_fn_attrs also give span information back for
|
||||
// where the attribute was defined. However, until this is found to be a
|
||||
// bottleneck, this does just fine.
|
||||
(overridden_link_name, tcx.get_attr(fi, sym::link_name).unwrap().span)
|
||||
(overridden_link_name, tcx.get_attr(fi, sym::link_name).unwrap().span())
|
||||
})
|
||||
{
|
||||
SymbolName::Link(overridden_link_name, overridden_link_name_span)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
use rustc_abi::ExternAbi;
|
||||
use rustc_attr_parsing::{AttributeKind, AttributeParser, ReprAttr};
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{AttrArgs, AttrItem, AttrKind, GenericParamKind, PatExprKind, PatKind};
|
||||
use rustc_hir::{AttrArgs, AttrItem, Attribute, GenericParamKind, PatExprKind, PatKind};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::config::CrateType;
|
||||
use rustc_session::{declare_lint, declare_lint_pass};
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::{BytePos, Ident, Span, sym};
|
||||
use {rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir};
|
||||
use {rustc_ast as ast, rustc_hir as hir};
|
||||
|
||||
use crate::lints::{
|
||||
NonCamelCaseType, NonCamelCaseTypeSub, NonSnakeCaseDiag, NonSnakeCaseDiagSub,
|
||||
|
|
@ -161,10 +162,10 @@ impl NonCamelCaseTypes {
|
|||
|
||||
impl EarlyLintPass for NonCamelCaseTypes {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) {
|
||||
let has_repr_c = it
|
||||
.attrs
|
||||
.iter()
|
||||
.any(|attr| attr::find_repr_attrs(cx.sess(), attr).contains(&attr::ReprC));
|
||||
let has_repr_c = matches!(
|
||||
AttributeParser::parse_limited(cx.sess(), &it.attrs, sym::repr, it.span, true),
|
||||
Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.iter().any(|(r, _)| r == &ReprAttr::ReprC)
|
||||
);
|
||||
|
||||
if has_repr_c {
|
||||
return;
|
||||
|
|
@ -343,7 +344,7 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase {
|
|||
} else {
|
||||
ast::attr::find_by_name(cx.tcx.hir().attrs(hir::CRATE_HIR_ID), sym::crate_name)
|
||||
.and_then(|attr| {
|
||||
if let AttrKind::Normal(n) = &attr.kind
|
||||
if let Attribute::Unparsed(n) = attr
|
||||
&& let AttrItem { args: AttrArgs::Eq { eq_span: _, expr: lit }, .. } =
|
||||
n.as_ref()
|
||||
&& let ast::LitKind::Str(name, ..) = lit.kind
|
||||
|
|
|
|||
|
|
@ -251,19 +251,23 @@ impl Level {
|
|||
|
||||
/// Converts an `Attribute` to a level.
|
||||
pub fn from_attr(attr: &impl AttributeExt) -> Option<Self> {
|
||||
Self::from_symbol(attr.name_or_empty(), Some(attr.id()))
|
||||
Self::from_symbol(attr.name_or_empty(), || Some(attr.id()))
|
||||
}
|
||||
|
||||
/// Converts a `Symbol` to a level.
|
||||
pub fn from_symbol(s: Symbol, id: Option<AttrId>) -> Option<Self> {
|
||||
match (s, id) {
|
||||
(sym::allow, _) => Some(Level::Allow),
|
||||
(sym::expect, Some(attr_id)) => {
|
||||
Some(Level::Expect(LintExpectationId::Unstable { attr_id, lint_index: None }))
|
||||
pub fn from_symbol(s: Symbol, id: impl FnOnce() -> Option<AttrId>) -> Option<Self> {
|
||||
match s {
|
||||
sym::allow => Some(Level::Allow),
|
||||
sym::expect => {
|
||||
if let Some(attr_id) = id() {
|
||||
Some(Level::Expect(LintExpectationId::Unstable { attr_id, lint_index: None }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
(sym::warn, _) => Some(Level::Warn),
|
||||
(sym::deny, _) => Some(Level::Deny),
|
||||
(sym::forbid, _) => Some(Level::Forbid),
|
||||
sym::warn => Some(Level::Warn),
|
||||
sym::deny => Some(Level::Deny),
|
||||
sym::forbid => Some(Level::Forbid),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ mod diagnostics;
|
|||
mod extension;
|
||||
mod hash_stable;
|
||||
mod lift;
|
||||
mod print_attribute;
|
||||
mod query;
|
||||
mod serialize;
|
||||
mod symbols;
|
||||
|
|
@ -175,3 +176,11 @@ decl_derive! {
|
|||
/// The error type is `u32`.
|
||||
try_from::try_from_u32
|
||||
}
|
||||
decl_derive! {
|
||||
[PrintAttribute] =>
|
||||
/// Derives `PrintAttribute` for `AttributeKind`.
|
||||
/// This macro is pretty specific to `rustc_attr_data_structures` and likely not that useful in
|
||||
/// other places. It's deriving something close to `Debug` without printing some extraenous
|
||||
/// things like spans.
|
||||
print_attribute::print_attribute
|
||||
}
|
||||
|
|
|
|||
145
compiler/rustc_macros/src/print_attribute.rs
Normal file
145
compiler/rustc_macros/src/print_attribute.rs
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote, quote_spanned};
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{Data, Fields, Ident};
|
||||
use synstructure::Structure;
|
||||
|
||||
fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream, TokenStream) {
|
||||
let string_name = name.to_string();
|
||||
let mut disps = vec![quote! {let mut __printed_anything = false;}];
|
||||
|
||||
match fields {
|
||||
Fields::Named(fields_named) => {
|
||||
let mut field_names = Vec::new();
|
||||
|
||||
for field in &fields_named.named {
|
||||
let name = field.ident.as_ref().unwrap();
|
||||
let string_name = name.to_string();
|
||||
disps.push(quote! {
|
||||
if __printed_anything && #name.print_something() {
|
||||
__p.word_space(",");
|
||||
__printed_anything = true;
|
||||
}
|
||||
__p.word(#string_name);
|
||||
__p.word_space(":");
|
||||
#name.print_attribute(__p);
|
||||
});
|
||||
field_names.push(name);
|
||||
}
|
||||
|
||||
(
|
||||
quote! { {#(#field_names),*} },
|
||||
quote! {
|
||||
__p.word(#string_name);
|
||||
if true #(&& !#field_names.print_something())* {
|
||||
return;
|
||||
}
|
||||
|
||||
__p.word("{");
|
||||
#(#disps)*
|
||||
__p.word("}");
|
||||
},
|
||||
quote! { true },
|
||||
)
|
||||
}
|
||||
Fields::Unnamed(fields_unnamed) => {
|
||||
let mut field_names = Vec::new();
|
||||
|
||||
for idx in 0..fields_unnamed.unnamed.len() {
|
||||
let name = format_ident!("f{idx}");
|
||||
disps.push(quote! {
|
||||
if __printed_anything && #name.print_something() {
|
||||
__p.word_space(",");
|
||||
__printed_anything = true;
|
||||
}
|
||||
#name.print_attribute(__p);
|
||||
});
|
||||
field_names.push(name);
|
||||
}
|
||||
|
||||
(
|
||||
quote! { (#(#field_names),*) },
|
||||
quote! {
|
||||
__p.word(#string_name);
|
||||
|
||||
if true #(&& !#field_names.print_something())* {
|
||||
return;
|
||||
}
|
||||
|
||||
__p.word("(");
|
||||
#(#disps)*
|
||||
__p.word(")");
|
||||
},
|
||||
quote! { true },
|
||||
)
|
||||
}
|
||||
Fields::Unit => (quote! {}, quote! { __p.word(#string_name) }, quote! { true }),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print_attribute(input: Structure<'_>) -> TokenStream {
|
||||
let span_error = |span, message: &str| {
|
||||
quote_spanned! { span => const _: () = ::core::compile_error!(#message); }
|
||||
};
|
||||
|
||||
// Must be applied to an enum type.
|
||||
let (code, printed) = match &input.ast().data {
|
||||
Data::Enum(e) => {
|
||||
let (arms, printed) = e
|
||||
.variants
|
||||
.iter()
|
||||
.map(|x| {
|
||||
let ident = &x.ident;
|
||||
let (pat, code, printed) = print_fields(ident, &x.fields);
|
||||
|
||||
(
|
||||
quote! {
|
||||
Self::#ident #pat => {#code}
|
||||
},
|
||||
quote! {
|
||||
Self::#ident #pat => {#printed}
|
||||
},
|
||||
)
|
||||
})
|
||||
.unzip::<_, _, Vec<_>, Vec<_>>();
|
||||
|
||||
(
|
||||
quote! {
|
||||
match self {
|
||||
#(#arms)*
|
||||
}
|
||||
},
|
||||
quote! {
|
||||
match self {
|
||||
#(#printed)*
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
Data::Struct(s) => {
|
||||
let (pat, code, printed) = print_fields(&input.ast().ident, &s.fields);
|
||||
(
|
||||
quote! {
|
||||
let Self #pat = self;
|
||||
#code
|
||||
},
|
||||
quote! {
|
||||
let Self #pat = self;
|
||||
#printed
|
||||
},
|
||||
)
|
||||
}
|
||||
Data::Union(u) => {
|
||||
return span_error(u.union_token.span(), "can't derive PrintAttribute on unions");
|
||||
}
|
||||
};
|
||||
|
||||
#[allow(keyword_idents_2024)]
|
||||
input.gen_impl(quote! {
|
||||
#[allow(unused)]
|
||||
gen impl PrintAttribute for @Self {
|
||||
fn print_something(&self) -> bool { #printed }
|
||||
fn print_attribute(&self, __p: &mut rustc_ast_pretty::pp::Printer) { #code }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -450,7 +450,7 @@ impl<'tcx> Collector<'tcx> {
|
|||
(name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule));
|
||||
}
|
||||
let Some((name, name_span)) = name else {
|
||||
sess.dcx().emit_err(errors::LinkRequiresName { span: m.span });
|
||||
sess.dcx().emit_err(errors::LinkRequiresName { span: m.span() });
|
||||
continue;
|
||||
};
|
||||
|
||||
|
|
@ -485,7 +485,7 @@ impl<'tcx> Collector<'tcx> {
|
|||
let link_ordinal_attr =
|
||||
self.tcx.get_attr(child_item, sym::link_ordinal).unwrap();
|
||||
sess.dcx().emit_err(errors::LinkOrdinalRawDylib {
|
||||
span: link_ordinal_attr.span,
|
||||
span: link_ordinal_attr.span(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1071,7 +1071,6 @@ impl<'a> CrateMetadataRef<'a> {
|
|||
let attrs: Vec<_> = self.get_item_attrs(id, sess).collect();
|
||||
SyntaxExtension::new(
|
||||
sess,
|
||||
tcx.features(),
|
||||
kind,
|
||||
self.get_span(id, sess),
|
||||
helper_attrs,
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue