1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
//! This crate contains the implementation for attributes used in the `pre` crate.
//!
//! Refer to the documentation of the `pre` crate for more information.
//!
//! This crate is not designed to be used as a standalone crate and might not work, when used
//! without the `pre` crate.

#![forbid(unsafe_code)]

use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use proc_macro_error::{abort_call_site, proc_macro_error};
use quote::quote;
use syn::{parse_macro_input, visit_mut::VisitMut, File};

use crate::pre_attr::PreAttrVisitor;

mod call;
mod call_handling;
mod documentation;
mod extern_crate;
mod helpers;
mod pre_attr;
mod precondition;

cfg_if::cfg_if! {
    if #[cfg(nightly)] {
        mod const_generics_impl;
        pub(crate) use crate::const_generics_impl::{render_assure, render_pre};
    } else {
        mod struct_impl;
        pub(crate) use crate::struct_impl::{render_assure, render_pre};
    }
}

#[proc_macro_attribute]
#[proc_macro_error]
pub fn pre(attr: TokenStream, file: TokenStream) -> TokenStream {
    let dummy_file: TokenStream2 = file.clone().into();
    proc_macro_error::set_dummy(quote! {
        #dummy_file
    });

    let mut file = parse_macro_input!(file as File);

    PreAttrVisitor::new(attr.into()).visit_file_mut(&mut file);

    let output = quote! {
        #file
    };

    // Reset the dummy here, in case errors were emitted while generating the code.
    // This will use the most up-to-date version of the generated code.
    proc_macro_error::set_dummy(quote! {
        #output
    });

    output.into()
}

#[proc_macro_attribute]
#[proc_macro_error]
pub fn assure(_: TokenStream, _: TokenStream) -> TokenStream {
    // This macro currently only has two purposes:
    // - Exist as a place to put documentation for the actual `assure` attribute, which is
    // implemented inside the `pre` attribute.
    // - Emit an error with a more helpful message than "attribute not found", if the user uses
    // `assure` in the wrong place.
    abort_call_site!(
        "this attribute by itself is currently non-functional";
        help = "use it on an expression in an item wrapped by a `pre` attribute"
    )
}

#[proc_macro_attribute]
#[proc_macro_error]
pub fn forward(_: TokenStream, _: TokenStream) -> TokenStream {
    // This macro currently only has two purposes:
    // - Exist as a place to put documentation for the actual `forward` attribute, which is
    // implemented inside the `pre` attribute.
    // - Emit an error with a more helpful message than "attribute not found", if the user uses
    // `forward` in the wrong place.
    abort_call_site!(
        "this attribute by itself is currently non-functional";
        help = "use it on an expression in an item wrapped by a `pre` attribute"
    )
}

#[proc_macro_attribute]
#[proc_macro_error]
pub fn extern_crate(attr: TokenStream, module: TokenStream) -> TokenStream {
    let attr = parse_macro_input!(attr as extern_crate::ExternCrateAttr);
    let module = parse_macro_input!(module as extern_crate::Module);

    let output = module.render(attr);

    // Reset the dummy here, in case errors were emitted while generating the code.
    // This will use the most up-to-date version of the generated code.
    proc_macro_error::set_dummy(quote! {
        #output
    });

    output.into()
}