use super::statistic::Statistics;
use crate::{rap_debug, rap_info, rap_trace};
use rustc_hir::{
BodyId, BodyOwnerKind, FnDecl,
def_id::{DefId, LocalDefId},
intravisit::{FnKind, Visitor, walk_block, walk_fn},
};
use rustc_middle::{
hir::nested_filter,
ty::{self, FnSig, ParamEnv, Ty, TyCtxt, TyKind},
};
use rustc_span::Span;
use std::io::Write;
pub struct FnVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
stats: Statistics<'tcx>,
}
fn is_api_public(fn_def_id: impl Into<DefId>, tcx: TyCtxt<'_>) -> bool {
let fn_def_id: DefId = fn_def_id.into();
let local_id = fn_def_id.expect_local();
rap_trace!(
"vis: {:?} (path: {}) => {:?}",
fn_def_id,
tcx.def_path_str(fn_def_id),
tcx.effective_visibilities(()).effective_vis(local_id)
);
tcx.effective_visibilities(()).is_directly_public(local_id)
}
impl<'tcx> FnVisitor<'tcx> {
pub fn new(tcx: TyCtxt<'tcx>) -> FnVisitor<'tcx> {
FnVisitor {
tcx,
stats: Statistics::default(),
}
}
pub fn statistic(self) -> Statistics<'tcx> {
self.stats
}
fn work_at_fn<'v>(
&mut self,
fk: FnKind<'v>,
fd: &'v FnDecl<'v>,
b: BodyId,
span: Span,
id: LocalDefId,
) {
let fn_did = id.to_def_id();
if !is_api_public(fn_did, self.tcx) {
return;
}
rap_debug!("API path: {}", self.tcx.def_path_str(fn_did));
rap_debug!("type: {}", self.tcx.type_of(fn_did).instantiate_identity());
let is_generic = self
.tcx
.generics_of(fn_did)
.requires_monomorphization(self.tcx);
let fn_sig = self.tcx.fn_sig(fn_did);
rap_debug!("fn_sig: {}", fn_sig.instantiate_identity());
for input in fn_sig.instantiate_identity().inputs_and_output().iter() {
rap_debug!("param: {:?}", input);
let input_ty = input.skip_binder();
if let TyKind::Ref(r, ty, _) = input.skip_binder().kind() {
rap_debug!("region kind: {:?} {:?}", r.type_flags(), r.kind());
match r.kind() {
ty::ReEarlyParam(re) => {
rap_debug!("ReEarlyParam: {:?}", re);
}
ty::ReBound(idx, bound) => {
rap_debug!("ReBound: {:?} {:?}", idx, bound);
}
_ => {}
}
}
}
rap_debug!("type(debug): {:?}", self.tcx.type_of(fn_did));
rap_debug!("fn_sig(debug): {:?}", fn_sig);
let late_fn_sig = self
.tcx
.liberate_late_bound_regions(fn_did, fn_sig.instantiate_identity());
rap_debug!("late_fn_sig: {:?}", late_fn_sig);
if is_generic {
self.stats.pub_generic_api.insert(fn_did);
} else {
self.stats.pub_non_generic_api.insert(fn_did);
}
if fk.header().map_or(false, |header| header.is_unsafe()) {
self.stats.pub_unsafe_api.insert(fn_did);
}
}
}
impl<'tcx> Visitor<'tcx> for FnVisitor<'tcx> {
type NestedFilter = nested_filter::OnlyBodies;
fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
self.tcx
}
fn visit_fn(
&mut self,
fk: FnKind<'tcx>,
fd: &'tcx FnDecl<'tcx>,
b: BodyId,
span: Span,
id: LocalDefId,
) -> Self::Result {
self.work_at_fn(fk, fd, b, span, id);
walk_fn(self, fk, fd, b, id);
}
fn visit_block(&mut self, b: &'tcx rustc_hir::Block<'tcx>) -> Self::Result {
let r = b.rules;
if matches!(r, rustc_hir::BlockCheckMode::UnsafeBlock(_)) {
self.stats.unsafe_block.push(*b)
}
walk_block(self, b);
}
}