Repository: gimli-rs/addr2line (rust)

Issue:
Promote the CLI example to a `bin` crate
As stated in #244, there are some use cases where an addr2line replacement can be greatly useful.

Please consider promoting the example to a `bin` crate (by moving to `src/main.rs`), which allows it to be more easily installed through `cargo install`.

Pull request:
Move addr2line from examples to src/bin
Closes #245 

Patch:
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index 991fda9..3bd53b7 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -34,9 +34,13 @@ jobs:
         rustup install ${{matrix.rust_channel}}
         rustup default ${{matrix.rust_channel}}
     - name: Test debug
-      run: cargo test --verbose
+      run: |
+        cargo test --verbose
+        cargo test --verbose --features bin
     - name: Test release
-      run: cargo test --verbose --release
+      run: |
+        cargo test --verbose --release
+        cargo test --verbose --release --features bin
 
   features:
     runs-on: ubuntu-latest
@@ -49,6 +53,7 @@ jobs:
       - run: cargo build --no-default-features --features std-object
       - run: cargo build --no-default-features --features fallible-iterator
       - run: cargo build --no-default-features --features smallvec
+      - run: cargo build --no-default-features --features bin
 
   bench:
     runs-on: ubuntu-latest
@@ -98,7 +103,7 @@ jobs:
           rustup install stable
           rustup default stable
       - name: Build addr2line
-        run: cargo build --example addr2line
+        run: cargo build --features bin
       - name: Run cargo-tarpaulin
         run: cargo tarpaulin --skip-clean --ignore-tests --out Lcov --implicit-test-threads
       - name: Upload to Coveralls
diff --git a/Cargo.toml b/Cargo.toml
index 8eddc91..9e79d21 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -21,6 +21,10 @@ smallvec = { version = "1", default-features = false, optional = true }
 rustc-demangle = { version = "0.1", optional = true }
 cpp_demangle = { version = "0.4", default-features = false, features = ["alloc"], optional = true }
 
+# bin dependencies
+clap = { version = "4.3.21", features = ["wrap_help"], optional = true }
+typed-arena = { version = "2", optional = true }
+
 # Internal feature, only used when building as part of libstd, not part of the
 # stable interface of this crate.
 core = { version = '1.0.0', optional = true, package = 'rustc-std-workspace-core' }
@@ -28,12 +32,10 @@ alloc = { version = '1.0.0', optional = true, package = 'rustc-std-workspace-all
 compiler_builtins = { version = '0.1.2', optional = true }
 
 [dev-dependencies]
-clap = { version = "4.3.21", features = ["wrap_help"] }
 backtrace = "0.3.13"
 findshlibs = "0.10"
 libtest-mimic = "0.7.2"
 auxiliary = { path = "tests/auxiliary" }
-typed-arena = "2"
 
 [profile.release]
 debug = true
@@ -46,6 +48,7 @@ codegen-units = 1
 default = ["rustc-demangle", "cpp_demangle", "std-object", "fallible-iterator", "smallvec", "memmap2"]
 std = ["gimli/std"]
 std-object = ["std", "object", "object/std", "object/compression", "gimli/endian-reader"]
+bin = ["default", "dep:clap", "dep:typed-arena"]
 
 # Internal feature, only used when building as part of libstd, not part of the
 # stable interface of this crate.
@@ -54,7 +57,7 @@ rustc-dep-of-std = ['core', 'alloc', 'compiler_builtins', 'gimli/rustc-dep-of-st
 [[test]]
 name = "output_equivalence"
 harness = false
-required-features = ["default"]
+required-features = ["bin"]
 
 [[test]]
 name = "correctness"
@@ -64,6 +67,6 @@ required-features = ["default"]
 name = "parse"
 required-features = ["std-object"]
 
-[[example]]
+[[bin]]
 name = "addr2line"
-required-features = ["default"]
+required-features = ["bin"]
diff --git a/benchmark.sh b/benchmark.sh
index ca4c4f6..e126050 100755
--- a/benchmark.sh
+++ b/benchmark.sh
@@ -45,7 +45,7 @@ dirname=$(perl -e 'use Cwd "abs_path";print abs_path(shift)' "$(dirname "$0")")
 		fn="target/release/addr2line-$commit"
 		if [[ ! -e "$fn" ]]; then
 			cargo build --release --example addr2line
-			cp target/release/examples/addr2line "$fn"
+			cp target/release/addr2line "$fn"
 		fi
 		if [[ "$ref" != "$commit" ]]; then
 			ln -sfn "addr2line-$commit" target/release/addr2line-"$ref"
diff --git a/examples/addr2line.rs b/src/bin/addr2line.rs
similarity index 100%
rename from examples/addr2line.rs
rename to src/bin/addr2line.rs
diff --git a/src/lib.rs b/src/lib.rs
index c2fc689..91b823a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -828,6 +828,9 @@ impl Lines {
 
             let address = row.address();
             let file_index = row.file_index();
+            // Convert line and column to u32 to save a little memory.
+            // We'll handle the special case of line 0 later,
+            // and return left edge as column 0 in the public API.
             let line = row.line().map(NonZeroU64::get).unwrap_or(0) as u32;
             let column = match row.column() {
                 gimli::ColumnType::LeftEdge => 0,
@@ -1432,7 +1435,8 @@ impl<'ctx> Iterator for LocationRangeUnitIter<'ctx> {
                         Location {
                             file,
                             line: if row.line != 0 { Some(row.line) } else { None },
-                            column: if row.column != 0 {
+                            // If row.line is specified then row.column always has meaning.
+                            column: if row.line != 0 {
                                 Some(row.column)
                             } else {
                                 None
@@ -1714,6 +1718,8 @@ pub struct Location<'a> {
     /// The line number.
     pub line: Option<u32>,
     /// The column number.
+    ///
+    /// A value of `Some(0)` indicates the left edge.
     pub column: Option<u32>,
 }
 


Test patch:
diff --git a/tests/output_equivalence.rs b/tests/output_equivalence.rs
index ef026e3..bed6721 100644
--- a/tests/output_equivalence.rs
+++ b/tests/output_equivalence.rs
@@ -60,7 +60,6 @@ fn run_test(flags: Option<&str>) -> Result<(), Failed> {
     if exe.file_name().unwrap().to_str().unwrap() == "deps" {
         assert!(exe.pop());
     }
-    exe.push("examples");
     exe.push("addr2line");
 
     assert!(exe.is_file());