Skip to main content

rustc_builtin_macros/deriving/
reborrow.rs

1use rustc_ast::{self as ast, AttrArgs, 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
8use crate::deriving::generic::*;
9use crate::deriving::new_path;
10
11pub(crate) fn expand_deriving_reborrow(
12    cx: &ExtCtxt<'_>,
13    span: Span,
14    item: &ast::Item,
15    push: &mut dyn FnMut(Box<ast::Item>),
16    _is_const: bool,
17) {
18    let Some((ident, generics)) = struct_def(cx, span, item, sym::Reborrow) else {
19        return;
20    };
21
22    push_marker_impl(cx, span, ident, generics, sym::Reborrow, Vec::new(), push);
23}
24
25pub(crate) fn expand_deriving_coerce_shared(
26    cx: &ExtCtxt<'_>,
27    span: Span,
28    item: &ast::Item,
29    push: &mut dyn FnMut(Box<ast::Item>),
30    _is_const: bool,
31) {
32    let Some((ident, generics)) = struct_def(cx, span, item, sym::CoerceShared) else {
33        return;
34    };
35    let Some(target) = coerce_shared_target(cx, span, item) else {
36        return;
37    };
38
39    push_marker_impl(cx, span, ident, generics, sym::CoerceShared, ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [target]))vec![target], push);
40}
41
42fn struct_def<'a>(
43    cx: &ExtCtxt<'_>,
44    span: Span,
45    item: &'a ast::Item,
46    trait_name: Symbol,
47) -> Option<(Ident, &'a Generics)> {
48    match &item.kind {
49        ItemKind::Struct(ident, generics, _) => Some((*ident, generics)),
50        ItemKind::Enum(..) => {
51            cx.dcx().emit_err(UnsupportedItem { span, trait_name, kind: "enum" });
52            None
53        }
54        ItemKind::Union(..) => {
55            cx.dcx().emit_err(UnsupportedItem { span, trait_name, kind: "union" });
56            None
57        }
58        _ => {
59            cx.dcx().emit_err(UnsupportedItem { span, trait_name, kind: "item" });
60            None
61        }
62    }
63}
64
65fn coerce_shared_target(cx: &ExtCtxt<'_>, span: Span, item: &ast::Item) -> Option<Box<ast::Ty>> {
66    let mut attrs = item.attrs.iter().filter(|attr| attr.has_name(sym::coerce_shared));
67    let Some(attr) = attrs.next() else {
68        cx.dcx().emit_err(MissingTarget { span });
69        return None;
70    };
71    if let Some(duplicate) = attrs.next() {
72        cx.dcx().emit_err(DuplicateTarget { first: attr.span, duplicate: duplicate.span });
73        return None;
74    }
75
76    let AttrArgs::Delimited(args) = &attr.get_normal_item().args else {
77        cx.dcx().emit_err(MalformedTarget { span: attr.span });
78        return None;
79    };
80    if args.delim != token::Delimiter::Parenthesis || args.tokens.is_empty() {
81        cx.dcx().emit_err(MalformedTarget { span: attr.span });
82        return None;
83    }
84
85    let mut parser = cx.new_parser_from_tts(args.tokens.clone());
86    let target = match parser.parse_ty() {
87        Ok(target) => target,
88        Err(err) => {
89            err.cancel();
90            cx.dcx().emit_err(MalformedTarget { span: attr.span });
91            return None;
92        }
93    };
94    if parser.token != token::Eof {
95        cx.dcx().emit_err(MalformedTarget { span: attr.span });
96        return None;
97    }
98
99    Some(target)
100}
101
102fn push_marker_impl(
103    cx: &ExtCtxt<'_>,
104    span: Span,
105    ident: Ident,
106    generics: &Generics,
107    trait_name: Symbol,
108    trait_args: Vec<Box<ast::Ty>>,
109    push: &mut dyn FnMut(Box<ast::Item>),
110) {
111    let trait_path = new_path(cx, span, &[sym::core, sym::marker, trait_name], trait_args);
112    let trait_ref = cx.trait_ref(trait_path);
113
114    let self_params: Vec<_> =
115        generics.params.iter().map(|p| generic_param_to_arg(cx, p, p.span())).collect();
116    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));
117
118    push(cx.item_trait_impl(
119        span,
120        {
    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)],
121        generics_without_defaults(generics),
122        ast::Safety::Default,
123        false,
124        trait_ref,
125        self_ty,
126        ThinVec::new(),
127    ));
128}
129
130#[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)]
131#[diag("`derive({$trait_name})` is only supported for structs, not {$kind}s", code = E0802)]
132struct UnsupportedItem {
133    #[primary_span]
134    span: Span,
135    trait_name: Symbol,
136    kind: &'static str,
137}
138
139#[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)]
140#[diag("`derive(CoerceShared)` requires exactly one `#[coerce_shared(Target)]` attribute", code = E0802)]
141struct MissingTarget {
142    #[primary_span]
143    span: Span,
144}
145
146#[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)]
147#[diag("duplicate `#[coerce_shared(Target)]` attribute for `derive(CoerceShared)`", code = E0802)]
148struct DuplicateTarget {
149    #[primary_span]
150    duplicate: Span,
151    #[note("first `#[coerce_shared(Target)]` attribute is here")]
152    first: Span,
153}
154
155#[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)]
156#[diag("malformed `#[coerce_shared(Target)]` attribute for `derive(CoerceShared)`", code = E0802)]
157#[note("expected a single target type, for example `#[coerce_shared(Target<'a, T>)]`")]
158struct MalformedTarget {
159    #[primary_span]
160    span: Span,
161}