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