linkme-impl 0.3.7

Implementation detail of the linkme crate
Documentation
use crate::{attr, linker};
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::parse::{Parse, ParseStream, Result};
use syn::{
    bracketed, Attribute, Error, GenericArgument, Ident, Lifetime, PathArguments, Token, Type,
    Visibility,
};

struct Declaration {
    attrs: Vec<Attribute>,
    vis: Visibility,
    ident: Ident,
    ty: Type,
}

impl Parse for Declaration {
    fn parse(input: ParseStream) -> Result<Self> {
        let attrs = input.call(Attribute::parse_outer)?;
        let vis: Visibility = input.parse()?;
        input.parse::<Token![static]>()?;
        let mut_token: Option<Token![mut]> = input.parse()?;
        if let Some(mut_token) = mut_token {
            return Err(Error::new_spanned(
                mut_token,
                "static mut is not supported by distributed_slice",
            ));
        }
        let ident: Ident = input.parse()?;
        input.parse::<Token![:]>()?;
        let ty: Type = input.parse()?;
        input.parse::<Token![=]>()?;

        let content;
        bracketed!(content in input);
        content.parse::<Token![..]>()?;

        input.parse::<Token![;]>()?;

        Ok(Declaration {
            attrs,
            vis,
            ident,
            ty,
        })
    }
}

pub fn expand(input: TokenStream) -> TokenStream {
    let msg = "distributed_slice is not implemented for this platform";
    let error = Error::new_spanned(&input, msg);
    let unsupported_platform = error.to_compile_error();

    let decl: Declaration = match syn::parse2(input) {
        Ok(decl) => decl,
        Err(err) => return err.to_compile_error(),
    };

    let mut attrs = decl.attrs;
    let vis = decl.vis;
    let ident = decl.ident;
    let mut ty = decl.ty;
    let name = ident.to_string();

    let linkme_path = match attr::linkme_path(&mut attrs) {
        Ok(path) => path,
        Err(err) => return err.to_compile_error(),
    };

    populate_static_lifetimes(&mut ty);

    let used = if cfg!(feature = "used_linker") {
        quote!(#[used(linker)])
    } else {
        quote!(#[used])
    };

    let linux_section = linker::linux::section(&ident);
    let linux_section_start = linker::linux::section_start(&ident);
    let linux_section_stop = linker::linux::section_stop(&ident);
    let linux_dupcheck = linux_section.replacen("linkme", "linkm2", 1);
    let linux_dupcheck_start = linux_section_start.replacen("linkme", "linkm2", 1);
    let linux_dupcheck_stop = linux_section_stop.replacen("linkme", "linkm2", 1);

    let macho_section = linker::macho::section(&ident);
    let macho_section_start = linker::macho::section_start(&ident);
    let macho_section_stop = linker::macho::section_stop(&ident);
    let macho_dupcheck = macho_section.replacen("linkme", "linkm2", 1);
    let macho_dupcheck_start = macho_section_start.replacen("linkme", "linkm2", 1);
    let macho_dupcheck_stop = macho_section_stop.replacen("linkme", "linkm2", 1);

    let windows_section = linker::windows::section(&ident);
    let windows_section_start = linker::windows::section_start(&ident);
    let windows_section_stop = linker::windows::section_stop(&ident);
    let windows_dupcheck = windows_section.replacen("linkme", "linkm2", 1);
    let windows_dupcheck_start = windows_section_start.replacen("linkme", "linkm2", 1);
    let windows_dupcheck_stop = windows_section_stop.replacen("linkme", "linkm2", 1);

    let illumos_section = linker::illumos::section(&ident);
    let illumos_section_start = linker::illumos::section_start(&ident);
    let illumos_section_stop = linker::illumos::section_stop(&ident);
    let illumos_dupcheck = illumos_section.replacen("linkme", "linkm2", 1);
    let illumos_dupcheck_start = illumos_section_start.replacen("linkme", "linkm2", 1);
    let illumos_dupcheck_stop = illumos_section_stop.replacen("linkme", "linkm2", 1);

    let freebsd_section = linker::freebsd::section(&ident);
    let freebsd_section_start = linker::freebsd::section_start(&ident);
    let freebsd_section_stop = linker::freebsd::section_stop(&ident);
    let freebsd_dupcheck = freebsd_section.replacen("linkme", "linkm2", 1);
    let freebsd_dupcheck_start = freebsd_section_start.replacen("linkme", "linkm2", 1);
    let freebsd_dupcheck_stop = freebsd_section_stop.replacen("linkme", "linkm2", 1);

    let call_site = Span::call_site();
    let link_section_macro_str = format!("_linkme_macro_{}", ident);
    let link_section_macro = Ident::new(&link_section_macro_str, call_site);

    quote! {
        #(#attrs)*
        #vis static #ident: #linkme_path::DistributedSlice<#ty> = {
            #[cfg(any(
                target_os = "none",
                target_os = "linux",
                target_os = "macos",
                target_os = "ios",
                target_os = "tvos",
                target_os = "illumos",
                target_os = "freebsd",
            ))]
            extern "Rust" {
                #[cfg_attr(any(target_os = "none", target_os = "linux"), link_name = #linux_section_start)]
                #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_name = #macho_section_start)]
                #[cfg_attr(target_os = "illumos", link_name = #illumos_section_start)]
                #[cfg_attr(target_os = "freebsd", link_name = #freebsd_section_start)]
                static LINKME_START: <#ty as #linkme_path::__private::Slice>::Element;

                #[cfg_attr(any(target_os = "none", target_os = "linux"), link_name = #linux_section_stop)]
                #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_name = #macho_section_stop)]
                #[cfg_attr(target_os = "illumos", link_name = #illumos_section_stop)]
                #[cfg_attr(target_os = "freebsd", link_name = #freebsd_section_stop)]
                static LINKME_STOP: <#ty as #linkme_path::__private::Slice>::Element;

                #[cfg_attr(any(target_os = "none", target_os = "linux"), link_name = #linux_dupcheck_start)]
                #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_name = #macho_dupcheck_start)]
                #[cfg_attr(target_os = "illumos", link_name = #illumos_dupcheck_start)]
                #[cfg_attr(target_os = "freebsd", link_name = #freebsd_dupcheck_start)]
                static DUPCHECK_START: #linkme_path::__private::usize;

                #[cfg_attr(any(target_os = "none", target_os = "linux"), link_name = #linux_dupcheck_stop)]
                #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_name = #macho_dupcheck_stop)]
                #[cfg_attr(target_os = "illumos", link_name = #illumos_dupcheck_stop)]
                #[cfg_attr(target_os = "freebsd", link_name = #freebsd_dupcheck_stop)]
                static DUPCHECK_STOP: #linkme_path::__private::usize;
            }

            #[cfg(target_os = "windows")]
            #[link_section = #windows_section_start]
            static LINKME_START: [<#ty as #linkme_path::__private::Slice>::Element; 0] = [];

            #[cfg(target_os = "windows")]
            #[link_section = #windows_section_stop]
            static LINKME_STOP: [<#ty as #linkme_path::__private::Slice>::Element; 0] = [];

            #[cfg(target_os = "windows")]
            #[link_section = #windows_dupcheck_start]
            static DUPCHECK_START: () = ();

            #[cfg(target_os = "windows")]
            #[link_section = #windows_dupcheck_stop]
            static DUPCHECK_STOP: () = ();

            #used
            #[cfg(any(target_os = "none", target_os = "linux", target_os = "illumos", target_os = "freebsd"))]
            #[cfg_attr(any(target_os = "none", target_os = "linux"), link_section = #linux_section)]
            #[cfg_attr(target_os = "illumos", link_section = #illumos_section)]
            #[cfg_attr(target_os = "freebsd", link_section = #freebsd_section)]
            static mut LINKME_PLEASE: [<#ty as #linkme_path::__private::Slice>::Element; 0] = [];

            #used
            #[cfg_attr(any(target_os = "none", target_os = "linux"), link_section = #linux_dupcheck)]
            #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_section = #macho_dupcheck)]
            #[cfg_attr(target_os = "windows", link_section = #windows_dupcheck)]
            #[cfg_attr(target_os = "illumos", link_section = #illumos_dupcheck)]
            #[cfg_attr(target_os = "freebsd", link_section = #freebsd_dupcheck)]
            static DUPCHECK: #linkme_path::__private::usize = 1;

            #[cfg(not(any(
                target_os = "none",
                target_os = "linux",
                target_os = "macos",
                target_os = "ios",
                target_os = "tvos",
                target_os = "windows",
                target_os = "illumos",
                target_os = "freebsd",
            )))]
            #unsupported_platform

            #linkme_path::__private::assert!(
                #linkme_path::__private::mem::size_of::<<#ty as #linkme_path::__private::Slice>::Element>() > 0,
            );

            unsafe {
                #linkme_path::DistributedSlice::private_new(
                    #name,
                    &LINKME_START,
                    &LINKME_STOP,
                    &DUPCHECK_START,
                    &DUPCHECK_STOP,
                )
            }
        };

        #[doc(hidden)]
        #[macro_export]
        macro_rules! #link_section_macro {
            (
                #![linkme_macro = $macro:path]
                #![linkme_sort_key = $key:tt]
                $item:item
            ) => {
                $macro ! {
                    #![linkme_linux_section = concat!(#linux_section, $key)]
                    #![linkme_macho_section = concat!(#macho_section, $key)]
                    #![linkme_windows_section = concat!(#windows_section, $key)]
                    #![linkme_illumos_section = concat!(#illumos_section, $key)]
                    #![linkme_freebsd_section = concat!(#freebsd_section, $key)]
                    $item
                }
            };
            (
                #![linkme_linux_section = $linux_section:expr]
                #![linkme_macho_section = $macho_section:expr]
                #![linkme_windows_section = $windows_section:expr]
                #![linkme_illumos_section = $illumos_section:expr]
                #![linkme_freebsd_section = $freebsd_section:expr]
                $item:item
            ) => {
                #used
                #[cfg_attr(any(target_os = "none", target_os = "linux"), link_section = $linux_section)]
                #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_section = $macho_section)]
                #[cfg_attr(target_os = "windows", link_section = $windows_section)]
                #[cfg_attr(target_os = "illumos", link_section = $illumos_section)]
                #[cfg_attr(target_os = "freebsd", link_section = $freebsd_section)]
                $item
            };
            ($item:item) => {
                #used
                #[cfg_attr(any(target_os = "none", target_os = "linux"), link_section = #linux_section)]
                #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_section = #macho_section)]
                #[cfg_attr(target_os = "windows", link_section = #windows_section)]
                #[cfg_attr(target_os = "illumos", link_section = #illumos_section)]
                #[cfg_attr(target_os = "freebsd", link_section = #freebsd_section)]
                $item
            };
        }

        #[doc(hidden)]
        #vis use #link_section_macro as #ident;
    }
}

fn populate_static_lifetimes(ty: &mut Type) {
    match ty {
        Type::Array(ty) => populate_static_lifetimes(&mut ty.elem),
        Type::Group(ty) => populate_static_lifetimes(&mut ty.elem),
        Type::Paren(ty) => populate_static_lifetimes(&mut ty.elem),
        Type::Path(ty) => {
            if let Some(qself) = &mut ty.qself {
                populate_static_lifetimes(&mut qself.ty);
            }
            for segment in &mut ty.path.segments {
                if let PathArguments::AngleBracketed(segment) = &mut segment.arguments {
                    for arg in &mut segment.args {
                        if let GenericArgument::Type(arg) = arg {
                            populate_static_lifetimes(arg);
                        }
                    }
                }
            }
        }
        Type::Ptr(ty) => populate_static_lifetimes(&mut ty.elem),
        Type::Reference(ty) => {
            if ty.lifetime.is_none() {
                ty.lifetime = Some(Lifetime::new("'static", ty.and_token.span));
            }
            populate_static_lifetimes(&mut ty.elem);
        }
        Type::Slice(ty) => populate_static_lifetimes(&mut ty.elem),
        Type::Tuple(ty) => ty.elems.iter_mut().for_each(populate_static_lifetimes),
        Type::ImplTrait(_)
        | Type::Infer(_)
        | Type::Macro(_)
        | Type::Never(_)
        | Type::TraitObject(_)
        | Type::BareFn(_)
        | Type::Verbatim(_) => {}
        #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
        _ => unimplemented!("unknown Type"),
    }
}