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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#![cfg_attr(rustc_is_unstable, feature(proc_macro_diagnostic, proc_macro_span))]
use {
proc_macro::{Span, TokenStream},
quote::{quote, quote_spanned, ToTokens},
syn::{fold::Fold, parse_quote_spanned, spanned::Spanned, visit::Visit},
};
macro_rules! if_unstable {
{ then { $($then:tt)* } else { $($else:tt)* } } => {
#[allow(unreachable_code)]
if cfg!(rustc_is_unstable) {
#[cfg(not(rustc_is_unstable))] {
unreachable!();
}
#[cfg(rustc_is_unstable)] {
$($then)*
}
} else {
#[cfg(rustc_is_unstable)] {
unreachable!();
}
$($else)*
}
}
}
#[proc_macro_attribute]
pub fn turn_off_the_borrow_checker(_attribute: TokenStream, input: TokenStream) -> TokenStream {
let mut suppressor = BorrowCheckerSuppressor {
suppressed_references: vec![],
};
let output = if let Ok(as_file) = syn::parse(input.clone()) {
suppressor.fold_file(as_file).to_token_stream()
} else if let Ok(as_expr) = syn::parse(input.clone()) {
suppressor.fold_expr(as_expr).to_token_stream()
} else if let Ok(as_stmt) = syn::parse(input) {
suppressor.fold_stmt(as_stmt).to_token_stream()
} else {
return quote! { compile_error!("unsupported use of #[turn_off_the_borrow_checker]") }
.into();
};
if_unstable! {
then {
proc_macro::Diagnostic::spanned(
vec![Span::call_site().parent().unwrap_or_else(Span::call_site)],
proc_macro::Level::Warning,
"this suppresses the borrow checker in an unsafe, unsound, and unstable way \
that produces undefined behaviour. this is not suitable for any purpose beyond \
educational experimentation.",
).emit();
if suppressor.suppressed_references.len() > 1 {
proc_macro::Diagnostic::spanned(
suppressor.suppressed_references,
proc_macro::Level::Warning,
"the borrow checker is suppressed for these references.",
).emit();
}
output.into_token_stream().into()
} else {
static DANGER: std::sync::Once = std::sync::Once::new();
DANGER.call_once(|| {
eprintln!();
eprintln!(" DANGER This project is using the the #[you_can::turn_off_the_borrow_checker]");
eprintln!(" DANGER macro, which is inherently unsafe, unsound, and unstable. This is not");
eprintln!(" DANGER suitable for any purpose beyond educational experimentation.");
eprintln!();
});
quote_spanned! {
Span::call_site().into() =>
#[warn(unsafe_code)]
#output
}.into_token_stream().into()
}
}
}
#[derive(Debug, Default)]
struct BorrowCheckerSuppressor {
suppressed_references: Vec<Span>,
}
impl Fold for BorrowCheckerSuppressor {
fn fold_expr(&mut self, node: syn::Expr) -> syn::Expr {
match node {
syn::Expr::Reference(node) => {
let node = syn::fold::fold_expr_reference(self, node);
self.suppressed_references.push(node.span().unwrap());
syn::Expr::Block(parse_quote_spanned! { node.span() =>
{
let r#ref = #node;
unsafe { ::you_can::borrow_unchecked(r#ref) }
}
})
},
_ => syn::fold::fold_expr(self, node),
}
}
fn fold_expr_if(&mut self, mut node: syn::ExprIf) -> syn::ExprIf {
if matches!(*node.cond, syn::Expr::Let(_)) {
let mut ref_collector = RefCollector::default();
ref_collector.visit_expr(&node.cond);
let refs = ref_collector.refs;
self.suppressed_references.extend(ref_collector.spans);
let then_stmts = node.then_branch.stmts.clone();
node.then_branch = parse_quote_spanned! { node.span() =>
{
#(let #refs = unsafe { ::you_can::borrow_unchecked(#refs) };)*
#(#then_stmts)*
}
};
}
syn::fold::fold_expr_if(self, node)
}
fn fold_arm(&mut self, mut node: syn::Arm) -> syn::Arm {
let mut ref_collector = RefCollector::default();
ref_collector.visit_pat(&node.pat);
let refs = ref_collector.refs;
self.suppressed_references.extend(ref_collector.spans);
let body = node.body.clone();
node.body = parse_quote_spanned! { node.span() =>
{
#(let #refs = unsafe { ::you_can::borrow_unchecked(#refs) };)*
#body
}
};
syn::fold::fold_arm(self, node)
}
}
#[derive(Debug, Default)]
struct RefCollector {
refs: Vec<syn::Ident>,
spans: Vec<Span>,
}
impl<'ast> Visit<'ast> for RefCollector {
fn visit_pat_ident(&mut self, node: &'ast syn::PatIdent) {
if node.by_ref.is_some() {
self.refs.push(node.ident.clone());
self.spans.push(node.span().unwrap());
}
}
}