Repository: xd009642/tarpaulin
Pull request #1544 (2024-05-08 08:54:51)

Bump syn from 1.0.109 to 2.0.4
I have taken this over as dependabot is incapable of such a chonker. 

Bumps [syn](https://github.com/dtolnay/syn) from 1.0.109 to 2.0.4.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](dtolnay/syn@1.0.109...2.0.4)

---
updated-dependencies:
- dependency-name: syn
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com><!--- 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 958c6ea..e5730cc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,10 @@
 From 2019 onwards, all notable changes to tarpaulin will be documented in this
 file.
 
+## [Unreleased]
+### Changed
+- Upgraded to syn2 and removed branch coverage module. This only had impact in debug dumps so shouldn't impact users
+
 ## [0.29.2] 2024-05-08
 ### Changed
 - Update jobserver crate to allow building on alpine
diff --git a/Cargo.lock b/Cargo.lock
index 4b287e5..d5c5fed 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -47,15 +47,6 @@ dependencies = [
  "libc",
 ]
 
-[[package]]
-name = "ansi_term"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
-dependencies = [
- "winapi",
-]
-
 [[package]]
 name = "anstream"
 version = "0.6.14"
@@ -111,17 +102,6 @@ version = "1.0.83"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3"
 
-[[package]]
-name = "atty"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
-dependencies = [
- "hermit-abi 0.1.19",
- "libc",
- "winapi",
-]
-
 [[package]]
 name = "autocfg"
 version = "1.3.0"
@@ -204,9 +184,8 @@ dependencies = [
  "cargo_metadata",
  "cfg-if",
  "chrono",
- "clap 4.5.4",
+ "clap",
  "coveralls-api",
- "enum-display",
  "fallible-iterator",
  "gimli 0.29.0",
  "git2",
@@ -230,7 +209,7 @@ dependencies = [
  "rusty-fork",
  "serde",
  "serde_json",
- "syn 1.0.109",
+ "syn 2.0.61",
  "toml",
  "tracing",
  "tracing-subscriber",
@@ -289,21 +268,6 @@ dependencies = [
  "windows-targets 0.52.5",
 ]
 
-[[package]]
-name = "clap"
-version = "2.34.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
-dependencies = [
- "ansi_term",
- "atty",
- "bitflags 1.3.2",
- "strsim 0.8.0",
- "textwrap",
- "unicode-width",
- "vec_map",
-]
-
 [[package]]
 name = "clap"
 version = "4.5.4"
@@ -323,7 +287,7 @@ dependencies = [
  "anstream",
  "anstyle",
  "clap_lex",
- "strsim 0.11.1",
+ "strsim",
 ]
 
 [[package]]
@@ -332,7 +296,7 @@ version = "4.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
 dependencies = [
- "heck 0.5.0",
+ "heck",
  "proc-macro2",
  "quote",
  "syn 2.0.61",
@@ -350,15 +314,6 @@ version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
 
-[[package]]
-name = "convert_case"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
-dependencies = [
- "unicode-segmentation",
-]
-
 [[package]]
 name = "core-foundation"
 version = "0.9.4"
@@ -428,26 +383,6 @@ dependencies = [
  "cfg-if",
 ]
 
-[[package]]
-name = "enum-display"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02058bb25d8d0605829af88230427dd5cd50661590bd2b09d1baf7c64c417f24"
-dependencies = [
- "enum-display-macro",
-]
-
-[[package]]
-name = "enum-display-macro"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4be2cf2fe7b971b1865febbacd4d8df544aa6bd377cca011a6d69dcf4c60d94"
-dependencies = [
- "convert_case",
- "quote",
- "syn 1.0.109",
-]
-
 [[package]]
 name = "equivalent"
 version = "1.0.1"
@@ -642,30 +577,12 @@ version = "0.14.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
 
-[[package]]
-name = "heck"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
-dependencies = [
- "unicode-segmentation",
-]
-
 [[package]]
 name = "heck"
 version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
 
-[[package]]
-name = "hermit-abi"
-version = "0.1.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
-dependencies = [
- "libc",
-]
-
 [[package]]
 name = "hermit-abi"
 version = "0.3.9"
@@ -941,10 +858,8 @@ dependencies = [
  "md5",
  "nom",
  "object 0.26.2",
- "structopt",
  "thiserror",
  "tracing",
- "tracing-subscriber",
 ]
 
 [[package]]
@@ -1081,7 +996,7 @@ version = "1.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
 dependencies = [
- "hermit-abi 0.3.9",
+ "hermit-abi",
  "libc",
 ]
 
@@ -1205,30 +1120,6 @@ version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
 
-[[package]]
-name = "proc-macro-error"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
-dependencies = [
- "proc-macro-error-attr",
- "proc-macro2",
- "quote",
- "syn 1.0.109",
- "version_check",
-]
-
-[[package]]
-name = "proc-macro-error-attr"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
-dependencies = [
- "proc-macro2",
- "quote",
- "version_check",
-]
-
 [[package]]
 name = "proc-macro2"
 version = "1.0.82"
@@ -1587,42 +1478,12 @@ version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
 
-[[package]]
-name = "strsim"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
-
 [[package]]
 name = "strsim"
 version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
 
-[[package]]
-name = "structopt"
-version = "0.3.26"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10"
-dependencies = [
- "clap 2.34.0",
- "lazy_static",
- "structopt-derive",
-]
-
-[[package]]
-name = "structopt-derive"
-version = "0.4.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
-dependencies = [
- "heck 0.3.3",
- "proc-macro-error",
- "proc-macro2",
- "quote",
- "syn 1.0.109",
-]
-
 [[package]]
 name = "syn"
 version = "1.0.109"
@@ -1684,15 +1545,6 @@ dependencies = [
  "windows-sys 0.52.0",
 ]
 
-[[package]]
-name = "textwrap"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
-dependencies = [
- "unicode-width",
-]
-
 [[package]]
 name = "thiserror"
 version = "1.0.60"
@@ -1947,18 +1799,6 @@ dependencies = [
  "tinyvec",
 ]
 
-[[package]]
-name = "unicode-segmentation"
-version = "1.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
-
-[[package]]
-name = "unicode-width"
-version = "0.1.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
-
 [[package]]
 name = "url"
 version = "2.5.0"
@@ -1988,12 +1828,6 @@ version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
 
-[[package]]
-name = "vec_map"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
-
 [[package]]
 name = "version_check"
 version = "0.9.4"
diff --git a/Cargo.toml b/Cargo.toml
index a35615f..7158b66 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -31,7 +31,7 @@ git2 = "0.18"
 humantime-serde = "1"
 indexmap = { version = "~1.8", features = ["serde-1"] }
 lazy_static = "1.0"
-llvm_profparser = "0.5.0"
+llvm_profparser = { version = "0.5.0", default-features = false }
 object = "0.35"
 num_cpus = "1.16.0"
 proc-macro2 = { version = "1.0", features = ["span-locations"] }
@@ -41,7 +41,7 @@ regex = "1.10"
 rustc-demangle = "0.1.24"
 serde = { version = "1.0", features = ["derive"] }
 serde_json = "1.0"
-syn = { version = "1.0", features = ["full"] }
+syn = { version = "2.0", features = ["full"] }
 toml = "0.8"
 tracing = { version = "0.1", default-features = false }
 tracing-subscriber = { version = "0.3.18", default-features = false, features = [
@@ -54,7 +54,6 @@ tracing-subscriber = { version = "0.3.18", default-features = false, features =
 ] }
 walkdir = "2.5.0"
 glob = "0.3.1"
-enum-display = "0.1.4"
 
 [target.'cfg(all(target_os = "linux", any(target_arch = "x86_64", target_arch = "x86")))'.dependencies]
 libc = "0.2.94"
diff --git a/build.rs b/build.rs
index 4ff6d02..2b1a0fd 100644
--- a/build.rs
+++ b/build.rs
@@ -2,6 +2,9 @@ use rustc_version::{version, version_meta, Channel};
 use std::env;
 
 fn main() {
+    println!("cargo::rustc-check-cfg=cfg(ptrace_supported)");
+    println!("cargo::rustc-check-cfg=cfg(tarpaulin_include)");
+
     assert!(version().expect("Couldn't get compiler version").major >= 1);
 
     let channel = version_meta()
diff --git a/src/branching/mod.rs b/src/branching/mod.rs
deleted file mode 100644
index 3ef1b65..0000000
--- a/src/branching/mod.rs
+++ /dev/null
@@ -1,403 +0,0 @@
-use std::collections::{BTreeMap, HashMap};
-use std::path::{Path, PathBuf};
-use syn::spanned::Spanned;
-use syn::*;
-
-#[derive(Clone, Debug, Default, Eq, PartialEq)]
-pub struct BranchContext {
-    /// Map of files and the branches contained within them
-    files: HashMap<PathBuf, BranchAnalysis>,
-}
-
-impl BranchContext {
-    pub fn is_branch<P: AsRef<Path>>(&self, path: P, line: usize) -> bool {
-        if let Some(file) = self.files.get(path.as_ref()) {
-            file.is_branch(line)
-        } else {
-            false
-        }
-    }
-}
-
-/// Coverage context for all the branches
-#[derive(Clone, Debug, Default, Eq, PartialEq)]
-pub struct BranchAnalysis {
-    /// Each key is `LineRange` showing a region of the code containing a set of branches with the
-    /// value being a `LineRange` for each branch in the code
-    branches: BTreeMap<LineRange, Branches>,
-}
-
-impl BranchAnalysis {
-    pub fn new() -> Self {
-        Self::default()
-    }
-    /// Returns true if the line is part of a branch
-    pub fn is_branch(&self, line: usize) -> bool {
-        self.branches.iter().any(|(k, _)| k.contains(line))
-    }
-
-    pub fn register_expr(&mut self, expr: &Expr) {
-        let range = LineRange::from(expr);
-        match *expr {
-            Expr::If(ref e) => {
-                self.branches.insert(range, e.into());
-            }
-            Expr::Match(ref e) => {
-                self.branches.insert(range, e.into());
-            }
-            Expr::ForLoop(ref e) => {
-                self.branches.insert(range, e.into());
-            }
-            Expr::While(ref e) => {
-                self.branches.insert(range, e.into());
-            }
-            _ => {}
-        }
-    }
-}
-
-/// Represents possible branches through an execution
-#[derive(Clone, Debug, Default, Eq, PartialEq)]
-pub struct Branches {
-    /// Line ranges for each branch
-    ranges: Vec<LineRange>,
-    /// Whether there is an implicit or empty default branch i.e. missing or empty `else` in an
-    /// `if` statement
-    implicit_default: bool,
-}
-
-impl From<&ExprIf> for Branches {
-    fn from(expr: &ExprIf) -> Self {
-        let mut then = LineRange::from(&expr.then_branch);
-        then.end -= 1;
-        let mut ranges = vec![then];
-        let mut else_block = expr.else_branch.as_ref().map(|x| x.1.clone());
-        let mut implicit_default = else_block.is_none();
-        while let Some(el) = else_block {
-            let mut lr = LineRange::from(&el);
-            if let Expr::If(ref i) = *el {
-                then = LineRange::from(&i.then_branch);
-                then.end -= 1;
-                else_block = i.else_branch.as_ref().map(|x| x.1.clone());
-                implicit_default = else_block.is_none();
-                if let Some(s) = &else_block {
-                    let lr2 = LineRange::from(s);
-                    if lr2.start < lr.end {
-                        lr.end = lr2.start - 1;
-                    }
-                    ranges.push(lr);
-                } else {
-                    ranges.push(then);
-                }
-            } else {
-                else_block = None;
-                ranges.push(lr);
-            }
-        }
-        Self {
-            ranges,
-            implicit_default,
-        }
-    }
-}
-
-impl From<&ExprMatch> for Branches {
-    fn from(expr: &ExprMatch) -> Self {
-        let ranges = expr.arms.iter().map(LineRange::from).collect::<Vec<_>>();
-        Self {
-            implicit_default: false,
-            ranges,
-        }
-    }
-}
-
-impl From<&ExprForLoop> for Branches {
-    fn from(expr: &ExprForLoop) -> Self {
-        let mut range = LineRange::from(&expr.body);
-        range.start = expr.expr.span().end().line + 1;
-        Self {
-            implicit_default: true,
-            ranges: vec![range],
-        }
-    }
-}
-
-impl From<&ExprWhile> for Branches {
-    fn from(expr: &ExprWhile) -> Self {
-        let mut range = LineRange::from(&expr.body);
-        range.start = expr.cond.span().end().line + 1;
-        Self {
-            implicit_default: true,
-            ranges: vec![range],
-        }
-    }
-}
-
-/// The start and end of contiguous range of lines. The range is contained within
-/// `start..end`
-#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
-pub struct LineRange {
-    /// Start of the line range (inclusive)
-    start: usize,
-    /// End of the line range (exclusive)
-    end: usize,
-}
-
-impl<T> From<T> for LineRange
-where
-    T: spanned::Spanned,
-{
-    fn from(t: T) -> Self {
-        Self {
-            start: t.span().start().line,
-            end: t.span().end().line + 1,
-        }
-    }
-}
-
-impl LineRange {
-    pub fn new(start: usize, end: usize) -> Self {
-        Self { start, end }
-    }
-
-    /// Returns true if the line is contained within the line range
-    pub fn contains(&self, line: usize) -> bool {
-        line >= self.start && line < self.end
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use syn::{parse_file, Expr, Item, Stmt};
-
-    #[test]
-    fn if_branches() {
-        let source = "fn foo(x: i32) {
-            if x > 0 {
-                println!(\"BOO\");
-            } else if x < 0 {
-                todo!()
-            }
-        }";
-        let parser = parse_file(source).unwrap();
-        let func = &parser.items[0];
-        let mut branches = None;
-        if let Item::Fn(func) = func {
-            for stmt in &func.block.stmts {
-                match stmt {
-                    Stmt::Semi(Expr::If(i), _) | Stmt::Expr(Expr::If(i)) => {
-                        branches = Some(Branches::from(i));
-                    }
-                    _ => {}
-                }
-            }
-        }
-        let branches = branches.unwrap();
-        assert!(branches.implicit_default);
-        assert_eq!(branches.ranges.len(), 2);
-        assert_eq!(branches.ranges[0], LineRange::new(2, 4));
-        assert_eq!(branches.ranges[1], LineRange::new(4, 6));
-        let source = "fn foo(x: i32) {
-            if x > 0 {
-                println!(\"BOO\");
-            }
-            else
-            {
-                todo!()
-            }
-        }";
-        let parser = parse_file(source).unwrap();
-        let func = &parser.items[0];
-        let mut branches = None;
-        if let Item::Fn(func) = func {
-            for stmt in &func.block.stmts {
-                match stmt {
-                    Stmt::Semi(Expr::If(i), _) | Stmt::Expr(Expr::If(i)) => {
-                        branches = Some(Branches::from(i));
-                    }
-                    _ => {}
-                }
-            }
-        }
-        let branches = branches.unwrap();
-        assert!(!branches.implicit_default);
-        assert_eq!(branches.ranges.len(), 2);
-        assert_eq!(branches.ranges[0], LineRange::new(2, 4));
-        assert_eq!(branches.ranges[1], LineRange::new(6, 9));
-    }
-
-    #[test]
-    fn if_elif_else() {
-        let source = "fn foo(x: i32) {
-            if x > 0 {
-                println!(\"BOO\");
-            } else if x < 0 {
-                todo!()
-            }
-            else
-            {
-                todo!()
-            }
-        }";
-        let parser = parse_file(source).unwrap();
-        let func = &parser.items[0];
-        let mut branches = None;
-        if let Item::Fn(func) = func {
-            for stmt in &func.block.stmts {
-                match stmt {
-                    Stmt::Semi(Expr::If(i), _) | Stmt::Expr(Expr::If(i)) => {
-                        branches = Some(Branches::from(i));
-                    }
-                    _ => {}
-                }
-            }
-        }
-        let branches = branches.unwrap();
-        println!("Branches: {branches:?}");
-        assert!(!branches.implicit_default);
-        assert_eq!(branches.ranges.len(), 3);
-        assert_eq!(branches.ranges[0], LineRange::new(2, 4));
-        assert_eq!(branches.ranges[1], LineRange::new(4, 7));
-        assert_eq!(branches.ranges[2], LineRange::new(8, 11));
-    }
-
-    #[test]
-    fn match_branches() {
-        let source = "fn foo(x: i32) {
-            match 4 {
-                0 => {
-                    todo!();
-                },
-                1 => {
-                    todo!();
-                },
-                _ => {},
-            }
-        }";
-        let parser = parse_file(source).unwrap();
-        let func = &parser.items[0];
-        let mut branches = None;
-        if let Item::Fn(func) = func {
-            for stmt in &func.block.stmts {
-                match stmt {
-                    Stmt::Semi(Expr::Match(m), _) | Stmt::Expr(Expr::Match(m)) => {
-                        branches = Some(Branches::from(m));
-                    }
-                    _ => {}
-                }
-            }
-        }
-        let branches = branches.unwrap();
-        assert!(!branches.implicit_default);
-        assert_eq!(branches.ranges.len(), 3);
-        assert_eq!(branches.ranges[0], LineRange::new(3, 6));
-        assert_eq!(branches.ranges[1], LineRange::new(6, 9));
-        assert_eq!(branches.ranges[2], LineRange::new(9, 10));
-    }
-
-    #[test]
-    fn for_branches() {
-        let source = "fn foo(x: i32) {
-            for i in 0..1 {
-                todo!();
-            }
-        }";
-        let parser = parse_file(source).unwrap();
-        let func = &parser.items[0];
-        let mut branches = None;
-        if let Item::Fn(func) = func {
-            for stmt in &func.block.stmts {
-                match stmt {
-                    Stmt::Semi(Expr::ForLoop(f), _) | Stmt::Expr(Expr::ForLoop(f)) => {
-                        branches = Some(Branches::from(f));
-                    }
-                    _ => {}
-                }
-            }
-        }
-        let branches = branches.unwrap();
-        assert!(branches.implicit_default);
-        assert_eq!(branches.ranges.len(), 1);
-        assert_eq!(branches.ranges[0], LineRange::new(3, 5));
-    }
-
-    #[test]
-    fn while_branches() {
-        let source = "fn foo(x: i32) {
-            let mut i = 0;
-            while i < 10 {
-                i+=1;
-            }
-        }";
-        let parser = parse_file(source).unwrap();
-        let func = &parser.items[0];
-        let mut branches = None;
-        if let Item::Fn(func) = func {
-            for stmt in &func.block.stmts {
-                match stmt {
-                    Stmt::Semi(Expr::While(w), _) | Stmt::Expr(Expr::While(w)) => {
-                        branches = Some(Branches::from(w));
-                    }
-                    _ => {}
-                }
-            }
-        }
-        let branches = branches.unwrap();
-        assert!(branches.implicit_default);
-        assert_eq!(branches.ranges.len(), 1);
-        assert_eq!(branches.ranges[0], LineRange::new(4, 6));
-    }
-
-    #[test]
-    fn branch_analysis_context() {
-        let mut analysis = BranchAnalysis::new();
-        let source = "fn foo(x: i32) {
-            if x > 0 {
-                println!(\"BOO\");
-            } else if x < 0 {
-                todo!()
-            }
-            println!(\"hello\");
-            match x {
-                0..5 => println!(\"less than 6\"),
-                _ => println!(\"greater than 5\"),
-            }
-            for i in 0..5 {
-                let _x = i*2;
-            }
-            while false {
-                unreachable!();
-            }
-        }";
-        let parser = parse_file(source).unwrap();
-        let func = &parser.items[0];
-        if let Item::Fn(func) = func {
-            for stmt in &func.block.stmts {
-                match stmt {
-                    Stmt::Semi(e, _) | Stmt::Expr(e) => {
-                        analysis.register_expr(e);
-                    }
-                    _ => {}
-                }
-            }
-        }
-
-        assert!(!analysis.is_branch(0));
-        assert!(analysis.is_branch(2));
-        assert!(analysis.is_branch(3));
-        assert!(analysis.is_branch(4));
-        assert!(!analysis.is_branch(7));
-        assert!(analysis.is_branch(9));
-        assert!(analysis.is_branch(10));
-        assert!(analysis.is_branch(13));
-        assert!(analysis.is_branch(16));
-
-        // Quick sanity check
-        let mut context = BranchContext::default();
-        context.files.insert(PathBuf::from("foo.rs"), analysis);
-        assert!(!context.is_branch("foo.rs", 1));
-        assert!(context.is_branch("foo.rs", 2));
-        assert!(!context.is_branch("bar.rs", 2));
-    }
-}
diff --git a/src/config/types.rs b/src/config/types.rs
index 224b6a0..8d9a830 100644
--- a/src/config/types.rs
+++ b/src/config/types.rs
@@ -1,22 +1,11 @@
 use clap::ValueEnum;
 use coveralls_api::CiService;
-use enum_display::EnumDisplay;
 use serde::{Deserialize, Serialize};
+use std::fmt;
 use std::str::FromStr;
 
 #[derive(
-    Debug,
-    Copy,
-    Clone,
-    PartialEq,
-    Eq,
-    Hash,
-    Ord,
-    PartialOrd,
-    Deserialize,
-    Serialize,
-    ValueEnum,
-    EnumDisplay,
+    Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Deserialize, Serialize, ValueEnum,
 )]
 #[value(rename_all = "PascalCase")]
 pub enum Color {
@@ -25,6 +14,16 @@ pub enum Color {
     Never,
 }
 
+impl fmt::Display for Color {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Color::Auto => write!(f, "Auto"),
+            Color::Always => write!(f, "Always"),
+            Color::Never => write!(f, "Never"),
+        }
+    }
+}
+
 #[derive(
     Debug,
     Default,
diff --git a/src/lib.rs b/src/lib.rs
index d547c71..7a707a7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -14,7 +14,6 @@ use tracing::{debug, error, info, warn};
 use tracing_subscriber::{filter::LevelFilter, EnvFilter};
 
 pub mod args;
-pub mod branching;
 pub mod cargo;
 pub mod config;
 pub mod errors;
diff --git a/src/report/safe_json.rs b/src/report/safe_json.rs
index cc83775..59aafa9 100644
--- a/src/report/safe_json.rs
+++ b/src/report/safe_json.rs
@@ -11,10 +11,11 @@ impl Default for SafeFormatter {
 }
 
 impl serde_json::ser::Formatter for SafeFormatter {
-    fn write_string_fragment<W: ?Sized>(&mut self, writer: &mut W, fragment: &str) -> io::Result<()>
-    where
-        W: io::Write,
-    {
+    fn write_string_fragment<W: ?Sized + io::Write>(
+        &mut self,
+        writer: &mut W,
+        fragment: &str,
+    ) -> io::Result<()> {
         let mut start = 0;
         let mut code_length = 0;
         for ch in fragment.chars() {
diff --git a/src/source_analysis/attributes.rs b/src/source_analysis/attributes.rs
index 461a1e9..dc1befa 100644
--- a/src/source_analysis/attributes.rs
+++ b/src/source_analysis/attributes.rs
@@ -1,7 +1,7 @@
 use crate::source_analysis::prelude::*;
 use syn::*;
 
-mod predicates {
+pub mod predicates {
     pub fn is_test_attribute(id: &syn::Path) -> bool {
         id.segments
             .last()
@@ -18,21 +18,17 @@ impl SourceAnalysis {
         let mut check_cover = true;
         for attr in attrs {
             analysis.ignore_tokens(attr);
-            if let Ok(x) = attr.parse_meta() {
-                if check_cfg_attr(&x) {
+            if check_cfg_attr(&attr.meta) {
+                check_cover = false;
+            } else if attr.meta.path().is_ident("cfg") {
+                let mut skip = false;
+                let _ = attr.parse_nested_meta(|meta| {
+                    skip |=
+                        predicates::is_test_attribute(&meta.path) && !ctx.config.include_tests();
+                    Ok(())
+                });
+                if skip {
                     check_cover = false;
-                } else if x.path().is_ident("cfg") {
-                    if let Meta::List(ref ml) = x {
-                        let mut skip = false;
-                        for c in &ml.nested {
-                            if let NestedMeta::Meta(Meta::Path(ref i)) = c {
-                                skip |= i.is_ident("test") && !ctx.config.include_tests();
-                            }
-                        }
-                        if skip {
-                            check_cover = false;
-                        }
-                    }
                 }
             }
             if !check_cover {
@@ -44,6 +40,7 @@ impl SourceAnalysis {
 }
 
 pub(crate) fn check_cfg_attr(attr: &Meta) -> bool {
+    tracing::trace!("cfg attr: {}", attr.to_token_stream());
     let mut ignore_span = false;
     let id = attr.path();
 
@@ -51,36 +48,32 @@ pub(crate) fn check_cfg_attr(attr: &Meta) -> bool {
         ignore_span = true;
     } else if id.is_ident("cfg") {
         if let Meta::List(ml) = attr {
-            'outer: for p in ml.nested.iter() {
-                if let NestedMeta::Meta(Meta::List(ref i)) = p {
-                    if i.path.is_ident("not") {
-                        for n in i.nested.iter() {
-                            if let NestedMeta::Meta(Meta::Path(ref pth)) = n {
-                                if pth.is_ident("tarpaulin_include") || pth.is_ident("tarpaulin") {
-                                    ignore_span = true;
-                                    break 'outer;
-                                }
-                            }
-                        }
-                    }
+            let _ = ml.parse_nested_meta(|nested| {
+                if nested.path.is_ident("not") {
+                    nested.parse_nested_meta(|meta| {
+                        ignore_span |= meta.path.is_ident("tarpaulin_include")
+                            || meta.path.is_ident("tarpaulin");
+                        Ok(())
+                    })
+                } else {
+                    Ok(())
                 }
-            }
+            });
         }
     } else if id.is_ident("cfg_attr") {
         if let Meta::List(ml) = attr {
             let tarp_cfged_ignores = &["no_coverage"];
-            if let NestedMeta::Meta(Meta::Path(ref i)) = ml.nested[0] {
-                if i.is_ident("tarpaulin") {
-                    for p in ml.nested.iter().skip(1) {
-                        if let NestedMeta::Meta(Meta::Path(ref path)) = p {
-                            if tarp_cfged_ignores.iter().any(|x| path.is_ident(x)) {
-                                ignore_span = true;
-                                break;
-                            }
-                        }
-                    }
+            let mut first = true;
+            let mut is_tarpaulin = false;
+            let _ = ml.parse_nested_meta(|nested| {
+                if first && nested.path.is_ident("tarpaulin") {
+                    first = false;
+                    is_tarpaulin = true;
+                } else if !first && is_tarpaulin {
+                    ignore_span |= tarp_cfged_ignores.iter().any(|x| nested.path.is_ident(x));
                 }
-            }
+                Ok(())
+            });
         }
     } else if predicates::is_test_attribute(id) {
         ignore_span = true;
diff --git a/src/source_analysis/expressions.rs b/src/source_analysis/expressions.rs
index a756c3b..b8dfda9 100644
--- a/src/source_analysis/expressions.rs
+++ b/src/source_analysis/expressions.rs
@@ -3,10 +3,6 @@ use syn::{punctuated::Pair, punctuated::Punctuated, token::Comma, *};
 
 impl SourceAnalysis {
     pub(crate) fn process_expr(&mut self, expr: &Expr, ctx: &Context) -> SubResult {
-        if ctx.config.branch_coverage {
-            let branches = self.get_branch_analysis(ctx.file.to_path_buf());
-            branches.register_expr(expr);
-        }
         let res = match expr {
             Expr::Macro(m) => self.visit_macro_call(&m.mac, ctx),
             Expr::Struct(s) => self.visit_struct_expr(s, ctx),
@@ -274,15 +270,15 @@ impl SourceAnalysis {
         let u_line = unsafe_expr.unsafe_token.span().start().line;
         let mut res = SubResult::Ok;
         let blk = &unsafe_expr.block;
-        if u_line != blk.brace_token.span.start().line || blk.stmts.is_empty() {
+        if u_line != blk.brace_token.span.join().start().line || blk.stmts.is_empty() {
             let analysis = self.get_line_analysis(ctx.file.to_path_buf());
             analysis.ignore_tokens(unsafe_expr.unsafe_token);
         } else if let Some(first_stmt) = blk.stmts.first() {
             let s = match first_stmt {
                 Stmt::Local(l) => l.span(),
                 Stmt::Item(i) => i.span(),
-                Stmt::Expr(e) => e.span(),
-                Stmt::Semi(e, _) => e.span(),
+                Stmt::Expr(e, _) => e.span(),
+                Stmt::Macro(m) => m.span(),
             };
             if u_line != s.start().line {
                 let analysis = self.get_line_analysis(ctx.file.to_path_buf());
@@ -298,7 +294,7 @@ impl SourceAnalysis {
         } else {
             let analysis = self.get_line_analysis(ctx.file.to_path_buf());
             analysis.ignore_tokens(unsafe_expr.unsafe_token);
-            analysis.ignore_span(blk.brace_token.span);
+            analysis.ignore_span(blk.span());
         }
         res
     }
diff --git a/src/source_analysis/items.rs b/src/source_analysis/items.rs
index 4d8fe3a..0222e87 100644
--- a/src/source_analysis/items.rs
+++ b/src/source_analysis/items.rs
@@ -48,38 +48,16 @@ impl SourceAnalysis {
     fn visit_mod(&mut self, module: &ItemMod, ctx: &Context) {
         let analysis = self.get_line_analysis(ctx.file.to_path_buf());
         analysis.ignore_tokens(module.mod_token);
-        let mut check_insides = true;
-        for attr in &module.attrs {
-            if let Ok(x) = attr.parse_meta() {
-                if check_cfg_attr(&x) {
-                    analysis.ignore_tokens(module);
-                    if let Some((ref braces, _)) = module.content {
-                        analysis.ignore_span(braces.span);
-                    }
-                    check_insides = false;
-                    break;
-                } else if !ctx.config.include_tests() && x.path().is_ident("cfg") {
-                    if let Meta::List(ref ml) = x {
-                        for nested in &ml.nested {
-                            if let NestedMeta::Meta(Meta::Path(ref i)) = *nested {
-                                if i.is_ident("test") {
-                                    check_insides = false;
-                                    analysis.ignore_tokens(module.mod_token);
-                                    if let Some((ref braces, _)) = module.content {
-                                        analysis.ignore_span(braces.span);
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
+        let check_insides = self.check_attr_list(&module.attrs, ctx);
         if check_insides {
             if let Some((_, ref items)) = module.content {
                 self.process_items(items, ctx);
             }
         } else {
+            if let Some((ref braces, _)) = module.content {
+                let analysis = self.get_line_analysis(ctx.file.to_path_buf());
+                analysis.ignore_span(braces.span.join());
+            }
             // Get the file or directory name of the module
             let mut p = if let Some(parent) = ctx.file.parent() {
                 parent.join(module.ident.to_string())
@@ -100,22 +78,19 @@ impl SourceAnalysis {
         let mut ignore_span = false;
         let is_generic = is_sig_generic(&func.sig);
         for attr in &func.attrs {
-            if let Ok(x) = attr.parse_meta() {
-                let id = x.path();
-                if id.is_ident("test") || id.segments.last().is_some_and(|seg| seg.ident == "test")
-                {
-                    test_func = true;
-                } else if id.is_ident("derive") {
-                    let analysis = self.get_line_analysis(ctx.file.to_path_buf());
-                    analysis.ignore_span(attr.bracket_token.span);
-                } else if id.is_ident("inline") {
-                    is_inline = true;
-                } else if id.is_ident("ignore") {
-                    ignored_attr = true;
-                } else if check_cfg_attr(&x) {
-                    ignore_span = true;
-                    break;
-                }
+            let id = attr.path();
+            if id.is_ident("test") || id.segments.last().is_some_and(|seg| seg.ident == "test") {
+                test_func = true;
+            } else if id.is_ident("derive") {
+                let analysis = self.get_line_analysis(ctx.file.to_path_buf());
+                analysis.ignore_span(attr.span());
+            } else if id.is_ident("inline") {
+                is_inline = true;
+            } else if id.is_ident("ignore") {
+                ignored_attr = true;
+            } else if check_cfg_attr(&attr.meta) {
+                ignore_span = true;
+                break;
             }
         }
         if ignore_span
@@ -128,7 +103,7 @@ impl SourceAnalysis {
             if is_inline || is_generic || force_cover {
                 let analysis = self.get_line_analysis(ctx.file.to_path_buf());
                 // We need to force cover!
-                analysis.cover_span(func.block.brace_token.span, Some(ctx.file_contents));
+                analysis.cover_span(func.block.span(), Some(ctx.file_contents));
             }
             if self
                 .process_statements(&func.block.stmts, ctx)
@@ -163,7 +138,7 @@ impl SourceAnalysis {
         let check_cover = self.check_attr_list(&trait_item.attrs, ctx);
         if check_cover {
             for item in &trait_item.items {
-                if let TraitItem::Method(ref i) = *item {
+                if let TraitItem::Fn(ref i) = *item {
                     if self.check_attr_list(&i.attrs, ctx) {
                         let item = i.clone();
                         if let Some(block) = item.default {
@@ -201,19 +176,26 @@ impl SourceAnalysis {
         let check_cover = self.check_attr_list(&impl_blk.attrs, ctx);
         if check_cover {
             for item in &impl_blk.items {
-                if let ImplItem::Method(ref i) = *item {
-                    let item = i.clone();
-                    let item_fn = ItemFn {
-                        attrs: item.attrs,
-                        vis: item.vis,
-                        sig: item.sig,
-                        block: Box::new(item.block),
-                    };
+                match *item {
+                    ImplItem::Fn(ref i) => {
+                        let item = i.clone();
+                        let item_fn = ItemFn {
+                            attrs: item.attrs,
+                            vis: item.vis,
+                            sig: item.sig,
+                            block: Box::new(item.block),
+                        };
 
-                    // If the impl is on a generic, we need to force cover
-                    let force_cover = !impl_blk.generics.params.is_empty();
+                        // If the impl is on a generic, we need to force cover
+                        let force_cover = !impl_blk.generics.params.is_empty();
 
-                    self.visit_fn(&item_fn, ctx, force_cover);
+                        self.visit_fn(&item_fn, ctx, force_cover);
+                    }
+                    ImplItem::Type(_) => {
+                        let analysis = self.get_line_analysis(ctx.file.to_path_buf());
+                        analysis.ignore_span(item.span());
+                    }
+                    _ => {}
                 }
             }
             self.visit_generics(&impl_blk.generics, ctx);
diff --git a/src/source_analysis/macros.rs b/src/source_analysis/macros.rs
index 6232553..e4879a4 100644
--- a/src/source_analysis/macros.rs
+++ b/src/source_analysis/macros.rs
@@ -4,6 +4,24 @@ use std::cmp::{max, min};
 use std::ops::Range;
 use syn::*;
 
+pub fn ignore_macro_name(ident: &Ident, ctx: &Context) -> (SubResult, bool) {
+    let ident_s = ident.to_string();
+    let unreachable = ident == "unreachable";
+    let standard_ignores =
+        ident == "unimplemented" || ident == "include" || ident == "cfg" || ident == "todo";
+    let ignore_panic = ctx.config.ignore_panics
+        && (ident == "panic"
+            || ident_s.starts_with("assert")
+            || ident_s.starts_with("debug_assert"));
+    let sub = if unreachable {
+        SubResult::Unreachable
+    } else {
+        SubResult::Ok
+    };
+    let should_ignore = standard_ignores || ignore_panic || unreachable;
+    (sub, should_ignore)
+}
+
 impl SourceAnalysis {
     pub(crate) fn visit_macro_call(&mut self, mac: &Macro, ctx: &Context) -> SubResult {
         let analysis = self.get_line_analysis(ctx.file.to_path_buf());
@@ -13,19 +31,12 @@ impl SourceAnalysis {
             arguments: _,
         }) = mac.path.segments.last()
         {
-            let ident_s = ident.to_string();
-            let unreachable = ident == "unreachable";
-            let standard_ignores =
-                ident == "unimplemented" || ident == "include" || ident == "cfg" || ident == "todo";
-            let ignore_panic = ctx.config.ignore_panics
-                && (ident == "panic"
-                    || ident_s.starts_with("assert")
-                    || ident_s.starts_with("debug_assert"));
-            if standard_ignores || ignore_panic || unreachable {
+            let (sub, ignore_macro) = ignore_macro_name(ident, ctx);
+            if ignore_macro {
                 analysis.ignore_tokens(mac);
                 skip = true;
             }
-            if unreachable {
+            if sub == SubResult::Unreachable {
                 return SubResult::Unreachable;
             }
         }
diff --git a/src/source_analysis/mod.rs b/src/source_analysis/mod.rs
index 2dfe9f7..14b6e3a 100644
--- a/src/source_analysis/mod.rs
+++ b/src/source_analysis/mod.rs
@@ -1,4 +1,3 @@
-use crate::branching::BranchAnalysis;
 use crate::config::{Config, RunType};
 use crate::path_utils::{get_source_walker, is_source_file};
 use lazy_static::lazy_static;
@@ -261,7 +260,6 @@ impl LineAnalysis {
 #[derive(Default)]
 pub struct SourceAnalysis {
     pub lines: HashMap<PathBuf, LineAnalysis>,
-    pub branches: HashMap<PathBuf, BranchAnalysis>,
     ignored_modules: Vec<PathBuf>,
 }
 
@@ -276,10 +274,6 @@ impl SourceAnalysis {
             .or_insert_with(|| LineAnalysis::new_from_file(&path).unwrap_or_default())
     }
 
-    pub fn get_branch_analysis(&mut self, path: PathBuf) -> &mut BranchAnalysis {
-        self.branches.entry(path).or_default()
-    }
-
     fn is_ignored_module(&self, path: &Path) -> bool {
         self.ignored_modules.iter().any(|x| path.starts_with(x))
     }
@@ -476,10 +470,6 @@ impl SourceAnalysis {
                     trace!("Coverable lines: {:?}", lines);
                 }
             }
-            if config.branch_coverage {
-                trace!("Branch analysis");
-                trace!("{:?}", self.branches);
-            }
         }
     }
 }
diff --git a/src/source_analysis/statements.rs b/src/source_analysis/statements.rs
index bf74de5..c92465e 100644
--- a/src/source_analysis/statements.rs
+++ b/src/source_analysis/statements.rs
@@ -10,8 +10,9 @@ impl SourceAnalysis {
         for stmt in stmts.iter() {
             let res = match stmt {
                 Stmt::Item(i) => self.process_items(&[i.clone()], ctx),
-                Stmt::Expr(i) | Stmt::Semi(i, _) => self.process_expr(i, ctx),
+                Stmt::Expr(i, _) => self.process_expr(i, ctx),
                 Stmt::Local(i) => self.process_local(i, ctx),
+                Stmt::Macro(i) => self.process_macro(i, ctx),
             };
             unreachable |= res.is_unreachable();
             if SubResult::Definite == res {
@@ -28,9 +29,35 @@ impl SourceAnalysis {
         }
     }
 
+    fn process_macro(&mut self, mac: &StmtMacro, ctx: &Context) -> SubResult {
+        let check_cover = self.check_attr_list(&mac.attrs, ctx);
+        if check_cover {
+            if let Some(macro_name) = mac.mac.path.segments.last() {
+                let (sub, should_ignore) = ignore_macro_name(&macro_name.ident, ctx);
+                let analysis = self.get_line_analysis(ctx.file.to_path_buf());
+                if should_ignore {
+                    analysis.ignore_tokens(mac);
+                } else {
+                    // lets just merge the macros into one big logical line
+                    let start = mac.span().start().line;
+                    for i in start..mac.span().end().line {
+                        analysis.logical_lines.insert(i + 1, start);
+                    }
+                }
+                sub
+            } else {
+                SubResult::Ok
+            }
+        } else {
+            let analysis = self.get_line_analysis(ctx.file.to_path_buf());
+            analysis.ignore_tokens(mac);
+            SubResult::Ok
+        }
+    }
+
     fn process_local(&mut self, local: &Local, ctx: &Context) -> SubResult {
         let mut result = SubResult::Ok;
-        if let Some((eq, expr)) = &local.init {
+        if let Some(init) = &local.init {
             // Process if the local wasn't ignored with an attribute
             let check_cover = self.check_attr_list(&local.attrs, ctx);
             let analysis = self.get_line_analysis(ctx.file.to_path_buf());
@@ -47,16 +74,19 @@ impl SourceAnalysis {
                     if lhs.start().line != base_line {
                         analysis.logical_lines.insert(lhs.start().line, base_line);
                     }
-                    let eq = eq.span();
+                    let eq = init.eq_token.span();
                     if eq.start().line != base_line {
                         analysis.logical_lines.insert(eq.start().line, base_line);
                     }
-                    if expr.span().start().line != base_line {
+                    if init.expr.span().start().line != base_line {
                         analysis
                             .logical_lines
-                            .insert(expr.span().start().line, base_line);
+                            .insert(init.expr.span().start().line, base_line);
+                    }
+                    result += self.process_expr(&init.expr, ctx);
+                    if let Some((_, expr)) = &init.diverge {
+                        self.process_expr(expr, ctx);
                     }
-                    result += self.process_expr(expr, ctx);
                 }
             } else {
                 analysis.ignore_tokens(local);


Test patch:
diff --git a/src/source_analysis/tests.rs b/src/source_analysis/tests.rs
index dd240bd..f634cae 100644
--- a/src/source_analysis/tests.rs
+++ b/src/source_analysis/tests.rs
@@ -1,7 +1,9 @@
 use crate::source_analysis::prelude::*;
 use syn::parse_file;
+use tracing_test::traced_test;
 
 #[test]
+#[traced_test]
 fn logical_lines_let_bindings() {
     let config = Config::default();
     let mut analysis = SourceAnalysis::new();
@@ -54,6 +56,7 @@ fn logical_lines_let_bindings() {
 }
 
 #[test]
+#[traced_test]
 fn match_pattern_logical_lines() {
     let config = Config::default();
     let ctx = Context {
@@ -86,6 +89,7 @@ fn match_pattern_logical_lines() {
 }
 
 #[test]
+#[traced_test]
 fn line_analysis_works() {
     let mut la = LineAnalysis::new();
     assert!(!la.should_ignore(0));
@@ -99,6 +103,7 @@ fn line_analysis_works() {
 }
 
 #[test]
+#[traced_test]
 fn filter_str_literals() {
     let config = Config::default();
     let ctx = Context {
@@ -115,9 +120,8 @@ fn filter_str_literals() {
     let mut analysis = SourceAnalysis::new();
     analysis.process_items(&parser.items, &ctx);
     let lines = analysis.get_line_analysis(ctx.file.to_path_buf());
-    assert!(lines.ignore.len() > 1);
-    assert!(lines.ignore.contains(&Lines::Line(3)));
-    assert!(lines.ignore.contains(&Lines::Line(4)));
+    assert_eq!(lines.logical_lines[&3], 2);
+    assert_eq!(lines.logical_lines[&4], 2);
 
     let ctx = Context {
         config: &config,
@@ -155,10 +159,12 @@ fn filter_str_literals() {
     let mut analysis = SourceAnalysis::new();
     analysis.process_items(&parser.items, &ctx);
     let lines = analysis.get_line_analysis(ctx.file.to_path_buf());
-    assert!(lines.ignore.contains(&Lines::Line(5)));
+    assert_eq!(lines.logical_lines[&5], 4);
+    assert_eq!(lines.logical_lines[&6], 4);
 }
 
 #[test]
+#[traced_test]
 fn filter_struct_members() {
     let config = Config::default();
     let ctx = Context {
@@ -193,6 +199,7 @@ fn filter_struct_members() {
 }
 
 #[test]
+#[traced_test]
 fn filter_enum_members() {
     let config = Config::default();
     let ctx = Context {
@@ -215,6 +222,7 @@ fn filter_enum_members() {
 }
 
 #[test]
+#[traced_test]
 fn filter_struct_consts() {
     let config = Config::default();
     let ctx = Context {
@@ -238,6 +246,7 @@ fn filter_struct_consts() {
 }
 
 #[test]
+#[traced_test]
 fn filter_unreachable_unchecked() {
     let config = Config::default();
     let ctx = Context {
@@ -256,6 +265,7 @@ fn filter_unreachable_unchecked() {
 }
 
 #[test]
+#[traced_test]
 fn filter_loop_attr() {
     let config = Config::default();
     let ctx = Context {
@@ -284,6 +294,7 @@ fn filter_loop_attr() {
 }
 
 #[test]
+#[traced_test]
 fn filter_mods() {
     let config = Config::default();
     let ctx = Context {
@@ -324,6 +335,7 @@ fn filter_mods() {
 }
 
 #[test]
+#[traced_test]
 fn filter_macros() {
     let config = Config::default();
     let ctx = Context {
@@ -385,6 +397,7 @@ fn filter_macros() {
 }
 
 #[test]
+#[traced_test]
 fn filter_tests() {
     let mut config = Config::default();
     config.set_include_tests(true);
@@ -436,6 +449,7 @@ fn filter_tests() {
     assert!(!lines.ignore.contains(&Lines::Line(2)));
     assert!(!lines.ignore.contains(&Lines::Line(3)));
 
+    tracing::trace!("Starting new analyis");
     let ctx = Context {
         config: &igconfig,
         file_contents: "#[test]\nfn mytest() { \n assert!(true);\n}",
@@ -450,6 +464,7 @@ fn filter_tests() {
 }
 
 #[test]
+#[traced_test]
 fn filter_nonstd_tests() {
     let mut igconfig = Config::default();
     igconfig.set_include_tests(false);
@@ -519,6 +534,7 @@ fn filter_nonstd_tests() {
 }
 
 #[test]
+#[traced_test]
 fn include_nonstd_tests() {
     let mut config = Config::default();
     config.set_include_tests(true);
@@ -573,6 +589,7 @@ fn include_nonstd_tests() {
 }
 
 #[test]
+#[traced_test]
 fn filter_test_utilities() {
     let mut config = Config::default();
     config.set_include_tests(false);
@@ -619,6 +636,7 @@ fn filter_test_utilities() {
 }
 
 #[test]
+#[traced_test]
 fn filter_where() {
     let config = Config::default();
     let ctx = Context {
@@ -669,6 +687,7 @@ fn filter_where() {
 }
 
 #[test]
+#[traced_test]
 fn filter_derives() {
     let config = Config::default();
     let ctx = Context {
@@ -697,6 +716,7 @@ fn filter_derives() {
 }
 
 #[test]
+#[traced_test]
 fn filter_unsafe() {
     let config = Config::default();
     let ctx = Context {
@@ -726,6 +746,7 @@ fn filter_unsafe() {
 }
 
 #[test]
+#[traced_test]
 fn cover_generic_impl_methods() {
     let config = Config::default();
     let ctx = Context {
@@ -767,6 +788,7 @@ fn cover_generic_impl_methods() {
 }
 
 #[test]
+#[traced_test]
 fn cover_default_trait_methods() {
     let config = Config::default();
     let ctx = Context {
@@ -788,6 +810,7 @@ fn cover_default_trait_methods() {
 }
 
 #[test]
+#[traced_test]
 fn cover_impl_trait_generic_fns() {
     let config = Config::default();
     let ctx = Context {
@@ -807,6 +830,7 @@ fn cover_impl_trait_generic_fns() {
 }
 
 #[test]
+#[traced_test]
 fn filter_method_args() {
     let config = Config::default();
     let ctx = Context {
@@ -844,6 +868,7 @@ fn filter_method_args() {
 }
 
 #[test]
+#[traced_test]
 fn filter_use_statements() {
     let config = Config::default();
     let ctx = Context {
@@ -862,6 +887,7 @@ fn filter_use_statements() {
 }
 
 #[test]
+#[traced_test]
 fn include_inline_fns() {
     let config = Config::default();
     let ctx = Context {
@@ -891,6 +917,7 @@ fn include_inline_fns() {
 }
 
 #[test]
+#[traced_test]
 fn cover_callable_noargs() {
     let config = Config::default();
     let ctx = Context {
@@ -909,6 +936,7 @@ fn cover_callable_noargs() {
 }
 
 #[test]
+#[traced_test]
 fn filter_closure_contents() {
     let config = Config::default();
     let ctx = Context {
@@ -929,6 +957,7 @@ fn filter_closure_contents() {
 }
 
 #[test]
+#[traced_test]
 fn tarpaulin_skip_attr() {
     let config = Config::default();
     let ctx = Context {
@@ -1010,6 +1039,7 @@ fn tarpaulin_skip_attr() {
 }
 
 #[test]
+#[traced_test]
 fn tarpaulin_skip_trait_attrs() {
     let config = Config::default();
     let ctx = Context {
@@ -1065,6 +1095,7 @@ fn tarpaulin_skip_trait_attrs() {
 }
 
 #[test]
+#[traced_test]
 fn tarpaulin_skip_impl_attrs() {
     let config = Config::default();
     let ctx = Context {
@@ -1123,6 +1154,7 @@ fn tarpaulin_skip_impl_attrs() {
 }
 
 #[test]
+#[traced_test]
 fn filter_block_contents() {
     let config = Config::default();
     let ctx = Context {
@@ -1149,6 +1181,7 @@ fn filter_block_contents() {
 }
 
 #[test]
+#[traced_test]
 fn filter_consts() {
     let config = Config::default();
     let ctx = Context {
@@ -1167,6 +1200,7 @@ fn filter_consts() {
 }
 
 #[test]
+#[traced_test]
 fn optional_panic_ignore() {
     let config = Config::default();
     let ctx = Context {
@@ -1218,6 +1252,7 @@ fn optional_panic_ignore() {
 }
 
 #[test]
+#[traced_test]
 fn filter_nested_blocks() {
     let config = Config::default();
     let ctx = Context {
@@ -1250,6 +1285,7 @@ fn filter_nested_blocks() {
 }
 
 #[test]
+#[traced_test]
 fn filter_multi_line_decls() {
     let config = Config::default();
     let ctx = Context {
@@ -1310,6 +1346,7 @@ fn filter_multi_line_decls() {
 }
 
 #[test]
+#[traced_test]
 fn unreachable_propagate() {
     let config = Config::default();
     let ctx = Context {
@@ -1385,6 +1422,7 @@ fn unreachable_propagate() {
 }
 
 #[test]
+#[traced_test]
 fn unreachable_include_returns() {
     let config = Config::default();
     let ctx = Context {
@@ -1444,6 +1482,7 @@ fn unreachable_include_returns() {
 }
 
 #[test]
+#[traced_test]
 fn unreachable_include_loops() {
     let config = Config::default();
     let ctx = Context {
@@ -1511,6 +1550,7 @@ fn unreachable_include_loops() {
 }
 
 #[test]
+#[traced_test]
 fn single_line_callables() {
     let config = Config::default();
     let ctx = Context {
@@ -1545,6 +1585,7 @@ fn single_line_callables() {
 }
 
 #[test]
+#[traced_test]
 fn visit_generics() {
     let config = Config::default();
     let ctx = Context {
@@ -1591,6 +1632,7 @@ fn visit_generics() {
 }
 
 #[test]
+#[traced_test]
 fn ignore_comment() {
     let config = Config::default();
     let ctx = Context {
@@ -1625,6 +1667,7 @@ fn ignore_comment() {
 }
 
 #[test]
+#[traced_test]
 fn py_attr() {
     let config = Config::default();
     let ctx = Context {
@@ -1659,3 +1702,43 @@ fn py_attr() {
     assert!(lines.ignore.contains(&Lines::Line(11)));
     assert!(lines.ignore.contains(&Lines::Line(13)));
 }
+
+#[test]
+#[traced_test]
+fn handle_c_strs() {
+    let config = Config::default();
+    let ctx = Context {
+        config: &config,
+        file_contents: r#"fn main() {
+            const some_c_string: &CStr = c"foo";
+        }"#,
+        file: Path::new(""),
+        ignore_mods: RefCell::new(HashSet::new()),
+    };
+    let parser = parse_file(ctx.file_contents).unwrap();
+    let mut analysis = SourceAnalysis::new();
+    analysis.process_items(&parser.items, &ctx);
+}
+
+#[test]
+#[traced_test]
+fn ignore_trait_types() {
+    let config = Config::default();
+    let ctx = Context {
+        config: &config,
+        file_contents: "pub trait Foo { type X; }
+            struct Bar;
+
+            impl Foo for Bar {
+                type X = i32;
+            }
+        ",
+        file: Path::new(""),
+        ignore_mods: RefCell::new(HashSet::new()),
+    };
+    let parser = parse_file(ctx.file_contents).unwrap();
+    let mut analysis = SourceAnalysis::new();
+    analysis.process_items(&parser.items, &ctx);
+    let lines = &analysis.lines[Path::new("")];
+    assert!(lines.ignore.contains(&Lines::Line(5)));
+}