From 659f4200960808fd6ad4e4305332b16ae1b403c4 Mon Sep 17 00:00:00 2001 From: Vadim Chugunov Date: Wed, 3 Aug 2016 18:51:17 -0700 Subject: [PATCH] Fix debug line number info for macro expansions. Macro expansions result in code tagged with completely different debug locations than the surrounding expressions. This wrecks havoc on debugger's ability the step over source lines. This change fixes the problem by tagging expanded code as "inlined" at the macro expansion site, which allows the debugger to sort it out. Note that only the outermost expansion is currently handled, stepping into a macro will still result in stepping craziness. --- src/librustc/middle/region.rs | 2 +- src/librustc_llvm/ffi.rs | 7 ++ .../debuginfo/create_scope_map.rs | 46 ++++++++-- src/librustc_trans/debuginfo/metadata.rs | 16 +++- src/librustc_trans/debuginfo/mod.rs | 9 +- src/librustc_trans/debuginfo/source_loc.rs | 74 ++++++++------- src/librustc_trans/intrinsic.rs | 2 +- src/librustc_trans/mir/mod.rs | 77 +++++++++++++--- src/rustllvm/RustWrapper.cpp | 24 +++++ src/test/debuginfo/macro-stepping.rs | 91 +++++++++++++++++++ 10 files changed, 286 insertions(+), 62 deletions(-) create mode 100644 src/test/debuginfo/macro-stepping.rs diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 6f0ad087dc589..faf2f7dae08c5 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -237,7 +237,7 @@ impl CodeExtent { // (This is the special case aluded to in the // doc-comment for this method) let stmt_span = blk.stmts[r.first_statement_index as usize].span; - Some(Span { lo: stmt_span.hi, ..blk.span }) + Some(Span { lo: stmt_span.hi, hi: blk.span.hi, expn_id: stmt_span.expn_id }) } } } diff --git a/src/librustc_llvm/ffi.rs b/src/librustc_llvm/ffi.rs index 6301c57c55540..9b089b1f82052 100644 --- a/src/librustc_llvm/ffi.rs +++ b/src/librustc_llvm/ffi.rs @@ -637,6 +637,8 @@ extern { pub fn LLVMAddNamedMetadataOperand(M: ModuleRef, Str: *const c_char, Val: ValueRef); + pub fn LLVMRustMetadataAsValue(context: ContextRef, metadata: MetadataRef) -> ValueRef; + pub fn LLVMRustValueAsMetadata(value: ValueRef) -> MetadataRef; /* Operations on scalar constants */ pub fn LLVMConstInt(IntTy: TypeRef, N: c_ulonglong, SignExtend: Bool) @@ -1796,6 +1798,11 @@ extern { Col: c_uint) -> DILexicalBlock; + pub fn LLVMRustDIBuilderCreateLexicalBlockFile(Builder: DIBuilderRef, + Scope: DIScope, + File: DIFile) + -> DILexicalBlock; + pub fn LLVMRustDIBuilderCreateStaticVariable(Builder: DIBuilderRef, Context: DIScope, Name: *const c_char, diff --git a/src/librustc_trans/debuginfo/create_scope_map.rs b/src/librustc_trans/debuginfo/create_scope_map.rs index fe6a48d4c559d..7947ff7c8a986 100644 --- a/src/librustc_trans/debuginfo/create_scope_map.rs +++ b/src/librustc_trans/debuginfo/create_scope_map.rs @@ -29,6 +29,8 @@ use rustc_data_structures::bitvec::BitVector; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use rustc::hir::{self, PatKind}; +use syntax_pos::BytePos; + // 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. // @@ -68,11 +70,29 @@ pub fn create_scope_map(cx: &CrateContext, return scope_map; } +#[derive(Clone, Copy, Debug)] +pub struct MirDebugScope { + pub scope_metadata: DIScope, + pub start_pos: BytePos, + pub end_pos: BytePos, +} + +impl MirDebugScope { + pub fn is_valid(&self) -> bool { + !self.scope_metadata.is_null() + } +} + /// 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 { +pub fn create_mir_scopes(fcx: &FunctionContext) -> IndexVec { 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 null_scope = MirDebugScope { + scope_metadata: ptr::null_mut(), + start_pos: BytePos(0), + end_pos: BytePos(0) + }; + let mut scopes = IndexVec::from_elem(null_scope, &mir.visibility_scopes); let fn_metadata = match fcx.debug_context { FunctionDebugContext::RegularContext(box ref data) => data.fn_metadata, @@ -102,8 +122,8 @@ fn make_mir_scope(ccx: &CrateContext, has_variables: &BitVector, fn_metadata: DISubprogram, scope: VisibilityScope, - scopes: &mut IndexVec) { - if !scopes[scope].is_null() { + scopes: &mut IndexVec) { + if scopes[scope].is_valid() { return; } @@ -113,7 +133,12 @@ fn make_mir_scope(ccx: &CrateContext, scopes[parent] } else { // The root is the function itself. - scopes[scope] = fn_metadata; + let loc = span_start(ccx, mir.span); + scopes[scope] = MirDebugScope { + scope_metadata: fn_metadata, + start_pos: loc.file.start_pos, + end_pos: loc.file.end_pos, + }; return; }; @@ -124,22 +149,27 @@ fn make_mir_scope(ccx: &CrateContext, // 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 { + if parent_scope.scope_metadata != 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); + let scope_metadata = unsafe { llvm::LLVMRustDIBuilderCreateLexicalBlock( DIB(ccx), - parent_scope, + parent_scope.scope_metadata, file_metadata, loc.line as c_uint, loc.col.to_usize() as c_uint) }; + scopes[scope] = MirDebugScope { + scope_metadata: scope_metadata, + start_pos: loc.file.start_pos, + end_pos: loc.file.end_pos, + }; } // local helper functions for walking the AST. diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs index 8011347d3eb12..50107ce6b4b50 100644 --- a/src/librustc_trans/debuginfo/metadata.rs +++ b/src/librustc_trans/debuginfo/metadata.rs @@ -23,7 +23,7 @@ use context::SharedCrateContext; use session::Session; use llvm::{self, ValueRef}; -use llvm::debuginfo::{DIType, DIFile, DIScope, DIDescriptor, DICompositeType}; +use llvm::debuginfo::{DIType, DIFile, DIScope, DIDescriptor, DICompositeType, DILexicalBlock}; use rustc::hir::def_id::DefId; use rustc::hir::pat_util; @@ -2104,3 +2104,17 @@ pub fn create_argument_metadata(bcx: Block, arg: &hir::Arg) { span); }) } + +// Creates an "extension" of an existing DIScope into another file. +pub fn extend_scope_to_file(ccx: &CrateContext, + scope_metadata: DIScope, + file: &syntax_pos::FileMap) + -> DILexicalBlock { + let file_metadata = file_metadata(ccx, &file.name, &file.abs_path); + unsafe { + llvm::LLVMRustDIBuilderCreateLexicalBlockFile( + DIB(ccx), + scope_metadata, + file_metadata) + } +} diff --git a/src/librustc_trans/debuginfo/mod.rs b/src/librustc_trans/debuginfo/mod.rs index 464c32c3cc725..7a79e2de713a0 100644 --- a/src/librustc_trans/debuginfo/mod.rs +++ b/src/librustc_trans/debuginfo/mod.rs @@ -43,7 +43,7 @@ use std::cell::{Cell, RefCell}; use std::ffi::CString; use std::ptr; -use syntax_pos::{self, Span, Pos}; +use syntax_pos::{self, Span}; use syntax::ast; use syntax::attr::IntType; @@ -55,7 +55,7 @@ pub mod metadata; mod create_scope_map; mod source_loc; -pub use self::create_scope_map::create_mir_scopes; +pub use self::create_scope_map::{create_mir_scopes, MirDebugScope}; pub use self::source_loc::start_emitting_source_locations; pub use self::source_loc::get_cleanup_debug_loc_for_ast_node; pub use self::source_loc::with_source_location_override; @@ -64,6 +64,7 @@ pub use self::metadata::create_argument_metadata; pub use self::metadata::create_captured_var_metadata; pub use self::metadata::create_global_var_metadata; pub use self::metadata::create_local_var_metadata; +pub use self::metadata::extend_scope_to_file; #[allow(non_upper_case_globals)] const DW_TAG_auto_variable: c_uint = 0x100; @@ -509,7 +510,7 @@ pub fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, argument_index) }; source_loc::set_debug_location(cx, None, - InternalDebugLocation::new(scope_metadata, loc.line, loc.col.to_usize())); + InternalDebugLocation::KnownLocation(scope_metadata, span.lo, None)); unsafe { let debug_loc = llvm::LLVMGetCurrentDebugLocation(cx.raw_builder()); let instr = llvm::LLVMRustDIBuilderInsertDeclareAtEnd( @@ -542,7 +543,7 @@ pub fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum DebugLoc { At(ast::NodeId, Span), - ScopeAt(DIScope, Span), + ScopeAt(DIScope, Span, Option<(DIScope, Span)>), None } diff --git a/src/librustc_trans/debuginfo/source_loc.rs b/src/librustc_trans/debuginfo/source_loc.rs index d288b9dcef70b..2789120bdc049 100644 --- a/src/librustc_trans/debuginfo/source_loc.rs +++ b/src/librustc_trans/debuginfo/source_loc.rs @@ -10,7 +10,7 @@ use self::InternalDebugLocation::*; -use super::utils::{debug_context, span_start}; +use super::utils::debug_context; use super::metadata::{scope_metadata,UNKNOWN_COLUMN_NUMBER}; use super::{FunctionDebugContext, DebugLoc}; @@ -21,7 +21,7 @@ use common::{NodeIdAndSpan, CrateContext, FunctionContext}; use libc::c_uint; use std::ptr; -use syntax_pos::{self, Span, Pos}; +use syntax_pos::{self, Span, BytePos}; use syntax::ast; pub fn get_cleanup_debug_loc_for_ast_node<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, @@ -108,21 +108,20 @@ pub fn set_source_location(fcx: &FunctionContext, } let dbg_loc = if function_debug_context.source_locations_enabled.get() { - let (scope, span) = match debug_loc { + match debug_loc { DebugLoc::At(node_id, span) => { - (scope_metadata(fcx, node_id, span), span) + let scope = scope_metadata(fcx, node_id, span); + KnownLocation(scope, span.lo, None) } - DebugLoc::ScopeAt(scope, span) => (scope, span), - DebugLoc::None => { - set_debug_location(fcx.ccx, builder, UnknownLocation); - return; + DebugLoc::ScopeAt(scope, span, inlined_at) => { + let inlined_at_loc = match inlined_at { + Some((scope, span)) => Some((scope, span.lo)), + None => None + }; + KnownLocation(scope, span.lo, inlined_at_loc) } - }; - - debug!("set_source_location: {}", - fcx.ccx.sess().codemap().span_to_string(span)); - let loc = span_start(fcx.ccx, span); - InternalDebugLocation::new(scope, loc.line, loc.col.to_usize()) + DebugLoc::None => UnknownLocation + } } else { UnknownLocation }; @@ -173,23 +172,12 @@ pub fn start_emitting_source_locations(fcx: &FunctionContext) { } } - #[derive(Copy, Clone, PartialEq)] pub enum InternalDebugLocation { - KnownLocation { scope: DIScope, line: usize, col: usize }, + KnownLocation(DIScope, BytePos, Option<(DIScope, BytePos)>), UnknownLocation } -impl InternalDebugLocation { - pub fn new(scope: DIScope, line: usize, col: usize) -> InternalDebugLocation { - KnownLocation { - scope: scope, - line: line, - col: col, - } - } -} - pub fn set_debug_location(cx: &CrateContext, builder: Option, debug_location: InternalDebugLocation) { @@ -199,19 +187,37 @@ pub fn set_debug_location(cx: &CrateContext, } } + let cm = cx.sess().codemap(); let metadata_node = match debug_location { - KnownLocation { scope, line, .. } => { - // Always set the column to zero like Clang and GCC - let col = UNKNOWN_COLUMN_NUMBER; - debug!("setting debug location to {} {}", line, col); - + KnownLocation(scope, pos, inlined_at) => { + + let inlined_at_loc = match inlined_at { + Some((scope, pos)) => { + let loc = cm.lookup_char_pos(pos); + unsafe { + llvm::LLVMRustValueAsMetadata( + llvm::LLVMRustDIBuilderCreateDebugLocation( + debug_context(cx).llcontext, + loc.line as c_uint, + UNKNOWN_COLUMN_NUMBER as c_uint, + scope, + ptr::null_mut()) + ) + } + }, + None => ptr::null_mut() + }; + + let loc = cm.lookup_char_pos(pos); + debug!("setting debug location to line {}", loc.line); + // Set the column to zero like Clang and GCC unsafe { llvm::LLVMRustDIBuilderCreateDebugLocation( debug_context(cx).llcontext, - line as c_uint, - col as c_uint, + loc.line as c_uint, + UNKNOWN_COLUMN_NUMBER as c_uint, scope, - ptr::null_mut()) + inlined_at_loc) } } UnknownLocation => { diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs index 2f27aed065d80..0206aa5797187 100644 --- a/src/librustc_trans/intrinsic.rs +++ b/src/librustc_trans/intrinsic.rs @@ -121,7 +121,7 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, let name = tcx.item_name(def_id).as_str(); let span = match call_debug_location { - DebugLoc::At(_, span) | DebugLoc::ScopeAt(_, span) => span, + DebugLoc::At(_, span) | DebugLoc::ScopeAt(_, span, _) => span, DebugLoc::None => { span_bug!(fcx.span.unwrap_or(DUMMY_SP), "intrinsic `{}` called with missing span", name); diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs index 8f723d288c9eb..eebadc50a2046 100644 --- a/src/librustc_trans/mir/mod.rs +++ b/src/librustc_trans/mir/mod.rs @@ -10,18 +10,17 @@ use libc::c_uint; use llvm::{self, ValueRef}; -use llvm::debuginfo::DIScope; use rustc::ty; use rustc::mir::repr as mir; use rustc::mir::tcx::LvalueTy; use session::config::FullDebugInfo; use base; use common::{self, Block, BlockAndBuilder, CrateContext, FunctionContext, C_null}; -use debuginfo::{self, declare_local, DebugLoc, VariableAccess, VariableKind}; +use debuginfo::{self, declare_local, DebugLoc, VariableAccess, VariableKind, FunctionDebugContext}; use machine; use type_of; -use syntax_pos::DUMMY_SP; +use syntax_pos::{DUMMY_SP, NO_EXPANSION, Span}; use syntax::parse::token::keywords; use std::ops::Deref; @@ -102,12 +101,64 @@ pub struct MirContext<'bcx, 'tcx:'bcx> { locals: IndexVec>, /// Debug information for MIR scopes. - scopes: IndexVec + scopes: IndexVec, } impl<'blk, 'tcx> MirContext<'blk, 'tcx> { - pub fn debug_loc(&self, source_info: mir::SourceInfo) -> DebugLoc { - DebugLoc::ScopeAt(self.scopes[source_info.scope], source_info.span) + pub fn debug_loc(&mut self, source_info: mir::SourceInfo) -> DebugLoc { + match self.fcx.debug_context { + FunctionDebugContext::DebugInfoDisabled | + FunctionDebugContext::FunctionWithoutDebugInfo => { + // Can't return DebugLoc::None here because intrinsic::trans_intrinsic_call() + // relies on debug location to obtain span of the call site. + return DebugLoc::ScopeAt(self.scopes[source_info.scope].scope_metadata, + source_info.span, None); + } + FunctionDebugContext::RegularContext(_) =>{} + } + + if source_info.span.expn_id == NO_EXPANSION { + let scope_metadata = self.scope_metadata_for_span(source_info.scope, source_info.span); + DebugLoc::ScopeAt(scope_metadata, source_info.span, None) + } else { + // If current span is a result of a macro expansion, we walk up the expansion chain + // till we reach the outermost expansion site. + let mut scope_id = source_info.scope; + let mut span = source_info.span; + let cm = self.fcx.ccx.sess().codemap(); + while span.expn_id != NO_EXPANSION { + span = cm.source_callsite(span); + } + // Ditto for scope + while self.mir.visibility_scopes[scope_id].span.expn_id != NO_EXPANSION { + let parent_scope_id = self.mir.visibility_scopes[scope_id].parent_scope; + scope_id = match parent_scope_id { + Some(id) => id, + None => break // Can happen if the current function is defined inside a macro. + } + } + let scope_metadata = self.scope_metadata_for_span(source_info.scope, source_info.span); + let inlined_at = (self.scope_metadata_for_span(scope_id, span), span); + DebugLoc::ScopeAt(scope_metadata, source_info.span, Some(inlined_at)) + } + } + + // DILocations inherit source file name from the parent DIScope. Due to macro expansions + // it may so happen that the current span belongs to a different file than the DIScope + // corresponding to span's containing visibility scope. If so, we need to create a DIScope + // "extension" into that file. + fn scope_metadata_for_span(&self, scope_id: mir::VisibilityScope, span: Span) + -> llvm::debuginfo::DIScope { + let scope_metadata = self.scopes[scope_id].scope_metadata; + if span.lo < self.scopes[scope_id].start_pos || + span.lo > self.scopes[scope_id].end_pos { + let cm = self.fcx.ccx.sess().codemap(); + debuginfo::extend_scope_to_file(self.fcx.ccx, + scope_metadata, + &cm.lookup_char_pos(span.lo).file) + } else { + scope_metadata + } } } @@ -162,8 +213,8 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { let args = arg_local_refs(&bcx, &mir, &scopes, &lvalue_locals); let vars = mir.var_decls.iter().enumerate().map(|(i, decl)| { let ty = bcx.monomorphize(&decl.ty); - let scope = scopes[decl.source_info.scope]; - let dbg = !scope.is_null() && bcx.sess().opts.debuginfo == FullDebugInfo; + let debug_scope = scopes[decl.source_info.scope]; + let dbg = debug_scope.is_valid() && bcx.sess().opts.debuginfo == FullDebugInfo; let local = mir.local_index(&mir::Lvalue::Var(mir::Var::new(i))).unwrap(); if !lvalue_locals.contains(local.index()) && !dbg { @@ -173,7 +224,7 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { let lvalue = LvalueRef::alloca(&bcx, ty, &decl.name.as_str()); if dbg { bcx.with_block(|bcx| { - declare_local(bcx, decl.name, ty, scope, + declare_local(bcx, decl.name, ty, debug_scope.scope_metadata, VariableAccess::DirectVariable { alloca: lvalue.llval }, VariableKind::LocalVariable, decl.source_info.span); }); @@ -230,7 +281,7 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { cleanup_kinds: cleanup_kinds, landing_pads: IndexVec::from_elem(None, mir.basic_blocks()), locals: locals, - scopes: scopes + scopes: scopes, }; let mut visited = BitVector::new(mir.basic_blocks().len()); @@ -270,7 +321,7 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { /// indirect. fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, mir: &mir::Mir<'tcx>, - scopes: &IndexVec, + scopes: &IndexVec, lvalue_locals: &BitVector) -> Vec> { let fcx = bcx.fcx(); @@ -280,8 +331,8 @@ fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, // Get the argument scope, if it exists and if we need it. let arg_scope = scopes[mir::ARGUMENT_VISIBILITY_SCOPE]; - let arg_scope = if !arg_scope.is_null() && bcx.sess().opts.debuginfo == FullDebugInfo { - Some(arg_scope) + let arg_scope = if arg_scope.is_valid() && bcx.sess().opts.debuginfo == FullDebugInfo { + Some(arg_scope.scope_metadata) } else { None }; diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 0da25e7ac57b7..3dd2a730d3a40 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -521,6 +521,15 @@ extern "C" LLVMRustMetadataRef LLVMRustDIBuilderCreateLexicalBlock( )); } +extern "C" LLVMRustMetadataRef LLVMRustDIBuilderCreateLexicalBlockFile( + LLVMRustDIBuilderRef Builder, + LLVMRustMetadataRef Scope, + LLVMRustMetadataRef File) { + return wrap(Builder->createLexicalBlockFile( + unwrapDI(Scope), + unwrapDI(File))); +} + extern "C" LLVMRustMetadataRef LLVMRustDIBuilderCreateStaticVariable( LLVMRustDIBuilderRef Builder, LLVMRustMetadataRef Context, @@ -1223,3 +1232,18 @@ extern "C" void LLVMRustUnsetComdat(LLVMValueRef V) { GlobalObject *GV = unwrap(V); GV->setComdat(nullptr); } + +extern "C" LLVMValueRef LLVMRustMetadataAsValue(LLVMContextRef context, + LLVMRustMetadataRef metadata) { + if (metadata) + return wrap(MetadataAsValue::get(*unwrap(context), unwrapDIptr(metadata))); + else + return NULL; +} + +extern "C" LLVMRustMetadataRef LLVMRustValueAsMetadata(LLVMValueRef value) { + if (value) + return wrap(cast(unwrap(value)->getMetadata())); + else + return NULL; +} diff --git a/src/test/debuginfo/macro-stepping.rs b/src/test/debuginfo/macro-stepping.rs new file mode 100644 index 0000000000000..3fb4d3d7bf7cc --- /dev/null +++ b/src/test/debuginfo/macro-stepping.rs @@ -0,0 +1,91 @@ +// Copyright 2013-2016 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. + +// ignore-windows +// min-lldb-version: 310 + +// compile-flags:-g -Zorbit + +// === GDB TESTS =================================================================================== + +// gdb-command:run +// gdb-command:next +// gdb-command:f +// gdb-check:[...]#loc1[...] +// gdb-command:next +// gdb-command:f +// gdb-check:[...]#loc2[...] +// gdb-command:next +// gdb-command:f +// gdb-check:[...]#loc3[...] +// gdb-command:next +// gdb-command:f +// gdb-check:[...]#loc4[...] +// gdb-command:next +// gdb-command:f +// gdb-check:[...]#loc5[...] + +// === LLDB TESTS ================================================================================== + +// lldb-command:set set stop-line-count-before 0 +// lldb-command:set set stop-line-count-after 1 +// Can't set both to zero or lldb will stop printing source at all. So it will output the current +// line and the next. We deal with this by having at least 2 lines between the #loc's + +// lldb-command:run +// lldb-command:next +// lldb-command:f +// lldb-check:[...]#loc1[...] +// lldb-command:next +// lldb-command:f +// lldb-check:[...]#loc2[...] +// lldb-command:next +// lldb-command:f +// lldb-check:[...]#loc3[...] +// lldb-command:next +// lldb-command:f +// lldb-check:[...]#loc4[...] +// lldb-command:next +// lldb-command:f +// lldb-check:[...]#loc5[...] + +#![allow(unused)] + +macro_rules! foo { + () => { + let a = 1; + let b = 2; + let c = 3; + } +} + +macro_rules! foo2 { + () => { + foo!(); + let x = 1; + foo!(); + } +} + +fn main() { + zzz(); // #break + + foo!(); // #loc1 + + foo2!(); // #loc2 + + let x = vec![42]; // #loc3 + + println!("Hello {}", "world"); // #loc4 + + zzz(); // #loc5 +} + +fn zzz() {()}