]> git.lizzy.rs Git - rust.git/blob - src/librustc_trans/debuginfo/source_loc.rs
Changed issue number to 36105
[rust.git] / src / librustc_trans / debuginfo / source_loc.rs
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.
4 //
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.
10
11 use self::InternalDebugLocation::*;
12
13 use super::utils::{debug_context, span_start};
14 use super::metadata::{scope_metadata,UNKNOWN_COLUMN_NUMBER};
15 use super::{FunctionDebugContext, DebugLoc};
16
17 use llvm;
18 use llvm::debuginfo::DIScope;
19 use builder::Builder;
20 use common::{NodeIdAndSpan, CrateContext, FunctionContext};
21
22 use libc::c_uint;
23 use std::ptr;
24 use syntax_pos::{self, Span, Pos};
25 use syntax::ast;
26
27 pub fn get_cleanup_debug_loc_for_ast_node<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
28                                                     node_id: ast::NodeId,
29                                                     node_span: Span,
30                                                     is_block: bool)
31                                                  -> NodeIdAndSpan {
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
36     //
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`.
40     //
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.
44     //
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
49     // debugger.
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.
53     //
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.
60
61     let mut cleanup_span = node_span;
62
63     if is_block {
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
66         // whole expression.
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();
70
71             if !bytes.is_empty() && &bytes[bytes.len()-1..] == b"}" {
72                 cleanup_span = Span {
73                     lo: node_span.hi - syntax_pos::BytePos(1),
74                     hi: node_span.hi,
75                     expn_id: node_span.expn_id
76                 };
77             }
78         }
79     }
80
81     NodeIdAndSpan {
82         id: node_id,
83         span: cleanup_span
84     }
85 }
86
87
88 /// Sets the current debug location at the beginning of the span.
89 ///
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);
99             return;
100         }
101         FunctionDebugContext::RegularContext(box ref data) => data
102     };
103
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.
107         return;
108     }
109
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)
114             }
115             DebugLoc::ScopeAt(scope, span) => (scope, span),
116             DebugLoc::None => {
117                 set_debug_location(fcx.ccx, builder, UnknownLocation);
118                 return;
119             }
120         };
121
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())
126     } else {
127         UnknownLocation
128     };
129     set_debug_location(fcx.ccx, builder, dbg_loc);
130 }
131
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,
135                                            debug_loc: DebugLoc,
136                                            wrapped_function: F) -> R
137     where F: FnOnce() -> R
138 {
139     match fcx.debug_context {
140         FunctionDebugContext::DebugInfoDisabled => {
141             wrapped_function()
142         }
143         FunctionDebugContext::FunctionWithoutDebugInfo => {
144             set_debug_location(fcx.ccx, None, UnknownLocation);
145             wrapped_function()
146         }
147         FunctionDebugContext::RegularContext(box ref function_debug_context) => {
148             if function_debug_context.source_location_override.get() {
149                 wrapped_function()
150             } else {
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);
155                 result
156             }
157         }
158     }
159 }
160
161 /// Enables emitting source locations for the given functions.
162 ///
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)
171         },
172         _ => { /* safe to ignore */ }
173     }
174 }
175
176
177 #[derive(Copy, Clone, PartialEq)]
178 pub enum InternalDebugLocation {
179     KnownLocation { scope: DIScope, line: usize, col: usize },
180     UnknownLocation
181 }
182
183 impl InternalDebugLocation {
184     pub fn new(scope: DIScope, line: usize, col: usize) -> InternalDebugLocation {
185         KnownLocation {
186             scope: scope,
187             line: line,
188             col: col,
189         }
190     }
191 }
192
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() {
198             return;
199         }
200     }
201
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);
207
208             unsafe {
209                 llvm::LLVMRustDIBuilderCreateDebugLocation(
210                     debug_context(cx).llcontext,
211                     line as c_uint,
212                     col as c_uint,
213                     scope,
214                     ptr::null_mut())
215             }
216         }
217         UnknownLocation => {
218             debug!("clearing debug location ");
219             ptr::null_mut()
220         }
221     };
222
223     if builder.is_none() {
224         debug_context(cx).current_debug_location.set(debug_location);
225     }
226
227     let builder = builder.unwrap_or_else(|| cx.raw_builder());
228     unsafe {
229         llvm::LLVMSetCurrentDebugLocation(builder, metadata_node);
230     }
231 }