Repository: xd009642/tarpaulin
Pull request #1563 (2024-05-28 22:03:26)

Start revamping the function handling stuff
<!--- When contributing to tarpaulin all contributions should branch off the -->
<!--- develop branch as master is only used for releases. --> 

<!--- Check CONTRIBUTING.md for more information-->

<!--- Please describe the changes in the PR and them motivation below -->



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9fea344..ff7e836 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@ file.
 ## [Unreleased] 
 ### Changed
 - No longer print rustflags for report configs with `--print-rust-flags` 
+- Now use source code to get function names and locations instead of debug information
 
 ## [0.30.0] 2024-05-10
 ### Changed
diff --git a/src/lib.rs b/src/lib.rs
index dad33e5..3c13c8e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -253,6 +253,7 @@ pub fn launch_tarpaulin(
     let executables = cargo::get_tests(config)?;
     if !config.no_run {
         let project_analysis = SourceAnalysis::get_analysis(config);
+        result.set_functions(project_analysis.create_function_map());
         let project_analysis = project_analysis.lines;
         let mut other_bins = config.objects().to_vec();
         other_bins.extend(executables.binaries.iter().cloned());
diff --git a/src/report/cobertura.rs b/src/report/cobertura.rs
index 68686c7..6b87f95 100644
--- a/src/report/cobertura.rs
+++ b/src/report/cobertura.rs
@@ -441,7 +441,7 @@ mod tests {
         let empty_trace = Trace::new_stub(2);
         let mut address = HashSet::new();
         address.insert(2);
-        let hit_trace = Trace::new(3, address, 1, None);
+        let hit_trace = Trace::new(3, address, 1);
 
         let source_file = PathBuf::from("fake/src/lib.rs");
 
diff --git a/src/report/lcov.rs b/src/report/lcov.rs
index 6cdf507..1f94c5b 100644
--- a/src/report/lcov.rs
+++ b/src/report/lcov.rs
@@ -1,6 +1,7 @@
 use crate::config::Config;
 use crate::errors::RunError;
 use crate::traces::{CoverageStat, TraceMap};
+use std::collections::BTreeMap;
 use std::fs::File;
 use std::io::Write;
 
@@ -26,20 +27,31 @@ fn write_lcov(mut file: impl Write, coverage_data: &TraceMap) -> Result<(), RunE
         let mut fnda: Vec<String> = vec![];
         let mut da: Vec<(u64, u64)> = vec![];
 
-        for trace in traces {
-            if trace.fn_name.is_some() {
-                let fn_name = trace.fn_name.clone().unwrap();
-                let fn_hits = match trace.stats {
-                    CoverageStat::Line(hits) => hits,
-                    _ => {
-                        return Err(RunError::Lcov(
-                            "Function doesn't have hits number".to_string(),
-                        ))
-                    }
-                };
+        let mut fn_locs = coverage_data
+            .get_functions(&path)
+            .map(|x| ((x.start, x.end), &x.name))
+            .collect::<BTreeMap<_, _>>();
 
-                fns.push(format!("FN:{},{}", trace.line, fn_name));
-                fnda.push(format!("FNDA:{fn_hits},{fn_name}"));
+        let mut first_fn = fn_locs.pop_first();
+
+        for trace in traces {
+            match &first_fn {
+                Some(((start, end), name)) if (*start..=*end).contains(&trace.line) => {
+                    let fn_hits = match trace.stats {
+                        CoverageStat::Line(hits) => hits,
+                        _ => {
+                            return Err(RunError::Lcov(
+                                "Function doesn't have hits number".to_string(),
+                            ))
+                        }
+                    };
+
+                    fns.push(format!("FN:{},{}", trace.line, name));
+                    fnda.push(format!("FNDA:{fn_hits},{name}"));
+
+                    first_fn = fn_locs.pop_first();
+                }
+                _ => {}
             }
 
             if let CoverageStat::Line(hits) = trace.stats {
@@ -82,8 +94,10 @@ fn write_lcov(mut file: impl Write, coverage_data: &TraceMap) -> Result<(), RunE
 #[cfg(test)]
 mod tests {
     use super::*;
+    use crate::source_analysis::Function;
     use crate::traces::*;
     use lcov::{record::Record, Reader};
+    use std::collections::HashMap;
     use std::io::Cursor;
     use std::path::{Path, PathBuf};
 
@@ -97,7 +111,6 @@ mod tests {
                 stats: CoverageStat::Line(1),
                 address: Default::default(),
                 length: 0,
-                fn_name: None,
             },
         );
         traces.add_trace(
@@ -107,7 +120,6 @@ mod tests {
                 stats: CoverageStat::Line(0),
                 address: Default::default(),
                 length: 0,
-                fn_name: None,
             },
         );
 
@@ -118,10 +130,20 @@ mod tests {
                 stats: CoverageStat::Line(9),
                 address: Default::default(),
                 length: 0,
-                fn_name: Some("baz".to_string()),
             },
         );
 
+        let mut functions = HashMap::new();
+        functions.insert(
+            PathBuf::from("bar.rs"),
+            vec![Function {
+                name: "baz".to_string(),
+                start: 14,
+                end: 20,
+            }],
+        );
+        traces.set_functions(functions);
+
         let mut data = vec![];
         let cursor = Cursor::new(&mut data);
 
diff --git a/src/source_analysis/items.rs b/src/source_analysis/items.rs
index 0222e87..89add8a 100644
--- a/src/source_analysis/items.rs
+++ b/src/source_analysis/items.rs
@@ -46,6 +46,7 @@ impl SourceAnalysis {
     }
 
     fn visit_mod(&mut self, module: &ItemMod, ctx: &Context) {
+        let _guard = ctx.push_to_symbol_stack(module.ident.to_string());
         let analysis = self.get_line_analysis(ctx.file.to_path_buf());
         analysis.ignore_tokens(module.mod_token);
         let check_insides = self.check_attr_list(&module.attrs, ctx);
@@ -72,6 +73,15 @@ impl SourceAnalysis {
     }
 
     fn visit_fn(&mut self, func: &ItemFn, ctx: &Context, force_cover: bool) {
+        let _guard = ctx.push_to_symbol_stack(func.sig.ident.to_string());
+        {
+            let analysis = self.get_line_analysis(ctx.file.to_path_buf());
+            let span = func.span();
+            analysis.functions.insert(
+                ctx.get_qualified_name(),
+                (func.sig.span().start().line, span.end().line),
+            );
+        }
         let mut test_func = false;
         let mut ignored_attr = false;
         let mut is_inline = false;
@@ -135,6 +145,7 @@ impl SourceAnalysis {
     }
 
     fn visit_trait(&mut self, trait_item: &ItemTrait, ctx: &Context) {
+        let _guard = ctx.push_to_symbol_stack(trait_item.ident.to_string());
         let check_cover = self.check_attr_list(&trait_item.attrs, ctx);
         if check_cover {
             for item in &trait_item.items {
@@ -173,6 +184,23 @@ impl SourceAnalysis {
     }
 
     fn visit_impl(&mut self, impl_blk: &ItemImpl, ctx: &Context) {
+        let self_ty_name = impl_blk
+            .self_ty
+            .to_token_stream()
+            .to_string()
+            .replace(' ', "");
+        let _guard = match &impl_blk.trait_ {
+            Some((_, path, _)) => {
+                let trait_name = path
+                    .segments
+                    .last()
+                    .map(|x| x.ident.to_string())
+                    .unwrap_or_else(|| path.to_token_stream().to_string());
+                let name = format!("<impl {} for {}>", trait_name, self_ty_name);
+                ctx.push_to_symbol_stack(name)
+            }
+            None => ctx.push_to_symbol_stack(self_ty_name),
+        };
         let check_cover = self.check_attr_list(&impl_blk.attrs, ctx);
         if check_cover {
             for item in &impl_blk.items {
diff --git a/src/source_analysis/mod.rs b/src/source_analysis/mod.rs
index 14b6e3a..53032cc 100644
--- a/src/source_analysis/mod.rs
+++ b/src/source_analysis/mod.rs
@@ -4,6 +4,7 @@ use lazy_static::lazy_static;
 use proc_macro2::{Span, TokenStream};
 use quote::ToTokens;
 use regex::Regex;
+use serde::{Deserialize, Serialize};
 use std::cell::RefCell;
 use std::collections::{HashMap, HashSet};
 use std::ffi::OsStr;
@@ -12,7 +13,7 @@ use std::io::{self, BufRead, BufReader, Read};
 use std::path::{Path, PathBuf};
 use syn::spanned::Spanned;
 use syn::*;
-use tracing::{trace, warn};
+use tracing::{debug, trace, warn};
 use walkdir::WalkDir;
 
 mod attributes;
@@ -55,6 +56,7 @@ pub struct LineAnalysis {
     pub logical_lines: HashMap<usize, usize>,
     /// Shows the line length of the provided file
     max_line: usize,
+    pub functions: HashMap<String, (usize, usize)>,
 }
 
 /// Provides context to the source analysis stage including the tarpaulin
@@ -69,6 +71,34 @@ pub(crate) struct Context<'a> {
     /// Other parts of context are immutable like tarpaulin config and users
     /// source code. This is discovered during hence use of interior mutability
     ignore_mods: RefCell<HashSet<PathBuf>>,
+    /// As we traverse the structures we want to grab module names etc so we can get proper names
+    /// for our functions etc
+    pub(crate) symbol_stack: RefCell<Vec<String>>,
+}
+
+pub(crate) struct StackGuard<'a>(&'a RefCell<Vec<String>>);
+
+impl<'a> Drop for StackGuard<'a> {
+    fn drop(&mut self) {
+        self.0.borrow_mut().pop();
+    }
+}
+
+impl<'a> Context<'a> {
+    pub(crate) fn push_to_symbol_stack(&self, mut ident: String) -> StackGuard<'_> {
+        if !(ident.starts_with("<") && ident.ends_with(">")) {
+            ident = ident.replace(' ', "");
+        }
+        self.symbol_stack.borrow_mut().push(ident);
+        StackGuard(&self.symbol_stack)
+    }
+
+    pub(crate) fn get_qualified_name(&self) -> String {
+        let stack = self.symbol_stack.borrow();
+        let name = stack.join("::");
+        debug!("Found function: {}", name);
+        name
+    }
 }
 
 /// When the `LineAnalysis` results are mapped to their files there needs to be
@@ -257,6 +287,23 @@ impl LineAnalysis {
     }
 }
 
+impl Function {
+    fn new(name: &str, span: (usize, usize)) -> Self {
+        Self {
+            name: name.to_string(),
+            start: span.0 as u64,
+            end: span.1 as u64,
+        }
+    }
+}
+
+#[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Deserialize, Serialize)]
+pub struct Function {
+    pub name: String,
+    pub start: u64,
+    pub end: u64,
+}
+
 #[derive(Default)]
 pub struct SourceAnalysis {
     pub lines: HashMap<PathBuf, LineAnalysis>,
@@ -268,6 +315,21 @@ impl SourceAnalysis {
         Default::default()
     }
 
+    pub fn create_function_map(&self) -> HashMap<PathBuf, Vec<Function>> {
+        self.lines
+            .iter()
+            .map(|(file, analysis)| {
+                let mut functions: Vec<Function> = analysis
+                    .functions
+                    .iter()
+                    .map(|(function, span)| Function::new(function, *span))
+                    .collect();
+                functions.sort_unstable_by(|a, b| a.start.cmp(&b.start));
+                (file.to_path_buf(), functions)
+            })
+            .collect()
+    }
+
     pub fn get_line_analysis(&mut self, path: PathBuf) -> &mut LineAnalysis {
         self.lines
             .entry(path.clone())
@@ -298,7 +360,6 @@ impl SourceAnalysis {
             analysis.ignore_all();
             result.lines.insert(e.clone(), analysis);
         }
-
         result.debug_printout(config);
 
         result
@@ -339,6 +400,7 @@ impl SourceAnalysis {
                             file_contents: &content,
                             file: path,
                             ignore_mods: RefCell::new(HashSet::new()),
+                            symbol_stack: RefCell::new(vec![]),
                         };
                         if self.check_attr_list(&file.attrs, &ctx) {
                             self.find_ignorable_lines(&ctx);
diff --git a/src/traces.rs b/src/traces.rs
index c7b1b2c..d3b9f75 100644
--- a/src/traces.rs
+++ b/src/traces.rs
@@ -1,8 +1,8 @@
+use crate::source_analysis::Function;
 use serde::{Deserialize, Serialize};
 use std::cmp::{Ord, Ordering};
 use std::collections::btree_map::Iter;
 use std::collections::{BTreeMap, HashMap, HashSet};
-use std::fmt::{Display, Formatter, Result};
 use std::ops::Add;
 use std::path::{Path, PathBuf};
 use tracing::trace;
@@ -52,15 +52,6 @@ impl Add for CoverageStat {
     }
 }
 
-impl Display for CoverageStat {
-    fn fmt(&self, f: &mut Formatter) -> Result {
-        match *self {
-            CoverageStat::Line(x) => write!(f, "hits: {x}"),
-            _ => write!(f, ""),
-        }
-    }
-}
-
 #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
 pub struct Trace {
     /// Line the trace is on in the file
@@ -71,8 +62,6 @@ pub struct Trace {
     pub length: usize,
     /// Coverage stats
     pub stats: CoverageStat,
-    /// Function name
-    pub fn_name: Option<String>,
 }
 
 #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
@@ -84,13 +73,12 @@ pub struct Location {
 }
 
 impl Trace {
-    pub fn new(line: u64, address: HashSet<u64>, length: usize, fn_name: Option<String>) -> Self {
+    pub fn new(line: u64, address: HashSet<u64>, length: usize) -> Self {
         Self {
             line,
             address,
             length,
             stats: CoverageStat::Line(0),
-            fn_name,
         }
     }
 
@@ -100,7 +88,6 @@ impl Trace {
             address: HashSet::new(),
             length: 0,
             stats: CoverageStat::Line(0),
-            fn_name: None,
         }
     }
 }
@@ -170,16 +157,20 @@ pub fn coverage_percentage<'a>(traces: impl Iterator<Item = &'a Trace>) -> f64 {
 /// add, query and change traces.
 #[derive(Debug, Default, Deserialize, Serialize)]
 pub struct TraceMap {
-    /// Traces in the program mapped to the given file
+    ///rTraces in the program mapped to the given file
     traces: BTreeMap<PathBuf, Vec<Trace>>,
+    functions: HashMap<PathBuf, Vec<Function>>,
 }
 
 impl TraceMap {
     /// Create a new TraceMap
     pub fn new() -> TraceMap {
-        TraceMap {
-            traces: BTreeMap::new(),
-        }
+        Self::default()
+    }
+
+    pub fn set_functions(&mut self, functions: HashMap<PathBuf, Vec<Function>>) {
+        println!("{:?}", functions);
+        self.functions = functions;
     }
 
     /// Returns true if there are no traces
@@ -196,6 +187,8 @@ impl TraceMap {
     /// This adds records which are missing and adds the statistics gathered to
     /// existing records
     pub fn merge(&mut self, other: &TraceMap) {
+        self.functions
+            .extend(other.functions.iter().map(|(k, v)| (k.clone(), v.clone())));
         for (k, values) in other.iter() {
             if !self.traces.contains_key(k) {
                 self.traces.insert(k.clone(), values.clone());
@@ -298,13 +291,6 @@ impl TraceMap {
         }
     }
 
-    /// Gets a mutable reference to a trace at a given address
-    /// Returns None if there is no trace at that address
-    pub fn get_trace_mut(&mut self, address: u64) -> Option<&mut Trace> {
-        self.all_traces_mut()
-            .find(|val| val.address.contains(&address))
-    }
-
     pub fn get_location(&self, address: u64) -> Option<Location> {
         for (k, v) in &self.traces {
             if let Some(s) = v
@@ -342,20 +328,11 @@ impl TraceMap {
             .flat_map(|(_, v)| v.iter())
     }
 
-    /// Gets all traces in folder, doesn't go into other folders for that you
-    /// want get_child_traces
-    pub fn get_traces<'a>(&'a self, root: &'a Path) -> impl Iterator<Item = &'a Trace> + 'a {
-        let i: Box<dyn Iterator<Item = &'a Trace> + 'a> = if root.is_file() {
-            Box::new(self.get_child_traces(root))
-        } else {
-            Box::new(
-                self.traces
-                    .iter()
-                    .filter(move |&(k, _)| k.parent() == Some(root))
-                    .flat_map(|(_, v)| v.iter()),
-            )
+    pub fn get_functions(&self, file: &Path) -> impl Iterator<Item = &Function> {
+        let i: Box<dyn Iterator<Item = &Function>> = match self.functions.get(file) {
+            Some(f) => Box::new(f.iter()),
+            None => Box::new(std::iter::empty()),
         };
-
         i
     }
 
@@ -461,7 +438,6 @@ mod tests {
             address,
             length: 0,
             stats: CoverageStat::Line(1),
-            fn_name: Some(String::from("f")),
         };
         t1.add_trace(Path::new("file.rs"), trace_1);
 
@@ -483,7 +459,6 @@ mod tests {
             address,
             length: 0,
             stats: CoverageStat::Line(1),
-            fn_name: Some(String::from("f")),
         };
         t1.add_trace(Path::new("file.rs"), a_trace.clone());
         t2.add_trace(
@@ -493,7 +468,6 @@ mod tests {
                 address: HashSet::new(),
                 length: 0,
                 stats: CoverageStat::Line(2),
-                fn_name: Some(String::from("f")),
             },
         );
 
@@ -518,7 +492,6 @@ mod tests {
             address,
             length: 0,
             stats: CoverageStat::Line(1),
-            fn_name: Some(String::from("f1")),
         };
         t1.add_trace(Path::new("file.rs"), a_trace.clone());
         t2.add_trace(
@@ -528,7 +501,6 @@ mod tests {
                 address: HashSet::new(),
                 length: 0,
                 stats: CoverageStat::Line(2),
-                fn_name: Some(String::from("f2")),
             },
         );
 
@@ -554,7 +526,6 @@ mod tests {
                 address: address.clone(),
                 length: 0,
                 stats: CoverageStat::Line(5),
-                fn_name: Some(String::from("f")),
             },
         );
         t2.add_trace(
@@ -564,7 +535,6 @@ mod tests {
                 address: address.clone(),
                 length: 0,
                 stats: CoverageStat::Line(2),
-                fn_name: Some(String::from("f")),
             },
         );
         t1.merge(&t2);
@@ -576,7 +546,6 @@ mod tests {
                 address: address.clone(),
                 length: 0,
                 stats: CoverageStat::Line(7),
-                fn_name: Some(String::from("f")),
             })
         );
         // Deduplicating should have no effect.
@@ -589,7 +558,6 @@ mod tests {
                 address,
                 length: 0,
                 stats: CoverageStat::Line(7),
-                fn_name: Some(String::from("f")),
             })
         );
     }


Test patch:
diff --git a/src/source_analysis/tests.rs b/src/source_analysis/tests.rs
index 8927a52..ae0e1bc 100644
--- a/src/source_analysis/tests.rs
+++ b/src/source_analysis/tests.rs
@@ -15,6 +15,7 @@ fn logical_lines_let_bindings() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     analysis.process_items(&parser.items, &ctx);
@@ -38,6 +39,7 @@ fn logical_lines_let_bindings() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
 
     let parser = parse_file(ctx.file_contents).unwrap();
@@ -73,6 +75,7 @@ fn match_pattern_logical_lines() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
 
     let parser = parse_file(ctx.file_contents).unwrap();
@@ -111,6 +114,7 @@ fn filter_str_literals() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -129,6 +133,7 @@ fn filter_str_literals() {
         fn write(s:&str){}",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -150,6 +155,7 @@ fn filter_str_literals() {
         ",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -167,6 +173,7 @@ fn filter_struct_members() {
         file_contents: "#[derive(Debug)]\npub struct Struct {\npub i: i32,\nj:String,\n}",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -183,6 +190,7 @@ fn filter_struct_members() {
         file_contents: "#[derive(Debug)]\npub struct Struct (\n i32\n);",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -201,6 +209,7 @@ fn filter_enum_members() {
         file_contents: "#[derive(Debug)]\npub enum E {\nI1,\nI2(u32),\nI3{\nx:u32,\n},\n}",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -229,6 +238,7 @@ fn filter_struct_consts() {
             }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -248,6 +258,7 @@ fn filter_unreachable_unchecked() {
             }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -274,6 +285,7 @@ fn filter_loop_attr() {
             }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -292,6 +304,7 @@ fn filter_mods() {
         file_contents: "mod foo {\nfn double(x:i32)->i32 {\n x*2\n}\n}",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -304,6 +317,7 @@ fn filter_mods() {
         file_contents: "mod foo;",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -316,6 +330,7 @@ fn filter_mods() {
         file_contents: "mod foo{}",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -332,6 +347,7 @@ fn filter_macros() {
         file_contents: "\n\nfn unused() {\nunimplemented!();\n}",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -346,6 +362,7 @@ fn filter_macros() {
         file_contents: "\n\nfn unused() {\nunreachable!();\n}",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -365,6 +382,7 @@ fn filter_macros() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -377,6 +395,7 @@ fn filter_macros() {
         file_contents: "fn unused() {\nprintln!(\"text\");\n}",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -401,6 +420,7 @@ fn filter_tests() {
                 }\n}",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -417,6 +437,7 @@ fn filter_tests() {
                 }\n}",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
 
     let mut analysis = SourceAnalysis::new();
@@ -429,6 +450,7 @@ fn filter_tests() {
         file_contents: "#[test]\nfn mytest() { \n assert!(true);\n}",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -443,6 +465,7 @@ fn filter_tests() {
         file_contents: "#[test]\nfn mytest() { \n assert!(true);\n}",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let mut analysis = SourceAnalysis::new();
     analysis.process_items(&parser.items, &ctx);
@@ -467,6 +490,7 @@ fn filter_nonstd_tests() {
             }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -482,6 +506,7 @@ fn filter_nonstd_tests() {
                 }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -497,6 +522,7 @@ fn filter_nonstd_tests() {
                 }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -512,6 +538,7 @@ fn filter_nonstd_tests() {
                 }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -536,6 +563,7 @@ fn include_nonstd_tests() {
             }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -551,6 +579,7 @@ fn include_nonstd_tests() {
                 }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -566,6 +595,7 @@ fn include_nonstd_tests() {
                 }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -589,6 +619,7 @@ fn filter_test_utilities() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -611,6 +642,7 @@ fn filter_test_utilities() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -630,6 +662,7 @@ fn filter_where() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -645,6 +678,7 @@ fn filter_where() {
             }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -662,6 +696,7 @@ fn filter_where() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -678,6 +713,7 @@ fn filter_derives() {
         file_contents: "#[derive(Debug)]\nstruct T;",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -690,6 +726,7 @@ fn filter_derives() {
         file_contents: "\n#[derive(Copy, Eq)]\nunion x { x:i32, y:f32}",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -706,6 +743,7 @@ fn filter_unsafe() {
         file_contents: "fn unsafe_fn() {\n let x=1;\nunsafe {\nprintln!(\"{}\", x);\n}\n}",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -719,6 +757,7 @@ fn filter_unsafe() {
         file_contents: "fn unsafe_fn() {\n let x=1;\nunsafe {println!(\"{}\", x);}\n}",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -740,6 +779,7 @@ fn cover_generic_impl_methods() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -760,6 +800,7 @@ fn cover_generic_impl_methods() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -780,6 +821,7 @@ fn cover_default_trait_methods() {
             }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -799,6 +841,7 @@ fn cover_impl_trait_generic_fns() {
             }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -836,6 +879,7 @@ fn filter_method_args() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -854,6 +898,7 @@ fn filter_use_statements() {
         use std::{ffi::CString, os::raw::c_char};",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -879,6 +924,7 @@ fn include_inline_fns() {
             }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -902,6 +948,7 @@ fn cover_callable_noargs() {
             }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -922,6 +969,7 @@ fn filter_closure_contents() {
             }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -967,6 +1015,7 @@ fn tarpaulin_skip_attr() {
         ",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -1000,6 +1049,7 @@ fn tarpaulin_skip_attr() {
         ",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -1030,6 +1080,7 @@ fn tarpaulin_skip_trait_attrs() {
         ",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -1055,6 +1106,7 @@ fn tarpaulin_skip_trait_attrs() {
         ",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -1086,6 +1138,7 @@ fn tarpaulin_skip_impl_attrs() {
         ",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -1113,6 +1166,7 @@ fn tarpaulin_skip_impl_attrs() {
         ",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -1141,6 +1195,7 @@ fn filter_block_contents() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -1160,6 +1215,7 @@ fn filter_consts() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -1184,6 +1240,7 @@ fn optional_panic_ignore() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -1208,6 +1265,7 @@ fn optional_panic_ignore() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
 
     let parser = parse_file(ctx.file_contents).unwrap();
@@ -1243,6 +1301,7 @@ fn filter_nested_blocks() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -1263,6 +1322,7 @@ fn filter_multi_line_decls() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -1283,6 +1343,7 @@ fn filter_multi_line_decls() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -1302,6 +1363,7 @@ fn filter_multi_line_decls() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -1323,6 +1385,7 @@ fn unreachable_propagate() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -1349,6 +1412,7 @@ fn unreachable_propagate() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -1372,6 +1436,7 @@ fn unreachable_propagate() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -1402,6 +1467,7 @@ fn unreachable_include_returns() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -1430,6 +1496,7 @@ fn unreachable_include_returns() {
         ",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -1458,6 +1525,7 @@ fn unreachable_include_loops() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -1479,6 +1547,7 @@ fn unreachable_include_loops() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -1500,6 +1569,7 @@ fn unreachable_include_loops() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -1535,6 +1605,7 @@ fn single_line_callables() {
         ",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -1575,6 +1646,7 @@ fn visit_generics() {
         ",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -1611,6 +1683,7 @@ fn ignore_comment() {
         }",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let mut analysis = SourceAnalysis::new();
     analysis.find_ignorable_lines(&ctx);
@@ -1651,6 +1724,7 @@ fn py_attr() {
         ",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -1672,6 +1746,7 @@ fn handle_c_strs() {
         }"#,
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -1692,6 +1767,7 @@ fn ignore_trait_types() {
         ",
         file: Path::new(""),
         ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
     };
     let parser = parse_file(ctx.file_contents).unwrap();
     let mut analysis = SourceAnalysis::new();
@@ -1699,3 +1775,149 @@ fn ignore_trait_types() {
     let lines = &analysis.lines[Path::new("")];
     assert!(lines.ignore.contains(&Lines::Line(5)));
 }
+
+#[test]
+fn get_function_names() {
+    let config = Config::default();
+    let ctx = Context {
+        config: &config,
+        file_contents: r#"use std::fmt;
+
+            pub fn add(left: usize, right: usize) -> usize { // 3
+                left + right
+            }
+
+            struct Wrapper<T>(T);
+
+            pub trait Foo {
+                fn five() -> u32 { // 10
+                    5
+                }
+                fn four() -> u32;
+            }
+
+            struct Marker;
+
+            impl Foo for Marker {
+                fn four() -> u32 { //19
+                    3
+                }
+            }
+
+            impl<T> fmt::Display for Wrapper<T> {
+                fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // 25
+                    writeln!(f, "Wrapped")
+                }
+            }
+
+            impl<T> Wrapper<T> {
+                fn unwrap(self) -> T { //31
+                    self.0
+                }
+            }
+
+            impl Marker {
+                fn marked() -> bool { //37
+                    true
+                }
+            }
+
+            fn nonsense() { //42
+                fn inner() { //43
+                    println!("yay?");
+                }
+                let x = |y| println!("{}", y); //46
+                println!("yabadabadoo");
+                inner();
+                x(6);
+            }
+
+
+            mod beep {
+                use super::*;
+
+
+                fn it_works() { //57
+                    let result = add(2, 2);
+                    assert_eq!(result, 4);
+                }
+            }
+
+            trait Foo2 {
+                fn five() -> u32;
+            }
+
+            impl Foo2 for Marker {
+                fn five() -> u32 { // 68
+                    10
+                }
+            }
+        "#,
+        file: Path::new("src.rs"),
+        ignore_mods: RefCell::new(HashSet::new()),
+        symbol_stack: RefCell::new(Vec::new()),
+    };
+
+    let parser = parse_file(ctx.file_contents).unwrap();
+    let mut analysis = SourceAnalysis::new();
+    analysis.process_items(&parser.items, &ctx);
+
+    let function_map = analysis.create_function_map();
+
+    let functions = &function_map[Path::new("src.rs")];
+
+    let expected_fns = vec![
+        Function {
+            name: "add".to_string(),
+            start: 3,
+            end: 5,
+        },
+        Function {
+            name: "Foo::five".to_string(),
+            start: 10,
+            end: 12,
+        },
+        Function {
+            name: "<impl Foo for Marker>::four".to_string(),
+            start: 19,
+            end: 21,
+        },
+        Function {
+            name: "<impl Display for Wrapper<T>>::fmt".to_string(),
+            start: 25,
+            end: 27,
+        },
+        Function {
+            name: "Wrapper<T>::unwrap".to_string(),
+            start: 31,
+            end: 33,
+        },
+        Function {
+            name: "Marker::marked".to_string(),
+            start: 37,
+            end: 39,
+        },
+        Function {
+            name: "nonsense".to_string(),
+            start: 42,
+            end: 50,
+        },
+        Function {
+            name: "nonsense::inner".to_string(),
+            start: 43,
+            end: 45,
+        },
+        Function {
+            name: "beep::it_works".to_string(),
+            start: 57,
+            end: 60,
+        },
+        Function {
+            name: "<impl Foo2 for Marker>::five".to_string(),
+            start: 68,
+            end: 70,
+        },
+    ];
+
+    assert_eq!(functions, &expected_fns);
+}
diff --git a/src/test_loader.rs b/src/test_loader.rs
index 2d675aa..5df3add 100644
--- a/src/test_loader.rs
+++ b/src/test_loader.rs
@@ -328,7 +328,7 @@ fn get_line_addresses<'data>(
                             k.line
                         );
                     }
-                    tracemap.add_trace(&k.path, Trace::new(k.line, address, 1, fn_name));
+                    tracemap.add_trace(&k.path, Trace::new(k.line, address, 1));
                 }
                 result.merge(&tracemap);
             }