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
#![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,
}
}