llvm_ir/
debugloc.rs

1use std::cmp::{Ordering, PartialOrd};
2use std::fmt;
3
4/// Describes a "debug location" (source location)
5#[derive(PartialEq, Eq, Clone, Debug, Hash)]
6pub struct DebugLoc {
7    /// The source line number
8    pub line: u32,
9    /// The source column number
10    ///
11    /// `Instruction`s and `Terminator`s have this info (and will have `Some`
12    /// here), while `GlobalVariable`s and `Function`s do not have this info (and
13    /// will have `None`)
14    pub col: Option<u32>,
15    /// The source filename
16    pub filename: String,
17    /// The source directory, if available
18    pub directory: Option<String>,
19}
20
21impl PartialOrd for DebugLoc {
22    #[rustfmt::skip] // self on one line, other on the next
23    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
24        // compare in the order (directory, filename, line, col)
25        Some(
26            (&self.directory, &self.filename, &self.line, &self.col)
27                .cmp(&(&other.directory, &other.filename, &other.line, &other.col))
28        )
29    }
30}
31
32impl Ord for DebugLoc {
33    #[rustfmt::skip] // self on one line, other on the next
34    fn cmp(&self, other: &Self) -> Ordering {
35        // compare in the order (directory, filename, line, col)
36        (&self.directory, &self.filename, &self.line, &self.col)
37            .cmp(&(&other.directory, &other.filename, &other.line, &other.col))
38    }
39}
40
41impl fmt::Display for DebugLoc {
42    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
43        let pretty_directory = match &self.directory {
44            Some(dir) => dir,
45            None => "",
46        };
47        let need_slash = match &self.directory {
48            Some(dir) => !dir.is_empty() && !dir.ends_with('/') && !self.filename.starts_with('/'),
49            None => false,
50        };
51        let pretty_filename = match &self.filename as &str {
52            "" => "<no filename available>",
53            filename if !pretty_directory.is_empty() => {
54                filename.trim_start_matches(pretty_directory)
55            },
56            filename => filename,
57        };
58        let pretty_column = match self.col {
59            Some(col) => format!(", col {}", col),
60            None => String::new(),
61        };
62        write!(
63            f,
64            "{}{}{}, line {}{}",
65            pretty_directory,
66            if need_slash { "/" } else { "" },
67            pretty_filename,
68            self.line,
69            pretty_column,
70        )
71    }
72}
73
74pub trait HasDebugLoc {
75    /// Returns the `DebugLoc` associated with the given `Instruction`,
76    /// `Terminator`, `GlobalVariable`, or `Function`; or `None` if it doesn't
77    /// have a `DebugLoc`.
78    ///
79    /// Reasons something might not have a `DebugLoc` include:
80    ///     (1) the file was compiled without debuginfo;
81    ///     (2) for an `Instruction`, it might not directly correspond to any source
82    ///     line. For instance, it may be just setting up the stack frame for a
83    ///     function.
84    fn get_debug_loc(&self) -> &Option<DebugLoc>;
85}
86
87// ********* //
88// from_llvm //
89// ********* //
90
91use crate::from_llvm::*;
92use crate::llvm_sys::*;
93
94impl DebugLoc {
95    /// `value`: must represent an Instruction, Terminator, GlobalVariable, or Function
96    ///
97    /// Returns `None` if the object does not have a `DebugLoc`
98    pub(crate) fn from_llvm_no_col(value: LLVMValueRef) -> Option<Self> {
99        match unsafe { get_debugloc_filename(value) } {
100            None => None, // if no filename, assume no debugloc. To my knowledge, everything with a debugloc has a filename.
101            Some(filename) => Some(Self {
102                line: unsafe { LLVMGetDebugLocLine(value) },
103                col: None,
104                filename,
105                directory: unsafe { get_debugloc_directory(value) },
106            }),
107        }
108    }
109
110    /// `value`: must represent an Instruction or Terminator
111    ///
112    /// Returns `None` if the object does not have a `DebugLoc`
113    pub(crate) fn from_llvm_with_col(value: LLVMValueRef) -> Option<Self> {
114        match Self::from_llvm_no_col(value) {
115            Some(mut debugloc) => {
116                debugloc.col = Some(unsafe { LLVMGetDebugLocColumn(value) });
117                Some(debugloc)
118            },
119            None => None,
120        }
121    }
122}