1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use self::InternalDebugLocation::*;
13 use super::utils::{debug_context, span_start};
14 use super::metadata::{scope_metadata,UNKNOWN_COLUMN_NUMBER};
15 use super::{FunctionDebugContext, DebugLoc};
18 use llvm::debuginfo::DIScope;
20 use common::{NodeIdAndSpan, CrateContext, FunctionContext};
24 use syntax_pos::{self, Span, Pos};
27 pub fn get_cleanup_debug_loc_for_ast_node<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
32 // A debug location needs two things:
33 // (1) A span (of which only the beginning will actually be used)
34 // (2) An AST node-id which will be used to look up the lexical scope
35 // for the location in the functions scope-map
37 // This function will calculate the debug location for compiler-generated
38 // cleanup calls that are executed when control-flow leaves the
39 // scope identified by `node_id`.
41 // For everything but block-like things we can simply take id and span of
42 // the given expression, meaning that from a debugger's view cleanup code is
43 // executed at the same source location as the statement/expr itself.
45 // Blocks are a special case. Here we want the cleanup to be linked to the
46 // closing curly brace of the block. The *scope* the cleanup is executed in
47 // is up to debate: It could either still be *within* the block being
48 // cleaned up, meaning that locals from the block are still visible in the
50 // Or it could be in the scope that the block is contained in, so any locals
51 // from within the block are already considered out-of-scope and thus not
52 // accessible in the debugger anymore.
54 // The current implementation opts for the second option: cleanup of a block
55 // already happens in the parent scope of the block. The main reason for
56 // this decision is that scoping becomes controlflow dependent when variable
57 // shadowing is involved and it's impossible to decide statically which
58 // scope is actually left when the cleanup code is executed.
59 // In practice it shouldn't make much of a difference.
61 let mut cleanup_span = node_span;
64 // Not all blocks actually have curly braces (e.g. simple closure
65 // bodies), in which case we also just want to return the span of the
67 let code_snippet = cx.sess().codemap().span_to_snippet(node_span);
68 if let Ok(code_snippet) = code_snippet {
69 let bytes = code_snippet.as_bytes();
71 if !bytes.is_empty() && &bytes[bytes.len()-1..] == b"}" {
73 lo: node_span.hi - syntax_pos::BytePos(1),
75 expn_id: node_span.expn_id
88 /// Sets the current debug location at the beginning of the span.
90 /// Maps to a call to llvm::LLVMSetCurrentDebugLocation(...).
91 pub fn set_source_location(fcx: &FunctionContext,
92 builder: Option<&Builder>,
93 debug_loc: DebugLoc) {
94 let builder = builder.map(|b| b.llbuilder);
95 let function_debug_context = match fcx.debug_context {
96 FunctionDebugContext::DebugInfoDisabled => return,
97 FunctionDebugContext::FunctionWithoutDebugInfo => {
98 set_debug_location(fcx.ccx, builder, UnknownLocation);
101 FunctionDebugContext::RegularContext(box ref data) => data
104 if function_debug_context.source_location_override.get() {
105 // Just ignore any attempts to set a new debug location while
106 // the override is active.
110 let dbg_loc = if function_debug_context.source_locations_enabled.get() {
111 let (scope, span) = match debug_loc {
112 DebugLoc::At(node_id, span) => {
113 (scope_metadata(fcx, node_id, span), span)
115 DebugLoc::ScopeAt(scope, span) => (scope, span),
117 set_debug_location(fcx.ccx, builder, UnknownLocation);
122 debug!("set_source_location: {}",
123 fcx.ccx.sess().codemap().span_to_string(span));
124 let loc = span_start(fcx.ccx, span);
125 InternalDebugLocation::new(scope, loc.line, loc.col.to_usize())
129 set_debug_location(fcx.ccx, builder, dbg_loc);
132 /// This function makes sure that all debug locations emitted while executing
133 /// `wrapped_function` are set to the given `debug_loc`.
134 pub fn with_source_location_override<F, R>(fcx: &FunctionContext,
136 wrapped_function: F) -> R
137 where F: FnOnce() -> R
139 match fcx.debug_context {
140 FunctionDebugContext::DebugInfoDisabled => {
143 FunctionDebugContext::FunctionWithoutDebugInfo => {
144 set_debug_location(fcx.ccx, None, UnknownLocation);
147 FunctionDebugContext::RegularContext(box ref function_debug_context) => {
148 if function_debug_context.source_location_override.get() {
151 debug_loc.apply(fcx);
152 function_debug_context.source_location_override.set(true);
153 let result = wrapped_function();
154 function_debug_context.source_location_override.set(false);
161 /// Enables emitting source locations for the given functions.
163 /// Since we don't want source locations to be emitted for the function prelude,
164 /// they are disabled when beginning to translate a new function. This functions
165 /// switches source location emitting on and must therefore be called before the
166 /// first real statement/expression of the function is translated.
167 pub fn start_emitting_source_locations(fcx: &FunctionContext) {
168 match fcx.debug_context {
169 FunctionDebugContext::RegularContext(box ref data) => {
170 data.source_locations_enabled.set(true)
172 _ => { /* safe to ignore */ }
177 #[derive(Copy, Clone, PartialEq)]
178 pub enum InternalDebugLocation {
179 KnownLocation { scope: DIScope, line: usize, col: usize },
183 impl InternalDebugLocation {
184 pub fn new(scope: DIScope, line: usize, col: usize) -> InternalDebugLocation {
193 pub fn set_debug_location(cx: &CrateContext,
194 builder: Option<llvm::BuilderRef>,
195 debug_location: InternalDebugLocation) {
196 if builder.is_none() {
197 if debug_location == debug_context(cx).current_debug_location.get() {
202 let metadata_node = match debug_location {
203 KnownLocation { scope, line, .. } => {
204 // Always set the column to zero like Clang and GCC
205 let col = UNKNOWN_COLUMN_NUMBER;
206 debug!("setting debug location to {} {}", line, col);
209 llvm::LLVMRustDIBuilderCreateDebugLocation(
210 debug_context(cx).llcontext,
218 debug!("clearing debug location ");
223 if builder.is_none() {
224 debug_context(cx).current_debug_location.set(debug_location);
227 let builder = builder.unwrap_or_else(|| cx.raw_builder());
229 llvm::LLVMSetCurrentDebugLocation(builder, metadata_node);