From 44670b1d9b50576f3f6909a805d7e8d629ecf170 Mon Sep 17 00:00:00 2001
From: Jose Garcia Crosta <jgcrosta@gmail.com>
Date: Wed, 31 Jul 2024 17:11:10 -0300
Subject: [PATCH 01/18] Edit dev_dependencies for dev-dependencies

---
 docs/docs/detectors/12-soroban-version.md     |  2 +-
 docs/docs/detectors/4-overflow-check.md       |  7 +++----
 .../vulnerabilities/12-soroban-version.md     |  6 +++---
 templates/test-case/Cargo.toml                |  4 ++--
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             | 20 +++----------------
 .../vulnerable-example/Cargo.toml             | 20 +++----------------
 .../remediated-example/Cargo.toml             |  3 +--
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             |  2 +-
 .../vulnerable-example/Cargo.toml             |  2 +-
 .../remediated-example/Cargo.toml             | 16 +++++++--------
 .../vulnerable-example/Cargo.toml             | 16 +++++++--------
 .../remediated-example/Cargo.toml             | 16 +++++++--------
 .../vulnerable-example/Cargo.toml             | 16 +++++++--------
 82 files changed, 119 insertions(+), 149 deletions(-)

diff --git a/docs/docs/detectors/12-soroban-version.md b/docs/docs/detectors/12-soroban-version.md
index e2e340bc..efd7da2a 100644
--- a/docs/docs/detectors/12-soroban-version.md
+++ b/docs/docs/detectors/12-soroban-version.md
@@ -14,7 +14,7 @@ Using an old version of Soroban can be dangerous, as it may have bugs or securit
 [dependencies]
 soroban-sdk = { version = "=20.0.0" }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { version = "=20.0.0", features = ["testutils"] }
 ```
 
diff --git a/docs/docs/detectors/4-overflow-check.md b/docs/docs/detectors/4-overflow-check.md
index b744ade1..86951d08 100644
--- a/docs/docs/detectors/4-overflow-check.md
+++ b/docs/docs/detectors/4-overflow-check.md
@@ -10,7 +10,6 @@ Integer overflow will trigger a panic in debug builds or will wrap in
 release mode. Division by zero will cause a panic in either mode. In some applications one
 wants explicitly checked, wrapping or saturating arithmetic.
 
-
 ### Example
 
 ```toml
@@ -26,7 +25,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = "20.0.0-rc2"
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { version = "=20.0.0", features = ["testutils"] }
 
 [features]
@@ -62,7 +61,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = "20.0.0-rc2"
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { version = "=20.0.0", features = ["testutils"] }
 
 [features]
@@ -87,4 +86,4 @@ debug-assertions = true
 
 ### Implementation
 
-The detector's implementation can be found at [this link](https://github.com/CoinFabrik/scout-soroban/tree/main/detectors/overflow-check).
\ No newline at end of file
+The detector's implementation can be found at [this link](https://github.com/CoinFabrik/scout-soroban/tree/main/detectors/overflow-check).
diff --git a/docs/docs/vulnerabilities/12-soroban-version.md b/docs/docs/vulnerabilities/12-soroban-version.md
index da149266..25a58d1a 100644
--- a/docs/docs/vulnerabilities/12-soroban-version.md
+++ b/docs/docs/vulnerabilities/12-soroban-version.md
@@ -17,7 +17,7 @@ Consider the following `Cargo.toml`:
     [dependencies]
     soroban-sdk = { version = "=19.0.0" }
 
-    [dev_dependencies]
+    [dev-dependencies]
     soroban-sdk = { version = "=19.0.0", features = ["testutils"] }
 ```
 
@@ -32,7 +32,7 @@ The vulnerable code example can be found [`here`](https://github.com/CoinFabrik/
     // Use the latest version available.
     soroban-sdk = { workspace = true }
 
-    [dev_dependencies]
+    [dev-dependencies]
     soroban-sdk = { workspace = true, features = ["testutils"] }
 ```
 
@@ -41,4 +41,4 @@ The remediated code example can be found [`here`](https://github.com/CoinFabrik/
 ## References
 
 - [Floating Pragma](https://swcregistry.io/docs/SWC-103/)
-- [outdated Compiler Version](https://swcregistry.io/docs/SWC-102/)
\ No newline at end of file
+- [outdated Compiler Version](https://swcregistry.io/docs/SWC-102/)
diff --git a/templates/test-case/Cargo.toml b/templates/test-case/Cargo.toml
index 68b36988..4f5e2bca 100644
--- a/templates/test-case/Cargo.toml
+++ b/templates/test-case/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { version = "=20.0.0" }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { version = "=20.0.0", features = ["testutils"] }
 
 [features]
@@ -27,4 +27,4 @@ lto = true
 
 [profile.release-with-logs]
 inherits = "release"
-debug-assertions = true
\ No newline at end of file
+debug-assertions = true
diff --git a/test-cases/assert-violation/assert-violation-1/remediated-example/Cargo.toml b/test-cases/assert-violation/assert-violation-1/remediated-example/Cargo.toml
index aabfe5cc..59fad1e1 100644
--- a/test-cases/assert-violation/assert-violation-1/remediated-example/Cargo.toml
+++ b/test-cases/assert-violation/assert-violation-1/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/assert-violation/assert-violation-1/vulnerable-example/Cargo.toml b/test-cases/assert-violation/assert-violation-1/vulnerable-example/Cargo.toml
index b3931c94..75f77e38 100644
--- a/test-cases/assert-violation/assert-violation-1/vulnerable-example/Cargo.toml
+++ b/test-cases/assert-violation/assert-violation-1/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/avoid-core-mem-forget/avoid-core-mem-forget-1/remediated-example/Cargo.toml b/test-cases/avoid-core-mem-forget/avoid-core-mem-forget-1/remediated-example/Cargo.toml
index d5b11867..36b50a6b 100644
--- a/test-cases/avoid-core-mem-forget/avoid-core-mem-forget-1/remediated-example/Cargo.toml
+++ b/test-cases/avoid-core-mem-forget/avoid-core-mem-forget-1/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/avoid-core-mem-forget/avoid-core-mem-forget-1/vulnerable-example/Cargo.toml b/test-cases/avoid-core-mem-forget/avoid-core-mem-forget-1/vulnerable-example/Cargo.toml
index 708b9a7f..aa9d8170 100644
--- a/test-cases/avoid-core-mem-forget/avoid-core-mem-forget-1/vulnerable-example/Cargo.toml
+++ b/test-cases/avoid-core-mem-forget/avoid-core-mem-forget-1/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/avoid-unsafe-block/avoid-unsafe-block-1/remediated-example/Cargo.toml b/test-cases/avoid-unsafe-block/avoid-unsafe-block-1/remediated-example/Cargo.toml
index a88a04a5..1405b414 100644
--- a/test-cases/avoid-unsafe-block/avoid-unsafe-block-1/remediated-example/Cargo.toml
+++ b/test-cases/avoid-unsafe-block/avoid-unsafe-block-1/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/avoid-unsafe-block/avoid-unsafe-block-1/vulnerable-example/Cargo.toml b/test-cases/avoid-unsafe-block/avoid-unsafe-block-1/vulnerable-example/Cargo.toml
index 10b4b428..e29166a3 100644
--- a/test-cases/avoid-unsafe-block/avoid-unsafe-block-1/vulnerable-example/Cargo.toml
+++ b/test-cases/avoid-unsafe-block/avoid-unsafe-block-1/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/divide-before-multiply/divide-before-multiply-1/remediated-example/Cargo.toml b/test-cases/divide-before-multiply/divide-before-multiply-1/remediated-example/Cargo.toml
index da115e12..8885629d 100644
--- a/test-cases/divide-before-multiply/divide-before-multiply-1/remediated-example/Cargo.toml
+++ b/test-cases/divide-before-multiply/divide-before-multiply-1/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/divide-before-multiply/divide-before-multiply-1/vulnerable-example/Cargo.toml b/test-cases/divide-before-multiply/divide-before-multiply-1/vulnerable-example/Cargo.toml
index 993b2fcd..bbc78a0e 100644
--- a/test-cases/divide-before-multiply/divide-before-multiply-1/vulnerable-example/Cargo.toml
+++ b/test-cases/divide-before-multiply/divide-before-multiply-1/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/divide-before-multiply/divide-before-multiply-2/remediated-example/Cargo.toml b/test-cases/divide-before-multiply/divide-before-multiply-2/remediated-example/Cargo.toml
index 62d3e70d..1b167005 100644
--- a/test-cases/divide-before-multiply/divide-before-multiply-2/remediated-example/Cargo.toml
+++ b/test-cases/divide-before-multiply/divide-before-multiply-2/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/divide-before-multiply/divide-before-multiply-2/vulnerable-example/Cargo.toml b/test-cases/divide-before-multiply/divide-before-multiply-2/vulnerable-example/Cargo.toml
index f4f86234..3790a7f4 100644
--- a/test-cases/divide-before-multiply/divide-before-multiply-2/vulnerable-example/Cargo.toml
+++ b/test-cases/divide-before-multiply/divide-before-multiply-2/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/divide-before-multiply/divide-before-multiply-3/remediated-example/Cargo.toml b/test-cases/divide-before-multiply/divide-before-multiply-3/remediated-example/Cargo.toml
index d2d4bf42..f9a4347c 100644
--- a/test-cases/divide-before-multiply/divide-before-multiply-3/remediated-example/Cargo.toml
+++ b/test-cases/divide-before-multiply/divide-before-multiply-3/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/divide-before-multiply/divide-before-multiply-3/vulnerable-example/Cargo.toml b/test-cases/divide-before-multiply/divide-before-multiply-3/vulnerable-example/Cargo.toml
index 53955cb7..d767b9c2 100644
--- a/test-cases/divide-before-multiply/divide-before-multiply-3/vulnerable-example/Cargo.toml
+++ b/test-cases/divide-before-multiply/divide-before-multiply-3/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/dos-unbounded-operation/dos-unbounded-operation-1/remediated-example/Cargo.toml b/test-cases/dos-unbounded-operation/dos-unbounded-operation-1/remediated-example/Cargo.toml
index 73ad69c7..dd523254 100644
--- a/test-cases/dos-unbounded-operation/dos-unbounded-operation-1/remediated-example/Cargo.toml
+++ b/test-cases/dos-unbounded-operation/dos-unbounded-operation-1/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/dos-unbounded-operation/dos-unbounded-operation-1/vulnerable-example/Cargo.toml b/test-cases/dos-unbounded-operation/dos-unbounded-operation-1/vulnerable-example/Cargo.toml
index 8bdaff6a..8931f7b9 100644
--- a/test-cases/dos-unbounded-operation/dos-unbounded-operation-1/vulnerable-example/Cargo.toml
+++ b/test-cases/dos-unbounded-operation/dos-unbounded-operation-1/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/dos-unbounded-operation/dos-unbounded-operation-2/remediated-example/Cargo.toml b/test-cases/dos-unbounded-operation/dos-unbounded-operation-2/remediated-example/Cargo.toml
index cbe0904b..bf952193 100644
--- a/test-cases/dos-unbounded-operation/dos-unbounded-operation-2/remediated-example/Cargo.toml
+++ b/test-cases/dos-unbounded-operation/dos-unbounded-operation-2/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/dos-unbounded-operation/dos-unbounded-operation-2/vulnerable-example/Cargo.toml b/test-cases/dos-unbounded-operation/dos-unbounded-operation-2/vulnerable-example/Cargo.toml
index 328d643c..c9389757 100644
--- a/test-cases/dos-unbounded-operation/dos-unbounded-operation-2/vulnerable-example/Cargo.toml
+++ b/test-cases/dos-unbounded-operation/dos-unbounded-operation-2/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/dos-unbounded-operation/dos-unbounded-operation-3/remediated-example/Cargo.toml b/test-cases/dos-unbounded-operation/dos-unbounded-operation-3/remediated-example/Cargo.toml
index d0a065be..00e57279 100644
--- a/test-cases/dos-unbounded-operation/dos-unbounded-operation-3/remediated-example/Cargo.toml
+++ b/test-cases/dos-unbounded-operation/dos-unbounded-operation-3/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/dos-unbounded-operation/dos-unbounded-operation-3/vulnerable-example/Cargo.toml b/test-cases/dos-unbounded-operation/dos-unbounded-operation-3/vulnerable-example/Cargo.toml
index 37b1b363..09061798 100644
--- a/test-cases/dos-unbounded-operation/dos-unbounded-operation-3/vulnerable-example/Cargo.toml
+++ b/test-cases/dos-unbounded-operation/dos-unbounded-operation-3/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/dos-unexpected-revert-with-vector/dos-unexpected-revert-with-vector-1/remediated-example/Cargo.toml b/test-cases/dos-unexpected-revert-with-vector/dos-unexpected-revert-with-vector-1/remediated-example/Cargo.toml
index 6e8aa2ee..b6add2e2 100644
--- a/test-cases/dos-unexpected-revert-with-vector/dos-unexpected-revert-with-vector-1/remediated-example/Cargo.toml
+++ b/test-cases/dos-unexpected-revert-with-vector/dos-unexpected-revert-with-vector-1/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/dos-unexpected-revert-with-vector/dos-unexpected-revert-with-vector-1/vulnerable-example/Cargo.toml b/test-cases/dos-unexpected-revert-with-vector/dos-unexpected-revert-with-vector-1/vulnerable-example/Cargo.toml
index b0cf5914..17a671e2 100644
--- a/test-cases/dos-unexpected-revert-with-vector/dos-unexpected-revert-with-vector-1/vulnerable-example/Cargo.toml
+++ b/test-cases/dos-unexpected-revert-with-vector/dos-unexpected-revert-with-vector-1/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/insufficiently-random-values/insufficiently-random-values-1/remediated-example/Cargo.toml b/test-cases/insufficiently-random-values/insufficiently-random-values-1/remediated-example/Cargo.toml
index 30b385f4..372f0314 100644
--- a/test-cases/insufficiently-random-values/insufficiently-random-values-1/remediated-example/Cargo.toml
+++ b/test-cases/insufficiently-random-values/insufficiently-random-values-1/remediated-example/Cargo.toml
@@ -10,7 +10,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/insufficiently-random-values/insufficiently-random-values-1/vulnerable-example/Cargo.toml b/test-cases/insufficiently-random-values/insufficiently-random-values-1/vulnerable-example/Cargo.toml
index 8ce272af..72082073 100644
--- a/test-cases/insufficiently-random-values/insufficiently-random-values-1/vulnerable-example/Cargo.toml
+++ b/test-cases/insufficiently-random-values/insufficiently-random-values-1/vulnerable-example/Cargo.toml
@@ -10,7 +10,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/iterators-over-indexing/iterators-over-indexing-1/remediated-example/Cargo.toml b/test-cases/iterators-over-indexing/iterators-over-indexing-1/remediated-example/Cargo.toml
index 20b3cfbf..c695e832 100644
--- a/test-cases/iterators-over-indexing/iterators-over-indexing-1/remediated-example/Cargo.toml
+++ b/test-cases/iterators-over-indexing/iterators-over-indexing-1/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/iterators-over-indexing/iterators-over-indexing-1/vulnerable-example/Cargo.toml b/test-cases/iterators-over-indexing/iterators-over-indexing-1/vulnerable-example/Cargo.toml
index d03444a9..7ff5c9ba 100644
--- a/test-cases/iterators-over-indexing/iterators-over-indexing-1/vulnerable-example/Cargo.toml
+++ b/test-cases/iterators-over-indexing/iterators-over-indexing-1/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/overflow-check/overflow-check-1/remediated-example/Cargo.toml b/test-cases/overflow-check/overflow-check-1/remediated-example/Cargo.toml
index 891c0f22..a7e7c02d 100644
--- a/test-cases/overflow-check/overflow-check-1/remediated-example/Cargo.toml
+++ b/test-cases/overflow-check/overflow-check-1/remediated-example/Cargo.toml
@@ -10,7 +10,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { version = "=20.0.0" }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { version = "=20.0.0", features = ["testutils"] }
 
 [features]
diff --git a/test-cases/overflow-check/overflow-check-1/vulnerable-example/Cargo.toml b/test-cases/overflow-check/overflow-check-1/vulnerable-example/Cargo.toml
index 746527ea..b38e7396 100644
--- a/test-cases/overflow-check/overflow-check-1/vulnerable-example/Cargo.toml
+++ b/test-cases/overflow-check/overflow-check-1/vulnerable-example/Cargo.toml
@@ -10,7 +10,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { version = "=20.0.0" }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { version = "=20.0.0", features = ["testutils"] }
 
 [features]
diff --git a/test-cases/set-contract-storage/set-contract-storage-1/remediated-example/Cargo.toml b/test-cases/set-contract-storage/set-contract-storage-1/remediated-example/Cargo.toml
index 2930db92..a509a7e1 100644
--- a/test-cases/set-contract-storage/set-contract-storage-1/remediated-example/Cargo.toml
+++ b/test-cases/set-contract-storage/set-contract-storage-1/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/set-contract-storage/set-contract-storage-1/vulnerable-example/Cargo.toml b/test-cases/set-contract-storage/set-contract-storage-1/vulnerable-example/Cargo.toml
index 48765b8c..cb945179 100644
--- a/test-cases/set-contract-storage/set-contract-storage-1/vulnerable-example/Cargo.toml
+++ b/test-cases/set-contract-storage/set-contract-storage-1/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/set-contract-storage/set-contract-storage-2/remediated-example/Cargo.toml b/test-cases/set-contract-storage/set-contract-storage-2/remediated-example/Cargo.toml
index 469bce4b..cf9972e9 100644
--- a/test-cases/set-contract-storage/set-contract-storage-2/remediated-example/Cargo.toml
+++ b/test-cases/set-contract-storage/set-contract-storage-2/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/set-contract-storage/set-contract-storage-2/vulnerable-example/Cargo.toml b/test-cases/set-contract-storage/set-contract-storage-2/vulnerable-example/Cargo.toml
index e588c294..778964f1 100644
--- a/test-cases/set-contract-storage/set-contract-storage-2/vulnerable-example/Cargo.toml
+++ b/test-cases/set-contract-storage/set-contract-storage-2/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/set-contract-storage/set-contract-storage-3/remediated-example/Cargo.toml b/test-cases/set-contract-storage/set-contract-storage-3/remediated-example/Cargo.toml
index bde6f22d..27f877b8 100644
--- a/test-cases/set-contract-storage/set-contract-storage-3/remediated-example/Cargo.toml
+++ b/test-cases/set-contract-storage/set-contract-storage-3/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/set-contract-storage/set-contract-storage-3/vulnerable-example/Cargo.toml b/test-cases/set-contract-storage/set-contract-storage-3/vulnerable-example/Cargo.toml
index 74b35b64..979457e7 100644
--- a/test-cases/set-contract-storage/set-contract-storage-3/vulnerable-example/Cargo.toml
+++ b/test-cases/set-contract-storage/set-contract-storage-3/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/set-contract-storage/set-contract-storage-4/remediated-example/Cargo.toml b/test-cases/set-contract-storage/set-contract-storage-4/remediated-example/Cargo.toml
index 6a6f8e4f..d8bc3007 100644
--- a/test-cases/set-contract-storage/set-contract-storage-4/remediated-example/Cargo.toml
+++ b/test-cases/set-contract-storage/set-contract-storage-4/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/set-contract-storage/set-contract-storage-4/vulnerable-example/Cargo.toml b/test-cases/set-contract-storage/set-contract-storage-4/vulnerable-example/Cargo.toml
index 2e010c67..59c47d45 100644
--- a/test-cases/set-contract-storage/set-contract-storage-4/vulnerable-example/Cargo.toml
+++ b/test-cases/set-contract-storage/set-contract-storage-4/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/soroban-version/soroban-version-1/remediated-example/Cargo.toml b/test-cases/soroban-version/soroban-version-1/remediated-example/Cargo.toml
index b82bc0ab..57d38b52 100644
--- a/test-cases/soroban-version/soroban-version-1/remediated-example/Cargo.toml
+++ b/test-cases/soroban-version/soroban-version-1/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/soroban-version/soroban-version-1/vulnerable-example/Cargo.toml b/test-cases/soroban-version/soroban-version-1/vulnerable-example/Cargo.toml
index 9d9efe06..4e7b3fb9 100644
--- a/test-cases/soroban-version/soroban-version-1/vulnerable-example/Cargo.toml
+++ b/test-cases/soroban-version/soroban-version-1/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unprotected-mapping-operation/unprotected-mapping-operation-1/remediated-example/Cargo.toml b/test-cases/unprotected-mapping-operation/unprotected-mapping-operation-1/remediated-example/Cargo.toml
index 65936dfe..65cc731b 100644
--- a/test-cases/unprotected-mapping-operation/unprotected-mapping-operation-1/remediated-example/Cargo.toml
+++ b/test-cases/unprotected-mapping-operation/unprotected-mapping-operation-1/remediated-example/Cargo.toml
@@ -10,7 +10,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unprotected-mapping-operation/unprotected-mapping-operation-1/vulnerable-example/Cargo.toml b/test-cases/unprotected-mapping-operation/unprotected-mapping-operation-1/vulnerable-example/Cargo.toml
index f4dd6328..45430a49 100644
--- a/test-cases/unprotected-mapping-operation/unprotected-mapping-operation-1/vulnerable-example/Cargo.toml
+++ b/test-cases/unprotected-mapping-operation/unprotected-mapping-operation-1/vulnerable-example/Cargo.toml
@@ -10,7 +10,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unprotected-mapping-operation/unprotected-mapping-operation-2/remediated-example/Cargo.toml b/test-cases/unprotected-mapping-operation/unprotected-mapping-operation-2/remediated-example/Cargo.toml
index fae91814..82350199 100644
--- a/test-cases/unprotected-mapping-operation/unprotected-mapping-operation-2/remediated-example/Cargo.toml
+++ b/test-cases/unprotected-mapping-operation/unprotected-mapping-operation-2/remediated-example/Cargo.toml
@@ -10,7 +10,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unprotected-mapping-operation/unprotected-mapping-operation-2/vulnerable-example/Cargo.toml b/test-cases/unprotected-mapping-operation/unprotected-mapping-operation-2/vulnerable-example/Cargo.toml
index f3b66e08..a8441538 100644
--- a/test-cases/unprotected-mapping-operation/unprotected-mapping-operation-2/vulnerable-example/Cargo.toml
+++ b/test-cases/unprotected-mapping-operation/unprotected-mapping-operation-2/vulnerable-example/Cargo.toml
@@ -10,7 +10,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unprotected-update-current-contract-wasm/unprotected-update-current-contract-wasm-1/remediated-example/Cargo.toml b/test-cases/unprotected-update-current-contract-wasm/unprotected-update-current-contract-wasm-1/remediated-example/Cargo.toml
index 4c498c24..86bf0c36 100644
--- a/test-cases/unprotected-update-current-contract-wasm/unprotected-update-current-contract-wasm-1/remediated-example/Cargo.toml
+++ b/test-cases/unprotected-update-current-contract-wasm/unprotected-update-current-contract-wasm-1/remediated-example/Cargo.toml
@@ -10,7 +10,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unprotected-update-current-contract-wasm/unprotected-update-current-contract-wasm-1/vulnerable-example/Cargo.toml b/test-cases/unprotected-update-current-contract-wasm/unprotected-update-current-contract-wasm-1/vulnerable-example/Cargo.toml
index 23a69ea8..3d281549 100644
--- a/test-cases/unprotected-update-current-contract-wasm/unprotected-update-current-contract-wasm-1/vulnerable-example/Cargo.toml
+++ b/test-cases/unprotected-update-current-contract-wasm/unprotected-update-current-contract-wasm-1/vulnerable-example/Cargo.toml
@@ -10,7 +10,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unprotected-update-current-contract-wasm/unprotected-update-current-contract-wasm-2/remediated-example/Cargo.toml b/test-cases/unprotected-update-current-contract-wasm/unprotected-update-current-contract-wasm-2/remediated-example/Cargo.toml
index e4ba18f1..f45b234e 100644
--- a/test-cases/unprotected-update-current-contract-wasm/unprotected-update-current-contract-wasm-2/remediated-example/Cargo.toml
+++ b/test-cases/unprotected-update-current-contract-wasm/unprotected-update-current-contract-wasm-2/remediated-example/Cargo.toml
@@ -8,24 +8,10 @@ version = "0.1.0"
 crate-type = ["cdylib"]
 
 [dependencies]
-soroban-sdk = "=20.0.0"
+soroban-sdk = { workspace = true }
 
-[dev_dependencies]
-soroban-sdk = { version = "=20.0.0", features = ["testutils"] }
+[dev-dependencies]
+soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
 testutils = ["soroban-sdk/testutils"]
-
-[profile.release]
-codegen-units = 1
-debug = 0
-debug-assertions = false
-lto = true
-opt-level = "z"
-overflow-checks = true
-panic = "abort"
-strip = "symbols"
-
-[profile.release-with-logs]
-debug-assertions = true
-inherits = "release"
diff --git a/test-cases/unprotected-update-current-contract-wasm/unprotected-update-current-contract-wasm-2/vulnerable-example/Cargo.toml b/test-cases/unprotected-update-current-contract-wasm/unprotected-update-current-contract-wasm-2/vulnerable-example/Cargo.toml
index d4d5390d..58531b62 100644
--- a/test-cases/unprotected-update-current-contract-wasm/unprotected-update-current-contract-wasm-2/vulnerable-example/Cargo.toml
+++ b/test-cases/unprotected-update-current-contract-wasm/unprotected-update-current-contract-wasm-2/vulnerable-example/Cargo.toml
@@ -8,24 +8,10 @@ version = "0.1.0"
 crate-type = ["cdylib"]
 
 [dependencies]
-soroban-sdk = "=20.0.0"
+soroban-sdk = { workspace = true }
 
-[dev_dependencies]
-soroban-sdk = { version = "=20.0.0", features = ["testutils"] }
+[dev-dependencies]
+soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
 testutils = ["soroban-sdk/testutils"]
-
-[profile.release]
-codegen-units = 1
-debug = 0
-debug-assertions = false
-lto = true
-opt-level = "z"
-overflow-checks = true
-panic = "abort"
-strip = "symbols"
-
-[profile.release-with-logs]
-debug-assertions = true
-inherits = "release"
diff --git a/test-cases/unrestricted-transfer-from/unrestricted-transfer-from-1/remediated-example/Cargo.toml b/test-cases/unrestricted-transfer-from/unrestricted-transfer-from-1/remediated-example/Cargo.toml
index 6ccfb57a..e70c19a0 100644
--- a/test-cases/unrestricted-transfer-from/unrestricted-transfer-from-1/remediated-example/Cargo.toml
+++ b/test-cases/unrestricted-transfer-from/unrestricted-transfer-from-1/remediated-example/Cargo.toml
@@ -9,9 +9,8 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
 testutils = ["soroban-sdk/testutils"]
-
diff --git a/test-cases/unrestricted-transfer-from/unrestricted-transfer-from-1/vulnerable-example/Cargo.toml b/test-cases/unrestricted-transfer-from/unrestricted-transfer-from-1/vulnerable-example/Cargo.toml
index faed6751..3681345f 100644
--- a/test-cases/unrestricted-transfer-from/unrestricted-transfer-from-1/vulnerable-example/Cargo.toml
+++ b/test-cases/unrestricted-transfer-from/unrestricted-transfer-from-1/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unsafe-expect/unsafe-expect-1/remediated-example/Cargo.toml b/test-cases/unsafe-expect/unsafe-expect-1/remediated-example/Cargo.toml
index dfd4e0d9..0e7ebf63 100644
--- a/test-cases/unsafe-expect/unsafe-expect-1/remediated-example/Cargo.toml
+++ b/test-cases/unsafe-expect/unsafe-expect-1/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unsafe-expect/unsafe-expect-1/vulnerable-example/Cargo.toml b/test-cases/unsafe-expect/unsafe-expect-1/vulnerable-example/Cargo.toml
index 6268a10d..7405854d 100644
--- a/test-cases/unsafe-expect/unsafe-expect-1/vulnerable-example/Cargo.toml
+++ b/test-cases/unsafe-expect/unsafe-expect-1/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unsafe-expect/unsafe-expect-2/remediated-example/Cargo.toml b/test-cases/unsafe-expect/unsafe-expect-2/remediated-example/Cargo.toml
index 74d9f3c5..ad51b084 100644
--- a/test-cases/unsafe-expect/unsafe-expect-2/remediated-example/Cargo.toml
+++ b/test-cases/unsafe-expect/unsafe-expect-2/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unsafe-expect/unsafe-expect-2/vulnerable-example/Cargo.toml b/test-cases/unsafe-expect/unsafe-expect-2/vulnerable-example/Cargo.toml
index c1025367..931400c0 100644
--- a/test-cases/unsafe-expect/unsafe-expect-2/vulnerable-example/Cargo.toml
+++ b/test-cases/unsafe-expect/unsafe-expect-2/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unsafe-expect/unsafe-expect-3/remediated-example/Cargo.toml b/test-cases/unsafe-expect/unsafe-expect-3/remediated-example/Cargo.toml
index 47be8699..0a3cea87 100644
--- a/test-cases/unsafe-expect/unsafe-expect-3/remediated-example/Cargo.toml
+++ b/test-cases/unsafe-expect/unsafe-expect-3/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unsafe-expect/unsafe-expect-3/vulnerable-example/Cargo.toml b/test-cases/unsafe-expect/unsafe-expect-3/vulnerable-example/Cargo.toml
index 37d22468..8d5efe1a 100644
--- a/test-cases/unsafe-expect/unsafe-expect-3/vulnerable-example/Cargo.toml
+++ b/test-cases/unsafe-expect/unsafe-expect-3/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unsafe-expect/unsafe-expect-4/remediated-example/Cargo.toml b/test-cases/unsafe-expect/unsafe-expect-4/remediated-example/Cargo.toml
index 610f6d0f..afc51b55 100644
--- a/test-cases/unsafe-expect/unsafe-expect-4/remediated-example/Cargo.toml
+++ b/test-cases/unsafe-expect/unsafe-expect-4/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unsafe-expect/unsafe-expect-4/vulnerable-example/Cargo.toml b/test-cases/unsafe-expect/unsafe-expect-4/vulnerable-example/Cargo.toml
index f84637e9..6769c822 100644
--- a/test-cases/unsafe-expect/unsafe-expect-4/vulnerable-example/Cargo.toml
+++ b/test-cases/unsafe-expect/unsafe-expect-4/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unsafe-expect/unsafe-expect-5/remediated-example/Cargo.toml b/test-cases/unsafe-expect/unsafe-expect-5/remediated-example/Cargo.toml
index 5f3f6c12..12b58fa9 100644
--- a/test-cases/unsafe-expect/unsafe-expect-5/remediated-example/Cargo.toml
+++ b/test-cases/unsafe-expect/unsafe-expect-5/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unsafe-expect/unsafe-expect-5/vulnerable-example/Cargo.toml b/test-cases/unsafe-expect/unsafe-expect-5/vulnerable-example/Cargo.toml
index 7dd2b61c..11b84d96 100644
--- a/test-cases/unsafe-expect/unsafe-expect-5/vulnerable-example/Cargo.toml
+++ b/test-cases/unsafe-expect/unsafe-expect-5/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unsafe-map-get/unsafe-map-get-1/remediated-example/Cargo.toml b/test-cases/unsafe-map-get/unsafe-map-get-1/remediated-example/Cargo.toml
index c70637a3..75c99c5e 100644
--- a/test-cases/unsafe-map-get/unsafe-map-get-1/remediated-example/Cargo.toml
+++ b/test-cases/unsafe-map-get/unsafe-map-get-1/remediated-example/Cargo.toml
@@ -10,7 +10,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unsafe-map-get/unsafe-map-get-1/vulnerable-example/Cargo.toml b/test-cases/unsafe-map-get/unsafe-map-get-1/vulnerable-example/Cargo.toml
index 5c661412..da6ae429 100644
--- a/test-cases/unsafe-map-get/unsafe-map-get-1/vulnerable-example/Cargo.toml
+++ b/test-cases/unsafe-map-get/unsafe-map-get-1/vulnerable-example/Cargo.toml
@@ -10,7 +10,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unsafe-unwrap/unsafe-unwrap-1/remediated-example/Cargo.toml b/test-cases/unsafe-unwrap/unsafe-unwrap-1/remediated-example/Cargo.toml
index a33b76ee..0ec7d07d 100644
--- a/test-cases/unsafe-unwrap/unsafe-unwrap-1/remediated-example/Cargo.toml
+++ b/test-cases/unsafe-unwrap/unsafe-unwrap-1/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unsafe-unwrap/unsafe-unwrap-1/vulnerable-example/Cargo.toml b/test-cases/unsafe-unwrap/unsafe-unwrap-1/vulnerable-example/Cargo.toml
index 42f74d39..efd522fb 100644
--- a/test-cases/unsafe-unwrap/unsafe-unwrap-1/vulnerable-example/Cargo.toml
+++ b/test-cases/unsafe-unwrap/unsafe-unwrap-1/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unsafe-unwrap/unsafe-unwrap-2/remediated-example/Cargo.toml b/test-cases/unsafe-unwrap/unsafe-unwrap-2/remediated-example/Cargo.toml
index bb0d0f36..5fd2052e 100644
--- a/test-cases/unsafe-unwrap/unsafe-unwrap-2/remediated-example/Cargo.toml
+++ b/test-cases/unsafe-unwrap/unsafe-unwrap-2/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unsafe-unwrap/unsafe-unwrap-2/vulnerable-example/Cargo.toml b/test-cases/unsafe-unwrap/unsafe-unwrap-2/vulnerable-example/Cargo.toml
index 32ba2f01..2bb1b256 100644
--- a/test-cases/unsafe-unwrap/unsafe-unwrap-2/vulnerable-example/Cargo.toml
+++ b/test-cases/unsafe-unwrap/unsafe-unwrap-2/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unsafe-unwrap/unsafe-unwrap-3/remediated-example/Cargo.toml b/test-cases/unsafe-unwrap/unsafe-unwrap-3/remediated-example/Cargo.toml
index 52be41f1..e1d600fa 100644
--- a/test-cases/unsafe-unwrap/unsafe-unwrap-3/remediated-example/Cargo.toml
+++ b/test-cases/unsafe-unwrap/unsafe-unwrap-3/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unsafe-unwrap/unsafe-unwrap-3/vulnerable-example/Cargo.toml b/test-cases/unsafe-unwrap/unsafe-unwrap-3/vulnerable-example/Cargo.toml
index 6570b63c..3d61879e 100644
--- a/test-cases/unsafe-unwrap/unsafe-unwrap-3/vulnerable-example/Cargo.toml
+++ b/test-cases/unsafe-unwrap/unsafe-unwrap-3/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unsafe-unwrap/unsafe-unwrap-4/remediated-example/Cargo.toml b/test-cases/unsafe-unwrap/unsafe-unwrap-4/remediated-example/Cargo.toml
index f583d6b1..1b913f69 100644
--- a/test-cases/unsafe-unwrap/unsafe-unwrap-4/remediated-example/Cargo.toml
+++ b/test-cases/unsafe-unwrap/unsafe-unwrap-4/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unsafe-unwrap/unsafe-unwrap-4/vulnerable-example/Cargo.toml b/test-cases/unsafe-unwrap/unsafe-unwrap-4/vulnerable-example/Cargo.toml
index 06bbb0c3..473a4853 100644
--- a/test-cases/unsafe-unwrap/unsafe-unwrap-4/vulnerable-example/Cargo.toml
+++ b/test-cases/unsafe-unwrap/unsafe-unwrap-4/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unsafe-unwrap/unsafe-unwrap-5/remediated-example/Cargo.toml b/test-cases/unsafe-unwrap/unsafe-unwrap-5/remediated-example/Cargo.toml
index 0b51ce12..dfa84315 100644
--- a/test-cases/unsafe-unwrap/unsafe-unwrap-5/remediated-example/Cargo.toml
+++ b/test-cases/unsafe-unwrap/unsafe-unwrap-5/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unsafe-unwrap/unsafe-unwrap-5/vulnerable-example/Cargo.toml b/test-cases/unsafe-unwrap/unsafe-unwrap-5/vulnerable-example/Cargo.toml
index 5af85e08..916ac985 100644
--- a/test-cases/unsafe-unwrap/unsafe-unwrap-5/vulnerable-example/Cargo.toml
+++ b/test-cases/unsafe-unwrap/unsafe-unwrap-5/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unsafe-unwrap/unsafe-unwrap-6/remediated-example/Cargo.toml b/test-cases/unsafe-unwrap/unsafe-unwrap-6/remediated-example/Cargo.toml
index 32e823c1..82d8f95b 100644
--- a/test-cases/unsafe-unwrap/unsafe-unwrap-6/remediated-example/Cargo.toml
+++ b/test-cases/unsafe-unwrap/unsafe-unwrap-6/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unsafe-unwrap/unsafe-unwrap-6/vulnerable-example/Cargo.toml b/test-cases/unsafe-unwrap/unsafe-unwrap-6/vulnerable-example/Cargo.toml
index b09b504d..b29afba1 100644
--- a/test-cases/unsafe-unwrap/unsafe-unwrap-6/vulnerable-example/Cargo.toml
+++ b/test-cases/unsafe-unwrap/unsafe-unwrap-6/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unused-return-enum/unused-return-enum-1/remediated-example/Cargo.toml b/test-cases/unused-return-enum/unused-return-enum-1/remediated-example/Cargo.toml
index 80a5dec2..8446627a 100644
--- a/test-cases/unused-return-enum/unused-return-enum-1/remediated-example/Cargo.toml
+++ b/test-cases/unused-return-enum/unused-return-enum-1/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unused-return-enum/unused-return-enum-1/vulnerable-example/Cargo.toml b/test-cases/unused-return-enum/unused-return-enum-1/vulnerable-example/Cargo.toml
index 529f902f..fd1739bd 100644
--- a/test-cases/unused-return-enum/unused-return-enum-1/vulnerable-example/Cargo.toml
+++ b/test-cases/unused-return-enum/unused-return-enum-1/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unused-return-enum/unused-return-enum-2/remediated-example/Cargo.toml b/test-cases/unused-return-enum/unused-return-enum-2/remediated-example/Cargo.toml
index 01337627..1dd7140f 100644
--- a/test-cases/unused-return-enum/unused-return-enum-2/remediated-example/Cargo.toml
+++ b/test-cases/unused-return-enum/unused-return-enum-2/remediated-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/unused-return-enum/unused-return-enum-2/vulnerable-example/Cargo.toml b/test-cases/unused-return-enum/unused-return-enum-2/vulnerable-example/Cargo.toml
index d0496327..a9ac9fe0 100644
--- a/test-cases/unused-return-enum/unused-return-enum-2/vulnerable-example/Cargo.toml
+++ b/test-cases/unused-return-enum/unused-return-enum-2/vulnerable-example/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = { workspace = true }
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { workspace = true, features = ["testutils"] }
 
 [features]
diff --git a/test-cases/vec-could-be-mapping/vec-could-be-mapping-1/remediated-example/Cargo.toml b/test-cases/vec-could-be-mapping/vec-could-be-mapping-1/remediated-example/Cargo.toml
index 8d726cb7..9b4e6811 100644
--- a/test-cases/vec-could-be-mapping/vec-could-be-mapping-1/remediated-example/Cargo.toml
+++ b/test-cases/vec-could-be-mapping/vec-could-be-mapping-1/remediated-example/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
+edition = "2021"
 name = "vec-could-be-mapping-remediated-1"
 version = "0.1.0"
-edition = "2021"
 
 [lib]
 crate-type = ["cdylib"]
@@ -9,22 +9,22 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = "20.0.0-rc2"
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { version = "=20.0.0", features = ["testutils"] }
 
 [features]
 testutils = ["soroban-sdk/testutils"]
 
 [profile.release]
-opt-level = "z"
-overflow-checks = true
+codegen-units = 1
 debug = 0
-strip = "symbols"
 debug-assertions = false
-panic = "abort"
-codegen-units = 1
 lto = true
+opt-level = "z"
+overflow-checks = true
+panic = "abort"
+strip = "symbols"
 
 [profile.release-with-logs]
+debug-assertions = true
 inherits = "release"
-debug-assertions = true
\ No newline at end of file
diff --git a/test-cases/vec-could-be-mapping/vec-could-be-mapping-1/vulnerable-example/Cargo.toml b/test-cases/vec-could-be-mapping/vec-could-be-mapping-1/vulnerable-example/Cargo.toml
index f6d61396..860c0f59 100644
--- a/test-cases/vec-could-be-mapping/vec-could-be-mapping-1/vulnerable-example/Cargo.toml
+++ b/test-cases/vec-could-be-mapping/vec-could-be-mapping-1/vulnerable-example/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
+edition = "2021"
 name = "vec-could-be-mapping-vulnerable-1"
 version = "0.1.0"
-edition = "2021"
 
 [lib]
 crate-type = ["cdylib"]
@@ -9,22 +9,22 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = "20.0.0-rc2"
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { version = "=20.0.0", features = ["testutils"] }
 
 [features]
 testutils = ["soroban-sdk/testutils"]
 
 [profile.release]
-opt-level = "z"
-overflow-checks = true
+codegen-units = 1
 debug = 0
-strip = "symbols"
 debug-assertions = false
-panic = "abort"
-codegen-units = 1
 lto = true
+opt-level = "z"
+overflow-checks = true
+panic = "abort"
+strip = "symbols"
 
 [profile.release-with-logs]
+debug-assertions = true
 inherits = "release"
-debug-assertions = true
\ No newline at end of file
diff --git a/test-cases/zero-address/zero-address-1/remediated-example/Cargo.toml b/test-cases/zero-address/zero-address-1/remediated-example/Cargo.toml
index 7694d9af..fc542d0e 100644
--- a/test-cases/zero-address/zero-address-1/remediated-example/Cargo.toml
+++ b/test-cases/zero-address/zero-address-1/remediated-example/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
+edition = "2021"
 name = "zero-address-remediated-1"
 version = "0.1.0"
-edition = "2021"
 
 [lib]
 crate-type = ["cdylib"]
@@ -9,22 +9,22 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = "20.0.0-rc2"
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { version = "=20.0.0", features = ["testutils"] }
 
 [features]
 testutils = ["soroban-sdk/testutils"]
 
 [profile.release]
-opt-level = "z"
-overflow-checks = true
+codegen-units = 1
 debug = 0
-strip = "symbols"
 debug-assertions = false
-panic = "abort"
-codegen-units = 1
 lto = true
+opt-level = "z"
+overflow-checks = true
+panic = "abort"
+strip = "symbols"
 
 [profile.release-with-logs]
+debug-assertions = true
 inherits = "release"
-debug-assertions = true
\ No newline at end of file
diff --git a/test-cases/zero-address/zero-address-1/vulnerable-example/Cargo.toml b/test-cases/zero-address/zero-address-1/vulnerable-example/Cargo.toml
index 6e04a7c0..195c8b6e 100644
--- a/test-cases/zero-address/zero-address-1/vulnerable-example/Cargo.toml
+++ b/test-cases/zero-address/zero-address-1/vulnerable-example/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
+edition = "2021"
 name = "zero-address-vulnerable-1"
 version = "0.1.0"
-edition = "2021"
 
 [lib]
 crate-type = ["cdylib"]
@@ -9,22 +9,22 @@ crate-type = ["cdylib"]
 [dependencies]
 soroban-sdk = "20.0.0-rc2"
 
-[dev_dependencies]
+[dev-dependencies]
 soroban-sdk = { version = "=20.0.0", features = ["testutils"] }
 
 [features]
 testutils = ["soroban-sdk/testutils"]
 
 [profile.release]
-opt-level = "z"
-overflow-checks = true
+codegen-units = 1
 debug = 0
-strip = "symbols"
 debug-assertions = false
-panic = "abort"
-codegen-units = 1
 lto = true
+opt-level = "z"
+overflow-checks = true
+panic = "abort"
+strip = "symbols"
 
 [profile.release-with-logs]
+debug-assertions = true
 inherits = "release"
-debug-assertions = true
\ No newline at end of file

From c469ecdbda0f49e1e574644f954db490a5a48276 Mon Sep 17 00:00:00 2001
From: Jose Garcia Crosta <jgcrosta@gmail.com>
Date: Wed, 31 Jul 2024 17:11:30 -0300
Subject: [PATCH 02/18] Add detector and test-cases

---
 detectors/unnecessary-lint-allow/Cargo.toml   |  17 ++
 detectors/unnecessary-lint-allow/src/lib.rs   | 216 ++++++++++++++++++
 test-cases/unnecessary-lint-allow/Cargo.toml  |  21 ++
 .../remediated-example/Cargo.toml             |  17 ++
 .../remediated-example/src/lib.rs             |  50 ++++
 .../vulnerable-example/Cargo.toml             |  17 ++
 .../vulnerable-example/src/lib.rs             |  55 +++++
 7 files changed, 393 insertions(+)
 create mode 100644 detectors/unnecessary-lint-allow/Cargo.toml
 create mode 100644 detectors/unnecessary-lint-allow/src/lib.rs
 create mode 100644 test-cases/unnecessary-lint-allow/Cargo.toml
 create mode 100644 test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/remediated-example/Cargo.toml
 create mode 100644 test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/remediated-example/src/lib.rs
 create mode 100644 test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/vulnerable-example/Cargo.toml
 create mode 100644 test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/vulnerable-example/src/lib.rs

diff --git a/detectors/unnecessary-lint-allow/Cargo.toml b/detectors/unnecessary-lint-allow/Cargo.toml
new file mode 100644
index 00000000..055144b4
--- /dev/null
+++ b/detectors/unnecessary-lint-allow/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+edition = "2021"
+name = "unnecessary-lint-allow"
+version = "0.1.0"
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+clippy_utils = { workspace = true }
+dylint_linting = { workspace = true }
+if_chain = { workspace = true }
+serde = { version = "1", features = ["derive"] }
+utils = { workspace = true }
+
+[package.metadata.rust-analyzer]
+rustc_private = true
diff --git a/detectors/unnecessary-lint-allow/src/lib.rs b/detectors/unnecessary-lint-allow/src/lib.rs
new file mode 100644
index 00000000..7758ee2b
--- /dev/null
+++ b/detectors/unnecessary-lint-allow/src/lib.rs
@@ -0,0 +1,216 @@
+#![feature(rustc_private)]
+
+extern crate rustc_ast;
+extern crate rustc_hir;
+extern crate rustc_span;
+
+use std::collections::HashSet;
+
+use rustc_ast::{
+    token::{Delimiter, Token, TokenKind},
+    tokenstream::{TokenStream, TokenTree},
+    AttrArgs, AttrKind, Attribute,
+};
+use rustc_hir::{
+    intravisit::{walk_expr, FnKind, Visitor},
+    Body, FnDecl,
+};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_span::{def_id::LocalDefId, sym, FileName, FileNameDisplayPreference, Span};
+use serde::{Deserialize, Serialize};
+
+const LINT_MESSAGE: &str = "The `#[allow]` attribute is used to disable lints. It is recommended to fix the issues instead of disabling them.";
+
+dylint_linting::impl_late_lint!(
+    pub UNNECESSARY_LINT_ALLOW,
+    Warn,
+    LINT_MESSAGE,
+    UnnecessaryLintAllow::default(),
+    {
+        name: "Unnecessary Lint Allow",
+        long_message: "The `#[allow]` attribute is used to disable lints. It is recommended to fix the issues instead of disabling them.",
+        severity: "Medium",
+        help: "https://coinfabrik.github.io/scout-soroban/docs/detectors/unnecessary-lint-allow",
+        vulnerability_class: "Code Quality",
+    }
+);
+
+#[derive(Default, Debug)]
+struct UnnecessaryLintAllow {
+    findings: HashSet<AllowInfo>,
+}
+
+impl UnnecessaryLintAllow {
+    pub fn collect_attribute(
+        &mut self,
+        cx: &LateContext,
+        attr: &Attribute,
+        scope: Scope,
+        item_span: Span,
+    ) {
+        if attr.span.from_expansion() {
+            return;
+        }
+
+        if attr.has_name(sym::allow) {
+            if let AttrKind::Normal(item) = &attr.kind {
+                if let AttrArgs::Delimited(delimited_args) = &item.item.args {
+                    let lint_names = extract_lint_names(&delimited_args.tokens);
+                    for lint_name in lint_names {
+                        self.findings.insert(AllowInfo {
+                            lint_name,
+                            span: SerializableSpan::from_span(cx, item_span),
+                            scope,
+                        });
+                    }
+                }
+            }
+        }
+    }
+}
+
+fn extract_lint_names(tokens: &TokenStream) -> Vec<String> {
+    let mut lint_names = Vec::new();
+    for tree in tokens.trees() {
+        match tree {
+            TokenTree::Token(
+                Token {
+                    kind: TokenKind::Ident(ident, _),
+                    ..
+                },
+                _,
+            ) => {
+                lint_names.push(ident.to_string());
+            }
+            TokenTree::Delimited(_, _, Delimiter::Parenthesis, inner_stream) => {
+                // Recursively process nested token streams
+                lint_names.extend(extract_lint_names(inner_stream));
+            }
+            _ => {}
+        }
+    }
+    lint_names
+}
+
+#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug)]
+pub struct AllowInfo {
+    pub lint_name: String,
+    pub span: SerializableSpan,
+    pub scope: Scope,
+}
+
+#[derive(Serialize, Debug, Deserialize, Clone, Copy, Hash, Eq, PartialEq)]
+pub enum Scope {
+    Crate,
+    Enum,
+    Function,
+    Impl,
+    Line,
+    Struct,
+}
+
+#[derive(Serialize, Deserialize, Eq, PartialEq, Hash, Debug)]
+pub struct SerializableSpan {
+    pub file_name: String,
+    pub from_line: usize,
+    pub to_line: usize,
+}
+
+impl SerializableSpan {
+    pub fn from_span(cx: &LateContext, span: Span) -> Self {
+        let source_map = cx.sess().source_map();
+        let file = source_map.lookup_source_file(span.lo());
+        let file_name = match &file.name {
+            FileName::Real(name) => name
+                .to_string_lossy(FileNameDisplayPreference::Remapped)
+                .into_owned(),
+            _ => String::from("<unknown>"),
+        };
+
+        let lo_loc = source_map.lookup_char_pos(span.lo());
+        let hi_loc = source_map.lookup_char_pos(span.hi());
+
+        SerializableSpan {
+            file_name,
+            from_line: lo_loc.line,
+            to_line: hi_loc.line,
+        }
+    }
+}
+
+impl<'tcx> LateLintPass<'tcx> for UnnecessaryLintAllow {
+    fn check_crate_post(&mut self, _: &LateContext<'tcx>) {
+        for finding in &self.findings {
+            println!("Findings: {:?}", finding);
+        }
+    }
+
+    fn check_crate(&mut self, cx: &LateContext<'tcx>) {
+        for attr in cx.tcx.hir().attrs(rustc_hir::CRATE_HIR_ID) {
+            self.collect_attribute(cx, attr, Scope::Crate, attr.span);
+        }
+    }
+
+    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx rustc_hir::Item<'tcx>) {
+        if item.span.from_expansion() {
+            return;
+        }
+
+        let attrs = cx.tcx.hir().attrs(item.hir_id());
+        let scope = match item.kind {
+            rustc_hir::ItemKind::Struct(..) => Scope::Struct,
+            rustc_hir::ItemKind::Enum(..) => Scope::Enum,
+            rustc_hir::ItemKind::Impl(..) => Scope::Impl,
+            _ => return,
+        };
+
+        for attr in attrs.iter() {
+            self.collect_attribute(cx, attr, scope, item.span);
+        }
+    }
+
+    fn check_fn(
+        &mut self,
+        cx: &LateContext<'tcx>,
+        _: FnKind<'tcx>,
+        _: &'tcx FnDecl<'tcx>,
+        body: &'tcx Body<'tcx>,
+        span: Span,
+        local_def_id: LocalDefId,
+    ) {
+        if span.from_expansion() {
+            return;
+        }
+
+        let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id);
+        let attrs = cx.tcx.hir().attrs(hir_id);
+
+        for attr in attrs.iter() {
+            self.collect_attribute(cx, attr, Scope::Function, span);
+        }
+
+        // Use a visitor to check inner attributes
+        struct InnerAttrVisitor<'a, 'tcx> {
+            cx: &'a LateContext<'tcx>,
+            lint: &'a mut UnnecessaryLintAllow,
+        }
+
+        impl<'a, 'tcx> Visitor<'tcx> for InnerAttrVisitor<'a, 'tcx> {
+            fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>) {
+                let attrs = self.cx.tcx.hir().attrs(expr.hir_id);
+                if !attrs.is_empty() {
+                    for attr in attrs.iter() {
+                        self.lint
+                            .collect_attribute(self.cx, attr, Scope::Line, expr.span);
+                    }
+                }
+
+                // Continue visiting child nodes
+                walk_expr(self, expr);
+            }
+        }
+
+        let mut visitor = InnerAttrVisitor { cx, lint: self };
+        walk_expr(&mut visitor, body.value);
+    }
+}
diff --git a/test-cases/unnecessary-lint-allow/Cargo.toml b/test-cases/unnecessary-lint-allow/Cargo.toml
new file mode 100644
index 00000000..e0576682
--- /dev/null
+++ b/test-cases/unnecessary-lint-allow/Cargo.toml
@@ -0,0 +1,21 @@
+[workspace]
+exclude = [".cargo", "target"]
+members = ["unnecessary-lint-allow-*/*"]
+resolver = "2"
+
+[workspace.dependencies]
+soroban-sdk = { version = "=21.4.0" }
+
+[profile.release]
+codegen-units = 1
+debug = 0
+debug-assertions = false
+lto = true
+opt-level = "z"
+overflow-checks = true
+panic = "abort"
+strip = "symbols"
+
+[profile.release-with-logs]
+debug-assertions = true
+inherits = "release"
diff --git a/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/remediated-example/Cargo.toml b/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/remediated-example/Cargo.toml
new file mode 100644
index 00000000..cb27925d
--- /dev/null
+++ b/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/remediated-example/Cargo.toml
@@ -0,0 +1,17 @@
+
+[package]
+edition = "2021"
+name = "unnecessary-lint-allow-remediated-1"
+version = "0.1.0"
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+soroban-sdk = { workspace = true }
+
+[dev-dependencies]
+soroban-sdk = { workspace = true, features = ["testutils"] }
+
+[features]
+testutils = ["soroban-sdk/testutils"]
diff --git a/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/remediated-example/src/lib.rs b/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/remediated-example/src/lib.rs
new file mode 100644
index 00000000..1a231860
--- /dev/null
+++ b/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/remediated-example/src/lib.rs
@@ -0,0 +1,50 @@
+#![no_std]
+
+use soroban_sdk::{contract, contracterror, contractimpl};
+
+#[contract]
+pub struct UnnecessaryLintAllow;
+
+#[contracterror]
+#[derive(Copy, Clone)]
+pub enum AssertError {
+    GreaterThan10 = 1,
+}
+
+#[contractimpl]
+impl UnnecessaryLintAllow {
+    pub fn assert_if_greater_than_10(value: u128) -> Result<bool, AssertError> {
+        if value <= 10 {
+            Ok(true)
+        } else {
+            Err(AssertError::GreaterThan10)
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use soroban_sdk::Env;
+
+    use super::*;
+    #[test]
+    fn does_not_revert_if_greater() {
+        let env = Env::default();
+        let contract = UnnecessaryLintAllowClient::new(
+            &env,
+            &env.register_contract(None, UnnecessaryLintAllow {}),
+        );
+        assert!(contract.assert_if_greater_than_10(&5));
+    }
+
+    #[test]
+    #[should_panic(expected = "1")] // The custom error number is 1
+    fn reverts_if_greater() {
+        let env = Env::default();
+        let contract = UnnecessaryLintAllowClient::new(
+            &env,
+            &env.register_contract(None, UnnecessaryLintAllow {}),
+        );
+        contract.assert_if_greater_than_10(&11);
+    }
+}
diff --git a/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/vulnerable-example/Cargo.toml b/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/vulnerable-example/Cargo.toml
new file mode 100644
index 00000000..d4da8d0a
--- /dev/null
+++ b/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/vulnerable-example/Cargo.toml
@@ -0,0 +1,17 @@
+
+[package]
+edition = "2021"
+name = "unnecessary-lint-allow-vulnerable-1"
+version = "0.1.0"
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+soroban-sdk = { workspace = true }
+
+[dev-dependencies]
+soroban-sdk = { workspace = true, features = ["testutils"] }
+
+[features]
+testutils = ["soroban-sdk/testutils"]
diff --git a/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/vulnerable-example/src/lib.rs b/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/vulnerable-example/src/lib.rs
new file mode 100644
index 00000000..b7e5dd70
--- /dev/null
+++ b/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/vulnerable-example/src/lib.rs
@@ -0,0 +1,55 @@
+#![no_std]
+#![allow(assert_violation)]
+use soroban_sdk::{contract, contracterror, contractimpl};
+
+#[contract]
+#[allow(assert_violation)]
+pub struct UnnecessaryLintAllow;
+
+#[contracterror]
+#[derive(Copy, Clone)]
+#[allow(assert_violation)]
+pub enum AssertError {
+    GreaterThan10 = 1,
+}
+
+#[contractimpl]
+#[allow(assert_violation)]
+impl UnnecessaryLintAllow {
+    #[allow(assert_violation)]
+    pub fn assert_if_greater_than_10(value: u128) -> Result<bool, AssertError> {
+        if value <= 10 {
+            Ok(true)
+        } else {
+            #[allow(assert_violation)]
+            Err(AssertError::GreaterThan10)
+        }
+    }
+}
+
+// #[cfg(test)]
+// mod tests {
+//     use soroban_sdk::Env;
+
+//     use super::*;
+//     #[test]
+//     fn does_not_revert_if_greater() {
+//         let env = Env::default();
+//         let contract = UnnecessaryLintAllowClient::new(
+//             &env,
+//             &env.register_contract(None, UnnecessaryLintAllow {}),
+//         );
+//         assert!(contract.assert_if_greater_than_10(&5));
+//     }
+
+//     #[test]
+//     #[should_panic(expected = "1")] // The custom error number is 1
+//     fn reverts_if_greater() {
+//         let env = Env::default();
+//         let contract = UnnecessaryLintAllowClient::new(
+//             &env,
+//             &env.register_contract(None, UnnecessaryLintAllow {}),
+//         );
+//         contract.assert_if_greater_than_10(&11);
+//     }
+// }

From d1bf6466f3ef77f0606d08b755f2c839cdb2c6de Mon Sep 17 00:00:00 2001
From: Jose Garcia Crosta <jgcrosta@gmail.com>
Date: Thu, 1 Aug 2024 15:39:53 -0300
Subject: [PATCH 03/18] Create a stack-like struct for extracting lint names,
 and add types.rs for clarity

---
 detectors/unnecessary-lint-allow/Cargo.toml   |   1 -
 detectors/unnecessary-lint-allow/src/lib.rs   | 148 +++++++-----------
 detectors/unnecessary-lint-allow/src/types.rs |  48 ++++++
 3 files changed, 106 insertions(+), 91 deletions(-)
 create mode 100644 detectors/unnecessary-lint-allow/src/types.rs

diff --git a/detectors/unnecessary-lint-allow/Cargo.toml b/detectors/unnecessary-lint-allow/Cargo.toml
index 055144b4..925700a4 100644
--- a/detectors/unnecessary-lint-allow/Cargo.toml
+++ b/detectors/unnecessary-lint-allow/Cargo.toml
@@ -11,7 +11,6 @@ clippy_utils = { workspace = true }
 dylint_linting = { workspace = true }
 if_chain = { workspace = true }
 serde = { version = "1", features = ["derive"] }
-utils = { workspace = true }
 
 [package.metadata.rust-analyzer]
 rustc_private = true
diff --git a/detectors/unnecessary-lint-allow/src/lib.rs b/detectors/unnecessary-lint-allow/src/lib.rs
index 7758ee2b..53e3d10e 100644
--- a/detectors/unnecessary-lint-allow/src/lib.rs
+++ b/detectors/unnecessary-lint-allow/src/lib.rs
@@ -4,8 +4,9 @@ extern crate rustc_ast;
 extern crate rustc_hir;
 extern crate rustc_span;
 
-use std::collections::HashSet;
+mod types;
 
+use if_chain::if_chain;
 use rustc_ast::{
     token::{Delimiter, Token, TokenKind},
     tokenstream::{TokenStream, TokenTree},
@@ -15,11 +16,12 @@ use rustc_hir::{
     intravisit::{walk_expr, FnKind, Visitor},
     Body, FnDecl,
 };
-use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_span::{def_id::LocalDefId, sym, FileName, FileNameDisplayPreference, Span};
-use serde::{Deserialize, Serialize};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_span::{def_id::LocalDefId, sym, Span};
+use std::collections::{HashSet, VecDeque};
+use types::{AllowInfo, Scope, SpanInfo};
 
-const LINT_MESSAGE: &str = "The `#[allow]` attribute is used to disable lints. It is recommended to fix the issues instead of disabling them.";
+const LINT_MESSAGE: &str = "This `#[allow]` attribute may be unnecessary. Consider removing it if the lint is no longer triggered.";
 
 dylint_linting::impl_late_lint!(
     pub UNNECESSARY_LINT_ALLOW,
@@ -29,13 +31,13 @@ dylint_linting::impl_late_lint!(
     {
         name: "Unnecessary Lint Allow",
         long_message: "The `#[allow]` attribute is used to disable lints. It is recommended to fix the issues instead of disabling them.",
-        severity: "Medium",
+        severity: "Enhancement",
         help: "https://coinfabrik.github.io/scout-soroban/docs/detectors/unnecessary-lint-allow",
         vulnerability_class: "Code Quality",
     }
 );
 
-#[derive(Default, Debug)]
+#[derive(Default, Debug, Clone)]
 struct UnnecessaryLintAllow {
     findings: HashSet<AllowInfo>,
 }
@@ -48,104 +50,69 @@ impl UnnecessaryLintAllow {
         scope: Scope,
         item_span: Span,
     ) {
-        if attr.span.from_expansion() {
-            return;
-        }
-
-        if attr.has_name(sym::allow) {
-            if let AttrKind::Normal(item) = &attr.kind {
-                if let AttrArgs::Delimited(delimited_args) = &item.item.args {
-                    let lint_names = extract_lint_names(&delimited_args.tokens);
-                    for lint_name in lint_names {
-                        self.findings.insert(AllowInfo {
-                            lint_name,
-                            span: SerializableSpan::from_span(cx, item_span),
-                            scope,
-                        });
-                    }
+        if_chain! {
+            if !attr.span.from_expansion();
+            if attr.has_name(sym::allow);
+            if let AttrKind::Normal(item) = &attr.kind;
+            if let AttrArgs::Delimited(delimited_args) = &item.item.args;
+            then {
+                let lint_names = self.extract_lint_names(&delimited_args.tokens);
+                for lint_name in lint_names {
+                    self.findings.insert(AllowInfo {
+                        lint_name,
+                        span: SpanInfo::from_span(cx, item_span),
+                        scope,
+                    });
                 }
             }
         }
     }
-}
 
-fn extract_lint_names(tokens: &TokenStream) -> Vec<String> {
-    let mut lint_names = Vec::new();
-    for tree in tokens.trees() {
-        match tree {
-            TokenTree::Token(
-                Token {
-                    kind: TokenKind::Ident(ident, _),
-                    ..
-                },
-                _,
-            ) => {
-                lint_names.push(ident.to_string());
-            }
-            TokenTree::Delimited(_, _, Delimiter::Parenthesis, inner_stream) => {
-                // Recursively process nested token streams
-                lint_names.extend(extract_lint_names(inner_stream));
+    pub fn extract_lint_names(&self, tokens: &TokenStream) -> Vec<String> {
+        let mut lint_names = Vec::new();
+        let mut stack = VecDeque::new();
+        stack.push_back(tokens);
+
+        while let Some(current_stream) = stack.pop_back() {
+            for tree in current_stream.trees() {
+                match tree {
+                    TokenTree::Token(
+                        Token {
+                            kind: TokenKind::Ident(ident, _),
+                            ..
+                        },
+                        _,
+                    ) => {
+                        lint_names.push(ident.to_string());
+                    }
+                    TokenTree::Delimited(_, _, Delimiter::Parenthesis, inner_stream) => {
+                        // Push the inner stream onto the stack for later processing
+                        stack.push_back(inner_stream);
+                    }
+                    _ => {} // Ignore other token types
+                }
             }
-            _ => {}
         }
-    }
-    lint_names
-}
 
-#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug)]
-pub struct AllowInfo {
-    pub lint_name: String,
-    pub span: SerializableSpan,
-    pub scope: Scope,
-}
-
-#[derive(Serialize, Debug, Deserialize, Clone, Copy, Hash, Eq, PartialEq)]
-pub enum Scope {
-    Crate,
-    Enum,
-    Function,
-    Impl,
-    Line,
-    Struct,
-}
-
-#[derive(Serialize, Deserialize, Eq, PartialEq, Hash, Debug)]
-pub struct SerializableSpan {
-    pub file_name: String,
-    pub from_line: usize,
-    pub to_line: usize,
-}
-
-impl SerializableSpan {
-    pub fn from_span(cx: &LateContext, span: Span) -> Self {
-        let source_map = cx.sess().source_map();
-        let file = source_map.lookup_source_file(span.lo());
-        let file_name = match &file.name {
-            FileName::Real(name) => name
-                .to_string_lossy(FileNameDisplayPreference::Remapped)
-                .into_owned(),
-            _ => String::from("<unknown>"),
-        };
-
-        let lo_loc = source_map.lookup_char_pos(span.lo());
-        let hi_loc = source_map.lookup_char_pos(span.hi());
-
-        SerializableSpan {
-            file_name,
-            from_line: lo_loc.line,
-            to_line: hi_loc.line,
-        }
+        lint_names
     }
 }
 
 impl<'tcx> LateLintPass<'tcx> for UnnecessaryLintAllow {
     fn check_crate_post(&mut self, _: &LateContext<'tcx>) {
         for finding in &self.findings {
-            println!("Findings: {:?}", finding);
+            println!(
+                "Found unnecessary `#[allow({})]` attribute at {}:{}-{}",
+                finding.lint_name,
+                finding.span.file_name,
+                finding.span.from_line,
+                finding.span.to_line
+            );
         }
     }
 
     fn check_crate(&mut self, cx: &LateContext<'tcx>) {
+        // Collect crate-level attributes
         for attr in cx.tcx.hir().attrs(rustc_hir::CRATE_HIR_ID) {
             self.collect_attribute(cx, attr, Scope::Crate, attr.span);
         }
@@ -156,6 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryLintAllow {
             return;
         }
 
+        // Collect item-level attributes (struct, enum, impl)
         let attrs = cx.tcx.hir().attrs(item.hir_id());
         let scope = match item.kind {
             rustc_hir::ItemKind::Struct(..) => Scope::Struct,
@@ -178,18 +146,19 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryLintAllow {
         span: Span,
         local_def_id: LocalDefId,
     ) {
+        // If the function comes from a macro expansion, we ignore it
         if span.from_expansion() {
             return;
         }
 
+        // Collect function level attributes (function)
         let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id);
         let attrs = cx.tcx.hir().attrs(hir_id);
-
         for attr in attrs.iter() {
             self.collect_attribute(cx, attr, Scope::Function, span);
         }
 
-        // Use a visitor to check inner attributes
+        // Collect inner-level attributes (line)
         struct InnerAttrVisitor<'a, 'tcx> {
             cx: &'a LateContext<'tcx>,
             lint: &'a mut UnnecessaryLintAllow,
@@ -205,7 +174,6 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryLintAllow {
                     }
                 }
 
-                // Continue visiting child nodes
                 walk_expr(self, expr);
             }
         }
diff --git a/detectors/unnecessary-lint-allow/src/types.rs b/detectors/unnecessary-lint-allow/src/types.rs
new file mode 100644
index 00000000..1adaf462
--- /dev/null
+++ b/detectors/unnecessary-lint-allow/src/types.rs
@@ -0,0 +1,48 @@
+use rustc_lint::{LateContext, LintContext};
+use rustc_span::{FileName, FileNameDisplayPreference, Span};
+
+#[derive(Eq, Hash, PartialEq, Debug, Clone)]
+pub struct AllowInfo {
+    pub lint_name: String,
+    pub span: SpanInfo,
+    pub scope: Scope,
+}
+
+#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
+pub enum Scope {
+    Crate,
+    Enum,
+    Function,
+    Impl,
+    Line,
+    Struct,
+}
+
+#[derive(Eq, PartialEq, Hash, Debug, Clone)]
+pub struct SpanInfo {
+    pub file_name: String,
+    pub from_line: usize,
+    pub to_line: usize,
+}
+
+impl SpanInfo {
+    pub fn from_span(cx: &LateContext, span: Span) -> Self {
+        let source_map = cx.sess().source_map();
+        let file = source_map.lookup_source_file(span.lo());
+        let file_name = match &file.name {
+            FileName::Real(name) => name
+                .to_string_lossy(FileNameDisplayPreference::Remapped)
+                .into_owned(),
+            _ => String::from("<unknown>"),
+        };
+
+        let lo_loc = source_map.lookup_char_pos(span.lo());
+        let hi_loc = source_map.lookup_char_pos(span.hi());
+
+        SpanInfo {
+            file_name,
+            from_line: lo_loc.line,
+            to_line: hi_loc.line,
+        }
+    }
+}

From e29bf40ed26308f943a391b150da63c09c0e5005 Mon Sep 17 00:00:00 2001
From: Jose Garcia Crosta <jgcrosta@gmail.com>
Date: Thu, 1 Aug 2024 15:40:50 -0300
Subject: [PATCH 04/18] Remove test and excess attributes

---
 .../remediated-example/src/lib.rs             | 27 ----------------
 .../vulnerable-example/src/lib.rs             | 32 -------------------
 2 files changed, 59 deletions(-)

diff --git a/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/remediated-example/src/lib.rs b/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/remediated-example/src/lib.rs
index 1a231860..73fba1db 100644
--- a/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/remediated-example/src/lib.rs
+++ b/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/remediated-example/src/lib.rs
@@ -21,30 +21,3 @@ impl UnnecessaryLintAllow {
         }
     }
 }
-
-#[cfg(test)]
-mod tests {
-    use soroban_sdk::Env;
-
-    use super::*;
-    #[test]
-    fn does_not_revert_if_greater() {
-        let env = Env::default();
-        let contract = UnnecessaryLintAllowClient::new(
-            &env,
-            &env.register_contract(None, UnnecessaryLintAllow {}),
-        );
-        assert!(contract.assert_if_greater_than_10(&5));
-    }
-
-    #[test]
-    #[should_panic(expected = "1")] // The custom error number is 1
-    fn reverts_if_greater() {
-        let env = Env::default();
-        let contract = UnnecessaryLintAllowClient::new(
-            &env,
-            &env.register_contract(None, UnnecessaryLintAllow {}),
-        );
-        contract.assert_if_greater_than_10(&11);
-    }
-}
diff --git a/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/vulnerable-example/src/lib.rs b/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/vulnerable-example/src/lib.rs
index b7e5dd70..89e06e08 100644
--- a/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/vulnerable-example/src/lib.rs
+++ b/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/vulnerable-example/src/lib.rs
@@ -1,14 +1,11 @@
 #![no_std]
-#![allow(assert_violation)]
 use soroban_sdk::{contract, contracterror, contractimpl};
 
 #[contract]
-#[allow(assert_violation)]
 pub struct UnnecessaryLintAllow;
 
 #[contracterror]
 #[derive(Copy, Clone)]
-#[allow(assert_violation)]
 pub enum AssertError {
     GreaterThan10 = 1,
 }
@@ -16,40 +13,11 @@ pub enum AssertError {
 #[contractimpl]
 #[allow(assert_violation)]
 impl UnnecessaryLintAllow {
-    #[allow(assert_violation)]
     pub fn assert_if_greater_than_10(value: u128) -> Result<bool, AssertError> {
         if value <= 10 {
             Ok(true)
         } else {
-            #[allow(assert_violation)]
             Err(AssertError::GreaterThan10)
         }
     }
 }
-
-// #[cfg(test)]
-// mod tests {
-//     use soroban_sdk::Env;
-
-//     use super::*;
-//     #[test]
-//     fn does_not_revert_if_greater() {
-//         let env = Env::default();
-//         let contract = UnnecessaryLintAllowClient::new(
-//             &env,
-//             &env.register_contract(None, UnnecessaryLintAllow {}),
-//         );
-//         assert!(contract.assert_if_greater_than_10(&5));
-//     }
-
-//     #[test]
-//     #[should_panic(expected = "1")] // The custom error number is 1
-//     fn reverts_if_greater() {
-//         let env = Env::default();
-//         let contract = UnnecessaryLintAllowClient::new(
-//             &env,
-//             &env.register_contract(None, UnnecessaryLintAllow {}),
-//         );
-//         contract.assert_if_greater_than_10(&11);
-//     }
-// }

From 9393b13725921707ba7f01373b98f1a685df4a94 Mon Sep 17 00:00:00 2001
From: Jose Garcia Crosta <jgcrosta@gmail.com>
Date: Fri, 2 Aug 2024 12:04:04 -0300
Subject: [PATCH 05/18] Simplify scopes, improve attribute detection

---
 detectors/unnecessary-lint-allow/src/lib.rs   | 101 +++++++++---------
 detectors/unnecessary-lint-allow/src/types.rs |   6 +-
 2 files changed, 50 insertions(+), 57 deletions(-)

diff --git a/detectors/unnecessary-lint-allow/src/lib.rs b/detectors/unnecessary-lint-allow/src/lib.rs
index 53e3d10e..4080ed19 100644
--- a/detectors/unnecessary-lint-allow/src/lib.rs
+++ b/detectors/unnecessary-lint-allow/src/lib.rs
@@ -12,10 +12,7 @@ use rustc_ast::{
     tokenstream::{TokenStream, TokenTree},
     AttrArgs, AttrKind, Attribute,
 };
-use rustc_hir::{
-    intravisit::{walk_expr, FnKind, Visitor},
-    Body, FnDecl,
-};
+use rustc_hir::{intravisit::FnKind, Body, FnDecl, HirId, Item, CRATE_HIR_ID};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_span::{def_id::LocalDefId, sym, Span};
 use std::collections::{HashSet, VecDeque};
@@ -43,7 +40,22 @@ struct UnnecessaryLintAllow {
 }
 
 impl UnnecessaryLintAllow {
-    pub fn collect_attribute(
+    fn check_and_collect_attrs(
+        &mut self,
+        cx: &LateContext,
+        hir_id: HirId,
+        scope: Scope,
+        span: Span,
+    ) {
+        let attrs = cx.tcx.hir().attrs(hir_id);
+        if !attrs.is_empty() {
+            for attr in attrs.iter() {
+                self.collect_attribute(cx, attr, scope, span);
+            }
+        }
+    }
+
+    fn collect_attribute(
         &mut self,
         cx: &LateContext,
         attr: &Attribute,
@@ -68,7 +80,7 @@ impl UnnecessaryLintAllow {
         }
     }
 
-    pub fn extract_lint_names(&self, tokens: &TokenStream) -> Vec<String> {
+    fn extract_lint_names(&self, tokens: &TokenStream) -> Vec<String> {
         let mut lint_names = Vec::new();
         let mut stack = VecDeque::new();
         stack.push_back(tokens);
@@ -89,7 +101,7 @@ impl UnnecessaryLintAllow {
                         // Push the inner stream onto the stack for later processing
                         stack.push_back(inner_stream);
                     }
-                    _ => {} // Ignore other token types
+                    _ => {}
                 }
             }
         }
@@ -102,39 +114,51 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryLintAllow {
     fn check_crate_post(&mut self, _: &LateContext<'tcx>) {
         for finding in &self.findings {
             println!(
-                "Found unnecessary `#[allow({})]` attribute at {}:{}-{}",
+                "Found unnecessary `#[allow({})]` attribute at {}:{}-{}, type: {:?}",
                 finding.lint_name,
                 finding.span.file_name,
                 finding.span.from_line,
-                finding.span.to_line
+                finding.span.to_line,
+                finding.scope
             );
         }
     }
 
     fn check_crate(&mut self, cx: &LateContext<'tcx>) {
         // Collect crate-level attributes
-        for attr in cx.tcx.hir().attrs(rustc_hir::CRATE_HIR_ID) {
-            self.collect_attribute(cx, attr, Scope::Crate, attr.span);
-        }
+        self.check_and_collect_attrs(
+            cx,
+            CRATE_HIR_ID,
+            Scope::Crate,
+            cx.tcx.hir().span(CRATE_HIR_ID),
+        );
     }
 
-    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx rustc_hir::Item<'tcx>) {
+    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
         if item.span.from_expansion() {
             return;
         }
 
         // Collect item-level attributes (struct, enum, impl)
-        let attrs = cx.tcx.hir().attrs(item.hir_id());
-        let scope = match item.kind {
-            rustc_hir::ItemKind::Struct(..) => Scope::Struct,
-            rustc_hir::ItemKind::Enum(..) => Scope::Enum,
-            rustc_hir::ItemKind::Impl(..) => Scope::Impl,
-            _ => return,
-        };
-
-        for attr in attrs.iter() {
-            self.collect_attribute(cx, attr, scope, item.span);
+        self.check_and_collect_attrs(cx, item.hir_id(), Scope::Other, item.span);
+    }
+
+    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx rustc_hir::Stmt<'tcx>) {
+        if stmt.span.from_expansion() {
+            return;
+        }
+
+        // Collect statement-level attributes (let, return, etc.)
+        self.check_and_collect_attrs(cx, stmt.hir_id, Scope::Other, stmt.span);
+    }
+
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
+        if expr.span.from_expansion() {
+            return;
         }
+
+        // Collect expression-level attributes (function call, etc.)
+        self.check_and_collect_attrs(cx, expr.hir_id, Scope::Other, expr.span);
     }
 
     fn check_fn(
@@ -142,43 +166,16 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryLintAllow {
         cx: &LateContext<'tcx>,
         _: FnKind<'tcx>,
         _: &'tcx FnDecl<'tcx>,
-        body: &'tcx Body<'tcx>,
+        _: &'tcx Body<'tcx>,
         span: Span,
         local_def_id: LocalDefId,
     ) {
-        // If the function comes from a macro expansion, we ignore it
         if span.from_expansion() {
             return;
         }
 
         // Collect function level attributes (function)
         let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id);
-        let attrs = cx.tcx.hir().attrs(hir_id);
-        for attr in attrs.iter() {
-            self.collect_attribute(cx, attr, Scope::Function, span);
-        }
-
-        // Collect inner-level attributes (line)
-        struct InnerAttrVisitor<'a, 'tcx> {
-            cx: &'a LateContext<'tcx>,
-            lint: &'a mut UnnecessaryLintAllow,
-        }
-
-        impl<'a, 'tcx> Visitor<'tcx> for InnerAttrVisitor<'a, 'tcx> {
-            fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>) {
-                let attrs = self.cx.tcx.hir().attrs(expr.hir_id);
-                if !attrs.is_empty() {
-                    for attr in attrs.iter() {
-                        self.lint
-                            .collect_attribute(self.cx, attr, Scope::Line, expr.span);
-                    }
-                }
-
-                walk_expr(self, expr);
-            }
-        }
-
-        let mut visitor = InnerAttrVisitor { cx, lint: self };
-        walk_expr(&mut visitor, body.value);
+        self.check_and_collect_attrs(cx, hir_id, Scope::Other, span);
     }
 }
diff --git a/detectors/unnecessary-lint-allow/src/types.rs b/detectors/unnecessary-lint-allow/src/types.rs
index 1adaf462..e3838cf2 100644
--- a/detectors/unnecessary-lint-allow/src/types.rs
+++ b/detectors/unnecessary-lint-allow/src/types.rs
@@ -11,11 +11,7 @@ pub struct AllowInfo {
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 pub enum Scope {
     Crate,
-    Enum,
-    Function,
-    Impl,
-    Line,
-    Struct,
+    Other,
 }
 
 #[derive(Eq, PartialEq, Hash, Debug, Clone)]

From a69343c9c5a0e82b4d8985b24eadbacb93487192 Mon Sep 17 00:00:00 2001
From: Jose Garcia Crosta <jgcrosta@gmail.com>
Date: Thu, 8 Aug 2024 11:12:11 -0300
Subject: [PATCH 06/18] Update detector

---
 detectors/unnecessary-lint-allow/src/lib.rs   | 58 ++++++++-----------
 detectors/unnecessary-lint-allow/src/types.rs | 44 --------------
 2 files changed, 24 insertions(+), 78 deletions(-)
 delete mode 100644 detectors/unnecessary-lint-allow/src/types.rs

diff --git a/detectors/unnecessary-lint-allow/src/lib.rs b/detectors/unnecessary-lint-allow/src/lib.rs
index 4080ed19..56aa1cf5 100644
--- a/detectors/unnecessary-lint-allow/src/lib.rs
+++ b/detectors/unnecessary-lint-allow/src/lib.rs
@@ -4,8 +4,6 @@ extern crate rustc_ast;
 extern crate rustc_hir;
 extern crate rustc_span;
 
-mod types;
-
 use if_chain::if_chain;
 use rustc_ast::{
     token::{Delimiter, Token, TokenKind},
@@ -16,7 +14,6 @@ use rustc_hir::{intravisit::FnKind, Body, FnDecl, HirId, Item, CRATE_HIR_ID};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_span::{def_id::LocalDefId, sym, Span};
 use std::collections::{HashSet, VecDeque};
-use types::{AllowInfo, Scope, SpanInfo};
 
 const LINT_MESSAGE: &str = "This `#[allow]` attribute may be unnecessary. Consider removing it if the lint is no longer triggered.";
 
@@ -34,6 +31,19 @@ dylint_linting::impl_late_lint!(
     }
 );
 
+#[derive(Eq, Hash, PartialEq, Debug, Clone)]
+pub struct AllowInfo {
+    pub lint_name: String,
+    pub span: Span,
+    pub scope: Scope,
+}
+
+#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
+pub enum Scope {
+    Crate,
+    Other,
+}
+
 #[derive(Default, Debug, Clone)]
 struct UnnecessaryLintAllow {
     findings: HashSet<AllowInfo>,
@@ -47,21 +57,19 @@ impl UnnecessaryLintAllow {
         scope: Scope,
         span: Span,
     ) {
+        if span.from_expansion() {
+            return;
+        }
+
         let attrs = cx.tcx.hir().attrs(hir_id);
         if !attrs.is_empty() {
             for attr in attrs.iter() {
-                self.collect_attribute(cx, attr, scope, span);
+                self.collect_attribute(attr, scope, span);
             }
         }
     }
 
-    fn collect_attribute(
-        &mut self,
-        cx: &LateContext,
-        attr: &Attribute,
-        scope: Scope,
-        item_span: Span,
-    ) {
+    fn collect_attribute(&mut self, attr: &Attribute, scope: Scope, span: Span) {
         if_chain! {
             if !attr.span.from_expansion();
             if attr.has_name(sym::allow);
@@ -72,7 +80,7 @@ impl UnnecessaryLintAllow {
                 for lint_name in lint_names {
                     self.findings.insert(AllowInfo {
                         lint_name,
-                        span: SpanInfo::from_span(cx, item_span),
+                        span,
                         scope,
                     });
                 }
@@ -113,12 +121,10 @@ impl UnnecessaryLintAllow {
 impl<'tcx> LateLintPass<'tcx> for UnnecessaryLintAllow {
     fn check_crate_post(&mut self, _: &LateContext<'tcx>) {
         for finding in &self.findings {
-            println!(
-                "Found unnecessary `#[allow({})]` attribute at {}:{}-{}, type: {:?}",
-                finding.lint_name,
-                finding.span.file_name,
-                finding.span.from_line,
-                finding.span.to_line,
+            dbg!(
+                "Found unnecessary `#[allow({})]` attribute at {:?}, type: {:?}",
+                finding.lint_name.clone(),
+                finding.span,
                 finding.scope
             );
         }
@@ -135,28 +141,16 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryLintAllow {
     }
 
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
-        if item.span.from_expansion() {
-            return;
-        }
-
         // Collect item-level attributes (struct, enum, impl)
         self.check_and_collect_attrs(cx, item.hir_id(), Scope::Other, item.span);
     }
 
     fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx rustc_hir::Stmt<'tcx>) {
-        if stmt.span.from_expansion() {
-            return;
-        }
-
         // Collect statement-level attributes (let, return, etc.)
         self.check_and_collect_attrs(cx, stmt.hir_id, Scope::Other, stmt.span);
     }
 
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
-        if expr.span.from_expansion() {
-            return;
-        }
-
         // Collect expression-level attributes (function call, etc.)
         self.check_and_collect_attrs(cx, expr.hir_id, Scope::Other, expr.span);
     }
@@ -170,10 +164,6 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryLintAllow {
         span: Span,
         local_def_id: LocalDefId,
     ) {
-        if span.from_expansion() {
-            return;
-        }
-
         // Collect function level attributes (function)
         let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id);
         self.check_and_collect_attrs(cx, hir_id, Scope::Other, span);
diff --git a/detectors/unnecessary-lint-allow/src/types.rs b/detectors/unnecessary-lint-allow/src/types.rs
deleted file mode 100644
index e3838cf2..00000000
--- a/detectors/unnecessary-lint-allow/src/types.rs
+++ /dev/null
@@ -1,44 +0,0 @@
-use rustc_lint::{LateContext, LintContext};
-use rustc_span::{FileName, FileNameDisplayPreference, Span};
-
-#[derive(Eq, Hash, PartialEq, Debug, Clone)]
-pub struct AllowInfo {
-    pub lint_name: String,
-    pub span: SpanInfo,
-    pub scope: Scope,
-}
-
-#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
-pub enum Scope {
-    Crate,
-    Other,
-}
-
-#[derive(Eq, PartialEq, Hash, Debug, Clone)]
-pub struct SpanInfo {
-    pub file_name: String,
-    pub from_line: usize,
-    pub to_line: usize,
-}
-
-impl SpanInfo {
-    pub fn from_span(cx: &LateContext, span: Span) -> Self {
-        let source_map = cx.sess().source_map();
-        let file = source_map.lookup_source_file(span.lo());
-        let file_name = match &file.name {
-            FileName::Real(name) => name
-                .to_string_lossy(FileNameDisplayPreference::Remapped)
-                .into_owned(),
-            _ => String::from("<unknown>"),
-        };
-
-        let lo_loc = source_map.lookup_char_pos(span.lo());
-        let hi_loc = source_map.lookup_char_pos(span.hi());
-
-        SpanInfo {
-            file_name,
-            from_line: lo_loc.line,
-            to_line: hi_loc.line,
-        }
-    }
-}

From 2019ff3e842762d90524a4e817196c4b3b8057db Mon Sep 17 00:00:00 2001
From: Jose Garcia Crosta <jgcrosta@gmail.com>
Date: Fri, 9 Aug 2024 16:20:21 -0300
Subject: [PATCH 07/18] Remove scope from detector, use clippy-wrappers

---
 detectors/unnecessary-lint-allow/Cargo.toml |  1 +
 detectors/unnecessary-lint-allow/src/lib.rs | 51 ++++++---------------
 2 files changed, 15 insertions(+), 37 deletions(-)

diff --git a/detectors/unnecessary-lint-allow/Cargo.toml b/detectors/unnecessary-lint-allow/Cargo.toml
index 925700a4..dd04a840 100644
--- a/detectors/unnecessary-lint-allow/Cargo.toml
+++ b/detectors/unnecessary-lint-allow/Cargo.toml
@@ -8,6 +8,7 @@ crate-type = ["cdylib"]
 
 [dependencies]
 clippy_utils = { workspace = true }
+clippy_wrappers = { workspace = true }
 dylint_linting = { workspace = true }
 if_chain = { workspace = true }
 serde = { version = "1", features = ["derive"] }
diff --git a/detectors/unnecessary-lint-allow/src/lib.rs b/detectors/unnecessary-lint-allow/src/lib.rs
index 56aa1cf5..9b19b17b 100644
--- a/detectors/unnecessary-lint-allow/src/lib.rs
+++ b/detectors/unnecessary-lint-allow/src/lib.rs
@@ -4,13 +4,14 @@ extern crate rustc_ast;
 extern crate rustc_hir;
 extern crate rustc_span;
 
+use clippy_wrappers::span_lint;
 use if_chain::if_chain;
 use rustc_ast::{
     token::{Delimiter, Token, TokenKind},
     tokenstream::{TokenStream, TokenTree},
     AttrArgs, AttrKind, Attribute,
 };
-use rustc_hir::{intravisit::FnKind, Body, FnDecl, HirId, Item, CRATE_HIR_ID};
+use rustc_hir::{intravisit::FnKind, Body, Expr, FnDecl, HirId, Item, Stmt, CRATE_HIR_ID};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_span::{def_id::LocalDefId, sym, Span};
 use std::collections::{HashSet, VecDeque};
@@ -35,13 +36,6 @@ dylint_linting::impl_late_lint!(
 pub struct AllowInfo {
     pub lint_name: String,
     pub span: Span,
-    pub scope: Scope,
-}
-
-#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
-pub enum Scope {
-    Crate,
-    Other,
 }
 
 #[derive(Default, Debug, Clone)]
@@ -50,13 +44,7 @@ struct UnnecessaryLintAllow {
 }
 
 impl UnnecessaryLintAllow {
-    fn check_and_collect_attrs(
-        &mut self,
-        cx: &LateContext,
-        hir_id: HirId,
-        scope: Scope,
-        span: Span,
-    ) {
+    fn check_and_collect_attrs(&mut self, cx: &LateContext, hir_id: HirId, span: Span) {
         if span.from_expansion() {
             return;
         }
@@ -64,12 +52,12 @@ impl UnnecessaryLintAllow {
         let attrs = cx.tcx.hir().attrs(hir_id);
         if !attrs.is_empty() {
             for attr in attrs.iter() {
-                self.collect_attribute(attr, scope, span);
+                self.collect_attribute(attr, span);
             }
         }
     }
 
-    fn collect_attribute(&mut self, attr: &Attribute, scope: Scope, span: Span) {
+    fn collect_attribute(&mut self, attr: &Attribute, span: Span) {
         if_chain! {
             if !attr.span.from_expansion();
             if attr.has_name(sym::allow);
@@ -81,7 +69,6 @@ impl UnnecessaryLintAllow {
                     self.findings.insert(AllowInfo {
                         lint_name,
                         span,
-                        scope,
                     });
                 }
             }
@@ -119,40 +106,30 @@ impl UnnecessaryLintAllow {
 }
 
 impl<'tcx> LateLintPass<'tcx> for UnnecessaryLintAllow {
-    fn check_crate_post(&mut self, _: &LateContext<'tcx>) {
+    fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
         for finding in &self.findings {
-            dbg!(
-                "Found unnecessary `#[allow({})]` attribute at {:?}, type: {:?}",
-                finding.lint_name.clone(),
-                finding.span,
-                finding.scope
-            );
+            span_lint(cx, UNNECESSARY_LINT_ALLOW, finding.span, LINT_MESSAGE);
         }
     }
 
     fn check_crate(&mut self, cx: &LateContext<'tcx>) {
         // Collect crate-level attributes
-        self.check_and_collect_attrs(
-            cx,
-            CRATE_HIR_ID,
-            Scope::Crate,
-            cx.tcx.hir().span(CRATE_HIR_ID),
-        );
+        self.check_and_collect_attrs(cx, CRATE_HIR_ID, cx.tcx.hir().span(CRATE_HIR_ID));
     }
 
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
         // Collect item-level attributes (struct, enum, impl)
-        self.check_and_collect_attrs(cx, item.hir_id(), Scope::Other, item.span);
+        self.check_and_collect_attrs(cx, item.hir_id(), item.span);
     }
 
-    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx rustc_hir::Stmt<'tcx>) {
+    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) {
         // Collect statement-level attributes (let, return, etc.)
-        self.check_and_collect_attrs(cx, stmt.hir_id, Scope::Other, stmt.span);
+        self.check_and_collect_attrs(cx, stmt.hir_id, stmt.span);
     }
 
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
         // Collect expression-level attributes (function call, etc.)
-        self.check_and_collect_attrs(cx, expr.hir_id, Scope::Other, expr.span);
+        self.check_and_collect_attrs(cx, expr.hir_id, expr.span);
     }
 
     fn check_fn(
@@ -166,6 +143,6 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryLintAllow {
     ) {
         // Collect function level attributes (function)
         let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id);
-        self.check_and_collect_attrs(cx, hir_id, Scope::Other, span);
+        self.check_and_collect_attrs(cx, hir_id, span);
     }
 }

From 336ee9e789c878c62b0b8bc82b13b1ba1b61dc74 Mon Sep 17 00:00:00 2001
From: Jose Garcia Crosta <jgcrosta@gmail.com>
Date: Mon, 12 Aug 2024 12:17:28 -0300
Subject: [PATCH 08/18] Edit detector

---
 detectors/unnecessary-lint-allow/src/lib.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/detectors/unnecessary-lint-allow/src/lib.rs b/detectors/unnecessary-lint-allow/src/lib.rs
index 9b19b17b..e2d42536 100644
--- a/detectors/unnecessary-lint-allow/src/lib.rs
+++ b/detectors/unnecessary-lint-allow/src/lib.rs
@@ -13,7 +13,7 @@ use rustc_ast::{
 };
 use rustc_hir::{intravisit::FnKind, Body, Expr, FnDecl, HirId, Item, Stmt, CRATE_HIR_ID};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_span::{def_id::LocalDefId, sym, Span};
+use rustc_span::{def_id::LocalDefId, Span, Symbol};
 use std::collections::{HashSet, VecDeque};
 
 const LINT_MESSAGE: &str = "This `#[allow]` attribute may be unnecessary. Consider removing it if the lint is no longer triggered.";
@@ -60,7 +60,7 @@ impl UnnecessaryLintAllow {
     fn collect_attribute(&mut self, attr: &Attribute, span: Span) {
         if_chain! {
             if !attr.span.from_expansion();
-            if attr.has_name(sym::allow);
+            if attr.has_name(Symbol::intern("scout_allow"));
             if let AttrKind::Normal(item) = &attr.kind;
             if let AttrArgs::Delimited(delimited_args) = &item.item.args;
             then {

From 4c2a46c52e947ed01925baea2e6160b8900b0640 Mon Sep 17 00:00:00 2001
From: Jose Garcia Crosta <jgcrosta@gmail.com>
Date: Mon, 12 Aug 2024 15:03:13 -0300
Subject: [PATCH 09/18] Update detector and test-case

---
 detectors/unnecessary-lint-allow/src/lib.rs   | 106 +++---------------
 .../vulnerable-example/Cargo.toml             |   1 +
 .../vulnerable-example/src/lib.rs             |   3 +-
 3 files changed, 21 insertions(+), 89 deletions(-)

diff --git a/detectors/unnecessary-lint-allow/src/lib.rs b/detectors/unnecessary-lint-allow/src/lib.rs
index e2d42536..4bb2bc73 100644
--- a/detectors/unnecessary-lint-allow/src/lib.rs
+++ b/detectors/unnecessary-lint-allow/src/lib.rs
@@ -1,7 +1,6 @@
 #![feature(rustc_private)]
 
 extern crate rustc_ast;
-extern crate rustc_hir;
 extern crate rustc_span;
 
 use clippy_wrappers::span_lint;
@@ -9,20 +8,18 @@ use if_chain::if_chain;
 use rustc_ast::{
     token::{Delimiter, Token, TokenKind},
     tokenstream::{TokenStream, TokenTree},
-    AttrArgs, AttrKind, Attribute,
+    AttrArgs, AttrKind,
 };
-use rustc_hir::{intravisit::FnKind, Body, Expr, FnDecl, HirId, Item, Stmt, CRATE_HIR_ID};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_span::{def_id::LocalDefId, Span, Symbol};
-use std::collections::{HashSet, VecDeque};
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_span::Symbol;
+use std::collections::VecDeque;
 
 const LINT_MESSAGE: &str = "This `#[allow]` attribute may be unnecessary. Consider removing it if the lint is no longer triggered.";
 
-dylint_linting::impl_late_lint!(
+dylint_linting::declare_pre_expansion_lint! {
     pub UNNECESSARY_LINT_ALLOW,
     Warn,
     LINT_MESSAGE,
-    UnnecessaryLintAllow::default(),
     {
         name: "Unnecessary Lint Allow",
         long_message: "The `#[allow]` attribute is used to disable lints. It is recommended to fix the issues instead of disabling them.",
@@ -30,51 +27,9 @@ dylint_linting::impl_late_lint!(
         help: "https://coinfabrik.github.io/scout-soroban/docs/detectors/unnecessary-lint-allow",
         vulnerability_class: "Code Quality",
     }
-);
-
-#[derive(Eq, Hash, PartialEq, Debug, Clone)]
-pub struct AllowInfo {
-    pub lint_name: String,
-    pub span: Span,
-}
-
-#[derive(Default, Debug, Clone)]
-struct UnnecessaryLintAllow {
-    findings: HashSet<AllowInfo>,
 }
 
 impl UnnecessaryLintAllow {
-    fn check_and_collect_attrs(&mut self, cx: &LateContext, hir_id: HirId, span: Span) {
-        if span.from_expansion() {
-            return;
-        }
-
-        let attrs = cx.tcx.hir().attrs(hir_id);
-        if !attrs.is_empty() {
-            for attr in attrs.iter() {
-                self.collect_attribute(attr, span);
-            }
-        }
-    }
-
-    fn collect_attribute(&mut self, attr: &Attribute, span: Span) {
-        if_chain! {
-            if !attr.span.from_expansion();
-            if attr.has_name(Symbol::intern("scout_allow"));
-            if let AttrKind::Normal(item) = &attr.kind;
-            if let AttrArgs::Delimited(delimited_args) = &item.item.args;
-            then {
-                let lint_names = self.extract_lint_names(&delimited_args.tokens);
-                for lint_name in lint_names {
-                    self.findings.insert(AllowInfo {
-                        lint_name,
-                        span,
-                    });
-                }
-            }
-        }
-    }
-
     fn extract_lint_names(&self, tokens: &TokenStream) -> Vec<String> {
         let mut lint_names = Vec::new();
         let mut stack = VecDeque::new();
@@ -105,44 +60,19 @@ impl UnnecessaryLintAllow {
     }
 }
 
-impl<'tcx> LateLintPass<'tcx> for UnnecessaryLintAllow {
-    fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
-        for finding in &self.findings {
-            span_lint(cx, UNNECESSARY_LINT_ALLOW, finding.span, LINT_MESSAGE);
+impl EarlyLintPass for UnnecessaryLintAllow {
+    fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &rustc_ast::Attribute) {
+        if_chain! {
+            if !attr.span.from_expansion();
+            if attr.has_name(Symbol::intern("scout_allow"));
+            if let AttrKind::Normal(item) = &attr.kind;
+            if let AttrArgs::Delimited(delimited_args) = &item.item.args;
+            then {
+                let lint_names = self.extract_lint_names(&delimited_args.tokens);
+                for lint_name in lint_names {
+                    span_lint(cx, UNNECESSARY_LINT_ALLOW, attr.span, lint_name);
+                }
+            }
         }
     }
-
-    fn check_crate(&mut self, cx: &LateContext<'tcx>) {
-        // Collect crate-level attributes
-        self.check_and_collect_attrs(cx, CRATE_HIR_ID, cx.tcx.hir().span(CRATE_HIR_ID));
-    }
-
-    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
-        // Collect item-level attributes (struct, enum, impl)
-        self.check_and_collect_attrs(cx, item.hir_id(), item.span);
-    }
-
-    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) {
-        // Collect statement-level attributes (let, return, etc.)
-        self.check_and_collect_attrs(cx, stmt.hir_id, stmt.span);
-    }
-
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-        // Collect expression-level attributes (function call, etc.)
-        self.check_and_collect_attrs(cx, expr.hir_id, expr.span);
-    }
-
-    fn check_fn(
-        &mut self,
-        cx: &LateContext<'tcx>,
-        _: FnKind<'tcx>,
-        _: &'tcx FnDecl<'tcx>,
-        _: &'tcx Body<'tcx>,
-        span: Span,
-        local_def_id: LocalDefId,
-    ) {
-        // Collect function level attributes (function)
-        let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id);
-        self.check_and_collect_attrs(cx, hir_id, span);
-    }
 }
diff --git a/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/vulnerable-example/Cargo.toml b/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/vulnerable-example/Cargo.toml
index d4da8d0a..35927951 100644
--- a/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/vulnerable-example/Cargo.toml
+++ b/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/vulnerable-example/Cargo.toml
@@ -8,6 +8,7 @@ version = "0.1.0"
 crate-type = ["cdylib"]
 
 [dependencies]
+scout-utils = { version = "0.1.0" }
 soroban-sdk = { workspace = true }
 
 [dev-dependencies]
diff --git a/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/vulnerable-example/src/lib.rs b/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/vulnerable-example/src/lib.rs
index 89e06e08..56de7638 100644
--- a/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/vulnerable-example/src/lib.rs
+++ b/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/vulnerable-example/src/lib.rs
@@ -1,4 +1,5 @@
 #![no_std]
+use scout_utils::scout_allow;
 use soroban_sdk::{contract, contracterror, contractimpl};
 
 #[contract]
@@ -11,7 +12,7 @@ pub enum AssertError {
 }
 
 #[contractimpl]
-#[allow(assert_violation)]
+#[scout_allow(assert_violation)]
 impl UnnecessaryLintAllow {
     pub fn assert_if_greater_than_10(value: u128) -> Result<bool, AssertError> {
         if value <= 10 {

From 6238cbc9aaf1b999c136446db4f0ba809acfe511 Mon Sep 17 00:00:00 2001
From: Jose Garcia Crosta <jgcrosta@gmail.com>
Date: Mon, 12 Aug 2024 15:21:38 -0300
Subject: [PATCH 10/18] Update detector

---
 detectors/unnecessary-lint-allow/src/lib.rs | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/detectors/unnecessary-lint-allow/src/lib.rs b/detectors/unnecessary-lint-allow/src/lib.rs
index 4bb2bc73..97e7ac98 100644
--- a/detectors/unnecessary-lint-allow/src/lib.rs
+++ b/detectors/unnecessary-lint-allow/src/lib.rs
@@ -3,7 +3,7 @@
 extern crate rustc_ast;
 extern crate rustc_span;
 
-use clippy_wrappers::span_lint;
+use clippy_wrappers::span_lint_and_help;
 use if_chain::if_chain;
 use rustc_ast::{
     token::{Delimiter, Token, TokenKind},
@@ -14,7 +14,7 @@ use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_span::Symbol;
 use std::collections::VecDeque;
 
-const LINT_MESSAGE: &str = "This `#[allow]` attribute may be unnecessary. Consider removing it if the lint is no longer triggered.";
+const LINT_MESSAGE: &str = "This `#[scout_allow]` attribute may be unnecessary. Consider removing it if the lint is no longer triggered.";
 
 dylint_linting::declare_pre_expansion_lint! {
     pub UNNECESSARY_LINT_ALLOW,
@@ -22,7 +22,7 @@ dylint_linting::declare_pre_expansion_lint! {
     LINT_MESSAGE,
     {
         name: "Unnecessary Lint Allow",
-        long_message: "The `#[allow]` attribute is used to disable lints. It is recommended to fix the issues instead of disabling them.",
+        long_message: "The `#[scout_allow]` attribute may be unnecessary. Consider removing it if the lint is no longer triggered.",
         severity: "Enhancement",
         help: "https://coinfabrik.github.io/scout-soroban/docs/detectors/unnecessary-lint-allow",
         vulnerability_class: "Code Quality",
@@ -70,7 +70,14 @@ impl EarlyLintPass for UnnecessaryLintAllow {
             then {
                 let lint_names = self.extract_lint_names(&delimited_args.tokens);
                 for lint_name in lint_names {
-                    span_lint(cx, UNNECESSARY_LINT_ALLOW, attr.span, lint_name);
+                    span_lint_and_help(
+                        cx,
+                        UNNECESSARY_LINT_ALLOW,
+                        attr.span,
+                        LINT_MESSAGE,
+                        None,
+                        format!("The detector `{}` is no longer triggered. Consider removing the `#[scout_allow({lint_name})]` attribute if the lint is no longer triggered.", lint_name)
+                    );
                 }
             }
         }

From 916afcb62cd0cdcb3234b4056aef734ad57ac1cb Mon Sep 17 00:00:00 2001
From: Jose Garcia Crosta <jgcrosta@gmail.com>
Date: Tue, 13 Aug 2024 15:03:22 -0300
Subject: [PATCH 11/18] Edit detector

---
 detectors/unnecessary-lint-allow/src/lib.rs | 55 ++++++++++++---------
 1 file changed, 31 insertions(+), 24 deletions(-)

diff --git a/detectors/unnecessary-lint-allow/src/lib.rs b/detectors/unnecessary-lint-allow/src/lib.rs
index 97e7ac98..e1b45b0b 100644
--- a/detectors/unnecessary-lint-allow/src/lib.rs
+++ b/detectors/unnecessary-lint-allow/src/lib.rs
@@ -1,6 +1,7 @@
 #![feature(rustc_private)]
 
 extern crate rustc_ast;
+extern crate rustc_hir;
 extern crate rustc_span;
 
 use clippy_wrappers::span_lint_and_help;
@@ -8,11 +9,12 @@ use if_chain::if_chain;
 use rustc_ast::{
     token::{Delimiter, Token, TokenKind},
     tokenstream::{TokenStream, TokenTree},
-    AttrArgs, AttrKind,
+    visit::FnKind,
+    AttrArgs, AttrKind, Attribute, Expr, HasAttrs, Item, NodeId,
 };
-use rustc_lint::{EarlyContext, EarlyLintPass};
-use rustc_span::Symbol;
-use std::collections::VecDeque;
+use rustc_lint::{EarlyContext, EarlyLintPass, LateLintPass, LintContext};
+use rustc_span::{Span, Symbol};
+use std::collections::{HashMap, VecDeque};
 
 const LINT_MESSAGE: &str = "This `#[scout_allow]` attribute may be unnecessary. Consider removing it if the lint is no longer triggered.";
 
@@ -48,7 +50,6 @@ impl UnnecessaryLintAllow {
                         lint_names.push(ident.to_string());
                     }
                     TokenTree::Delimited(_, _, Delimiter::Parenthesis, inner_stream) => {
-                        // Push the inner stream onto the stack for later processing
                         stack.push_back(inner_stream);
                     }
                     _ => {}
@@ -58,28 +59,34 @@ impl UnnecessaryLintAllow {
 
         lint_names
     }
-}
 
-impl EarlyLintPass for UnnecessaryLintAllow {
-    fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &rustc_ast::Attribute) {
-        if_chain! {
-            if !attr.span.from_expansion();
-            if attr.has_name(Symbol::intern("scout_allow"));
-            if let AttrKind::Normal(item) = &attr.kind;
-            if let AttrArgs::Delimited(delimited_args) = &item.item.args;
-            then {
-                let lint_names = self.extract_lint_names(&delimited_args.tokens);
-                for lint_name in lint_names {
-                    span_lint_and_help(
-                        cx,
-                        UNNECESSARY_LINT_ALLOW,
-                        attr.span,
-                        LINT_MESSAGE,
-                        None,
-                        format!("The detector `{}` is no longer triggered. Consider removing the `#[scout_allow({lint_name})]` attribute if the lint is no longer triggered.", lint_name)
-                    );
+    fn check_scout_allow_attrs(&self, cx: &EarlyContext<'_>, attrs: &[Attribute], span: Span) {
+        for attr in attrs {
+            if_chain! {
+                if !attr.span.from_expansion();
+                if attr.has_name(Symbol::intern("scout_allow"));
+                if let AttrKind::Normal(item) = &attr.kind;
+                if let AttrArgs::Delimited(delimited_args) = &item.item.args;
+                then {
+                    let lint_names = self.extract_lint_names(&delimited_args.tokens);
+                    for lint_name in lint_names {
+                        span_lint_and_help(
+                            cx,
+                            UNNECESSARY_LINT_ALLOW,
+                            span,
+                            LINT_MESSAGE,
+                            None,
+                            format!("The detector `{}` is no longer triggered. Consider removing the `#[scout_allow({})]` attribute if the lint is no longer triggered.", lint_name, lint_name)
+                        );
+                    }
                 }
             }
         }
     }
 }
+
+impl EarlyLintPass for UnnecessaryLintAllow {
+    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
+        self.check_scout_allow_attrs(cx, &item.attrs, item.span);
+    }
+}

From a7be1a9f00580ce7f8e570c9efc4b6af7ae8a146 Mon Sep 17 00:00:00 2001
From: Jose Garcia Crosta <jgcrosta@gmail.com>
Date: Thu, 15 Aug 2024 16:18:57 -0300
Subject: [PATCH 12/18] Update detector

---
 detectors/unnecessary-lint-allow/Cargo.toml   |   1 +
 detectors/unnecessary-lint-allow/src/lib.rs   |  21 ++-
 .../unnecessary-lint-allow/src/processor.rs   | 131 ++++++++++++++++++
 3 files changed, 146 insertions(+), 7 deletions(-)
 create mode 100644 detectors/unnecessary-lint-allow/src/processor.rs

diff --git a/detectors/unnecessary-lint-allow/Cargo.toml b/detectors/unnecessary-lint-allow/Cargo.toml
index dd04a840..cd203eeb 100644
--- a/detectors/unnecessary-lint-allow/Cargo.toml
+++ b/detectors/unnecessary-lint-allow/Cargo.toml
@@ -12,6 +12,7 @@ clippy_wrappers = { workspace = true }
 dylint_linting = { workspace = true }
 if_chain = { workspace = true }
 serde = { version = "1", features = ["derive"] }
+serde_json = "=1.0.120"
 
 [package.metadata.rust-analyzer]
 rustc_private = true
diff --git a/detectors/unnecessary-lint-allow/src/lib.rs b/detectors/unnecessary-lint-allow/src/lib.rs
index e1b45b0b..41f3a82f 100644
--- a/detectors/unnecessary-lint-allow/src/lib.rs
+++ b/detectors/unnecessary-lint-allow/src/lib.rs
@@ -4,17 +4,19 @@ extern crate rustc_ast;
 extern crate rustc_hir;
 extern crate rustc_span;
 
+mod processor;
+pub use processor::should_include_finding;
+
 use clippy_wrappers::span_lint_and_help;
 use if_chain::if_chain;
 use rustc_ast::{
     token::{Delimiter, Token, TokenKind},
     tokenstream::{TokenStream, TokenTree},
-    visit::FnKind,
-    AttrArgs, AttrKind, Attribute, Expr, HasAttrs, Item, NodeId,
+    AttrArgs, AttrKind, Attribute, Item, ItemKind,
 };
-use rustc_lint::{EarlyContext, EarlyLintPass, LateLintPass, LintContext};
+use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_span::{Span, Symbol};
-use std::collections::{HashMap, VecDeque};
+use std::collections::VecDeque;
 
 const LINT_MESSAGE: &str = "This `#[scout_allow]` attribute may be unnecessary. Consider removing it if the lint is no longer triggered.";
 
@@ -34,10 +36,9 @@ dylint_linting::declare_pre_expansion_lint! {
 impl UnnecessaryLintAllow {
     fn extract_lint_names(&self, tokens: &TokenStream) -> Vec<String> {
         let mut lint_names = Vec::new();
-        let mut stack = VecDeque::new();
-        stack.push_back(tokens);
+        let mut stack = VecDeque::from([tokens]);
 
-        while let Some(current_stream) = stack.pop_back() {
+        while let Some(current_stream) = stack.pop_front() {
             for tree in current_stream.trees() {
                 match tree {
                     TokenTree::Token(
@@ -88,5 +89,11 @@ impl UnnecessaryLintAllow {
 impl EarlyLintPass for UnnecessaryLintAllow {
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
         self.check_scout_allow_attrs(cx, &item.attrs, item.span);
+
+        if let ItemKind::Impl(impl_) = &item.kind {
+            for impl_item in &impl_.items {
+                self.check_scout_allow_attrs(cx, &impl_item.attrs, impl_item.span);
+            }
+        }
     }
 }
diff --git a/detectors/unnecessary-lint-allow/src/processor.rs b/detectors/unnecessary-lint-allow/src/processor.rs
new file mode 100644
index 00000000..7a4d4d6b
--- /dev/null
+++ b/detectors/unnecessary-lint-allow/src/processor.rs
@@ -0,0 +1,131 @@
+    use serde::de::Error;
+    use serde_json::Value;
+    use std::{collections::HashMap, ffi::CStr, os::raw::c_char};
+
+    /// Determines whether a finding should be included.
+    ///
+    /// # Safety
+    ///
+    /// This function is marked as unsafe because it deals with raw pointers.
+    /// The caller is responsible for ensuring the safety of the pointers passed as arguments.
+    #[no_mangle]
+    pub unsafe extern "C" fn should_include_finding(
+        finding_json: *const c_char,
+        all_findings_json: *const c_char,
+    ) -> bool {
+        // Check for null pointers
+        if finding_json.is_null() || all_findings_json.is_null() {
+            return false;
+        }
+
+        let finding = match parse_json_value(finding_json) {
+            Ok(v) => v,
+            Err(_) => return false,
+        };
+
+        let all_findings: Vec<Value> = match parse_json_array(all_findings_json) {
+            Ok(v) => v,
+            Err(_) => return false,
+        };
+
+        should_include_finding_impl(&finding, &all_findings)
+    }
+
+    // Helper function to safely parse a single JSON value
+    unsafe fn parse_json_value(input: *const c_char) -> Result<Value, serde_json::Error> {
+        let c_str = CStr::from_ptr(input);
+        let input_str = c_str
+            .to_str()
+            .map_err(|_| serde_json::Error::custom("Invalid UTF-8"))?;
+        serde_json::from_str(input_str)
+    }
+
+    // Helper function to safely parse a JSON array
+    unsafe fn parse_json_array(input: *const c_char) -> Result<Vec<Value>, serde_json::Error> {
+        let c_str = CStr::from_ptr(input);
+        let input_str = c_str
+            .to_str()
+            .map_err(|_| serde_json::Error::custom("Invalid UTF-8"))?;
+        serde_json::from_str(input_str)
+    }
+
+    #[derive(Debug, Clone)]
+    struct Finding {
+        detector: String,
+        file_name: String,
+        span: (usize, usize),
+        allowed_lint: Option<String>,
+    }
+
+    fn spans_overlap(span1: (usize, usize), span2: (usize, usize)) -> bool {
+        span1.0 <= span2.1 && span2.0 <= span1.1
+    }
+
+    fn parse_finding(finding: &Value) -> Option<Finding> {
+        let code = finding.get("code")?.get("code")?.as_str()?;
+        let span = finding.get("spans")?.as_array()?.first()?;
+        let file_name = span.get("file_name")?.as_str()?;
+        let line_start = span.get("line_start")?.as_u64()?;
+        let line_end = span.get("line_end")?.as_u64()?;
+
+        let allowed_lint = if code == "unnecessary_lint_allow" {
+            finding
+                .get("children")?
+                .get(0)?
+                .get("message")?
+                .as_str()
+                .and_then(|msg| msg.split('`').nth(1).map(String::from))
+        } else {
+            None
+        };
+
+        Some(Finding {
+            detector: code.to_string(),
+            file_name: file_name.to_string(),
+            span: (line_start as usize, line_end as usize),
+            allowed_lint,
+        })
+    }
+
+    pub fn should_include_finding_impl(finding: &Value, all_findings: &[Value]) -> bool {
+        let current_finding = match parse_finding(finding) {
+            Some(f) => f,
+            None => return false, // If we can't parse the finding, we don't include it
+        };
+
+        let mut findings_by_file: HashMap<String, Vec<Finding>> = HashMap::new();
+        for f in all_findings {
+            if let Some(parsed) = parse_finding(f) {
+                findings_by_file
+                    .entry(parsed.file_name.clone())
+                    .or_default()
+                    .push(parsed);
+            }
+        }
+
+        if let Some(file_findings) = findings_by_file.get(&current_finding.file_name) {
+            let (unnecessary_allows, other_findings): (Vec<_>, Vec<_>) = file_findings
+                .iter()
+                .partition(|f| f.detector == "unnecessary_lint_allow");
+
+            if current_finding.detector == "unnecessary_lint_allow" {
+                if let Some(allowed_lint) = &current_finding.allowed_lint {
+                    let lint_present = other_findings.iter().any(|f| {
+                        &f.detector == allowed_lint && spans_overlap(f.span, current_finding.span)
+                    });
+                    return !lint_present; // Include if the lint is not present (unnecessary allow)
+                }
+            } else {
+                let is_allowed = unnecessary_allows.iter().any(|allow| {
+                    allow
+                        .allowed_lint
+                        .as_ref()
+                        .map_or(false, |lint| lint == &current_finding.detector)
+                        && spans_overlap(allow.span, current_finding.span)
+                });
+                return !is_allowed; // Include if the finding is not allowed
+            }
+        }
+
+        true // If we can't find the file or process the finding, we include it by default
+    }

From 166b2b2d43ed0abd9b88d4b98b39bc5bded088d6 Mon Sep 17 00:00:00 2001
From: Jose Garcia Crosta <jgcrosta@gmail.com>
Date: Fri, 16 Aug 2024 13:17:34 -0300
Subject: [PATCH 13/18] Improve some preprocessing stuff

---
 .../unnecessary-lint-allow/src/processor.rs   | 239 +++++++++---------
 1 file changed, 117 insertions(+), 122 deletions(-)

diff --git a/detectors/unnecessary-lint-allow/src/processor.rs b/detectors/unnecessary-lint-allow/src/processor.rs
index 7a4d4d6b..e1f04bf6 100644
--- a/detectors/unnecessary-lint-allow/src/processor.rs
+++ b/detectors/unnecessary-lint-allow/src/processor.rs
@@ -1,131 +1,126 @@
-    use serde::de::Error;
-    use serde_json::Value;
-    use std::{collections::HashMap, ffi::CStr, os::raw::c_char};
-
-    /// Determines whether a finding should be included.
-    ///
-    /// # Safety
-    ///
-    /// This function is marked as unsafe because it deals with raw pointers.
-    /// The caller is responsible for ensuring the safety of the pointers passed as arguments.
-    #[no_mangle]
-    pub unsafe extern "C" fn should_include_finding(
-        finding_json: *const c_char,
-        all_findings_json: *const c_char,
-    ) -> bool {
-        // Check for null pointers
-        if finding_json.is_null() || all_findings_json.is_null() {
-            return false;
-        }
-
-        let finding = match parse_json_value(finding_json) {
-            Ok(v) => v,
-            Err(_) => return false,
-        };
-
-        let all_findings: Vec<Value> = match parse_json_array(all_findings_json) {
-            Ok(v) => v,
-            Err(_) => return false,
-        };
-
-        should_include_finding_impl(&finding, &all_findings)
-    }
-
-    // Helper function to safely parse a single JSON value
-    unsafe fn parse_json_value(input: *const c_char) -> Result<Value, serde_json::Error> {
-        let c_str = CStr::from_ptr(input);
-        let input_str = c_str
-            .to_str()
-            .map_err(|_| serde_json::Error::custom("Invalid UTF-8"))?;
-        serde_json::from_str(input_str)
-    }
-
-    // Helper function to safely parse a JSON array
-    unsafe fn parse_json_array(input: *const c_char) -> Result<Vec<Value>, serde_json::Error> {
-        let c_str = CStr::from_ptr(input);
-        let input_str = c_str
-            .to_str()
-            .map_err(|_| serde_json::Error::custom("Invalid UTF-8"))?;
-        serde_json::from_str(input_str)
-    }
-
-    #[derive(Debug, Clone)]
-    struct Finding {
-        detector: String,
-        file_name: String,
-        span: (usize, usize),
-        allowed_lint: Option<String>,
-    }
-
-    fn spans_overlap(span1: (usize, usize), span2: (usize, usize)) -> bool {
-        span1.0 <= span2.1 && span2.0 <= span1.1
+use serde::de::Error;
+use serde_json::Value;
+use std::{collections::HashMap, ffi::CStr, os::raw::c_char};
+
+/// Determines whether a finding should be included.
+///
+/// # Safety
+///
+/// This function is marked as unsafe because it deals with raw pointers.
+/// The caller is responsible for ensuring the safety of the pointers passed as arguments.
+#[no_mangle]
+pub unsafe extern "C" fn should_include_finding(
+    finding_json: *const c_char,
+    all_findings_json: *const c_char,
+) -> bool {
+    // Check for null pointers
+    if finding_json.is_null() || all_findings_json.is_null() {
+        return false;
     }
 
-    fn parse_finding(finding: &Value) -> Option<Finding> {
-        let code = finding.get("code")?.get("code")?.as_str()?;
-        let span = finding.get("spans")?.as_array()?.first()?;
-        let file_name = span.get("file_name")?.as_str()?;
-        let line_start = span.get("line_start")?.as_u64()?;
-        let line_end = span.get("line_end")?.as_u64()?;
-
-        let allowed_lint = if code == "unnecessary_lint_allow" {
-            finding
-                .get("children")?
-                .get(0)?
-                .get("message")?
-                .as_str()
-                .and_then(|msg| msg.split('`').nth(1).map(String::from))
-        } else {
-            None
-        };
-
-        Some(Finding {
-            detector: code.to_string(),
-            file_name: file_name.to_string(),
-            span: (line_start as usize, line_end as usize),
-            allowed_lint,
-        })
-    }
-
-    pub fn should_include_finding_impl(finding: &Value, all_findings: &[Value]) -> bool {
-        let current_finding = match parse_finding(finding) {
-            Some(f) => f,
-            None => return false, // If we can't parse the finding, we don't include it
-        };
-
-        let mut findings_by_file: HashMap<String, Vec<Finding>> = HashMap::new();
-        for f in all_findings {
-            if let Some(parsed) = parse_finding(f) {
-                findings_by_file
-                    .entry(parsed.file_name.clone())
-                    .or_default()
-                    .push(parsed);
-            }
+    let finding = match parse_json(finding_json) {
+        Ok(v) => v,
+        Err(_) => return false,
+    };
+
+    let all_findings = match parse_json(all_findings_json) {
+        Ok(Value::Array(v)) => v,
+        _ => return false,
+    };
+
+    should_include_finding_impl(&finding, &all_findings)
+}
+
+unsafe fn parse_json(input: *const c_char) -> Result<Value, serde_json::Error> {
+    let c_str = CStr::from_ptr(input);
+    let input_str = c_str
+        .to_str()
+        .map_err(|_| serde_json::Error::custom("Invalid UTF-8"))?;
+    serde_json::from_str(input_str)
+}
+
+#[derive(Debug, Clone)]
+struct Finding {
+    detector: String,
+    file_name: String,
+    span: (usize, usize),
+    allowed_lint: Option<String>,
+}
+
+fn spans_overlap(span1: (usize, usize), span2: (usize, usize)) -> bool {
+    span1.0 <= span2.1 && span2.0 <= span1.1
+}
+
+fn parse_finding(finding: &Value) -> Option<Finding> {
+    let detector = finding.get("code")?.get("code")?.as_str()?;
+    let span = finding.get("spans")?.as_array()?.first()?;
+    let file_name = span.get("file_name")?.as_str()?;
+    let line_start = span.get("line_start")?.as_u64()?;
+    let line_end = span.get("line_end")?.as_u64()?;
+
+    let allowed_lint = if detector == "unnecessary_lint_allow" {
+        finding
+            .get("children")?
+            .get(0)?
+            .get("message")?
+            .as_str()
+            .and_then(|msg| msg.split('`').nth(1).map(String::from))
+    } else {
+        None
+    };
+
+    // Check for potential integer overflow when converting from u64 to usize
+    let start = usize::try_from(line_start).ok()?;
+    let end = usize::try_from(line_end).ok()?;
+
+    Some(Finding {
+        detector: detector.to_owned(),
+        file_name: file_name.to_owned(),
+        span: (start, end),
+        allowed_lint,
+    })
+}
+
+pub fn should_include_finding_impl(finding: &Value, all_findings: &[Value]) -> bool {
+    let current_finding = match parse_finding(finding) {
+        Some(f) => f,
+        None => return false, // If we can't parse the finding, we don't include it
+    };
+
+    let mut findings_by_file: HashMap<String, Vec<Finding>> = HashMap::new();
+    for f in all_findings {
+        if let Some(parsed) = parse_finding(f) {
+            findings_by_file
+                .entry(parsed.file_name.clone())
+                .or_default()
+                .push(parsed);
         }
+    }
 
-        if let Some(file_findings) = findings_by_file.get(&current_finding.file_name) {
-            let (unnecessary_allows, other_findings): (Vec<_>, Vec<_>) = file_findings
-                .iter()
-                .partition(|f| f.detector == "unnecessary_lint_allow");
+    if let Some(file_findings) = findings_by_file.get(&current_finding.file_name) {
+        let (unnecessary_allows, other_findings): (Vec<_>, Vec<_>) = file_findings
+            .iter()
+            .partition(|f| f.detector == "unnecessary_lint_allow");
 
-            if current_finding.detector == "unnecessary_lint_allow" {
-                if let Some(allowed_lint) = &current_finding.allowed_lint {
-                    let lint_present = other_findings.iter().any(|f| {
-                        &f.detector == allowed_lint && spans_overlap(f.span, current_finding.span)
-                    });
-                    return !lint_present; // Include if the lint is not present (unnecessary allow)
-                }
-            } else {
-                let is_allowed = unnecessary_allows.iter().any(|allow| {
-                    allow
-                        .allowed_lint
-                        .as_ref()
-                        .map_or(false, |lint| lint == &current_finding.detector)
-                        && spans_overlap(allow.span, current_finding.span)
+        if current_finding.detector == "unnecessary_lint_allow" {
+            if let Some(allowed_lint) = &current_finding.allowed_lint {
+                let lint_present = other_findings.iter().any(|f| {
+                    &f.detector == allowed_lint && spans_overlap(f.span, current_finding.span)
                 });
-                return !is_allowed; // Include if the finding is not allowed
+                !lint_present // Include if the lint is not present (unnecessary allow)
+            } else {
+                true // Include if we can't determine the allowed lint
             }
+        } else {
+            !unnecessary_allows.iter().any(|allow| {
+                allow
+                    .allowed_lint
+                    .as_ref()
+                    .map_or(false, |lint| lint == &current_finding.detector)
+                    && spans_overlap(allow.span, current_finding.span)
+            }) // Include if the finding is not allowed
         }
-
-        true // If we can't find the file or process the finding, we include it by default
+    } else {
+        true // If we can't find the file, we include it by default
     }
+}

From 8bf4ef51517c065b4254132ec36714879055cfe1 Mon Sep 17 00:00:00 2001
From: Jose Garcia Crosta <jgcrosta@gmail.com>
Date: Mon, 26 Aug 2024 16:03:20 -0300
Subject: [PATCH 14/18] Add suppressing message on remediated to test it works

---
 .../remediated-example/Cargo.toml             |  1 +
 .../remediated-example/src/lib.rs             | 20 ++++++-------------
 2 files changed, 7 insertions(+), 14 deletions(-)

diff --git a/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/remediated-example/Cargo.toml b/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/remediated-example/Cargo.toml
index cb27925d..42527218 100644
--- a/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/remediated-example/Cargo.toml
+++ b/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/remediated-example/Cargo.toml
@@ -8,6 +8,7 @@ version = "0.1.0"
 crate-type = ["cdylib"]
 
 [dependencies]
+scout-utils = { version = "0.1.0" }
 soroban-sdk = { workspace = true }
 
 [dev-dependencies]
diff --git a/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/remediated-example/src/lib.rs b/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/remediated-example/src/lib.rs
index 73fba1db..0e218e20 100644
--- a/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/remediated-example/src/lib.rs
+++ b/test-cases/unnecessary-lint-allow/unnecessary-lint-allow-1/remediated-example/src/lib.rs
@@ -1,23 +1,15 @@
 #![no_std]
-
-use soroban_sdk::{contract, contracterror, contractimpl};
+use scout_utils::scout_allow;
+use soroban_sdk::{contract, contractimpl};
 
 #[contract]
 pub struct UnnecessaryLintAllow;
 
-#[contracterror]
-#[derive(Copy, Clone)]
-pub enum AssertError {
-    GreaterThan10 = 1,
-}
-
 #[contractimpl]
+#[scout_allow(assert_violation)]
 impl UnnecessaryLintAllow {
-    pub fn assert_if_greater_than_10(value: u128) -> Result<bool, AssertError> {
-        if value <= 10 {
-            Ok(true)
-        } else {
-            Err(AssertError::GreaterThan10)
-        }
+    pub fn assert_if_greater_than_10(value: u128) -> bool {
+        assert!(value <= 10);
+        true
     }
 }

From 45bb204075417c7dfa1e4ad6261cbd35d390c4a7 Mon Sep 17 00:00:00 2001
From: Jose Garcia Crosta <jgcrosta@gmail.com>
Date: Tue, 27 Aug 2024 11:24:50 -0300
Subject: [PATCH 15/18] Add loop into detector

---
 detectors/unnecessary-lint-allow/src/lib.rs   |  2 +-
 .../unnecessary-lint-allow/src/processor.rs   | 86 +++++++++++++++----
 2 files changed, 70 insertions(+), 18 deletions(-)

diff --git a/detectors/unnecessary-lint-allow/src/lib.rs b/detectors/unnecessary-lint-allow/src/lib.rs
index 41f3a82f..bbeb5952 100644
--- a/detectors/unnecessary-lint-allow/src/lib.rs
+++ b/detectors/unnecessary-lint-allow/src/lib.rs
@@ -5,7 +5,7 @@ extern crate rustc_hir;
 extern crate rustc_span;
 
 mod processor;
-pub use processor::should_include_finding;
+pub use processor::process_findings;
 
 use clippy_wrappers::span_lint_and_help;
 use if_chain::if_chain;
diff --git a/detectors/unnecessary-lint-allow/src/processor.rs b/detectors/unnecessary-lint-allow/src/processor.rs
index e1f04bf6..9da0ffc9 100644
--- a/detectors/unnecessary-lint-allow/src/processor.rs
+++ b/detectors/unnecessary-lint-allow/src/processor.rs
@@ -1,34 +1,40 @@
 use serde::de::Error;
 use serde_json::Value;
-use std::{collections::HashMap, ffi::CStr, os::raw::c_char};
+use std::{collections::HashMap, ffi::CStr, ffi::CString, os::raw::c_char};
 
-/// Determines whether a finding should be included.
+/// Process the findings to filter out unnecessary findings.
 ///
 /// # Safety
 ///
 /// This function is marked as unsafe because it deals with raw pointers.
 /// The caller is responsible for ensuring the safety of the pointers passed as arguments.
 #[no_mangle]
-pub unsafe extern "C" fn should_include_finding(
-    finding_json: *const c_char,
-    all_findings_json: *const c_char,
-) -> bool {
-    // Check for null pointers
-    if finding_json.is_null() || all_findings_json.is_null() {
-        return false;
-    }
-
-    let finding = match parse_json(finding_json) {
-        Ok(v) => v,
-        Err(_) => return false,
+pub unsafe extern "C" fn process_findings(
+    successful_findings_json: *const c_char,
+    output_json: *const c_char,
+    inside_vscode: bool,
+) -> *mut c_char {
+    let successful_findings = match parse_json(successful_findings_json) {
+        Ok(Value::Array(v)) => v,
+        _ => return std::ptr::null_mut(),
     };
 
-    let all_findings = match parse_json(all_findings_json) {
+    let output = match parse_json(output_json) {
         Ok(Value::Array(v)) => v,
-        _ => return false,
+        _ => return std::ptr::null_mut(),
     };
 
-    should_include_finding_impl(&finding, &all_findings)
+    let (console_findings, output_string_vscode) =
+        process_findings_impl(successful_findings, output, inside_vscode);
+
+    let result = serde_json::json!({
+        "console_findings": console_findings,
+        "output_string_vscode": output_string_vscode
+    });
+
+    let result_string = serde_json::to_string(&result).unwrap_or_default();
+    let c_str = CString::new(result_string).unwrap_or_default();
+    c_str.into_raw()
 }
 
 unsafe fn parse_json(input: *const c_char) -> Result<Value, serde_json::Error> {
@@ -81,6 +87,52 @@ fn parse_finding(finding: &Value) -> Option<Finding> {
     })
 }
 
+fn process_findings_impl(
+    successful_findings: Vec<Value>,
+    output: Vec<Value>,
+    inside_vscode: bool,
+) -> (Vec<Value>, String) {
+    let console_findings: Vec<_> = successful_findings
+        .iter()
+        .filter(|&finding| should_include_finding_impl(finding, &successful_findings))
+        .cloned()
+        .collect();
+
+    let output_vscode: Vec<_> = if inside_vscode {
+        let all_findings: Vec<_> = output
+            .iter()
+            .filter_map(|val| val.get("message").cloned())
+            .collect();
+
+        output
+            .into_iter()
+            .filter(|val| {
+                val.get("message")
+                    .map(|message| should_include_finding_impl(message, &all_findings))
+                    .unwrap_or(true)
+            })
+            .collect()
+    } else {
+        Vec::new()
+    };
+
+    let output_string_vscode = output_vscode
+        .into_iter()
+        .filter_map(|finding| serde_json::to_string(&finding).ok())
+        .collect::<Vec<_>>()
+        .join("\n");
+
+    (console_findings, output_string_vscode)
+}
+
+// Add this function to free the memory allocated by process_findings
+#[no_mangle]
+pub unsafe extern "C" fn free_string(ptr: *mut c_char) {
+    if !ptr.is_null() {
+        let _ = CString::from_raw(ptr);
+    }
+}
+
 pub fn should_include_finding_impl(finding: &Value, all_findings: &[Value]) -> bool {
     let current_finding = match parse_finding(finding) {
         Some(f) => f,

From 6b25c711a7408fc8fca973bc80ba8fb68b7e5fe9 Mon Sep 17 00:00:00 2001
From: Jose Garcia Crosta <jgcrosta@gmail.com>
Date: Tue, 27 Aug 2024 11:30:53 -0300
Subject: [PATCH 16/18] Organize code and cache some stuff

---
 .../unnecessary-lint-allow/src/processor.rs   | 190 ++++++++++--------
 1 file changed, 107 insertions(+), 83 deletions(-)

diff --git a/detectors/unnecessary-lint-allow/src/processor.rs b/detectors/unnecessary-lint-allow/src/processor.rs
index 9da0ffc9..c1a3e714 100644
--- a/detectors/unnecessary-lint-allow/src/processor.rs
+++ b/detectors/unnecessary-lint-allow/src/processor.rs
@@ -2,49 +2,6 @@ use serde::de::Error;
 use serde_json::Value;
 use std::{collections::HashMap, ffi::CStr, ffi::CString, os::raw::c_char};
 
-/// Process the findings to filter out unnecessary findings.
-///
-/// # Safety
-///
-/// This function is marked as unsafe because it deals with raw pointers.
-/// The caller is responsible for ensuring the safety of the pointers passed as arguments.
-#[no_mangle]
-pub unsafe extern "C" fn process_findings(
-    successful_findings_json: *const c_char,
-    output_json: *const c_char,
-    inside_vscode: bool,
-) -> *mut c_char {
-    let successful_findings = match parse_json(successful_findings_json) {
-        Ok(Value::Array(v)) => v,
-        _ => return std::ptr::null_mut(),
-    };
-
-    let output = match parse_json(output_json) {
-        Ok(Value::Array(v)) => v,
-        _ => return std::ptr::null_mut(),
-    };
-
-    let (console_findings, output_string_vscode) =
-        process_findings_impl(successful_findings, output, inside_vscode);
-
-    let result = serde_json::json!({
-        "console_findings": console_findings,
-        "output_string_vscode": output_string_vscode
-    });
-
-    let result_string = serde_json::to_string(&result).unwrap_or_default();
-    let c_str = CString::new(result_string).unwrap_or_default();
-    c_str.into_raw()
-}
-
-unsafe fn parse_json(input: *const c_char) -> Result<Value, serde_json::Error> {
-    let c_str = CStr::from_ptr(input);
-    let input_str = c_str
-        .to_str()
-        .map_err(|_| serde_json::Error::custom("Invalid UTF-8"))?;
-    serde_json::from_str(input_str)
-}
-
 #[derive(Debug, Clone)]
 struct Finding {
     detector: String,
@@ -53,8 +10,43 @@ struct Finding {
     allowed_lint: Option<String>,
 }
 
-fn spans_overlap(span1: (usize, usize), span2: (usize, usize)) -> bool {
-    span1.0 <= span2.1 && span2.0 <= span1.1
+struct FileFindings {
+    unnecessary_allows: Vec<Finding>,
+    other_findings: Vec<Finding>,
+}
+
+struct FindingsCache {
+    by_file: HashMap<String, FileFindings>,
+}
+
+impl FindingsCache {
+    fn new(all_findings: &[Value]) -> Self {
+        let mut by_file: HashMap<String, FileFindings> = HashMap::new();
+
+        for finding in all_findings {
+            if let Some(parsed) = parse_finding(finding) {
+                by_file
+                    .entry(parsed.file_name.clone())
+                    .or_insert_with(|| FileFindings {
+                        unnecessary_allows: Vec::new(),
+                        other_findings: Vec::new(),
+                    })
+                    .add_finding(parsed);
+            }
+        }
+
+        FindingsCache { by_file }
+    }
+}
+
+impl FileFindings {
+    fn add_finding(&mut self, finding: Finding) {
+        if finding.detector == "unnecessary_lint_allow" {
+            self.unnecessary_allows.push(finding);
+        } else {
+            self.other_findings.push(finding);
+        }
+    }
 }
 
 fn parse_finding(finding: &Value) -> Option<Finding> {
@@ -87,28 +79,28 @@ fn parse_finding(finding: &Value) -> Option<Finding> {
     })
 }
 
+fn spans_overlap(span1: (usize, usize), span2: (usize, usize)) -> bool {
+    span1.0 <= span2.1 && span2.0 <= span1.1
+}
+
 fn process_findings_impl(
     successful_findings: Vec<Value>,
     output: Vec<Value>,
     inside_vscode: bool,
 ) -> (Vec<Value>, String) {
+    let findings_cache = FindingsCache::new(&successful_findings);
+
     let console_findings: Vec<_> = successful_findings
-        .iter()
-        .filter(|&finding| should_include_finding_impl(finding, &successful_findings))
-        .cloned()
+        .into_iter()
+        .filter(|finding| should_include_finding_impl(finding, &findings_cache))
         .collect();
 
     let output_vscode: Vec<_> = if inside_vscode {
-        let all_findings: Vec<_> = output
-            .iter()
-            .filter_map(|val| val.get("message").cloned())
-            .collect();
-
         output
             .into_iter()
             .filter(|val| {
                 val.get("message")
-                    .map(|message| should_include_finding_impl(message, &all_findings))
+                    .map(|message| should_include_finding_impl(message, &findings_cache))
                     .unwrap_or(true)
             })
             .collect()
@@ -125,54 +117,86 @@ fn process_findings_impl(
     (console_findings, output_string_vscode)
 }
 
-// Add this function to free the memory allocated by process_findings
-#[no_mangle]
-pub unsafe extern "C" fn free_string(ptr: *mut c_char) {
-    if !ptr.is_null() {
-        let _ = CString::from_raw(ptr);
-    }
-}
-
-pub fn should_include_finding_impl(finding: &Value, all_findings: &[Value]) -> bool {
+fn should_include_finding_impl(finding: &Value, cache: &FindingsCache) -> bool {
     let current_finding = match parse_finding(finding) {
         Some(f) => f,
         None => return false, // If we can't parse the finding, we don't include it
     };
 
-    let mut findings_by_file: HashMap<String, Vec<Finding>> = HashMap::new();
-    for f in all_findings {
-        if let Some(parsed) = parse_finding(f) {
-            findings_by_file
-                .entry(parsed.file_name.clone())
-                .or_default()
-                .push(parsed);
-        }
-    }
-
-    if let Some(file_findings) = findings_by_file.get(&current_finding.file_name) {
-        let (unnecessary_allows, other_findings): (Vec<_>, Vec<_>) = file_findings
-            .iter()
-            .partition(|f| f.detector == "unnecessary_lint_allow");
-
+    if let Some(file_findings) = cache.by_file.get(&current_finding.file_name) {
         if current_finding.detector == "unnecessary_lint_allow" {
             if let Some(allowed_lint) = &current_finding.allowed_lint {
-                let lint_present = other_findings.iter().any(|f| {
+                !file_findings.other_findings.iter().any(|f| {
                     &f.detector == allowed_lint && spans_overlap(f.span, current_finding.span)
-                });
-                !lint_present // Include if the lint is not present (unnecessary allow)
+                })
             } else {
                 true // Include if we can't determine the allowed lint
             }
         } else {
-            !unnecessary_allows.iter().any(|allow| {
+            !file_findings.unnecessary_allows.iter().any(|allow| {
                 allow
                     .allowed_lint
                     .as_ref()
                     .map_or(false, |lint| lint == &current_finding.detector)
                     && spans_overlap(allow.span, current_finding.span)
-            }) // Include if the finding is not allowed
+            })
         }
     } else {
         true // If we can't find the file, we include it by default
     }
 }
+
+/// Process the findings to filter out unnecessary findings.
+///
+/// # Safety
+///
+/// This function is marked as unsafe because it deals with raw pointers.
+/// The caller is responsible for ensuring the safety of the pointers passed as arguments.
+#[no_mangle]
+pub unsafe extern "C" fn process_findings(
+    successful_findings_json: *const c_char,
+    output_json: *const c_char,
+    inside_vscode: bool,
+) -> *mut c_char {
+    let successful_findings = match parse_json(successful_findings_json) {
+        Ok(Value::Array(v)) => v,
+        _ => return std::ptr::null_mut(),
+    };
+
+    let output = match parse_json(output_json) {
+        Ok(Value::Array(v)) => v,
+        _ => return std::ptr::null_mut(),
+    };
+
+    let (console_findings, output_string_vscode) =
+        process_findings_impl(successful_findings, output, inside_vscode);
+
+    let result = serde_json::json!({
+        "console_findings": console_findings,
+        "output_string_vscode": output_string_vscode
+    });
+
+    match serde_json::to_string(&result) {
+        Ok(result_string) => match CString::new(result_string) {
+            Ok(c_str) => c_str.into_raw(),
+            Err(_) => std::ptr::null_mut(),
+        },
+        Err(_) => std::ptr::null_mut(),
+    }
+}
+
+unsafe fn parse_json(input: *const c_char) -> Result<Value, serde_json::Error> {
+    let c_str = CStr::from_ptr(input);
+    let input_str = c_str
+        .to_str()
+        .map_err(|_| serde_json::Error::custom("Invalid UTF-8"))?;
+    serde_json::from_str(input_str)
+}
+
+// Add this function to free the memory allocated by process_findings
+#[no_mangle]
+pub unsafe extern "C" fn free_string(ptr: *mut c_char) {
+    if !ptr.is_null() {
+        let _ = CString::from_raw(ptr);
+    }
+}

From 0d06d3ec06dbf9fff7aaa58fb6e523abb6b27ea1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20Garc=C3=ADa=20Crosta?= <jgcrosta@gmail.com>
Date: Tue, 27 Aug 2024 11:35:40 -0300
Subject: [PATCH 17/18] Update template Cargo.toml

---
 templates/test-case/Cargo.toml | 1 -
 1 file changed, 1 deletion(-)

diff --git a/templates/test-case/Cargo.toml b/templates/test-case/Cargo.toml
index 9a687e66..5f4afc5d 100644
--- a/templates/test-case/Cargo.toml
+++ b/templates/test-case/Cargo.toml
@@ -27,5 +27,4 @@ strip = "symbols"
 
 [profile.release-with-logs]
 debug-assertions = true
-
 inherits = "release"

From 778806c6dcc6a84dc85db25011b68eb5eafae56a Mon Sep 17 00:00:00 2001
From: Jose Garcia Crosta <jgcrosta@gmail.com>
Date: Tue, 27 Aug 2024 11:54:49 -0300
Subject: [PATCH 18/18] Update comments

---
 detectors/unnecessary-lint-allow/src/processor.rs | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/detectors/unnecessary-lint-allow/src/processor.rs b/detectors/unnecessary-lint-allow/src/processor.rs
index c1a3e714..dfc1b3af 100644
--- a/detectors/unnecessary-lint-allow/src/processor.rs
+++ b/detectors/unnecessary-lint-allow/src/processor.rs
@@ -67,7 +67,6 @@ fn parse_finding(finding: &Value) -> Option<Finding> {
         None
     };
 
-    // Check for potential integer overflow when converting from u64 to usize
     let start = usize::try_from(line_start).ok()?;
     let end = usize::try_from(line_end).ok()?;
 
@@ -193,7 +192,7 @@ unsafe fn parse_json(input: *const c_char) -> Result<Value, serde_json::Error> {
     serde_json::from_str(input_str)
 }
 
-// Add this function to free the memory allocated by process_findings
+// Free the string allocated by the `process_findings` function. Should be called after processing everything
 #[no_mangle]
 pub unsafe extern "C" fn free_string(ptr: *mut c_char) {
     if !ptr.is_null() {