[RFC 2011] Minimal initial implementation · rust-lang/rust@605c64a (original) (raw)

1

``

`-

use rustc_ast::{ptr::P, Expr, Path};

`

``

1

`+

use crate::assert::expr_if_not;

`

``

2

`+

use rustc_ast::{

`

``

3

`+

attr,

`

``

4

`+

ptr::P,

`

``

5

`+

token,

`

``

6

`+

tokenstream::{DelimSpan, TokenStream, TokenTree},

`

``

7

`+

BorrowKind, Expr, ExprKind, ItemKind, MacArgs, MacCall, MacDelimiter, Mutability, Path,

`

``

8

`+

PathSegment, Stmt, UseTree, UseTreeKind, DUMMY_NODE_ID,

`

``

9

`+

};

`

``

10

`+

use rustc_ast_pretty::pprust;

`

``

11

`+

use rustc_data_structures::fx::FxHashSet;

`

2

12

`use rustc_expand::base::ExtCtxt;

`

3

``

`-

use rustc_span::Span;

`

``

13

`+

use rustc_span::{

`

``

14

`+

symbol::{sym, Ident, Symbol},

`

``

15

`+

Span,

`

``

16

`+

};

`

4

17

``

5

18

`pub(super) struct Context<'cx, 'a> {

`

``

19

`` +

// Top-level let captureN = Capture::new() statements

``

``

20

`+

capture_decls: Vec,

`

6

21

`cx: &'cx ExtCtxt<'a>,

`

``

22

`+

// Formatting string used for debugging

`

``

23

`+

fmt_string: String,

`

``

24

`` +

// Top-level let __local_bindN = &expr statements

``

``

25

`+

local_bind_decls: Vec,

`

``

26

`+

// Used to avoid capturing duplicated paths

`

``

27

`+

//

`

``

28


// ```rust

``

29

`+

// let a = 1i32;

`

``

30

`+

// assert!(add(a, a) == 3);

`

``

31


// ```

``

32

`+

paths: FxHashSet,

`

7

33

`span: Span,

`

8

34

`}

`

9

35

``

10

36

`impl<'cx, 'a> Context<'cx, 'a> {

`

11

37

`pub(super) fn new(cx: &'cx ExtCtxt<'a>, span: Span) -> Self {

`

12

``

`-

Self { cx, span }

`

``

38

`+

Self {

`

``

39

`+

capture_decls: <_>::default(),

`

``

40

`+

cx,

`

``

41

`+

fmt_string: <_>::default(),

`

``

42

`+

local_bind_decls: <_>::default(),

`

``

43

`+

paths: <_>::default(),

`

``

44

`+

span,

`

``

45

`+

}

`

13

46

`}

`

14

47

``

15

``

`` -

/// Builds the whole assert! expression.

``

``

48

`` +

/// Builds the whole assert! expression. For example, let elem = 1; assert!(elem == 1); expands to:

``

16

49

`///

`

``

50


/// ```rust

``

51

`+

/// let elem = 1;

`

17

52

`/// {

`

18

``

`-

/// use ::core::asserting::{ ... };

`

``

53

`+

/// #[allow(unused_imports)]

`

``

54

`+

/// use ::core::asserting::{TryCaptureGeneric, TryCapturePrintable};

`

``

55

`+

/// let mut __capture0 = ::core::asserting::Capture::new();

`

``

56

`+

/// let __local_bind0 = &elem;

`

``

57

`+

/// if !(

`

``

58

`+

/// *{

`

``

59

`+

/// (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0);

`

``

60

`+

/// __local_bind0

`

``

61

`+

/// } == 1

`

``

62

`+

/// ) {

`

``

63

`+

/// panic!("Assertion failed: elem == 1\nWith captures:\n elem = {}", __capture0)

`

``

64

`+

/// }

`

``

65

`+

/// }

`

``

66


/// ```

``

67

`+

pub(super) fn build(mut self, mut cond_expr: P, panic_path: Path) -> P {

`

``

68

`+

let expr_str = pprust::expr_to_string(&cond_expr);

`

``

69

`+

self.manage_cond_expr(&mut cond_expr);

`

``

70

`+

let initial_imports = self.build_initial_imports();

`

``

71

`+

let panic = self.build_panic(&expr_str, panic_path);

`

``

72

+

``

73

`+

let Self { capture_decls, cx, local_bind_decls, span, .. } = self;

`

``

74

+

``

75

`+

let mut stmts = Vec::with_capacity(4);

`

``

76

`+

stmts.push(initial_imports);

`

``

77

`+

stmts.extend(capture_decls.into_iter().map(|c| c.decl));

`

``

78

`+

stmts.extend(local_bind_decls);

`

``

79

`+

stmts.push(cx.stmt_expr(expr_if_not(cx, span, cond_expr, panic, None)));

`

``

80

`+

cx.expr_block(cx.block(span, stmts))

`

``

81

`+

}

`

``

82

+

``

83

`+

/// Initial trait imports

`

``

84

`+

///

`

``

85

`+

/// use ::core::asserting::{ ... };

`

``

86

`+

fn build_initial_imports(&self) -> Stmt {

`

``

87

`+

let nested_tree = |this: &Self, sym| {

`

``

88

`+

(

`

``

89

`+

UseTree {

`

``

90

`+

prefix: this.cx.path(this.span, vec![Ident::with_dummy_span(sym)]),

`

``

91

`+

kind: UseTreeKind::Simple(None, DUMMY_NODE_ID, DUMMY_NODE_ID),

`

``

92

`+

span: this.span,

`

``

93

`+

},

`

``

94

`+

DUMMY_NODE_ID,

`

``

95

`+

)

`

``

96

`+

};

`

``

97

`+

self.cx.stmt_item(

`

``

98

`+

self.span,

`

``

99

`+

self.cx.item(

`

``

100

`+

self.span,

`

``

101

`+

Ident::empty(),

`

``

102

`+

vec![self.cx.attribute(attr::mk_list_item(

`

``

103

`+

Ident::new(sym::allow, self.span),

`

``

104

`+

vec![attr::mk_nested_word_item(Ident::new(sym::unused_imports, self.span))],

`

``

105

`+

))],

`

``

106

`+

ItemKind::Use(UseTree {

`

``

107

`+

prefix: self.cx.path(self.span, self.cx.std_path(&[sym::asserting])),

`

``

108

`+

kind: UseTreeKind::Nested(vec![

`

``

109

`+

nested_tree(self, sym::TryCaptureGeneric),

`

``

110

`+

nested_tree(self, sym::TryCapturePrintable),

`

``

111

`+

]),

`

``

112

`+

span: self.span,

`

``

113

`+

}),

`

``

114

`+

),

`

``

115

`+

)

`

``

116

`+

}

`

``

117

+

``

118

`` +

/// The necessary custom panic!(...) expression.

``

``

119

`+

///

`

``

120

`+

/// panic!(

`

``

121

`+

/// "Assertion failed: ... \n With expansion: ...",

`

``

122

`+

/// __capture0,

`

``

123

`+

/// ...

`

``

124

`+

/// );

`

``

125

`+

fn build_panic(&self, expr_str: &str, panic_path: Path) -> P {

`

``

126

`+

let escaped_expr_str = escape_to_fmt(expr_str);

`

``

127

`+

let initial = [

`

``

128

`+

TokenTree::token(

`

``

129

`+

token::Literal(token::Lit {

`

``

130

`+

kind: token::LitKind::Str,

`

``

131

`+

symbol: Symbol::intern(&if self.fmt_string.is_empty() {

`

``

132

`+

format!("Assertion failed: {escaped_expr_str}")

`

``

133

`+

} else {

`

``

134

`+

format!(

`

``

135

`+

"Assertion failed: {escaped_expr_str}\nWith captures:\n{}",

`

``

136

`+

&self.fmt_string

`

``

137

`+

)

`

``

138

`+

}),

`

``

139

`+

suffix: None,

`

``

140

`+

}),

`

``

141

`+

self.span,

`

``

142

`+

),

`

``

143

`+

TokenTree::token(token::Comma, self.span),

`

``

144

`+

];

`

``

145

`+

let captures = self.capture_decls.iter().flat_map(|cap| {

`

``

146

`+

[

`

``

147

`+

TokenTree::token(token::Ident(cap.ident.name, false), cap.ident.span),

`

``

148

`+

TokenTree::token(token::Comma, self.span),

`

``

149

`+

]

`

``

150

`+

});

`

``

151

`+

self.cx.expr(

`

``

152

`+

self.span,

`

``

153

`+

ExprKind::MacCall(MacCall {

`

``

154

`+

path: panic_path,

`

``

155

`+

args: P(MacArgs::Delimited(

`

``

156

`+

DelimSpan::from_single(self.span),

`

``

157

`+

MacDelimiter::Parenthesis,

`

``

158

`+

initial.into_iter().chain(captures).collect::(),

`

``

159

`+

)),

`

``

160

`+

prior_type_ascription: None,

`

``

161

`+

}),

`

``

162

`+

)

`

``

163

`+

}

`

``

164

+

``

165

`` +

/// Recursive function called until cond_expr and fmt_str are fully modified.

``

``

166

`+

///

`

``

167

`+

/// See [Self::manage_initial_capture] and [Self::manage_try_capture]

`

``

168

`+

fn manage_cond_expr(&mut self, expr: &mut P) {

`

``

169

`+

match (*expr).kind {

`

``

170

`+

ExprKind::Binary(_, ref mut lhs, ref mut rhs) => {

`

``

171

`+

self.manage_cond_expr(lhs);

`

``

172

`+

self.manage_cond_expr(rhs);

`

``

173

`+

}

`

``

174

`+

ExprKind::Path(_, Path { ref segments, .. }) if let &[ref path_segment] = &segments[..] => {

`

``

175

`+

let path_ident = path_segment.ident;

`

``

176

`+

self.manage_initial_capture(expr, path_ident);

`

``

177

`+

}

`

``

178

`+

_ => {}

`

``

179

`+

}

`

``

180

`+

}

`

``

181

+

``

182

`` +

/// Pushes the top-level declarations and modifies expr to try capturing variables.

``

19

183

`///

`

20

``

`-

/// let mut __capture0 = Capture::new();

`

21

``

`-

/// ...

`

22

``

`-

/// ...

`

23

``

`-

/// ...

`

``

184

`` +

/// fmt_str, the formatting string used for debugging, is constructed to show possible

``

``

185

`+

/// captured variables.

`

``

186

`+

fn manage_initial_capture(&mut self, expr: &mut P, path_ident: Ident) {

`

``

187

`+

if self.paths.contains(&path_ident) {

`

``

188

`+

return;

`

``

189

`+

} else {

`

``

190

`+

self.fmt_string.push_str(" ");

`

``

191

`+

self.fmt_string.push_str(path_ident.as_str());

`

``

192

`+

self.fmt_string.push_str(" = {:?}\n");

`

``

193

`+

let _ = self.paths.insert(path_ident);

`

``

194

`+

}

`

``

195

`+

let curr_capture_idx = self.capture_decls.len();

`

``

196

`+

let capture_string = format!("__capture{curr_capture_idx}");

`

``

197

`+

let ident = Ident::new(Symbol::intern(&capture_string), self.span);

`

``

198

`+

let init_std_path = self.cx.std_path(&[sym::asserting, sym::Capture, sym::new]);

`

``

199

`+

let init = self.cx.expr_call(

`

``

200

`+

self.span,

`

``

201

`+

self.cx.expr_path(self.cx.path(self.span, init_std_path)),

`

``

202

`+

vec![],

`

``

203

`+

);

`

``

204

`+

let capture = Capture { decl: self.cx.stmt_let(self.span, true, ident, init), ident };

`

``

205

`+

self.capture_decls.push(capture);

`

``

206

`+

self.manage_try_capture(ident, curr_capture_idx, expr);

`

``

207

`+

}

`

``

208

+

``

209

`` +

/// Tries to copy __local_bindN into __captureN.

``

24

210

`///

`

25

``

`-

/// if !{

`

26

``

`-

/// ...

`

27

``

`-

/// ...

`

28

``

`-

/// ...

`

29

``

`-

/// } {

`

30

``

`-

/// panic!(

`

31

``

`-

/// "Assertion failed: ... \n With expansion: ...",

`

32

``

`-

/// __capture0,

`

33

``

`-

/// ...

`

34

``

`-

/// ...

`

35

``

`-

/// ...

`

36

``

`-

/// );

`

37

``

`-

/// }

`

``

211

`+

/// *{

`

``

212

`+

/// (&Wrapper(__local_bindN)).try_capture(&mut __captureN);

`

``

213

`+

/// __local_bindN

`

38

214

`/// }

`

39

``

`-

pub(super) fn build(self, _cond_expr: P, _panic_path: Path) -> P {

`

40

``

`-

let Self { cx, span, .. } = self;

`

41

``

`-

let stmts = Vec::new();

`

42

``

`-

cx.expr_block(cx.block(span, stmts))

`

``

215

`+

fn manage_try_capture(&mut self, capture: Ident, curr_capture_idx: usize, expr: &mut P) {

`

``

216

`+

let local_bind_string = format!("__local_bind{curr_capture_idx}");

`

``

217

`+

let local_bind = Ident::new(Symbol::intern(&local_bind_string), self.span);

`

``

218

`+

self.local_bind_decls.push(self.cx.stmt_let(

`

``

219

`+

self.span,

`

``

220

`+

false,

`

``

221

`+

local_bind,

`

``

222

`+

self.cx.expr_addr_of(self.span, expr.clone()),

`

``

223

`+

));

`

``

224

`+

let wrapper = self.cx.expr_call(

`

``

225

`+

self.span,

`

``

226

`+

self.cx.expr_path(

`

``

227

`+

self.cx.path(self.span, self.cx.std_path(&[sym::asserting, sym::Wrapper])),

`

``

228

`+

),

`

``

229

`+

vec![self.cx.expr_path(Path::from_ident(local_bind))],

`

``

230

`+

);

`

``

231

`+

let try_capture_call = self

`

``

232

`+

.cx

`

``

233

`+

.stmt_expr(expr_method_call(

`

``

234

`+

self.cx,

`

``

235

`+

PathSegment {

`

``

236

`+

args: None,

`

``

237

`+

id: DUMMY_NODE_ID,

`

``

238

`+

ident: Ident::new(sym::try_capture, self.span),

`

``

239

`+

},

`

``

240

`+

vec![

`

``

241

`+

expr_paren(self.cx, self.span, self.cx.expr_addr_of(self.span, wrapper)),

`

``

242

`+

expr_addr_of_mut(

`

``

243

`+

self.cx,

`

``

244

`+

self.span,

`

``

245

`+

self.cx.expr_path(Path::from_ident(capture)),

`

``

246

`+

),

`

``

247

`+

],

`

``

248

`+

self.span,

`

``

249

`+

))

`

``

250

`+

.add_trailing_semicolon();

`

``

251

`+

let local_bind_path = self.cx.expr_path(Path::from_ident(local_bind));

`

``

252

`+

let ret = self.cx.stmt_expr(local_bind_path);

`

``

253

`+

let block = self.cx.expr_block(self.cx.block(self.span, vec![try_capture_call, ret]));

`

``

254

`+

*expr = self.cx.expr_deref(self.span, block);

`

``

255

`+

}

`

``

256

`+

}

`

``

257

+

``

258

`+

/// Information about a captured element.

`

``

259

`+

#[derive(Debug)]

`

``

260

`+

struct Capture {

`

``

261

`` +

// Generated indexed Capture statement.

``

``

262

`+

//

`

``

263

`` +

// let __capture{} = Capture::new();

``

``

264

`+

decl: Stmt,

`

``

265

`` +

// The name of the generated indexed Capture variable.

``

``

266

`+

//

`

``

267

`` +

// __capture{}

``

``

268

`+

ident: Ident,

`

``

269

`+

}

`

``

270

+

``

271

`+

/// Escapes to use as a formatting string.

`

``

272

`+

fn escape_to_fmt(s: &str) -> String {

`

``

273

`+

let mut rslt = String::with_capacity(s.len());

`

``

274

`+

for c in s.chars() {

`

``

275

`+

rslt.extend(c.escape_debug());

`

``

276

`+

match c {

`

``

277

`+

'{' | '}' => rslt.push(c),

`

``

278

`+

_ => {}

`

``

279

`+

}

`

43

280

`}

`

``

281

`+

rslt

`

``

282

`+

}

`

``

283

+

``

284

`+

fn expr_addr_of_mut(cx: &ExtCtxt<'_>, sp: Span, e: P) -> P {

`

``

285

`+

cx.expr(sp, ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, e))

`

``

286

`+

}

`

``

287

+

``

288

`+

fn expr_method_call(

`

``

289

`+

cx: &ExtCtxt<'_>,

`

``

290

`+

path: PathSegment,

`

``

291

`+

args: Vec<P>,

`

``

292

`+

span: Span,

`

``

293

`+

) -> P {

`

``

294

`+

cx.expr(span, ExprKind::MethodCall(path, args, span))

`

``

295

`+

}

`

``

296

+

``

297

`+

fn expr_paren(cx: &ExtCtxt<'_>, sp: Span, e: P) -> P {

`

``

298

`+

cx.expr(sp, ExprKind::Paren(e))

`

44

299

`}

`