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