-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
266 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
[package] | ||
name = "test-hashmap" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[lib] | ||
crate-type = ["cdylib"] | ||
|
||
[dependencies] | ||
dylint_linting = { workspace = true } | ||
semver = "1.0.4" | ||
serde_json = "1.0" | ||
ureq = { version = "2.7.1", features = ["json"] } | ||
|
||
scout-audit-internal = { workspace = true } | ||
|
||
[dev-dependencies] | ||
dylint_testing = { workspace = true } | ||
|
||
[package.metadata.rust-analyzer] | ||
rustc_private = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
#![feature(rustc_private)] | ||
|
||
extern crate rustc_ast; | ||
extern crate rustc_data_structures; | ||
extern crate rustc_hir; | ||
extern crate rustc_lint; | ||
extern crate rustc_session; | ||
extern crate rustc_span; | ||
|
||
use std::collections::HashMap; | ||
|
||
use rustc_ast::{ | ||
visit::walk_expr as walk_ast_expr, visit::walk_item, visit::Visitor as AstVisitor, Item, | ||
ItemKind, | ||
}; | ||
use rustc_hir::{ | ||
def_id::LocalDefId, | ||
intravisit::{walk_body, walk_expr, FnKind, Visitor}, | ||
Body, Expr, ExprKind, FnDecl, ImplItemKind, QPath, | ||
}; | ||
use rustc_lint::{EarlyLintPass, LateContext, LateLintPass}; | ||
use rustc_session::{declare_lint, impl_lint_pass}; | ||
use rustc_span::Span; | ||
|
||
dylint_linting::dylint_library!(); | ||
|
||
declare_lint! { | ||
pub TEST_HASMAP, | ||
Warn, | ||
"Warns about the use of the `hasmap` function" | ||
} | ||
|
||
pub struct TestHasmap; | ||
|
||
pub struct TestHasmapEarly; | ||
|
||
impl_lint_pass!(TestHasmap => [TEST_HASMAP]); | ||
impl_lint_pass!(TestHasmapEarly => [TEST_HASMAP]); | ||
|
||
pub struct TestHasmapFinder<'tcx, 'tcx_ref> { | ||
// A map to track which functions call which other functions | ||
cx: &'tcx_ref LateContext<'tcx>, | ||
caller: String, | ||
call_graph: HashMap<String, Vec<String>>, | ||
} | ||
|
||
#[allow(clippy::no_mangle_with_rust_abi)] | ||
#[no_mangle] | ||
pub fn register_lints(sess: &rustc_session::Session, lint_store: &mut rustc_lint::LintStore) { | ||
// smoelius: Please keep the following `register_lints` calls sorted by crate name. | ||
lint_store.register_late_pass(|_| Box::new(TestHasmap)); | ||
lint_store.register_early_pass(|| Box::new(TestHasmapEarly)); | ||
} | ||
|
||
impl<'tcx> Visitor<'tcx> for TestHasmapFinder<'tcx, '_> { | ||
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { | ||
if let ExprKind::Call(mexpr, _) = &expr.kind { | ||
if let ExprKind::Path(path) = &mexpr.kind { | ||
if let QPath::TypeRelative(_, _) = &path { | ||
let def = self.cx.qpath_res(path, mexpr.hir_id); | ||
if let Some(def_id) = def.opt_def_id() { | ||
let func_name = self.cx.tcx.def_path_str(def_id); | ||
self.add_call_if_not_contains(self.caller.to_string(), func_name); | ||
} | ||
} | ||
} | ||
} | ||
|
||
walk_expr(self, expr); | ||
} | ||
} | ||
|
||
impl<'tcx> LateLintPass<'tcx> for TestHasmap { | ||
fn check_fn( | ||
&mut self, | ||
cx: &LateContext<'tcx>, | ||
fn_kind: FnKind<'tcx>, | ||
_: &'tcx FnDecl<'tcx>, | ||
body: &'tcx Body<'tcx>, | ||
_: Span, | ||
def_id: LocalDefId, | ||
) { | ||
// Return if the function is a method or comes from a macro | ||
if !matches!(fn_kind, FnKind::Method(_, fnsig) if !fnsig.span.from_expansion()) { | ||
return; | ||
} | ||
|
||
let fn_name = cx.tcx.def_path_str(def_id); | ||
let mut visitor = TestHasmapFinder { | ||
cx, | ||
caller: fn_name.clone(), | ||
call_graph: HashMap::new(), | ||
}; | ||
|
||
// Initialize the function entry in the call graph | ||
visitor.call_graph.entry(fn_name.clone()).or_default(); | ||
|
||
walk_body(&mut visitor, body); | ||
|
||
println!("Function: {:?}", visitor.call_graph); | ||
} | ||
} | ||
|
||
impl<'tcx> TestHasmapFinder<'tcx, '_> { | ||
fn add_call_if_not_contains(&mut self, caller: String, callee: String) { | ||
if let Some(calls) = self.call_graph.get_mut(&caller) { | ||
if !calls.contains(&callee.to_string()) { | ||
calls.push(callee.to_string()); | ||
} | ||
} | ||
} | ||
} | ||
|
||
impl EarlyLintPass for TestHasmapEarly { | ||
fn check_expr(&mut self, _: &rustc_lint::EarlyContext<'_>, ex: &rustc_ast::Expr) { | ||
walk_ast_expr(self, ex); | ||
} | ||
} | ||
|
||
impl<'tcx> AstVisitor<'tcx> for TestHasmapEarly { | ||
fn visit_expr(&mut self, ex: &'tcx rustc_ast::Expr) { | ||
println!("Expr: {:?}", ex); | ||
walk_ast_expr(self, ex); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
[package] | ||
name = "unused-return-enum" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[lib] | ||
crate-type = ["cdylib"] | ||
[dependencies] | ||
dylint_linting = { workspace = true } | ||
if_chain = { workspace = true } | ||
scout-audit-internal = { workspace = true } | ||
[dev-dependencies] | ||
dylint_testing = { workspace = true } | ||
|
||
[package.metadata.rust-analyzer] | ||
rustc_private = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
# Unused return enum | ||
|
||
### What it does | ||
|
||
It warns if a function that returns a Result type does not return the Result enum variant (Ok/Err). | ||
|
||
### Why is this bad? | ||
|
||
If any of the variants (Ok/Err) is not used, the code could be simplified or it could imply a bug. | ||
|
||
### Known problems | ||
|
||
If definitions of `Err()` and/or `Ok()` are in the code but do not flow to the return value due to the definition of a variable or because they are defined in a dead code block, the warning will not be shown. If the definitions are made in an auxiliary method, the warning will be shown, resulting in a false positive. | ||
|
||
### Example | ||
|
||
Instead of using: | ||
|
||
```rust | ||
#![no_std] | ||
|
||
use soroban_sdk::{contract, contracterror, contractimpl}; | ||
|
||
#[contract] | ||
pub struct UnusedReturnEnum; | ||
|
||
#[contracterror] | ||
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] | ||
#[repr(u32)] | ||
pub enum Error { | ||
/// An overflow was produced. | ||
Overflow = 1, | ||
} | ||
|
||
#[contractimpl] | ||
impl UnusedReturnEnum { | ||
/// Returns the percentage difference between two values. | ||
pub fn get_percentage_difference(balance1: u128, balance2: u128) -> Result<u128, Error> { | ||
let absolute_difference = balance1.abs_diff(balance2); | ||
let sum = balance1 + balance2; | ||
|
||
match 100u128.checked_mul(absolute_difference / sum) { | ||
Some(result) => result, | ||
None => panic!("Overflow"), | ||
}; | ||
|
||
Err(Error::Overflow) | ||
} | ||
} | ||
``` | ||
|
||
Use this: | ||
|
||
```rust | ||
#![no_std] | ||
|
||
use soroban_sdk::{contract, contracterror, contractimpl, testutils::arbitrary::arbitrary::Result}; | ||
|
||
#[contract] | ||
pub struct UnusedReturnEnum; | ||
|
||
#[contracterror] | ||
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] | ||
#[repr(u32)] | ||
pub enum Error { | ||
/// An overflow was produced. | ||
Overflow = 1, | ||
} | ||
|
||
#[contractimpl] | ||
impl UnusedReturnEnum { | ||
/// Returns the percentage difference between two values. | ||
pub fn get_percentage_difference(balance1: u128, balance2: u128) -> Result<u128, Error> { | ||
let absolute_difference = balance1.abs_diff(balance2); | ||
let sum = balance1 + balance2; | ||
|
||
match 100u128.checked_mul(absolute_difference / sum) { | ||
Some(result) => Ok(result), | ||
None => Err(Error::Overflow), | ||
} | ||
} | ||
} | ||
``` | ||
|
||
### Implementation | ||
|
||
The detector's implementation can be found at [this link](https://github.com/CoinFabrik/scout-soroban/tree/main/detectors/unused-return-enum). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters