rustc_builtin_macros/deriving/
reborrow.rs1use rustc_ast::{self as ast, AttrArgs, GenericArg, GenericParamKind, Generics, ItemKind, token};
2use rustc_errors::E0802;
3use rustc_expand::base::ExtCtxt;
4use rustc_macros::Diagnostic;
5use rustc_span::{Ident, Span, Symbol, sym};
6use thin_vec::ThinVec;
7
8macro_rules! path {
9 ($span:expr, $($part:ident)::*) => { vec![$(Ident::new(sym::$part, $span),)*] }
10}
11
12pub(crate) fn expand_deriving_reborrow(
13 cx: &ExtCtxt<'_>,
14 span: Span,
15 item: &ast::Item,
16 push: &mut dyn FnMut(Box<ast::Item>),
17 _is_const: bool,
18) {
19 let Some((ident, generics)) = struct_def(cx, span, item, sym::Reborrow) else {
20 return;
21 };
22
23 push_marker_impl(cx, span, ident, generics, sym::Reborrow, Vec::new(), push);
24}
25
26pub(crate) fn expand_deriving_coerce_shared(
27 cx: &ExtCtxt<'_>,
28 span: Span,
29 item: &ast::Item,
30 push: &mut dyn FnMut(Box<ast::Item>),
31 _is_const: bool,
32) {
33 let Some((ident, generics)) = struct_def(cx, span, item, sym::CoerceShared) else {
34 return;
35 };
36 let Some(target) = coerce_shared_target(cx, span, item) else {
37 return;
38 };
39
40 push_marker_impl(
41 cx,
42 span,
43 ident,
44 generics,
45 sym::CoerceShared,
46 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[GenericArg::Type(target)]))vec![GenericArg::Type(target)],
47 push,
48 );
49}
50
51fn struct_def<'a>(
52 cx: &ExtCtxt<'_>,
53 span: Span,
54 item: &'a ast::Item,
55 trait_name: Symbol,
56) -> Option<(Ident, &'a Generics)> {
57 match &item.kind {
58 ItemKind::Struct(ident, generics, _) => Some((*ident, generics)),
59 ItemKind::Enum(..) => {
60 cx.dcx().emit_err(UnsupportedItem { span, trait_name, kind: "enum" });
61 None
62 }
63 ItemKind::Union(..) => {
64 cx.dcx().emit_err(UnsupportedItem { span, trait_name, kind: "union" });
65 None
66 }
67 _ => {
68 cx.dcx().emit_err(UnsupportedItem { span, trait_name, kind: "item" });
69 None
70 }
71 }
72}
73
74fn coerce_shared_target(cx: &ExtCtxt<'_>, span: Span, item: &ast::Item) -> Option<Box<ast::Ty>> {
75 let mut attrs = item.attrs.iter().filter(|attr| attr.has_name(sym::coerce_shared));
76 let Some(attr) = attrs.next() else {
77 cx.dcx().emit_err(MissingTarget { span });
78 return None;
79 };
80 if let Some(duplicate) = attrs.next() {
81 cx.dcx().emit_err(DuplicateTarget { first: attr.span, duplicate: duplicate.span });
82 return None;
83 }
84
85 let AttrArgs::Delimited(args) = &attr.get_normal_item().args else {
86 cx.dcx().emit_err(MalformedTarget { span: attr.span });
87 return None;
88 };
89 if args.delim != token::Delimiter::Parenthesis || args.tokens.is_empty() {
90 cx.dcx().emit_err(MalformedTarget { span: attr.span });
91 return None;
92 }
93
94 let mut parser = cx.new_parser_from_tts(args.tokens.clone());
95 let target = match parser.parse_ty() {
96 Ok(target) => target,
97 Err(err) => {
98 err.cancel();
99 cx.dcx().emit_err(MalformedTarget { span: attr.span });
100 return None;
101 }
102 };
103 if parser.token != token::Eof {
104 cx.dcx().emit_err(MalformedTarget { span: attr.span });
105 return None;
106 }
107
108 Some(target)
109}
110
111fn push_marker_impl(
112 cx: &ExtCtxt<'_>,
113 span: Span,
114 ident: Ident,
115 generics: &Generics,
116 trait_name: Symbol,
117 trait_args: Vec<GenericArg>,
118 push: &mut dyn FnMut(Box<ast::Item>),
119) {
120 let mut trait_parts = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[Ident::new(sym::core, span), Ident::new(sym::marker, span)]))path!(span, core::marker);
121 trait_parts.push(Ident::new(trait_name, span));
122 let trait_path = cx.path_all(span, true, trait_parts, trait_args);
123 let trait_ref = cx.trait_ref(trait_path);
124
125 let self_params: Vec<_> = generics
126 .params
127 .iter()
128 .map(|param| match param.kind {
129 GenericParamKind::Lifetime => {
130 GenericArg::Lifetime(cx.lifetime(param.span(), param.ident))
131 }
132 GenericParamKind::Type { .. } => {
133 GenericArg::Type(cx.ty_ident(param.span(), param.ident))
134 }
135 GenericParamKind::Const { .. } => {
136 GenericArg::Const(cx.const_ident(param.span(), param.ident))
137 }
138 })
139 .collect();
140 let self_ty = cx.ty_path(cx.path_all(span, false, ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[ident]))vec![ident], self_params));
141
142 push(cx.item(
143 span,
144 {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(cx.attr_word(sym::automatically_derived, span));
vec
}thin_vec::thin_vec![cx.attr_word(sym::automatically_derived, span)],
145 ast::ItemKind::Impl(ast::Impl {
146 generics: impl_generics(cx, generics),
147 of_trait: Some(Box::new(ast::TraitImplHeader {
148 safety: ast::Safety::Default,
149 polarity: ast::ImplPolarity::Positive,
150 defaultness: ast::Defaultness::Implicit,
151 trait_ref,
152 })),
153 constness: ast::Const::No,
154 self_ty,
155 items: ThinVec::new(),
156 }),
157 ));
158}
159
160fn impl_generics(cx: &ExtCtxt<'_>, generics: &Generics) -> Generics {
161 Generics {
165 params: generics
166 .params
167 .iter()
168 .map(|param| match ¶m.kind {
169 GenericParamKind::Lifetime => {
170 cx.lifetime_param(param.span(), param.ident, param.bounds.clone())
171 }
172 GenericParamKind::Type { default: _ } => {
173 cx.typaram(param.span(), param.ident, param.bounds.clone(), None)
174 }
175 GenericParamKind::Const { ty, span: _, default: _ } => cx.const_param(
176 param.span(),
177 param.ident,
178 param.bounds.clone(),
179 ty.clone(),
180 None,
181 ),
182 })
183 .collect(),
184 where_clause: generics.where_clause.clone(),
185 span: generics.span,
186 }
187}
188
189#[derive(const _: () =
{
impl<'_sess> rustc_errors::Diagnostic<'_sess> for UnsupportedItem {
#[track_caller]
fn into_diag(self, dcx: rustc_errors::DiagCtxtHandle<'_sess>,
level: rustc_errors::Level) -> rustc_errors::Diag<'_sess> {
match self {
UnsupportedItem {
span: __binding_0,
trait_name: __binding_1,
kind: __binding_2 } => {
let mut diag =
rustc_errors::Diag::new(dcx, level,
rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("`derive({$trait_name})` is only supported for structs, not {$kind}s")));
diag.code(E0802);
;
diag.arg("trait_name", __binding_1);
diag.arg("kind", __binding_2);
diag.span(__binding_0);
diag
}
}
}
}
};Diagnostic)]
190#[diag("`derive({$trait_name})` is only supported for structs, not {$kind}s", code = E0802)]
191struct UnsupportedItem {
192 #[primary_span]
193 span: Span,
194 trait_name: Symbol,
195 kind: &'static str,
196}
197
198#[derive(const _: () =
{
impl<'_sess> rustc_errors::Diagnostic<'_sess> for MissingTarget {
#[track_caller]
fn into_diag(self, dcx: rustc_errors::DiagCtxtHandle<'_sess>,
level: rustc_errors::Level) -> rustc_errors::Diag<'_sess> {
match self {
MissingTarget { span: __binding_0 } => {
let mut diag =
rustc_errors::Diag::new(dcx, level,
rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("`derive(CoerceShared)` requires exactly one `#[coerce_shared(Target)]` attribute")));
diag.code(E0802);
;
diag.span(__binding_0);
diag
}
}
}
}
};Diagnostic)]
199#[diag("`derive(CoerceShared)` requires exactly one `#[coerce_shared(Target)]` attribute", code = E0802)]
200struct MissingTarget {
201 #[primary_span]
202 span: Span,
203}
204
205#[derive(const _: () =
{
impl<'_sess> rustc_errors::Diagnostic<'_sess> for DuplicateTarget {
#[track_caller]
fn into_diag(self, dcx: rustc_errors::DiagCtxtHandle<'_sess>,
level: rustc_errors::Level) -> rustc_errors::Diag<'_sess> {
match self {
DuplicateTarget { duplicate: __binding_0, first: __binding_1
} => {
let mut diag =
rustc_errors::Diag::new(dcx, level,
rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("duplicate `#[coerce_shared(Target)]` attribute for `derive(CoerceShared)`")));
diag.code(E0802);
;
diag.span(__binding_0);
diag.span_note(__binding_1,
rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("first `#[coerce_shared(Target)]` attribute is here")));
diag
}
}
}
}
};Diagnostic)]
206#[diag("duplicate `#[coerce_shared(Target)]` attribute for `derive(CoerceShared)`", code = E0802)]
207struct DuplicateTarget {
208 #[primary_span]
209 duplicate: Span,
210 #[note("first `#[coerce_shared(Target)]` attribute is here")]
211 first: Span,
212}
213
214#[derive(const _: () =
{
impl<'_sess> rustc_errors::Diagnostic<'_sess> for MalformedTarget {
#[track_caller]
fn into_diag(self, dcx: rustc_errors::DiagCtxtHandle<'_sess>,
level: rustc_errors::Level) -> rustc_errors::Diag<'_sess> {
match self {
MalformedTarget { span: __binding_0 } => {
let mut diag =
rustc_errors::Diag::new(dcx, level,
rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("malformed `#[coerce_shared(Target)]` attribute for `derive(CoerceShared)`")));
diag.code(E0802);
diag.note(rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("expected a single target type, for example `#[coerce_shared(Target<'a, T>)]`")));
;
diag.span(__binding_0);
diag
}
}
}
}
};Diagnostic)]
215#[diag("malformed `#[coerce_shared(Target)]` attribute for `derive(CoerceShared)`", code = E0802)]
216#[note("expected a single target type, for example `#[coerce_shared(Target<'a, T>)]`")]
217struct MalformedTarget {
218 #[primary_span]
219 span: Span,
220}