Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix token-interface-inference detector #383

Merged
merged 4 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ target/
*.pdb

# Ignore Cargo.lock of library files
test-cases/**/Cargo.lock
detectors/**/Cargo.lock
scout-audit-internal/Cargo.lock

Expand Down
5 changes: 2 additions & 3 deletions detectors/token-interface-inference/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@ version = "0.1.0"
crate-type = ["cdylib"]

[dependencies]
clippy_utils = { workspace = true }
clippy_wrappers = { workspace = true }
dylint_linting = { workspace = true }
if_chain = { workspace = true }
utils = { workspace = true }
edit-distance = "=2.1.2"
if_chain = { workspace = true }

[package.metadata.rust-analyzer]
rustc_private = true
63 changes: 23 additions & 40 deletions detectors/token-interface-inference/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,31 @@

extern crate rustc_errors;
extern crate rustc_hir;
extern crate rustc_middle;
extern crate rustc_span;

use clippy_utils::diagnostics::span_lint_and_help;
use clippy_wrappers::span_lint;
use edit_distance::edit_distance;
use rustc_hir::{intravisit::Visitor, Node};
use rustc_lint::{LateContext, LateLintPass};

use if_chain::if_chain;
use rustc_errors::MultiSpan;
use rustc_span::Span;

use std::collections::HashSet;
use std::vec;
use rustc_hir::{intravisit::FnKind, Body, FnDecl, Item, ItemKind, Node};
use rustc_lint::{LateContext, LateLintPass};
use rustc_span::{def_id::DefId, def_id::LocalDefId, Span};
use std::{
collections::HashMap,
collections::HashSet,
ops::{Div, Mul},
vec,
};
use utils::FunctionCallVisitor;

use rustc_span::def_id::DefId;

const LINT_MESSAGE: &str =
"This contract seems like a Token, consider implementing the Token Interface trait";
const CANONICAL_FUNCTIONS_AMOUNT: u16 = 10;
const INCLUDED_FUNCTIONS_THRESHOLD: u16 = 60;
const TOKEN_INTERFACE_PATH: &str = "soroban_sdk::token::TokenInterface";

dylint_linting::impl_late_lint! {
pub TOKEN_INTERFACE_INFERENCE,
Warn,
"",
LINT_MESSAGE,
TokenInterfaceInference::default(),
{
name: "Token Interface Implementation Analyzer",
Expand All @@ -44,24 +39,21 @@ dylint_linting::impl_late_lint! {

#[derive(Default)]
struct TokenInterfaceInference {
function_call_graph: HashMap<DefId, HashSet<DefId>>,
checked_functions: HashSet<String>,
canonical_funcs_def_id: HashSet<DefId>,
impl_token_interface_trait: bool,
detected_canonical_functions_count: u16,
funcs_spans: Vec<Span>,
}

impl<'tcx> LateLintPass<'tcx> for TokenInterfaceInference {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx rustc_hir::Item<'tcx>) {
if let rustc_hir::ItemKind::Impl(impl_block) = item.kind {
if let Some(trait_ref) = impl_block.of_trait {
let trait_def_id = trait_ref.path.res.def_id();
let trait_name = cx.tcx.def_path_str(trait_def_id);

if trait_name == "soroban_sdk::token::TokenInterface" {
self.impl_token_interface_trait = true;
}
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
if_chain! {
if let ItemKind::Impl(impl_block) = item.kind;
if let Some(trait_ref) = impl_block.of_trait;
if let Some(trait_def_id) = trait_ref.path.res.opt_def_id();
if cx.tcx.def_path_str(trait_def_id) == TOKEN_INTERFACE_PATH;
then {
self.impl_token_interface_trait = true;
}
}
}
Expand All @@ -78,35 +70,30 @@ impl<'tcx> LateLintPass<'tcx> for TokenInterfaceInference {
.div(CANONICAL_FUNCTIONS_AMOUNT)
>= INCLUDED_FUNCTIONS_THRESHOLD
{
span_lint_and_help(
span_lint(
cx,
TOKEN_INTERFACE_INFERENCE,
MultiSpan::from_spans(self.funcs_spans.clone()),
LINT_MESSAGE,
None,
"",
);
}
}

fn check_fn(
&mut self,
cx: &LateContext<'tcx>,
_: rustc_hir::intravisit::FnKind<'tcx>,
_fn_decl: &'tcx rustc_hir::FnDecl<'tcx>,
body: &'tcx rustc_hir::Body<'tcx>,
_: FnKind<'tcx>,
_: &'tcx FnDecl<'tcx>,
_: &'tcx Body<'tcx>,
span: Span,
local_def_id: rustc_span::def_id::LocalDefId,
local_def_id: LocalDefId,
) {
let def_id = local_def_id.to_def_id();
self.checked_functions.insert(cx.tcx.def_path_str(def_id));

if span.from_expansion() {
return;
}

let def_id = local_def_id.to_def_id();
let fn_name = cx.tcx.def_path_str(def_id);

let fn_name_span = if let Some(node) = cx.tcx.hir().get_if_local(def_id) {
match node {
Node::Item(item) => Some(item.ident.span),
Expand All @@ -117,10 +104,6 @@ impl<'tcx> LateLintPass<'tcx> for TokenInterfaceInference {
None
};

let mut function_call_visitor =
FunctionCallVisitor::new(cx, def_id, &mut self.function_call_graph);
function_call_visitor.visit_body(body);

// If the function is part of the token interface, I store its defid.
if verify_token_interface_function_similarity(fn_name.clone()) {
self.detected_canonical_functions_count += 1;
Expand Down
Loading
Loading