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
105
106
107
108
109
110
111
112
//! Codegen for `mockiato`. Do not use this crate directly.

#![recursion_limit = "128"]
#![feature(
    proc_macro_diagnostic,
    proc_macro_span,
    proc_macro_hygiene,
    bind_by_move_pattern_guards,
    decl_macro,
    box_syntax,
    box_patterns
)]
#![warn(clippy::dbg_macro, clippy::unimplemented)]
#![deny(
    rust_2018_idioms,
    future_incompatible,
    missing_debug_implementations,
    clippy::doc_markdown,
    clippy::default_trait_access,
    clippy::enum_glob_use,
    clippy::needless_borrow,
    clippy::large_digit_groups,
    clippy::explicit_into_iter_loop
)]

extern crate proc_macro;

mod constant;
mod diagnostic;
mod generate;
mod mockable;
mod parse;
mod result;
mod syn_ext;

use self::mockable::Mockable;
use crate::diagnostic::{Diagnostic, DiagnosticLevel, DiagnosticMessage};
use crate::result::Error;
use proc_macro::{
    Diagnostic as ProcMacroDiagnostic, Level as ProcMacroLevel, Span as ProcMacroSpan,
    TokenStream as ProcMacroTokenStream,
};
use proc_macro2::Span;
use syn::{parse_macro_input, AttributeArgs, Item};

#[doc(hidden)]
#[proc_macro_attribute]
pub fn mockable(args: ProcMacroTokenStream, input: ProcMacroTokenStream) -> ProcMacroTokenStream {
    let original_input = input.clone();

    let attr = parse_macro_input!(args as AttributeArgs);
    let item = parse_macro_input!(input as Item);

    let mockable = Mockable::new();

    match mockable.expand(attr, item) {
        Ok(output) => ProcMacroTokenStream::from(output),
        Err(error) => {
            emit_diagnostics(error);
            original_input
        }
    }
}

fn emit_diagnostics(error: Error) {
    error
        .diagnostics
        .into_iter()
        .map(to_proc_macro_diagnostic)
        .for_each(ProcMacroDiagnostic::emit);
}

fn to_proc_macro_diagnostic(source: Diagnostic) -> ProcMacroDiagnostic {
    let level = to_proc_macro_level(source.level);
    let span = to_proc_macro_span(source.span);
    let diagnostic = ProcMacroDiagnostic::spanned(span, level, source.message);
    let diagnostic = add_notes_to_proc_macro_diagnostic(diagnostic, source.notes);
    add_help_to_proc_macro_diagnostic(diagnostic, source.help)
}

fn add_help_to_proc_macro_diagnostic(
    diagnostic: ProcMacroDiagnostic,
    help: Vec<DiagnosticMessage>,
) -> ProcMacroDiagnostic {
    help.into_iter()
        .fold(diagnostic, |diagnostic, help| match help.span {
            Some(span) => diagnostic.span_help(to_proc_macro_span(span), help.message),
            None => diagnostic.help(help.message),
        })
}

fn add_notes_to_proc_macro_diagnostic(
    diagnostic: ProcMacroDiagnostic,
    notes: Vec<DiagnosticMessage>,
) -> ProcMacroDiagnostic {
    notes
        .into_iter()
        .fold(diagnostic, |diagnostic, note| match note.span {
            Some(span) => diagnostic.span_note(to_proc_macro_span(span), note.message),
            None => diagnostic.note(note.message),
        })
}

fn to_proc_macro_span(span: Span) -> ProcMacroSpan {
    span.unstable()
}

fn to_proc_macro_level(level: DiagnosticLevel) -> ProcMacroLevel {
    match level {
        DiagnosticLevel::Error => ProcMacroLevel::Error,
    }
}