]> git.lizzy.rs Git - rust.git/blob - src/librustc_trans/trans/debuginfo/create_scope_map.rs
828086800499a80d14d4dfd8ff05170f2eac8b12
[rust.git] / src / librustc_trans / trans / debuginfo / create_scope_map.rs
1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use super::metadata::file_metadata;
12 use super::utils::DIB;
13
14 use llvm;
15 use llvm::debuginfo::{DIScope, DISubprogram};
16 use trans::common::CrateContext;
17 use middle::pat_util;
18 use rustc::util::nodemap::NodeMap;
19
20 use libc::c_uint;
21 use syntax::codemap::{Span, Pos};
22 use syntax::{ast, codemap};
23
24 use rustc_front;
25 use rustc_front::hir;
26
27 // This procedure builds the *scope map* for a given function, which maps any
28 // given ast::NodeId in the function's AST to the correct DIScope metadata instance.
29 //
30 // This builder procedure walks the AST in execution order and keeps track of
31 // what belongs to which scope, creating DIScope DIEs along the way, and
32 // introducing *artificial* lexical scope descriptors where necessary. These
33 // artificial scopes allow GDB to correctly handle name shadowing.
34 pub fn create_scope_map(cx: &CrateContext,
35                         args: &[hir::Arg],
36                         fn_entry_block: &hir::Block,
37                         fn_metadata: DISubprogram,
38                         fn_ast_id: ast::NodeId)
39                         -> NodeMap<DIScope> {
40     let mut scope_map = NodeMap();
41
42     let def_map = &cx.tcx().def_map;
43
44     let mut scope_stack = vec!(ScopeStackEntry { scope_metadata: fn_metadata, name: None });
45     scope_map.insert(fn_ast_id, fn_metadata);
46
47     // Push argument identifiers onto the stack so arguments integrate nicely
48     // with variable shadowing.
49     for arg in args {
50         pat_util::pat_bindings(def_map, &*arg.pat, |_, node_id, _, path1| {
51             scope_stack.push(ScopeStackEntry { scope_metadata: fn_metadata,
52                                                name: Some(path1.node.name) });
53             scope_map.insert(node_id, fn_metadata);
54         })
55     }
56
57     // Clang creates a separate scope for function bodies, so let's do this too.
58     with_new_scope(cx,
59                    fn_entry_block.span,
60                    &mut scope_stack,
61                    &mut scope_map,
62                    |cx, scope_stack, scope_map| {
63         walk_block(cx, fn_entry_block, scope_stack, scope_map);
64     });
65
66     return scope_map;
67 }
68
69 // local helper functions for walking the AST.
70 fn with_new_scope<F>(cx: &CrateContext,
71                      scope_span: Span,
72                      scope_stack: &mut Vec<ScopeStackEntry> ,
73                      scope_map: &mut NodeMap<DIScope>,
74                      inner_walk: F) where
75     F: FnOnce(&CrateContext, &mut Vec<ScopeStackEntry>, &mut NodeMap<DIScope>),
76 {
77     // Create a new lexical scope and push it onto the stack
78     let loc = cx.sess().codemap().lookup_char_pos(scope_span.lo);
79     let file_metadata = file_metadata(cx, &loc.file.name);
80     let parent_scope = scope_stack.last().unwrap().scope_metadata;
81
82     let scope_metadata = unsafe {
83         llvm::LLVMDIBuilderCreateLexicalBlock(
84             DIB(cx),
85             parent_scope,
86             file_metadata,
87             loc.line as c_uint,
88             loc.col.to_usize() as c_uint)
89     };
90
91     scope_stack.push(ScopeStackEntry { scope_metadata: scope_metadata, name: None });
92
93     inner_walk(cx, scope_stack, scope_map);
94
95     // pop artificial scopes
96     while scope_stack.last().unwrap().name.is_some() {
97         scope_stack.pop();
98     }
99
100     if scope_stack.last().unwrap().scope_metadata != scope_metadata {
101         cx.sess().span_bug(scope_span, "debuginfo: Inconsistency in scope management.");
102     }
103
104     scope_stack.pop();
105 }
106
107 struct ScopeStackEntry {
108     scope_metadata: DIScope,
109     name: Option<ast::Name>
110 }
111
112 fn walk_block(cx: &CrateContext,
113               block: &hir::Block,
114               scope_stack: &mut Vec<ScopeStackEntry> ,
115               scope_map: &mut NodeMap<DIScope>) {
116     scope_map.insert(block.id, scope_stack.last().unwrap().scope_metadata);
117
118     // The interesting things here are statements and the concluding expression.
119     for statement in &block.stmts {
120         scope_map.insert(rustc_front::util::stmt_id(&**statement),
121                          scope_stack.last().unwrap().scope_metadata);
122
123         match statement.node {
124             hir::StmtDecl(ref decl, _) =>
125                 walk_decl(cx, &**decl, scope_stack, scope_map),
126             hir::StmtExpr(ref exp, _) |
127             hir::StmtSemi(ref exp, _) =>
128                 walk_expr(cx, &**exp, scope_stack, scope_map),
129         }
130     }
131
132     if let Some(ref exp) = block.expr {
133         walk_expr(cx, &**exp, scope_stack, scope_map);
134     }
135 }
136
137 fn walk_decl(cx: &CrateContext,
138              decl: &hir::Decl,
139              scope_stack: &mut Vec<ScopeStackEntry> ,
140              scope_map: &mut NodeMap<DIScope>) {
141     match *decl {
142         codemap::Spanned { node: hir::DeclLocal(ref local), .. } => {
143             scope_map.insert(local.id, scope_stack.last().unwrap().scope_metadata);
144
145             walk_pattern(cx, &*local.pat, scope_stack, scope_map);
146
147             if let Some(ref exp) = local.init {
148                 walk_expr(cx, &**exp, scope_stack, scope_map);
149             }
150         }
151         _ => ()
152     }
153 }
154
155 fn walk_pattern(cx: &CrateContext,
156                 pat: &hir::Pat,
157                 scope_stack: &mut Vec<ScopeStackEntry> ,
158                 scope_map: &mut NodeMap<DIScope>) {
159
160     let def_map = &cx.tcx().def_map;
161
162     // Unfortunately, we cannot just use pat_util::pat_bindings() or
163     // ast_util::walk_pat() here because we have to visit *all* nodes in
164     // order to put them into the scope map. The above functions don't do that.
165     match pat.node {
166         hir::PatIdent(_, ref path1, ref sub_pat_opt) => {
167
168             // Check if this is a binding. If so we need to put it on the
169             // scope stack and maybe introduce an artificial scope
170             if pat_util::pat_is_binding(def_map, &*pat) {
171
172                 let name = path1.node.name;
173
174                 // LLVM does not properly generate 'DW_AT_start_scope' fields
175                 // for variable DIEs. For this reason we have to introduce
176                 // an artificial scope at bindings whenever a variable with
177                 // the same name is declared in *any* parent scope.
178                 //
179                 // Otherwise the following error occurs:
180                 //
181                 // let x = 10;
182                 //
183                 // do_something(); // 'gdb print x' correctly prints 10
184                 //
185                 // {
186                 //     do_something(); // 'gdb print x' prints 0, because it
187                 //                     // already reads the uninitialized 'x'
188                 //                     // from the next line...
189                 //     let x = 100;
190                 //     do_something(); // 'gdb print x' correctly prints 100
191                 // }
192
193                 // Is there already a binding with that name?
194                 // N.B.: this comparison must be UNhygienic... because
195                 // gdb knows nothing about the context, so any two
196                 // variables with the same name will cause the problem.
197                 let need_new_scope = scope_stack
198                     .iter()
199                     .any(|entry| entry.name == Some(name));
200
201                 if need_new_scope {
202                     // Create a new lexical scope and push it onto the stack
203                     let loc = cx.sess().codemap().lookup_char_pos(pat.span.lo);
204                     let file_metadata = file_metadata(cx, &loc.file.name);
205                     let parent_scope = scope_stack.last().unwrap().scope_metadata;
206
207                     let scope_metadata = unsafe {
208                         llvm::LLVMDIBuilderCreateLexicalBlock(
209                             DIB(cx),
210                             parent_scope,
211                             file_metadata,
212                             loc.line as c_uint,
213                             loc.col.to_usize() as c_uint)
214                     };
215
216                     scope_stack.push(ScopeStackEntry {
217                         scope_metadata: scope_metadata,
218                         name: Some(name)
219                     });
220
221                 } else {
222                     // Push a new entry anyway so the name can be found
223                     let prev_metadata = scope_stack.last().unwrap().scope_metadata;
224                     scope_stack.push(ScopeStackEntry {
225                         scope_metadata: prev_metadata,
226                         name: Some(name)
227                     });
228                 }
229             }
230
231             scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
232
233             if let Some(ref sub_pat) = *sub_pat_opt {
234                 walk_pattern(cx, &**sub_pat, scope_stack, scope_map);
235             }
236         }
237
238         hir::PatWild(_) => {
239             scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
240         }
241
242         hir::PatEnum(_, ref sub_pats_opt) => {
243             scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
244
245             if let Some(ref sub_pats) = *sub_pats_opt {
246                 for p in sub_pats {
247                     walk_pattern(cx, &**p, scope_stack, scope_map);
248                 }
249             }
250         }
251
252         hir::PatQPath(..) => {
253             scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
254         }
255
256         hir::PatStruct(_, ref field_pats, _) => {
257             scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
258
259             for &codemap::Spanned {
260                 node: hir::FieldPat { pat: ref sub_pat, .. },
261                 ..
262             } in field_pats {
263                 walk_pattern(cx, &**sub_pat, scope_stack, scope_map);
264             }
265         }
266
267         hir::PatTup(ref sub_pats) => {
268             scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
269
270             for sub_pat in sub_pats {
271                 walk_pattern(cx, &**sub_pat, scope_stack, scope_map);
272             }
273         }
274
275         hir::PatBox(ref sub_pat) | hir::PatRegion(ref sub_pat, _) => {
276             scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
277             walk_pattern(cx, &**sub_pat, scope_stack, scope_map);
278         }
279
280         hir::PatLit(ref exp) => {
281             scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
282             walk_expr(cx, &**exp, scope_stack, scope_map);
283         }
284
285         hir::PatRange(ref exp1, ref exp2) => {
286             scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
287             walk_expr(cx, &**exp1, scope_stack, scope_map);
288             walk_expr(cx, &**exp2, scope_stack, scope_map);
289         }
290
291         hir::PatVec(ref front_sub_pats, ref middle_sub_pats, ref back_sub_pats) => {
292             scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
293
294             for sub_pat in front_sub_pats {
295                 walk_pattern(cx, &**sub_pat, scope_stack, scope_map);
296             }
297
298             if let Some(ref sub_pat) = *middle_sub_pats {
299                 walk_pattern(cx, &**sub_pat, scope_stack, scope_map);
300             }
301
302             for sub_pat in back_sub_pats {
303                 walk_pattern(cx, &**sub_pat, scope_stack, scope_map);
304             }
305         }
306     }
307 }
308
309 fn walk_expr(cx: &CrateContext,
310              exp: &hir::Expr,
311              scope_stack: &mut Vec<ScopeStackEntry> ,
312              scope_map: &mut NodeMap<DIScope>) {
313
314     scope_map.insert(exp.id, scope_stack.last().unwrap().scope_metadata);
315
316     match exp.node {
317         hir::ExprLit(_)   |
318         hir::ExprBreak(_) |
319         hir::ExprAgain(_) |
320         hir::ExprPath(..) => {}
321
322         hir::ExprCast(ref sub_exp, _)     |
323         hir::ExprAddrOf(_, ref sub_exp)  |
324         hir::ExprField(ref sub_exp, _) |
325         hir::ExprTupField(ref sub_exp, _) =>
326             walk_expr(cx, &**sub_exp, scope_stack, scope_map),
327
328         hir::ExprBox(ref place, ref sub_expr) => {
329             place.as_ref().map(
330                 |e| walk_expr(cx, &**e, scope_stack, scope_map));
331             walk_expr(cx, &**sub_expr, scope_stack, scope_map);
332         }
333
334         hir::ExprRet(ref exp_opt) => match *exp_opt {
335             Some(ref sub_exp) => walk_expr(cx, &**sub_exp, scope_stack, scope_map),
336             None => ()
337         },
338
339         hir::ExprUnary(_, ref sub_exp) => {
340             walk_expr(cx, &**sub_exp, scope_stack, scope_map);
341         }
342
343         hir::ExprAssignOp(_, ref lhs, ref rhs) |
344         hir::ExprIndex(ref lhs, ref rhs) |
345         hir::ExprBinary(_, ref lhs, ref rhs)    => {
346             walk_expr(cx, &**lhs, scope_stack, scope_map);
347             walk_expr(cx, &**rhs, scope_stack, scope_map);
348         }
349
350         hir::ExprRange(ref start, ref end) => {
351             start.as_ref().map(|e| walk_expr(cx, &**e, scope_stack, scope_map));
352             end.as_ref().map(|e| walk_expr(cx, &**e, scope_stack, scope_map));
353         }
354
355         hir::ExprVec(ref init_expressions) |
356         hir::ExprTup(ref init_expressions) => {
357             for ie in init_expressions {
358                 walk_expr(cx, &**ie, scope_stack, scope_map);
359             }
360         }
361
362         hir::ExprAssign(ref sub_exp1, ref sub_exp2) |
363         hir::ExprRepeat(ref sub_exp1, ref sub_exp2) => {
364             walk_expr(cx, &**sub_exp1, scope_stack, scope_map);
365             walk_expr(cx, &**sub_exp2, scope_stack, scope_map);
366         }
367
368         hir::ExprIf(ref cond_exp, ref then_block, ref opt_else_exp) => {
369             walk_expr(cx, &**cond_exp, scope_stack, scope_map);
370
371             with_new_scope(cx,
372                            then_block.span,
373                            scope_stack,
374                            scope_map,
375                            |cx, scope_stack, scope_map| {
376                 walk_block(cx, &**then_block, scope_stack, scope_map);
377             });
378
379             match *opt_else_exp {
380                 Some(ref else_exp) =>
381                     walk_expr(cx, &**else_exp, scope_stack, scope_map),
382                 _ => ()
383             }
384         }
385
386         hir::ExprWhile(ref cond_exp, ref loop_body, _) => {
387             walk_expr(cx, &**cond_exp, scope_stack, scope_map);
388
389             with_new_scope(cx,
390                            loop_body.span,
391                            scope_stack,
392                            scope_map,
393                            |cx, scope_stack, scope_map| {
394                 walk_block(cx, &**loop_body, scope_stack, scope_map);
395             })
396         }
397
398         hir::ExprLoop(ref block, _) |
399         hir::ExprBlock(ref block)   => {
400             with_new_scope(cx,
401                            block.span,
402                            scope_stack,
403                            scope_map,
404                            |cx, scope_stack, scope_map| {
405                 walk_block(cx, &**block, scope_stack, scope_map);
406             })
407         }
408
409         hir::ExprClosure(_, ref decl, ref block) => {
410             with_new_scope(cx,
411                            block.span,
412                            scope_stack,
413                            scope_map,
414                            |cx, scope_stack, scope_map| {
415                 for &hir::Arg { pat: ref pattern, .. } in &decl.inputs {
416                     walk_pattern(cx, &**pattern, scope_stack, scope_map);
417                 }
418
419                 walk_block(cx, &**block, scope_stack, scope_map);
420             })
421         }
422
423         hir::ExprCall(ref fn_exp, ref args) => {
424             walk_expr(cx, &**fn_exp, scope_stack, scope_map);
425
426             for arg_exp in args {
427                 walk_expr(cx, &**arg_exp, scope_stack, scope_map);
428             }
429         }
430
431         hir::ExprMethodCall(_, _, ref args) => {
432             for arg_exp in args {
433                 walk_expr(cx, &**arg_exp, scope_stack, scope_map);
434             }
435         }
436
437         hir::ExprMatch(ref discriminant_exp, ref arms, _) => {
438             walk_expr(cx, &**discriminant_exp, scope_stack, scope_map);
439
440             // For each arm we have to first walk the pattern as these might
441             // introduce new artificial scopes. It should be sufficient to
442             // walk only one pattern per arm, as they all must contain the
443             // same binding names.
444
445             for arm_ref in arms {
446                 let arm_span = arm_ref.pats[0].span;
447
448                 with_new_scope(cx,
449                                arm_span,
450                                scope_stack,
451                                scope_map,
452                                |cx, scope_stack, scope_map| {
453                     for pat in &arm_ref.pats {
454                         walk_pattern(cx, &**pat, scope_stack, scope_map);
455                     }
456
457                     if let Some(ref guard_exp) = arm_ref.guard {
458                         walk_expr(cx, &**guard_exp, scope_stack, scope_map)
459                     }
460
461                     walk_expr(cx, &*arm_ref.body, scope_stack, scope_map);
462                 })
463             }
464         }
465
466         hir::ExprStruct(_, ref fields, ref base_exp) => {
467             for &hir::Field { expr: ref exp, .. } in fields {
468                 walk_expr(cx, &**exp, scope_stack, scope_map);
469             }
470
471             match *base_exp {
472                 Some(ref exp) => walk_expr(cx, &**exp, scope_stack, scope_map),
473                 None => ()
474             }
475         }
476
477         hir::ExprInlineAsm(hir::InlineAsm { ref inputs,
478                                             ref outputs,
479                                             .. }) => {
480             // inputs, outputs: Vec<(String, P<Expr>)>
481             for &(_, ref exp) in inputs {
482                 walk_expr(cx, &**exp, scope_stack, scope_map);
483             }
484
485             for &(_, ref exp, _) in outputs {
486                 walk_expr(cx, &**exp, scope_stack, scope_map);
487             }
488         }
489     }
490 }