debuginfo: extract utils.rs
This commit is contained in:
parent
5753c8d6ca
commit
a015547894
2 changed files with 589 additions and 563 deletions
|
@ -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<DIScope> {
|
||||
let mut scope_map = NodeMap();
|
||||
|
||||
let def_map = &cx.tcx().def_map;
|
||||
|
||||
struct ScopeStackEntry {
|
||||
scope_metadata: DIScope,
|
||||
name: Option<ast::Name>
|
||||
}
|
||||
|
||||
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<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 = 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<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(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<ScopeStackEntry> ,
|
||||
scope_map: &mut NodeMap<DIScope>) {
|
||||
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<ScopeStackEntry> ,
|
||||
scope_map: &mut NodeMap<DIScope>) {
|
||||
|
||||
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<ScopeStackEntry> ,
|
||||
scope_map: &mut NodeMap<DIScope>) {
|
||||
|
||||
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<Expr>)>
|
||||
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
|
||||
//=-----------------------------------------------------------------------------
|
||||
|
|
584
src/librustc_trans/trans/debuginfo/utils.rs
Normal file
584
src/librustc_trans/trans/debuginfo/utils.rs
Normal file
|
@ -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 <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.
|
||||
|
||||
// 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<DIScope> {
|
||||
let mut scope_map = NodeMap();
|
||||
|
||||
let def_map = &cx.tcx().def_map;
|
||||
|
||||
struct ScopeStackEntry {
|
||||
scope_metadata: DIScope,
|
||||
name: Option<ast::Name>
|
||||
}
|
||||
|
||||
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<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 = 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<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(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<ScopeStackEntry> ,
|
||||
scope_map: &mut NodeMap<DIScope>) {
|
||||
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<ScopeStackEntry> ,
|
||||
scope_map: &mut NodeMap<DIScope>) {
|
||||
|
||||
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<ScopeStackEntry> ,
|
||||
scope_map: &mut NodeMap<DIScope>) {
|
||||
|
||||
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<Expr>)>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue