blob: fe6a48d4c559dac732df51158504bdb562f3f466 [file] [log] [blame]
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use super::FunctionDebugContext;
use super::metadata::file_metadata;
use super::utils::{DIB, span_start};
use llvm;
use llvm::debuginfo::{DIScope, DISubprogram};
use common::{CrateContext, FunctionContext};
use rustc::hir::pat_util;
use rustc::mir::repr::{Mir, VisibilityScope};
use rustc::util::nodemap::NodeMap;
use libc::c_uint;
use std::ptr;
use syntax_pos::{Span, Pos};
use syntax::{ast, codemap};
use rustc_data_structures::bitvec::BitVector;
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use rustc::hir::{self, PatKind};
// 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: &[hir::Arg],
fn_entry_block: &hir::Block,
fn_metadata: DISubprogram,
fn_ast_id: ast::NodeId)
-> NodeMap<DIScope> {
let mut scope_map = NodeMap();
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(&arg.pat, |_, node_id, _, path1| {
scope_stack.push(ScopeStackEntry { scope_metadata: fn_metadata,
name: Some(path1.node) });
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;
}
/// Produce DIScope DIEs for each MIR Scope which has variables defined in it.
/// If debuginfo is disabled, the returned vector is empty.
pub fn create_mir_scopes(fcx: &FunctionContext) -> IndexVec<VisibilityScope, DIScope> {
let mir = fcx.mir.clone().expect("create_mir_scopes: missing MIR for fn");
let mut scopes = IndexVec::from_elem(ptr::null_mut(), &mir.visibility_scopes);
let fn_metadata = match fcx.debug_context {
FunctionDebugContext::RegularContext(box ref data) => data.fn_metadata,
FunctionDebugContext::DebugInfoDisabled |
FunctionDebugContext::FunctionWithoutDebugInfo => {
return scopes;
}
};
// Find all the scopes with variables defined in them.
let mut has_variables = BitVector::new(mir.visibility_scopes.len());
for var in &mir.var_decls {
has_variables.insert(var.source_info.scope.index());
}
// Instantiate all scopes.
for idx in 0..mir.visibility_scopes.len() {
let scope = VisibilityScope::new(idx);
make_mir_scope(fcx.ccx, &mir, &has_variables, fn_metadata, scope, &mut scopes);
}
scopes
}
fn make_mir_scope(ccx: &CrateContext,
mir: &Mir,
has_variables: &BitVector,
fn_metadata: DISubprogram,
scope: VisibilityScope,
scopes: &mut IndexVec<VisibilityScope, DIScope>) {
if !scopes[scope].is_null() {
return;
}
let scope_data = &mir.visibility_scopes[scope];
let parent_scope = if let Some(parent) = scope_data.parent_scope {
make_mir_scope(ccx, mir, has_variables, fn_metadata, parent, scopes);
scopes[parent]
} else {
// The root is the function itself.
scopes[scope] = fn_metadata;
return;
};
if !has_variables.contains(scope.index()) {
// Do not create a DIScope if there are no variables
// defined in this MIR Scope, to avoid debuginfo bloat.
// However, we don't skip creating a nested scope if
// our parent is the root, because we might want to
// put arguments in the root and not have shadowing.
if parent_scope != fn_metadata {
scopes[scope] = parent_scope;
return;
}
}
let loc = span_start(ccx, scope_data.span);
scopes[scope] = unsafe {
let file_metadata = file_metadata(ccx, &loc.file.name, &loc.file.abs_path);
llvm::LLVMRustDIBuilderCreateLexicalBlock(
DIB(ccx),
parent_scope,
file_metadata,
loc.line as c_uint,
loc.col.to_usize() as c_uint)
};
}
// local helper functions for walking the AST.
fn with_new_scope<F>(cx: &CrateContext,
scope_span: Span,
scope_stack: &mut Vec<ScopeStackEntry> ,
scope_map: &mut NodeMap<DIScope>,
inner_walk: F) where
F: FnOnce(&CrateContext, &mut Vec<ScopeStackEntry>, &mut NodeMap<DIScope>),
{
// Create a new lexical scope and push it onto the stack
let loc = span_start(cx, scope_span);
let file_metadata = file_metadata(cx, &loc.file.name, &loc.file.abs_path);
let parent_scope = scope_stack.last().unwrap().scope_metadata;
let scope_metadata = unsafe {
llvm::LLVMRustDIBuilderCreateLexicalBlock(
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 {
span_bug!(scope_span, "debuginfo: Inconsistency in scope management.");
}
scope_stack.pop();
}
struct ScopeStackEntry {
scope_metadata: DIScope,
name: Option<ast::Name>
}
fn walk_block(cx: &CrateContext,
block: &hir::Block,
scope_stack: &mut Vec<ScopeStackEntry> ,
scope_map: &mut NodeMap<DIScope>) {
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(statement.node.id(),
scope_stack.last().unwrap().scope_metadata);
match statement.node {
hir::StmtDecl(ref decl, _) =>
walk_decl(cx, &decl, scope_stack, scope_map),
hir::StmtExpr(ref exp, _) |
hir::StmtSemi(ref exp, _) =>
walk_expr(cx, &exp, scope_stack, scope_map),
}
}
if let Some(ref exp) = block.expr {
walk_expr(cx, &exp, scope_stack, scope_map);
}
}
fn walk_decl(cx: &CrateContext,
decl: &hir::Decl,
scope_stack: &mut Vec<ScopeStackEntry> ,
scope_map: &mut NodeMap<DIScope>) {
match *decl {
codemap::Spanned { node: hir::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: &hir::Pat,
scope_stack: &mut Vec<ScopeStackEntry> ,
scope_map: &mut NodeMap<DIScope>) {
// 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 {
PatKind::Binding(_, ref path1, ref sub_pat_opt) => {
// 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 name = path1.node;
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 = span_start(cx, pat.span);
let file_metadata = file_metadata(cx, &loc.file.name, &loc.file.abs_path);
let parent_scope = scope_stack.last().unwrap().scope_metadata;
let scope_metadata = unsafe {
llvm::LLVMRustDIBuilderCreateLexicalBlock(
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);
}
}
PatKind::Wild => {
scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
}
PatKind::TupleStruct(_, ref sub_pats, _) => {
scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
for p in sub_pats {
walk_pattern(cx, &p, scope_stack, scope_map);
}
}
PatKind::Path(..) => {
scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
}
PatKind::Struct(_, ref field_pats, _) => {
scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
for &codemap::Spanned {
node: hir::FieldPat { pat: ref sub_pat, .. },
..
} in field_pats {
walk_pattern(cx, &sub_pat, scope_stack, scope_map);
}
}
PatKind::Tuple(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);
}
}
PatKind::Box(ref sub_pat) | PatKind::Ref(ref sub_pat, _) => {
scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
walk_pattern(cx, &sub_pat, scope_stack, scope_map);
}
PatKind::Lit(ref exp) => {
scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
walk_expr(cx, &exp, scope_stack, scope_map);
}
PatKind::Range(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);
}
PatKind::Vec(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);
}
}
}
}
fn walk_expr(cx: &CrateContext,
exp: &hir::Expr,
scope_stack: &mut Vec<ScopeStackEntry> ,
scope_map: &mut NodeMap<DIScope>) {
scope_map.insert(exp.id, scope_stack.last().unwrap().scope_metadata);
match exp.node {
hir::ExprLit(_) |
hir::ExprBreak(_) |
hir::ExprAgain(_) |
hir::ExprPath(..) => {}
hir::ExprCast(ref sub_exp, _) |
hir::ExprType(ref sub_exp, _) |
hir::ExprAddrOf(_, ref sub_exp) |
hir::ExprField(ref sub_exp, _) |
hir::ExprTupField(ref sub_exp, _) =>
walk_expr(cx, &sub_exp, scope_stack, scope_map),
hir::ExprBox(ref sub_expr) => {
walk_expr(cx, &sub_expr, scope_stack, scope_map);
}
hir::ExprRet(ref exp_opt) => match *exp_opt {
Some(ref sub_exp) => walk_expr(cx, &sub_exp, scope_stack, scope_map),
None => ()
},
hir::ExprUnary(_, ref sub_exp) => {
walk_expr(cx, &sub_exp, scope_stack, scope_map);
}
hir::ExprAssignOp(_, ref lhs, ref rhs) |
hir::ExprIndex(ref lhs, ref rhs) |
hir::ExprBinary(_, ref lhs, ref rhs) => {
walk_expr(cx, &lhs, scope_stack, scope_map);
walk_expr(cx, &rhs, scope_stack, scope_map);
}
hir::ExprVec(ref init_expressions) |
hir::ExprTup(ref init_expressions) => {
for ie in init_expressions {
walk_expr(cx, &ie, scope_stack, scope_map);
}
}
hir::ExprAssign(ref sub_exp1, ref sub_exp2) |
hir::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);
}
hir::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),
_ => ()
}
}
hir::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);
})
}
hir::ExprLoop(ref block, _) |
hir::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);
})
}
hir::ExprClosure(_, ref decl, ref block, _) => {
with_new_scope(cx,
block.span,
scope_stack,
scope_map,
|cx, scope_stack, scope_map| {
for &hir::Arg { pat: ref pattern, .. } in &decl.inputs {
walk_pattern(cx, &pattern, scope_stack, scope_map);
}
walk_block(cx, &block, scope_stack, scope_map);
})
}
hir::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);
}
}
hir::ExprMethodCall(_, _, ref args) => {
for arg_exp in args {
walk_expr(cx, &arg_exp, scope_stack, scope_map);
}
}
hir::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);
})
}
}
hir::ExprStruct(_, ref fields, ref base_exp) => {
for &hir::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 => ()
}
}
hir::ExprInlineAsm(_, ref outputs, ref inputs) => {
for output in outputs {
walk_expr(cx, output, scope_stack, scope_map);
}
for input in inputs {
walk_expr(cx, input, scope_stack, scope_map);
}
}
}
}