Skip to main content

rustc_builtin_macros/deriving/
reborrow.rs

1use 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    // Rebuild the generic parameter declarations because defaults are allowed on structs but
162    // rejected on impls. Preserve lifetime, type, and const parameters and their bounds, const
163    // parameter types, and the where-clause, while omitting type and const defaults.
164    Generics {
165        params: generics
166            .params
167            .iter()
168            .map(|param| match &param.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}