Skip to content

Commit

Permalink
feat(call_graph): Emit warnings when encountering foreign calls
Browse files Browse the repository at this point in the history
  • Loading branch information
zalanlevai committed Feb 26, 2025
1 parent 8552c02 commit a363771
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 1 deletion.
3 changes: 2 additions & 1 deletion mutest-driver/src/passes/analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -596,10 +596,11 @@ pub fn run(config: &mut Config) -> CompilerResult<Option<AnalysisPassResult>> {

if call_graph.virtual_calls_count >= 1 || call_graph.dynamic_calls_count >= 1 {
let total_calls_count = call_graph.total_calls_count();
println!("could not resolve {unresolved_pct:.2}% of function calls ({virtual} virtual, {dynamic} dynamic out of {total} function calls)",
println!("could not resolve {unresolved_pct:.2}% of function calls ({virtual} virtual, {dynamic} dynamic, {foreign} foreign out of {total} function calls)",
unresolved_pct = (call_graph.virtual_calls_count + call_graph.dynamic_calls_count) as f64 / total_calls_count as f64 * 100_f64,
virtual = call_graph.virtual_calls_count,
dynamic = call_graph.dynamic_calls_count,
foreign = call_graph.foreign_calls_count,
total = total_calls_count,
);
}
Expand Down
43 changes: 43 additions & 0 deletions mutest-emit/src/analysis/call_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::iter;

use rustc_hash::{FxHashSet, FxHashMap};
use rustc_middle::mir;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;

use crate::analysis::ast_lowering;
use crate::analysis::hir;
Expand Down Expand Up @@ -216,6 +217,7 @@ impl<'tcx> Callee<'tcx> {
pub struct CallGraph<'tcx> {
pub virtual_calls_count: usize,
pub dynamic_calls_count: usize,
pub foreign_calls_count: usize,
pub root_calls: FxHashSet<(hir::LocalDefId, Callee<'tcx>)>,
pub nested_calls: Vec<FxHashSet<(Callee<'tcx>, Callee<'tcx>)>>,
}
Expand Down Expand Up @@ -247,6 +249,7 @@ pub fn reachable_fns<'ast, 'tcx, 'tst>(
let mut call_graph = CallGraph {
virtual_calls_count: 0,
dynamic_calls_count: 0,
foreign_calls_count: 0,
root_calls: Default::default(),
nested_calls: iter::repeat_with(|| Default::default()).take(depth - 1).collect(),
};
Expand Down Expand Up @@ -304,6 +307,26 @@ pub fn reachable_fns<'ast, 'tcx, 'tst>(
diagnostic.emit();
}

if tcx.is_foreign_item(instance.def_id()) && !tcx.intrinsic(instance.def_id()).is_some() {
let codegen_fn_attrs = tcx.codegen_fn_attrs(instance.def_id());
let is_allocator_intrinsic = codegen_fn_attrs.flags.intersects(
CodegenFnAttrFlags::ALLOCATOR
| CodegenFnAttrFlags::DEALLOCATOR
| CodegenFnAttrFlags::REALLOCATOR
| CodegenFnAttrFlags::ALLOCATOR_ZEROED
);

if !is_allocator_intrinsic {
call_graph.foreign_calls_count += 1;

let mut diagnostic = tcx.dcx().struct_warn("encountered foreign call during call graph construction");
diagnostic.span(call.span);
diagnostic.span_label(call.span, format!("call to {}", tcx.def_path_str_with_args(instance.def_id(), instance.args)));
diagnostic.note(format!("in {}", tcx.def_path_str(test.def_id)));
diagnostic.emit();
}
}

Callee::new(instance.def_id(), instance.args)
}

Expand Down Expand Up @@ -408,6 +431,26 @@ pub fn reachable_fns<'ast, 'tcx, 'tst>(
diagnostic.emit();
}

if tcx.is_foreign_item(instance.def_id()) && !tcx.intrinsic(instance.def_id()).is_some() {
let codegen_fn_attrs = tcx.codegen_fn_attrs(instance.def_id());
let is_allocator_intrinsic = codegen_fn_attrs.flags.intersects(
CodegenFnAttrFlags::ALLOCATOR
| CodegenFnAttrFlags::DEALLOCATOR
| CodegenFnAttrFlags::REALLOCATOR
| CodegenFnAttrFlags::ALLOCATOR_ZEROED
);

if !is_allocator_intrinsic {
call_graph.foreign_calls_count += 1;

let mut diagnostic = tcx.dcx().struct_warn("encountered foreign call during call graph construction");
diagnostic.span(call.span);
diagnostic.span_label(call.span, format!("call to {}", tcx.def_path_str_with_args(instance.def_id(), instance.args)));
diagnostic.note(format!("in {}", tcx.def_path_str_with_args(caller.def_id, caller.generic_args)));
diagnostic.emit();
}
}

Callee::new(instance.def_id(), instance.args)
}

Expand Down
13 changes: 13 additions & 0 deletions tests/ui/call_graph/ignore_allocator_intrinsics_calls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//@ print-call-graph
//@ stderr: empty

use std::alloc::Layout;

fn make_allocator_intrinsics_call() {
let _ = unsafe { std::alloc::alloc(Layout::from_size_align_unchecked(8, 8)) };
}

#[test]
fn test() {
make_allocator_intrinsics_call();
}
14 changes: 14 additions & 0 deletions tests/ui/call_graph/ignore_intrinsics_calls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//@ print-call-graph
//@ stderr: empty

#![allow(internal_features)]
#![feature(core_intrinsics)]

fn make_intrinsics_call() {
let _ = core::intrinsics::caller_location();
}

#[test]
fn test() {
make_intrinsics_call();
}
20 changes: 20 additions & 0 deletions tests/ui/call_graph/warn_on_foreign_calls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//@ print-call-graph
//@ print-targets
//@ stdout
//@ stderr

extern "C" {
fn foreign();
}

unsafe extern "C" fn not_foreign() {}

fn make_extern_calls() {
unsafe { foreign() };
unsafe { not_foreign() };
}

#[test]
fn test() {
make_extern_calls();
}
10 changes: 10 additions & 0 deletions tests/ui/call_graph/warn_on_foreign_calls.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
warning: encountered foreign call during call graph construction
--> tests/ui/call_graph/warn_on_foreign_calls.rs:13:14
|
13 | unsafe { foreign() };
| ^^^^^^^^^ call to foreign
|
= note: in make_extern_calls

warning: 1 warning emitted

26 changes: 26 additions & 0 deletions tests/ui/call_graph/warn_on_foreign_calls.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

@@@ call graph @@@

entry points:

test test
-> make_extern_calls at tests/ui/call_graph/warn_on_foreign_calls.rs:12:1: 12:23 (#0)

nested calls at distance 1:

make_extern_calls at tests/ui/call_graph/warn_on_foreign_calls.rs:12:1: 12:23 (#0)
-> foreign at tests/ui/call_graph/warn_on_foreign_calls.rs:7:5: 7:17 (#0)
-> not_foreign at tests/ui/call_graph/warn_on_foreign_calls.rs:10:1: 10:35 (#0)

nested calls at distance 2:


@@@ targets @@@

tests -(1)-> [unsafe] not_foreign at tests/ui/call_graph/warn_on_foreign_calls.rs:10:1: 10:35 (#0)
(1) [tainted] test

tests -(0)-> [unsafe] make_extern_calls at tests/ui/call_graph/warn_on_foreign_calls.rs:12:1: 12:23 (#0)
(0) test

targets: 2 total; 0 safe; 2 unsafe (0 tainted)

0 comments on commit a363771

Please sign in to comment.