diff --git a/src/librustc_trans/trans/debuginfo/mod.rs b/src/librustc_trans/trans/debuginfo/mod.rs index e3b7ffb3f39..306bed6785a 100644 --- a/src/librustc_trans/trans/debuginfo/mod.rs +++ b/src/librustc_trans/trans/debuginfo/mod.rs @@ -12,6 +12,11 @@ mod doc; pub mod gdb; +mod utils; + +use self::utils::{debug_context, DIB, span_start, bytes_to_bits, size_and_align_of, + assert_type_for_node_id, get_namespace_and_span_for_item, fn_should_be_ignored, + contains_nodebug_attribute, create_scope_map}; use self::VariableAccess::*; use self::VariableKind::*; @@ -2978,569 +2983,6 @@ fn set_debug_location(cx: &CrateContext, debug_location: InternalDebugLocation) debug_context(cx).current_debug_location.set(debug_location); } -//=----------------------------------------------------------------------------- -// Utility Functions -//=----------------------------------------------------------------------------- - -fn contains_nodebug_attribute(attributes: &[ast::Attribute]) -> bool { - attributes.iter().any(|attr| { - let meta_item: &ast::MetaItem = &*attr.node.value; - match meta_item.node { - ast::MetaWord(ref value) => &value[..] == "no_debug", - _ => false - } - }) -} - -/// Return codemap::Loc corresponding to the beginning of the span -fn span_start(cx: &CrateContext, span: Span) -> codemap::Loc { - cx.sess().codemap().lookup_char_pos(span.lo) -} - -fn size_and_align_of(cx: &CrateContext, llvm_type: Type) -> (u64, u64) { - (machine::llsize_of_alloc(cx, llvm_type), machine::llalign_of_min(cx, llvm_type) as u64) -} - -fn bytes_to_bits(bytes: u64) -> u64 { - bytes * 8 -} - -#[inline] -fn debug_context<'a, 'tcx>(cx: &'a CrateContext<'a, 'tcx>) - -> &'a CrateDebugContext<'tcx> { - let debug_context: &'a CrateDebugContext<'tcx> = cx.dbg_cx().as_ref().unwrap(); - debug_context -} - -#[inline] -#[allow(non_snake_case)] -fn DIB(cx: &CrateContext) -> DIBuilderRef { - cx.dbg_cx().as_ref().unwrap().builder -} - -fn fn_should_be_ignored(fcx: &FunctionContext) -> bool { - match fcx.debug_context { - FunctionDebugContext::RegularContext(_) => false, - _ => true - } -} - -fn assert_type_for_node_id(cx: &CrateContext, - node_id: ast::NodeId, - error_reporting_span: Span) { - if !cx.tcx().node_types().contains_key(&node_id) { - cx.sess().span_bug(error_reporting_span, - "debuginfo: Could not find type for node id!"); - } -} - -fn get_namespace_and_span_for_item(cx: &CrateContext, def_id: ast::DefId) - -> (DIScope, Span) { - let containing_scope = namespace_for_item(cx, def_id).scope; - let definition_span = if def_id.krate == ast::LOCAL_CRATE { - cx.tcx().map.span(def_id.node) - } else { - // For external items there is no span information - codemap::DUMMY_SP - }; - - (containing_scope, definition_span) -} - -// This procedure builds the *scope map* for a given function, which maps any -// given ast::NodeId in the function's AST to the correct DIScope metadata instance. -// -// This builder procedure walks the AST in execution order and keeps track of -// what belongs to which scope, creating DIScope DIEs along the way, and -// introducing *artificial* lexical scope descriptors where necessary. These -// artificial scopes allow GDB to correctly handle name shadowing. -fn create_scope_map(cx: &CrateContext, - args: &[ast::Arg], - fn_entry_block: &ast::Block, - fn_metadata: DISubprogram, - fn_ast_id: ast::NodeId) - -> NodeMap { - let mut scope_map = NodeMap(); - - let def_map = &cx.tcx().def_map; - - struct ScopeStackEntry { - scope_metadata: DIScope, - name: Option - } - - let mut scope_stack = vec!(ScopeStackEntry { scope_metadata: fn_metadata, name: None }); - scope_map.insert(fn_ast_id, fn_metadata); - - // Push argument identifiers onto the stack so arguments integrate nicely - // with variable shadowing. - for arg in args { - pat_util::pat_bindings(def_map, &*arg.pat, |_, node_id, _, path1| { - scope_stack.push(ScopeStackEntry { scope_metadata: fn_metadata, - name: Some(path1.node.name) }); - scope_map.insert(node_id, fn_metadata); - }) - } - - // Clang creates a separate scope for function bodies, so let's do this too. - with_new_scope(cx, - fn_entry_block.span, - &mut scope_stack, - &mut scope_map, - |cx, scope_stack, scope_map| { - walk_block(cx, fn_entry_block, scope_stack, scope_map); - }); - - return scope_map; - - - // local helper functions for walking the AST. - fn with_new_scope(cx: &CrateContext, - scope_span: Span, - scope_stack: &mut Vec , - scope_map: &mut NodeMap, - inner_walk: F) where - F: FnOnce(&CrateContext, &mut Vec, &mut NodeMap), - { - // Create a new lexical scope and push it onto the stack - let loc = cx.sess().codemap().lookup_char_pos(scope_span.lo); - let file_metadata = file_metadata(cx, &loc.file.name); - let parent_scope = scope_stack.last().unwrap().scope_metadata; - - let scope_metadata = unsafe { - llvm::LLVMDIBuilderCreateLexicalBlock( - DIB(cx), - parent_scope, - file_metadata, - loc.line as c_uint, - loc.col.to_usize() as c_uint) - }; - - scope_stack.push(ScopeStackEntry { scope_metadata: scope_metadata, name: None }); - - inner_walk(cx, scope_stack, scope_map); - - // pop artificial scopes - while scope_stack.last().unwrap().name.is_some() { - scope_stack.pop(); - } - - if scope_stack.last().unwrap().scope_metadata != scope_metadata { - cx.sess().span_bug(scope_span, "debuginfo: Inconsistency in scope management."); - } - - scope_stack.pop(); - } - - fn walk_block(cx: &CrateContext, - block: &ast::Block, - scope_stack: &mut Vec , - scope_map: &mut NodeMap) { - scope_map.insert(block.id, scope_stack.last().unwrap().scope_metadata); - - // The interesting things here are statements and the concluding expression. - for statement in &block.stmts { - scope_map.insert(ast_util::stmt_id(&**statement), - scope_stack.last().unwrap().scope_metadata); - - match statement.node { - ast::StmtDecl(ref decl, _) => - walk_decl(cx, &**decl, scope_stack, scope_map), - ast::StmtExpr(ref exp, _) | - ast::StmtSemi(ref exp, _) => - walk_expr(cx, &**exp, scope_stack, scope_map), - ast::StmtMac(..) => () // Ignore macros (which should be expanded anyway). - } - } - - if let Some(ref exp) = block.expr { - walk_expr(cx, &**exp, scope_stack, scope_map); - } - } - - fn walk_decl(cx: &CrateContext, - decl: &ast::Decl, - scope_stack: &mut Vec , - scope_map: &mut NodeMap) { - match *decl { - codemap::Spanned { node: ast::DeclLocal(ref local), .. } => { - scope_map.insert(local.id, scope_stack.last().unwrap().scope_metadata); - - walk_pattern(cx, &*local.pat, scope_stack, scope_map); - - if let Some(ref exp) = local.init { - walk_expr(cx, &**exp, scope_stack, scope_map); - } - } - _ => () - } - } - - fn walk_pattern(cx: &CrateContext, - pat: &ast::Pat, - scope_stack: &mut Vec , - scope_map: &mut NodeMap) { - - let def_map = &cx.tcx().def_map; - - // Unfortunately, we cannot just use pat_util::pat_bindings() or - // ast_util::walk_pat() here because we have to visit *all* nodes in - // order to put them into the scope map. The above functions don't do that. - match pat.node { - ast::PatIdent(_, ref path1, ref sub_pat_opt) => { - - // Check if this is a binding. If so we need to put it on the - // scope stack and maybe introduce an artificial scope - if pat_util::pat_is_binding(def_map, &*pat) { - - let name = path1.node.name; - - // LLVM does not properly generate 'DW_AT_start_scope' fields - // for variable DIEs. For this reason we have to introduce - // an artificial scope at bindings whenever a variable with - // the same name is declared in *any* parent scope. - // - // Otherwise the following error occurs: - // - // let x = 10; - // - // do_something(); // 'gdb print x' correctly prints 10 - // - // { - // do_something(); // 'gdb print x' prints 0, because it - // // already reads the uninitialized 'x' - // // from the next line... - // let x = 100; - // do_something(); // 'gdb print x' correctly prints 100 - // } - - // Is there already a binding with that name? - // N.B.: this comparison must be UNhygienic... because - // gdb knows nothing about the context, so any two - // variables with the same name will cause the problem. - let need_new_scope = scope_stack - .iter() - .any(|entry| entry.name == Some(name)); - - if need_new_scope { - // Create a new lexical scope and push it onto the stack - let loc = cx.sess().codemap().lookup_char_pos(pat.span.lo); - let file_metadata = file_metadata(cx, &loc.file.name); - let parent_scope = scope_stack.last().unwrap().scope_metadata; - - let scope_metadata = unsafe { - llvm::LLVMDIBuilderCreateLexicalBlock( - DIB(cx), - parent_scope, - file_metadata, - loc.line as c_uint, - loc.col.to_usize() as c_uint) - }; - - scope_stack.push(ScopeStackEntry { - scope_metadata: scope_metadata, - name: Some(name) - }); - - } else { - // Push a new entry anyway so the name can be found - let prev_metadata = scope_stack.last().unwrap().scope_metadata; - scope_stack.push(ScopeStackEntry { - scope_metadata: prev_metadata, - name: Some(name) - }); - } - } - - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - - if let Some(ref sub_pat) = *sub_pat_opt { - walk_pattern(cx, &**sub_pat, scope_stack, scope_map); - } - } - - ast::PatWild(_) => { - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - } - - ast::PatEnum(_, ref sub_pats_opt) => { - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - - if let Some(ref sub_pats) = *sub_pats_opt { - for p in sub_pats { - walk_pattern(cx, &**p, scope_stack, scope_map); - } - } - } - - ast::PatQPath(..) => { - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - } - - ast::PatStruct(_, ref field_pats, _) => { - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - - for &codemap::Spanned { - node: ast::FieldPat { pat: ref sub_pat, .. }, - .. - } in field_pats.iter() { - walk_pattern(cx, &**sub_pat, scope_stack, scope_map); - } - } - - ast::PatTup(ref sub_pats) => { - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - - for sub_pat in sub_pats { - walk_pattern(cx, &**sub_pat, scope_stack, scope_map); - } - } - - ast::PatBox(ref sub_pat) | ast::PatRegion(ref sub_pat, _) => { - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - walk_pattern(cx, &**sub_pat, scope_stack, scope_map); - } - - ast::PatLit(ref exp) => { - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - walk_expr(cx, &**exp, scope_stack, scope_map); - } - - ast::PatRange(ref exp1, ref exp2) => { - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - walk_expr(cx, &**exp1, scope_stack, scope_map); - walk_expr(cx, &**exp2, scope_stack, scope_map); - } - - ast::PatVec(ref front_sub_pats, ref middle_sub_pats, ref back_sub_pats) => { - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - - for sub_pat in front_sub_pats { - walk_pattern(cx, &**sub_pat, scope_stack, scope_map); - } - - if let Some(ref sub_pat) = *middle_sub_pats { - walk_pattern(cx, &**sub_pat, scope_stack, scope_map); - } - - for sub_pat in back_sub_pats { - walk_pattern(cx, &**sub_pat, scope_stack, scope_map); - } - } - - ast::PatMac(_) => { - cx.sess().span_bug(pat.span, "debuginfo::create_scope_map() - \ - Found unexpanded macro."); - } - } - } - - fn walk_expr(cx: &CrateContext, - exp: &ast::Expr, - scope_stack: &mut Vec , - scope_map: &mut NodeMap) { - - scope_map.insert(exp.id, scope_stack.last().unwrap().scope_metadata); - - match exp.node { - ast::ExprLit(_) | - ast::ExprBreak(_) | - ast::ExprAgain(_) | - ast::ExprPath(..) => {} - - ast::ExprCast(ref sub_exp, _) | - ast::ExprAddrOf(_, ref sub_exp) | - ast::ExprField(ref sub_exp, _) | - ast::ExprTupField(ref sub_exp, _) | - ast::ExprParen(ref sub_exp) => - walk_expr(cx, &**sub_exp, scope_stack, scope_map), - - ast::ExprBox(ref place, ref sub_expr) => { - place.as_ref().map( - |e| walk_expr(cx, &**e, scope_stack, scope_map)); - walk_expr(cx, &**sub_expr, scope_stack, scope_map); - } - - ast::ExprRet(ref exp_opt) => match *exp_opt { - Some(ref sub_exp) => walk_expr(cx, &**sub_exp, scope_stack, scope_map), - None => () - }, - - ast::ExprUnary(_, ref sub_exp) => { - walk_expr(cx, &**sub_exp, scope_stack, scope_map); - } - - ast::ExprAssignOp(_, ref lhs, ref rhs) | - ast::ExprIndex(ref lhs, ref rhs) | - ast::ExprBinary(_, ref lhs, ref rhs) => { - walk_expr(cx, &**lhs, scope_stack, scope_map); - walk_expr(cx, &**rhs, scope_stack, scope_map); - } - - ast::ExprRange(ref start, ref end) => { - start.as_ref().map(|e| walk_expr(cx, &**e, scope_stack, scope_map)); - end.as_ref().map(|e| walk_expr(cx, &**e, scope_stack, scope_map)); - } - - ast::ExprVec(ref init_expressions) | - ast::ExprTup(ref init_expressions) => { - for ie in init_expressions { - walk_expr(cx, &**ie, scope_stack, scope_map); - } - } - - ast::ExprAssign(ref sub_exp1, ref sub_exp2) | - ast::ExprRepeat(ref sub_exp1, ref sub_exp2) => { - walk_expr(cx, &**sub_exp1, scope_stack, scope_map); - walk_expr(cx, &**sub_exp2, scope_stack, scope_map); - } - - ast::ExprIf(ref cond_exp, ref then_block, ref opt_else_exp) => { - walk_expr(cx, &**cond_exp, scope_stack, scope_map); - - with_new_scope(cx, - then_block.span, - scope_stack, - scope_map, - |cx, scope_stack, scope_map| { - walk_block(cx, &**then_block, scope_stack, scope_map); - }); - - match *opt_else_exp { - Some(ref else_exp) => - walk_expr(cx, &**else_exp, scope_stack, scope_map), - _ => () - } - } - - ast::ExprIfLet(..) => { - cx.sess().span_bug(exp.span, "debuginfo::create_scope_map() - \ - Found unexpanded if-let."); - } - - ast::ExprWhile(ref cond_exp, ref loop_body, _) => { - walk_expr(cx, &**cond_exp, scope_stack, scope_map); - - with_new_scope(cx, - loop_body.span, - scope_stack, - scope_map, - |cx, scope_stack, scope_map| { - walk_block(cx, &**loop_body, scope_stack, scope_map); - }) - } - - ast::ExprWhileLet(..) => { - cx.sess().span_bug(exp.span, "debuginfo::create_scope_map() - \ - Found unexpanded while-let."); - } - - ast::ExprForLoop(..) => { - cx.sess().span_bug(exp.span, "debuginfo::create_scope_map() - \ - Found unexpanded for loop."); - } - - ast::ExprMac(_) => { - cx.sess().span_bug(exp.span, "debuginfo::create_scope_map() - \ - Found unexpanded macro."); - } - - ast::ExprLoop(ref block, _) | - ast::ExprBlock(ref block) => { - with_new_scope(cx, - block.span, - scope_stack, - scope_map, - |cx, scope_stack, scope_map| { - walk_block(cx, &**block, scope_stack, scope_map); - }) - } - - ast::ExprClosure(_, ref decl, ref block) => { - with_new_scope(cx, - block.span, - scope_stack, - scope_map, - |cx, scope_stack, scope_map| { - for &ast::Arg { pat: ref pattern, .. } in &decl.inputs { - walk_pattern(cx, &**pattern, scope_stack, scope_map); - } - - walk_block(cx, &**block, scope_stack, scope_map); - }) - } - - ast::ExprCall(ref fn_exp, ref args) => { - walk_expr(cx, &**fn_exp, scope_stack, scope_map); - - for arg_exp in args { - walk_expr(cx, &**arg_exp, scope_stack, scope_map); - } - } - - ast::ExprMethodCall(_, _, ref args) => { - for arg_exp in args { - walk_expr(cx, &**arg_exp, scope_stack, scope_map); - } - } - - ast::ExprMatch(ref discriminant_exp, ref arms, _) => { - walk_expr(cx, &**discriminant_exp, scope_stack, scope_map); - - // For each arm we have to first walk the pattern as these might - // introduce new artificial scopes. It should be sufficient to - // walk only one pattern per arm, as they all must contain the - // same binding names. - - for arm_ref in arms { - let arm_span = arm_ref.pats[0].span; - - with_new_scope(cx, - arm_span, - scope_stack, - scope_map, - |cx, scope_stack, scope_map| { - for pat in &arm_ref.pats { - walk_pattern(cx, &**pat, scope_stack, scope_map); - } - - if let Some(ref guard_exp) = arm_ref.guard { - walk_expr(cx, &**guard_exp, scope_stack, scope_map) - } - - walk_expr(cx, &*arm_ref.body, scope_stack, scope_map); - }) - } - } - - ast::ExprStruct(_, ref fields, ref base_exp) => { - for &ast::Field { expr: ref exp, .. } in fields { - walk_expr(cx, &**exp, scope_stack, scope_map); - } - - match *base_exp { - Some(ref exp) => walk_expr(cx, &**exp, scope_stack, scope_map), - None => () - } - } - - ast::ExprInlineAsm(ast::InlineAsm { ref inputs, - ref outputs, - .. }) => { - // inputs, outputs: Vec<(String, P)> - for &(_, ref exp) in inputs { - walk_expr(cx, &**exp, scope_stack, scope_map); - } - - for &(_, ref exp, _) in outputs { - walk_expr(cx, &**exp, scope_stack, scope_map); - } - } - } - } -} - - //=----------------------------------------------------------------------------- // Type Names for Debug Info //=----------------------------------------------------------------------------- diff --git a/src/librustc_trans/trans/debuginfo/utils.rs b/src/librustc_trans/trans/debuginfo/utils.rs new file mode 100644 index 00000000000..161128279fe --- /dev/null +++ b/src/librustc_trans/trans/debuginfo/utils.rs @@ -0,0 +1,584 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://!rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Utility Functions. + +use super::{FunctionDebugContext, CrateDebugContext, namespace_for_item, file_metadata}; + +use llvm; +use llvm::debuginfo::{DIScope, DISubprogram, DIBuilderRef}; +use trans::machine; +use trans::common::{CrateContext, FunctionContext}; +use trans::type_::Type; +use middle::pat_util; +use util::nodemap::NodeMap; + +use libc::c_uint; +use syntax::codemap::{Span, Pos}; +use syntax::{ast, codemap, ast_util}; + + +pub fn contains_nodebug_attribute(attributes: &[ast::Attribute]) -> bool { + attributes.iter().any(|attr| { + let meta_item: &ast::MetaItem = &*attr.node.value; + match meta_item.node { + ast::MetaWord(ref value) => &value[..] == "no_debug", + _ => false + } + }) +} + +/// Return codemap::Loc corresponding to the beginning of the span +pub fn span_start(cx: &CrateContext, span: Span) -> codemap::Loc { + cx.sess().codemap().lookup_char_pos(span.lo) +} + +pub fn size_and_align_of(cx: &CrateContext, llvm_type: Type) -> (u64, u64) { + (machine::llsize_of_alloc(cx, llvm_type), machine::llalign_of_min(cx, llvm_type) as u64) +} + +pub fn bytes_to_bits(bytes: u64) -> u64 { + bytes * 8 +} + +#[inline] +pub fn debug_context<'a, 'tcx>(cx: &'a CrateContext<'a, 'tcx>) + -> &'a CrateDebugContext<'tcx> { + let debug_context: &'a CrateDebugContext<'tcx> = cx.dbg_cx().as_ref().unwrap(); + debug_context +} + +#[inline] +#[allow(non_snake_case)] +pub fn DIB(cx: &CrateContext) -> DIBuilderRef { + cx.dbg_cx().as_ref().unwrap().builder +} + +pub fn fn_should_be_ignored(fcx: &FunctionContext) -> bool { + match fcx.debug_context { + FunctionDebugContext::RegularContext(_) => false, + _ => true + } +} + +pub fn assert_type_for_node_id(cx: &CrateContext, + node_id: ast::NodeId, + error_reporting_span: Span) { + if !cx.tcx().node_types().contains_key(&node_id) { + cx.sess().span_bug(error_reporting_span, + "debuginfo: Could not find type for node id!"); + } +} + +pub fn get_namespace_and_span_for_item(cx: &CrateContext, def_id: ast::DefId) + -> (DIScope, Span) { + let containing_scope = namespace_for_item(cx, def_id).scope; + let definition_span = if def_id.krate == ast::LOCAL_CRATE { + cx.tcx().map.span(def_id.node) + } else { + // For external items there is no span information + codemap::DUMMY_SP + }; + + (containing_scope, definition_span) +} + +// This procedure builds the *scope map* for a given function, which maps any +// given ast::NodeId in the function's AST to the correct DIScope metadata instance. +// +// This builder procedure walks the AST in execution order and keeps track of +// what belongs to which scope, creating DIScope DIEs along the way, and +// introducing *artificial* lexical scope descriptors where necessary. These +// artificial scopes allow GDB to correctly handle name shadowing. +pub fn create_scope_map(cx: &CrateContext, + args: &[ast::Arg], + fn_entry_block: &ast::Block, + fn_metadata: DISubprogram, + fn_ast_id: ast::NodeId) + -> NodeMap { + let mut scope_map = NodeMap(); + + let def_map = &cx.tcx().def_map; + + struct ScopeStackEntry { + scope_metadata: DIScope, + name: Option + } + + let mut scope_stack = vec!(ScopeStackEntry { scope_metadata: fn_metadata, name: None }); + scope_map.insert(fn_ast_id, fn_metadata); + + // Push argument identifiers onto the stack so arguments integrate nicely + // with variable shadowing. + for arg in args { + pat_util::pat_bindings(def_map, &*arg.pat, |_, node_id, _, path1| { + scope_stack.push(ScopeStackEntry { scope_metadata: fn_metadata, + name: Some(path1.node.name) }); + scope_map.insert(node_id, fn_metadata); + }) + } + + // Clang creates a separate scope for function bodies, so let's do this too. + with_new_scope(cx, + fn_entry_block.span, + &mut scope_stack, + &mut scope_map, + |cx, scope_stack, scope_map| { + walk_block(cx, fn_entry_block, scope_stack, scope_map); + }); + + return scope_map; + + + // local helper functions for walking the AST. + fn with_new_scope(cx: &CrateContext, + scope_span: Span, + scope_stack: &mut Vec , + scope_map: &mut NodeMap, + inner_walk: F) where + F: FnOnce(&CrateContext, &mut Vec, &mut NodeMap), + { + // Create a new lexical scope and push it onto the stack + let loc = cx.sess().codemap().lookup_char_pos(scope_span.lo); + let file_metadata = file_metadata(cx, &loc.file.name); + let parent_scope = scope_stack.last().unwrap().scope_metadata; + + let scope_metadata = unsafe { + llvm::LLVMDIBuilderCreateLexicalBlock( + DIB(cx), + parent_scope, + file_metadata, + loc.line as c_uint, + loc.col.to_usize() as c_uint) + }; + + scope_stack.push(ScopeStackEntry { scope_metadata: scope_metadata, name: None }); + + inner_walk(cx, scope_stack, scope_map); + + // pop artificial scopes + while scope_stack.last().unwrap().name.is_some() { + scope_stack.pop(); + } + + if scope_stack.last().unwrap().scope_metadata != scope_metadata { + cx.sess().span_bug(scope_span, "debuginfo: Inconsistency in scope management."); + } + + scope_stack.pop(); + } + + fn walk_block(cx: &CrateContext, + block: &ast::Block, + scope_stack: &mut Vec , + scope_map: &mut NodeMap) { + scope_map.insert(block.id, scope_stack.last().unwrap().scope_metadata); + + // The interesting things here are statements and the concluding expression. + for statement in &block.stmts { + scope_map.insert(ast_util::stmt_id(&**statement), + scope_stack.last().unwrap().scope_metadata); + + match statement.node { + ast::StmtDecl(ref decl, _) => + walk_decl(cx, &**decl, scope_stack, scope_map), + ast::StmtExpr(ref exp, _) | + ast::StmtSemi(ref exp, _) => + walk_expr(cx, &**exp, scope_stack, scope_map), + ast::StmtMac(..) => () // Ignore macros (which should be expanded anyway). + } + } + + if let Some(ref exp) = block.expr { + walk_expr(cx, &**exp, scope_stack, scope_map); + } + } + + fn walk_decl(cx: &CrateContext, + decl: &ast::Decl, + scope_stack: &mut Vec , + scope_map: &mut NodeMap) { + match *decl { + codemap::Spanned { node: ast::DeclLocal(ref local), .. } => { + scope_map.insert(local.id, scope_stack.last().unwrap().scope_metadata); + + walk_pattern(cx, &*local.pat, scope_stack, scope_map); + + if let Some(ref exp) = local.init { + walk_expr(cx, &**exp, scope_stack, scope_map); + } + } + _ => () + } + } + + fn walk_pattern(cx: &CrateContext, + pat: &ast::Pat, + scope_stack: &mut Vec , + scope_map: &mut NodeMap) { + + let def_map = &cx.tcx().def_map; + + // Unfortunately, we cannot just use pat_util::pat_bindings() or + // ast_util::walk_pat() here because we have to visit *all* nodes in + // order to put them into the scope map. The above functions don't do that. + match pat.node { + ast::PatIdent(_, ref path1, ref sub_pat_opt) => { + + // Check if this is a binding. If so we need to put it on the + // scope stack and maybe introduce an artificial scope + if pat_util::pat_is_binding(def_map, &*pat) { + + let name = path1.node.name; + + // LLVM does not properly generate 'DW_AT_start_scope' fields + // for variable DIEs. For this reason we have to introduce + // an artificial scope at bindings whenever a variable with + // the same name is declared in *any* parent scope. + // + // Otherwise the following error occurs: + // + // let x = 10; + // + // do_something(); // 'gdb print x' correctly prints 10 + // + // { + // do_something(); // 'gdb print x' prints 0, because it + // // already reads the uninitialized 'x' + // // from the next line... + // let x = 100; + // do_something(); // 'gdb print x' correctly prints 100 + // } + + // Is there already a binding with that name? + // N.B.: this comparison must be UNhygienic... because + // gdb knows nothing about the context, so any two + // variables with the same name will cause the problem. + let need_new_scope = scope_stack + .iter() + .any(|entry| entry.name == Some(name)); + + if need_new_scope { + // Create a new lexical scope and push it onto the stack + let loc = cx.sess().codemap().lookup_char_pos(pat.span.lo); + let file_metadata = file_metadata(cx, &loc.file.name); + let parent_scope = scope_stack.last().unwrap().scope_metadata; + + let scope_metadata = unsafe { + llvm::LLVMDIBuilderCreateLexicalBlock( + DIB(cx), + parent_scope, + file_metadata, + loc.line as c_uint, + loc.col.to_usize() as c_uint) + }; + + scope_stack.push(ScopeStackEntry { + scope_metadata: scope_metadata, + name: Some(name) + }); + + } else { + // Push a new entry anyway so the name can be found + let prev_metadata = scope_stack.last().unwrap().scope_metadata; + scope_stack.push(ScopeStackEntry { + scope_metadata: prev_metadata, + name: Some(name) + }); + } + } + + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + + if let Some(ref sub_pat) = *sub_pat_opt { + walk_pattern(cx, &**sub_pat, scope_stack, scope_map); + } + } + + ast::PatWild(_) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + } + + ast::PatEnum(_, ref sub_pats_opt) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + + if let Some(ref sub_pats) = *sub_pats_opt { + for p in sub_pats { + walk_pattern(cx, &**p, scope_stack, scope_map); + } + } + } + + ast::PatQPath(..) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + } + + ast::PatStruct(_, ref field_pats, _) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + + for &codemap::Spanned { + node: ast::FieldPat { pat: ref sub_pat, .. }, + .. + } in field_pats.iter() { + walk_pattern(cx, &**sub_pat, scope_stack, scope_map); + } + } + + ast::PatTup(ref sub_pats) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + + for sub_pat in sub_pats { + walk_pattern(cx, &**sub_pat, scope_stack, scope_map); + } + } + + ast::PatBox(ref sub_pat) | ast::PatRegion(ref sub_pat, _) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + walk_pattern(cx, &**sub_pat, scope_stack, scope_map); + } + + ast::PatLit(ref exp) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + walk_expr(cx, &**exp, scope_stack, scope_map); + } + + ast::PatRange(ref exp1, ref exp2) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + walk_expr(cx, &**exp1, scope_stack, scope_map); + walk_expr(cx, &**exp2, scope_stack, scope_map); + } + + ast::PatVec(ref front_sub_pats, ref middle_sub_pats, ref back_sub_pats) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + + for sub_pat in front_sub_pats { + walk_pattern(cx, &**sub_pat, scope_stack, scope_map); + } + + if let Some(ref sub_pat) = *middle_sub_pats { + walk_pattern(cx, &**sub_pat, scope_stack, scope_map); + } + + for sub_pat in back_sub_pats { + walk_pattern(cx, &**sub_pat, scope_stack, scope_map); + } + } + + ast::PatMac(_) => { + cx.sess().span_bug(pat.span, "debuginfo::create_scope_map() - \ + Found unexpanded macro."); + } + } + } + + fn walk_expr(cx: &CrateContext, + exp: &ast::Expr, + scope_stack: &mut Vec , + scope_map: &mut NodeMap) { + + scope_map.insert(exp.id, scope_stack.last().unwrap().scope_metadata); + + match exp.node { + ast::ExprLit(_) | + ast::ExprBreak(_) | + ast::ExprAgain(_) | + ast::ExprPath(..) => {} + + ast::ExprCast(ref sub_exp, _) | + ast::ExprAddrOf(_, ref sub_exp) | + ast::ExprField(ref sub_exp, _) | + ast::ExprTupField(ref sub_exp, _) | + ast::ExprParen(ref sub_exp) => + walk_expr(cx, &**sub_exp, scope_stack, scope_map), + + ast::ExprBox(ref place, ref sub_expr) => { + place.as_ref().map( + |e| walk_expr(cx, &**e, scope_stack, scope_map)); + walk_expr(cx, &**sub_expr, scope_stack, scope_map); + } + + ast::ExprRet(ref exp_opt) => match *exp_opt { + Some(ref sub_exp) => walk_expr(cx, &**sub_exp, scope_stack, scope_map), + None => () + }, + + ast::ExprUnary(_, ref sub_exp) => { + walk_expr(cx, &**sub_exp, scope_stack, scope_map); + } + + ast::ExprAssignOp(_, ref lhs, ref rhs) | + ast::ExprIndex(ref lhs, ref rhs) | + ast::ExprBinary(_, ref lhs, ref rhs) => { + walk_expr(cx, &**lhs, scope_stack, scope_map); + walk_expr(cx, &**rhs, scope_stack, scope_map); + } + + ast::ExprRange(ref start, ref end) => { + start.as_ref().map(|e| walk_expr(cx, &**e, scope_stack, scope_map)); + end.as_ref().map(|e| walk_expr(cx, &**e, scope_stack, scope_map)); + } + + ast::ExprVec(ref init_expressions) | + ast::ExprTup(ref init_expressions) => { + for ie in init_expressions { + walk_expr(cx, &**ie, scope_stack, scope_map); + } + } + + ast::ExprAssign(ref sub_exp1, ref sub_exp2) | + ast::ExprRepeat(ref sub_exp1, ref sub_exp2) => { + walk_expr(cx, &**sub_exp1, scope_stack, scope_map); + walk_expr(cx, &**sub_exp2, scope_stack, scope_map); + } + + ast::ExprIf(ref cond_exp, ref then_block, ref opt_else_exp) => { + walk_expr(cx, &**cond_exp, scope_stack, scope_map); + + with_new_scope(cx, + then_block.span, + scope_stack, + scope_map, + |cx, scope_stack, scope_map| { + walk_block(cx, &**then_block, scope_stack, scope_map); + }); + + match *opt_else_exp { + Some(ref else_exp) => + walk_expr(cx, &**else_exp, scope_stack, scope_map), + _ => () + } + } + + ast::ExprIfLet(..) => { + cx.sess().span_bug(exp.span, "debuginfo::create_scope_map() - \ + Found unexpanded if-let."); + } + + ast::ExprWhile(ref cond_exp, ref loop_body, _) => { + walk_expr(cx, &**cond_exp, scope_stack, scope_map); + + with_new_scope(cx, + loop_body.span, + scope_stack, + scope_map, + |cx, scope_stack, scope_map| { + walk_block(cx, &**loop_body, scope_stack, scope_map); + }) + } + + ast::ExprWhileLet(..) => { + cx.sess().span_bug(exp.span, "debuginfo::create_scope_map() - \ + Found unexpanded while-let."); + } + + ast::ExprForLoop(..) => { + cx.sess().span_bug(exp.span, "debuginfo::create_scope_map() - \ + Found unexpanded for loop."); + } + + ast::ExprMac(_) => { + cx.sess().span_bug(exp.span, "debuginfo::create_scope_map() - \ + Found unexpanded macro."); + } + + ast::ExprLoop(ref block, _) | + ast::ExprBlock(ref block) => { + with_new_scope(cx, + block.span, + scope_stack, + scope_map, + |cx, scope_stack, scope_map| { + walk_block(cx, &**block, scope_stack, scope_map); + }) + } + + ast::ExprClosure(_, ref decl, ref block) => { + with_new_scope(cx, + block.span, + scope_stack, + scope_map, + |cx, scope_stack, scope_map| { + for &ast::Arg { pat: ref pattern, .. } in &decl.inputs { + walk_pattern(cx, &**pattern, scope_stack, scope_map); + } + + walk_block(cx, &**block, scope_stack, scope_map); + }) + } + + ast::ExprCall(ref fn_exp, ref args) => { + walk_expr(cx, &**fn_exp, scope_stack, scope_map); + + for arg_exp in args { + walk_expr(cx, &**arg_exp, scope_stack, scope_map); + } + } + + ast::ExprMethodCall(_, _, ref args) => { + for arg_exp in args { + walk_expr(cx, &**arg_exp, scope_stack, scope_map); + } + } + + ast::ExprMatch(ref discriminant_exp, ref arms, _) => { + walk_expr(cx, &**discriminant_exp, scope_stack, scope_map); + + // For each arm we have to first walk the pattern as these might + // introduce new artificial scopes. It should be sufficient to + // walk only one pattern per arm, as they all must contain the + // same binding names. + + for arm_ref in arms { + let arm_span = arm_ref.pats[0].span; + + with_new_scope(cx, + arm_span, + scope_stack, + scope_map, + |cx, scope_stack, scope_map| { + for pat in &arm_ref.pats { + walk_pattern(cx, &**pat, scope_stack, scope_map); + } + + if let Some(ref guard_exp) = arm_ref.guard { + walk_expr(cx, &**guard_exp, scope_stack, scope_map) + } + + walk_expr(cx, &*arm_ref.body, scope_stack, scope_map); + }) + } + } + + ast::ExprStruct(_, ref fields, ref base_exp) => { + for &ast::Field { expr: ref exp, .. } in fields { + walk_expr(cx, &**exp, scope_stack, scope_map); + } + + match *base_exp { + Some(ref exp) => walk_expr(cx, &**exp, scope_stack, scope_map), + None => () + } + } + + ast::ExprInlineAsm(ast::InlineAsm { ref inputs, + ref outputs, + .. }) => { + // inputs, outputs: Vec<(String, P)> + for &(_, ref exp) in inputs { + walk_expr(cx, &**exp, scope_stack, scope_map); + } + + for &(_, ref exp, _) in outputs { + walk_expr(cx, &**exp, scope_stack, scope_map); + } + } + } + } +}