tulip_derivative/
lib.rs

1// We need to support Rust 1.34 to stable
2#![allow(deprecated)]
3#![allow(renamed_and_removed_lints)] // support for multiple Clippy versions
4#![allow(clippy::unknown_clippy_lints)] // because of other #![allow]s
5#![allow(clippy::mem_replace_with_default)] // needs rustc 1.40
6#![allow(clippy::option_as_ref_deref)] // needs rustc 1.40
7#![allow(clippy::cyclomatic_complexity)] // old name of cognitive_complexity
8#![allow(clippy::cognitive_complexity)] // in code generated by macros
9#![allow(clippy::redundant_closure)]
10
11extern crate proc_macro;
12extern crate proc_macro2;
13#[macro_use]
14extern crate syn;
15
16#[macro_use]
17extern crate quote;
18
19mod ast;
20mod attr;
21mod bound;
22mod clone;
23mod cmp;
24mod debug;
25mod default;
26mod hash;
27mod matcher;
28mod paths;
29mod utils;
30
31use proc_macro::TokenStream;
32
33fn derive_impls(
34    input: &mut ast::Input,
35    errors: &mut proc_macro2::TokenStream,
36) -> proc_macro2::TokenStream {
37    let mut tokens = proc_macro2::TokenStream::new();
38
39    if input.attrs.clone.is_some() {
40        tokens.extend(clone::derive_clone(input));
41    }
42    if input.attrs.copy.is_some() {
43        tokens.extend(clone::derive_copy(input));
44    }
45    if input.attrs.debug.is_some() {
46        tokens.extend(debug::derive(input));
47    }
48    if let Some(ref default) = input.attrs.default {
49        tokens.extend(default::derive(input, default));
50    }
51    if input.attrs.eq.is_some() {
52        tokens.extend(cmp::derive_eq(input));
53    }
54    if input.attrs.hash.is_some() {
55        tokens.extend(hash::derive(input));
56    }
57    if input.attrs.partial_eq.is_some() {
58        tokens.extend(cmp::derive_partial_eq(input));
59    }
60    if input.attrs.partial_ord.is_some() {
61        tokens.extend(cmp::derive_partial_ord(input, errors));
62    }
63    if input.attrs.ord.is_some() {
64        tokens.extend(cmp::derive_ord(input, errors));
65    }
66
67    tokens.extend(std::mem::replace(errors, Default::default()));
68
69    tokens
70}
71
72#[cfg(not(tarpaulin_include))]
73#[cfg_attr(not(test), proc_macro_derive(Derivative, attributes(derivative)))]
74pub fn derivative(input: TokenStream) -> TokenStream {
75    derivative_internal(syn::parse_macro_input!(input as syn::DeriveInput)).into()
76}
77
78fn derivative_internal(input: syn::DeriveInput) -> proc_macro2::TokenStream {
79    let mut errors = proc_macro2::TokenStream::new();
80
81    let mut output = ast::Input::from_ast(&input, &mut errors)
82        .map(|mut input| derive_impls(&mut input, &mut errors))
83        .unwrap_or_default();
84
85    output.extend(errors);
86    output
87}
88
89#[test]
90fn macro_code_coverage() {
91    for entry in walkdir::WalkDir::new("tests")
92        .into_iter()
93        .map(|e| e.unwrap())
94        .filter(|e| e.file_type().is_file())
95        .filter(|e| e.path().extension().map(|e| e == "rs").unwrap_or(false))
96    {
97        let file = std::fs::File::open(entry.path()).unwrap();
98
99        runtime_macros_derive::emulate_derive_expansion_fallible(
100            file,
101            "Derivative",
102            derivative_internal,
103        )
104        .unwrap();
105    }
106}