diff --git a/src/analysis.rs b/src/analysis.rs index e1163f3..551097e 100644 --- a/src/analysis.rs +++ b/src/analysis.rs @@ -1,178 +1,161 @@ -use std::{ - collections::{HashMap, HashSet}, - path::Path, -}; +use std::{collections::HashSet, path::Path}; use etrace::some_or; -use rustc_hir::{ - def::Res, intravisit, intravisit::Visitor, Expr, ExprKind, ItemKind, Node, QPath, Ty, TyKind, -}; +use relational::{AbsMem, AccPath, Obj}; +use rustc_abi::FieldIdx; +use rustc_hir::ItemKind; +use rustc_index::IndexVec; use rustc_middle::{ - hir::nested_filter, - ty::{TyCtxt, TypeckResults}, + mir::{ + visit::{PlaceContext, Visitor}, + Local, LocalDecl, Location, Place, PlaceElem, ProjectionElem, + }, + ty::{TyCtxt, TyKind}, }; -use rustc_span::def_id::DefId; +use rustc_session::config::Input; +use rustc_span::def_id::LocalDefId; -use crate::{compile_util, graph}; - -pub fn analyze(path: &Path) { - let input = compile_util::path_to_input(path); - let config = compile_util::make_config(input); - compile_util::run_compiler(config, |tcx| { - let mut visitor = TyVisitor::new(tcx); - tcx.hir().visit_all_item_likes_in_crate(&mut visitor); - let foreign_tys: HashSet<_> = visitor - .foreign_types - .into_iter() - .flat_map(|id| graph::reachable_vertices(&visitor.type_graph, id, visitor.tys.len())) - .map(|id| visitor.tys[id]) - .collect(); +use crate::{compile_util, relational, ty_finder}; - for item_id in tcx.hir().items() { - let item = tcx.hir().item(item_id); - if !matches!( - item.kind, - ItemKind::Static(_, _, _) | ItemKind::Const(_, _, _) | ItemKind::Fn(_, _, _) - ) { - continue; - } - let typeck = tcx.typeck(item.owner_id); - let mut visitor = UnionVisitor::new(tcx, typeck, &foreign_tys); - visitor.visit_item(item); - if !visitor.map.is_empty() { - let def_path = tcx.def_path_str(item.owner_id.to_def_id()); - println!("{:?} {:?}", def_path, visitor.map); - } - } - }) - .unwrap(); +pub fn analyze_path(path: &Path) { + analyze_input(compile_util::path_to_input(path)) } -struct TyVisitor<'tcx> { - tcx: TyCtxt<'tcx>, - tys: Vec, - ty_ids: HashMap, - foreign_types: HashSet, - type_graph: HashMap>, +pub fn analyze_str(code: &str) { + analyze_input(compile_util::str_to_input(code)) } -impl<'tcx> TyVisitor<'tcx> { - fn new(tcx: TyCtxt<'tcx>) -> Self { - Self { - tcx, - tys: Vec::new(), - ty_ids: HashMap::new(), - foreign_types: HashSet::new(), - type_graph: HashMap::new(), - } - } +fn analyze_input(input: Input) { + let config = compile_util::make_config(input); + compile_util::run_compiler(config, analyze).unwrap() +} - fn ty_to_id(&mut self, ty: DefId) -> usize { - self.ty_ids.get(&ty).copied().unwrap_or_else(|| { - let id = self.tys.len(); - self.tys.push(ty); - self.ty_ids.insert(ty, id); - id - }) - } +pub fn analyze(tcx: TyCtxt<'_>) { + let visitor = ty_finder::TyVisitor::new(tcx); + let foreign_tys = visitor.find_foreign_tys(tcx); + let prog_info = relational::ProgInfo::new(tcx); - fn handle_ty(&mut self, ty: &'tcx Ty<'tcx>) { - let TyKind::Path(QPath::Resolved(_, path)) = ty.kind else { return }; - let Res::Def(_, def_id) = path.res else { return }; - if !def_id.is_local() { - return; + for item_id in tcx.hir().items() { + let item = tcx.hir().item(item_id); + if !matches!(item.kind, ItemKind::Fn(_, _, _)) { + continue; } - let id = self.ty_to_id(def_id); - - let hir = self.tcx.hir(); - let mut hir_id = ty.hir_id; - while let Some(parent_id) = hir.opt_parent_id(hir_id) { - let node = hir.get(parent_id); - match node { - Node::ForeignItem(_) => { - self.foreign_types.insert(id); - break; - } - Node::Item(item) => { - if matches!( - item.kind, - ItemKind::Struct(_, _) | ItemKind::Union(_, _) | ItemKind::TyAlias(_, _) - ) { - let item_def_id = item.owner_id.to_def_id(); - let item_id = self.ty_to_id(item_def_id); - self.type_graph.entry(item_id).or_default().insert(id); + let local_def_id = item_id.owner_id.def_id; + let def_id = local_def_id.to_def_id(); + let body = tcx.optimized_mir(def_id); + let mut visitor = PlaceVisitor::new(tcx, &body.local_decls, &foreign_tys); + visitor.visit_body(body); + if !visitor.accesses.is_empty() { + println!("{:?}", local_def_id); + let states = relational::analyze_fn(tcx, &prog_info, local_def_id); + for access in &visitor.accesses { + let state = &states[&access.location]; + let (path, is_deref) = access.get_path(state); + let g = state.g(); + let obj = g.get_obj(&path, is_deref); + println!("{:?}", access.ctx); + if let Some(obj) = obj { + let Obj::Compound(fields) = obj else { unreachable!() }; + for (i, obj) in fields { + if let Obj::Ptr(loc) = obj { + if loc.projection().is_empty() { + let node = &g.nodes[loc.root()]; + if let Some(v) = node.at_addr { + println!("{}: {}", i, v); + } else { + println!("{}: loc<{:?}>", i, obj); + } + } else { + println!("{}: loc<{:?}>", i, obj); + } + } else { + println!("{}: {:?}", i, obj); + } } - break; + } else { + println!("None"); } - _ => hir_id = parent_id, } } } } -impl<'tcx> Visitor<'tcx> for TyVisitor<'tcx> { - type NestedFilter = nested_filter::OnlyBodies; - - fn nested_visit_map(&mut self) -> Self::Map { - self.tcx.hir() - } - - fn visit_ty(&mut self, ty: &'tcx Ty<'tcx>) { - self.handle_ty(ty); - intravisit::walk_ty(self, ty); - } -} - -struct UnionVisitor<'tcx, 'a> { +struct PlaceVisitor<'tcx, 'a> { tcx: TyCtxt<'tcx>, - typeck: &'a TypeckResults<'tcx>, - foreign_tys: &'a HashSet, - map: HashMap>, + local_decls: &'a IndexVec>, + foreign_tys: &'a HashSet, + accesses: Vec>, } -impl<'tcx, 'a> UnionVisitor<'tcx, 'a> { +impl<'tcx, 'a> PlaceVisitor<'tcx, 'a> { fn new( tcx: TyCtxt<'tcx>, - typeck: &'a TypeckResults<'tcx>, - foreign_tys: &'a HashSet, + local_decls: &'a IndexVec>, + foreign_tys: &'a HashSet, ) -> Self { Self { tcx, - typeck, + local_decls, foreign_tys, - map: HashMap::new(), + accesses: vec![], } } +} - fn handle_expr(&mut self, expr: &'tcx Expr<'tcx>) { - let ExprKind::Field(expr, ident) = expr.kind else { return }; - let ty = self.typeck.expr_ty(expr); - let adt_def = some_or!(ty.ty_adt_def(), return); - if !adt_def.is_union() { - return; - } - let def_id = adt_def.did(); - if !def_id.is_local() { - return; - } - if self.foreign_tys.contains(&def_id) { - return; +impl<'tcx> Visitor<'tcx> for PlaceVisitor<'tcx, '_> { + fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { + if place.projection.len() > 0 { + for i in 0..(place.projection.len() - 1) { + let ty = Place::ty_from( + place.local, + &place.projection[..=i], + self.local_decls, + self.tcx, + ) + .ty; + let TyKind::Adt(adt_def, _) = ty.kind() else { continue }; + if !adt_def.is_union() { + continue; + } + let def_id = some_or!(adt_def.did().as_local(), continue); + if self.foreign_tys.contains(&def_id) { + continue; + } + let ProjectionElem::Field(f, _) = place.projection[i + 1] else { unreachable!() }; + let access = FieldAccess { + ty: def_id, + local: place.local, + projection: &place.projection[..=i], + field: f, + ctx: context, + location, + }; + self.accesses.push(access); + } } - let ty = format!("{:?}", ty); - let field = ident.name.to_string(); - self.map.entry(ty).or_default().insert(field); + self.super_place(place, context, location); } } -impl<'tcx> Visitor<'tcx> for UnionVisitor<'tcx, '_> { - type NestedFilter = nested_filter::OnlyBodies; - - fn nested_visit_map(&mut self) -> Self::Map { - self.tcx.hir() - } +#[derive(Debug, Clone, Copy)] +struct FieldAccess<'tcx> { + #[allow(unused)] + ty: LocalDefId, + local: Local, + projection: &'tcx [PlaceElem<'tcx>], + #[allow(unused)] + field: FieldIdx, + ctx: PlaceContext, + location: Location, +} - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - self.handle_expr(expr); - intravisit::walk_expr(self, expr); +impl FieldAccess<'_> { + fn get_path(&self, state: &AbsMem) -> (AccPath, bool) { + assert!(!self.projection.is_empty()); + AccPath::from_local_projection( + self.local, + &self.projection[..self.projection.len() - 1], + state, + ) } } diff --git a/src/bin/urcrat.rs b/src/bin/urcrat.rs index b178b16..1ea678f 100644 --- a/src/bin/urcrat.rs +++ b/src/bin/urcrat.rs @@ -23,5 +23,6 @@ fn main() { } let file = args.input.join("c2rust-lib.rs"); - relational::analyze_path(&file); + // relational::analyze_path(&file); + analysis::analyze_path(&file); } diff --git a/src/lib.rs b/src/lib.rs index 0daff95..84f4751 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,3 +57,4 @@ pub mod disjoint_set; pub mod graph; pub mod relational; pub mod steensgaard; +pub mod ty_finder; diff --git a/src/relational/analysis.rs b/src/relational/analysis.rs index eabd804..b6b4e5f 100644 --- a/src/relational/analysis.rs +++ b/src/relational/analysis.rs @@ -35,94 +35,105 @@ fn analyze_input(input: Input) -> AnalysisResults { } pub fn analyze(tcx: TyCtxt<'_>) -> AnalysisResults { - let hir = tcx.hir(); - let may_aliases = steensgaard::analyze(tcx); - let alias_graph = may_aliases.get_alias_graph(); - - let func_ids: Vec<_> = hir - .items() - .filter_map(|item_id| { - let item = hir.item(item_id); - if item.ident.name.as_str() == "main" || !matches!(item.kind, ItemKind::Fn(_, _, _)) { - return None; - } - Some(item_id.owner_id.def_id) - }) + let prog_info = relational::ProgInfo::new(tcx); + let functions = prog_info + .reachability + .keys() + .map(|def_id| (*def_id, analyze_fn(tcx, &prog_info, *def_id))) .collect(); + AnalysisResults { functions } +} - let (indirect_assigns, call_graph): (HashMap<_, _>, HashMap<_, _>) = func_ids - .iter() - .map(|def_id| { - let body = tcx.optimized_mir(def_id.to_def_id()); - let (indirect_assigns, callees) = visit_body(*def_id, body, &alias_graph, tcx); - ((*def_id, indirect_assigns), (*def_id, callees)) - }) - .unzip(); - let mut reachability = graph::transitive_closure(&call_graph); - for (caller, callees) in &mut reachability { - callees.insert(*caller); - } +#[derive(Debug)] +pub struct ProgInfo { + indirect_assigns: HashMap>, + reachability: HashMap>, + alias_graph: steensgaard::AliasGraph, +} - let mut functions = HashMap::new(); - for local_def_id in func_ids.iter().copied() { - let def_id = local_def_id.to_def_id(); - let body = tcx.optimized_mir(def_id); - - println!("{:?}", def_id); - // for bbd in body.basic_blocks.iter() { - // for stmt in &bbd.statements { - // println!("{:?}", stmt); - // } - // if !matches!( - // bbd.terminator().kind, - // TerminatorKind::Return | TerminatorKind::Assert { .. } - // ) { - // println!("{:?}", bbd.terminator().kind); - // } - // } - - let pre_rpo_map = get_rpo_map(body); - let loop_blocks = get_loop_blocks(body, &pre_rpo_map); - let rpo_map = compute_rpo_map(body, &loop_blocks); - let dead_locals = get_dead_locals(body, tcx); - let local_tys = body - .local_decls - .iter() - .map(|decl| TyStructure::from_ty(decl.ty, def_id, tcx)) - .collect(); - let local_ptr_tys = body - .local_decls - .iter_enumerated() - .filter_map(|(local, decl)| { - let (TyKind::RawPtr(TypeAndMut { ty, .. }) | TyKind::Ref(_, ty, _)) = - decl.ty.kind() - else { +impl ProgInfo { + pub fn new(tcx: TyCtxt<'_>) -> Self { + let hir = tcx.hir(); + + let may_aliases = steensgaard::analyze(tcx); + let alias_graph = may_aliases.get_alias_graph(); + + let func_ids: Vec<_> = hir + .items() + .filter_map(|item_id| { + let item = hir.item(item_id); + if item.ident.name.as_str() == "main" || !matches!(item.kind, ItemKind::Fn(_, _, _)) + { return None; - }; - Some((local, TyStructure::from_ty(*ty, def_id, tcx))) + } + Some(item_id.owner_id.def_id) }) .collect(); - let analyzer = Analyzer { - tcx, - body, - rpo_map, - dead_locals, - local_tys, - local_ptr_tys, - local_def_id, - indirect_assigns: &indirect_assigns, - reachability: &reachability, - alias_graph: &alias_graph, - }; - functions.insert(def_id, analyzer.analyze()); + + let (indirect_assigns, call_graph): (HashMap<_, _>, HashMap<_, _>) = func_ids + .iter() + .map(|def_id| { + let body = tcx.optimized_mir(def_id.to_def_id()); + let (indirect_assigns, callees) = visit_body(*def_id, body, &alias_graph, tcx); + ((*def_id, indirect_assigns), (*def_id, callees)) + }) + .unzip(); + let mut reachability = graph::transitive_closure(&call_graph); + for (caller, callees) in &mut reachability { + callees.insert(*caller); + } + + Self { + indirect_assigns, + reachability, + alias_graph, + } } +} - AnalysisResults { functions } +pub fn analyze_fn( + tcx: TyCtxt<'_>, + prog_info: &ProgInfo, + local_def_id: LocalDefId, +) -> HashMap { + let def_id = local_def_id.to_def_id(); + let body = tcx.optimized_mir(def_id); + let pre_rpo_map = get_rpo_map(body); + let loop_blocks = get_loop_blocks(body, &pre_rpo_map); + let rpo_map = compute_rpo_map(body, &loop_blocks); + let dead_locals = get_dead_locals(body, tcx); + let local_tys = body + .local_decls + .iter() + .map(|decl| TyStructure::from_ty(decl.ty, def_id, tcx)) + .collect(); + let local_ptr_tys = body + .local_decls + .iter_enumerated() + .filter_map(|(local, decl)| { + let (TyKind::RawPtr(TypeAndMut { ty, .. }) | TyKind::Ref(_, ty, _)) = decl.ty.kind() + else { + return None; + }; + Some((local, TyStructure::from_ty(*ty, def_id, tcx))) + }) + .collect(); + let analyzer = Analyzer { + tcx, + body, + rpo_map, + dead_locals, + local_tys, + local_ptr_tys, + local_def_id, + prog_info, + }; + analyzer.analyze() } #[derive(Debug, Clone)] pub struct AnalysisResults { - pub functions: HashMap>, + pub functions: HashMap>, } #[allow(missing_debug_implementations)] @@ -134,9 +145,7 @@ pub struct Analyzer<'tcx, 'a> { local_tys: Vec, local_ptr_tys: HashMap, local_def_id: LocalDefId, - indirect_assigns: &'a HashMap>, - reachability: &'a HashMap>, - alias_graph: &'a steensgaard::AliasGraph, + prog_info: &'a ProgInfo, } impl<'tcx> Analyzer<'tcx, '_> { @@ -194,32 +203,37 @@ impl<'tcx> Analyzer<'tcx, '_> { } pub fn resolve_indirect_calls(&self, local: Local) -> HashSet { - self.alias_graph + self.prog_info + .alias_graph .find_fn_may_aliases(self.local_def_id, local, self.tcx) } pub fn locals_invalidated_by_call(&self, callee: LocalDefId) -> HashSet<(Local, usize)> { - self.reachability[&callee] + self.prog_info.reachability[&callee] .iter() .flat_map(|func| { - self.indirect_assigns[func].iter().flat_map(|local| { - self.alias_graph - .find_may_aliases(*func, *local) - .into_iter() - .filter_map(|alias| { - if alias.function == self.local_def_id { - Some((alias.local, alias.depth)) - } else { - None - } - }) - }) + self.prog_info.indirect_assigns[func] + .iter() + .flat_map(|local| { + self.prog_info + .alias_graph + .find_may_aliases(*func, *local) + .into_iter() + .filter_map(|alias| { + if alias.function == self.local_def_id { + Some((alias.local, alias.depth)) + } else { + None + } + }) + }) }) .collect() } pub fn find_may_aliases(&self, local: Local) -> HashSet<(Local, usize)> { - self.alias_graph + self.prog_info + .alias_graph .find_may_aliases(self.local_def_id, local) .into_iter() .filter_map(|alias| { diff --git a/src/relational/domains.rs b/src/relational/domains.rs index 1dc0b89..529cdea 100644 --- a/src/relational/domains.rs +++ b/src/relational/domains.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeMap, HashMap, HashSet}; use etrace::some_or; use rustc_middle::mir::Local; @@ -294,11 +294,22 @@ impl Node { } } -#[derive(Debug, Clone, Default)] +#[derive(Clone, Default)] pub struct Graph { pub nodes: Vec, - pub locals: HashMap, - pub ints: HashMap, + locals: HashMap, + ints: HashMap, +} + +impl std::fmt::Debug for Graph { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let nodes: BTreeMap<_, _> = self.nodes.iter().enumerate().map(|(i, n)| (i, n)).collect(); + let locals: BTreeMap<_, _> = self.locals.iter().map(|(l, n)| (*l, *n)).collect(); + f.debug_struct("Graph") + .field("nodes", &nodes) + .field("locals", &locals) + .finish() + } } impl Graph { @@ -423,6 +434,20 @@ impl Graph { } } + pub fn get_obj(&self, x: &AccPath, deref: bool) -> Option<&Obj> { + if deref { + let id = self.locals.get(&x.local)?; + let mut loc = self.get_pointed_loc(*id, &[])?; + loc.projection.extend(x.projection.to_owned()); + let node = &self.nodes[loc.root]; + node.obj.project(&loc.projection) + } else { + let id = self.locals.get(&x.local)?; + let node = &self.nodes[*id]; + node.obj.project(&x.projection) + } + } + fn lvalue(&mut self, x: &AccPath, deref: bool) -> &mut Obj { if deref { let (id, _) = self.get_local_node_mut(x.local); @@ -659,16 +684,15 @@ impl Graph { *obj = Obj::default(); } return; - } else { - locs = locs - .into_iter() - .flat_map(|loc| { - let obj = some_or!(self.obj_at_location(&loc), return vec![]); - obj.pointing_locations() - }) - .collect(); - depth -= 1; } + locs = locs + .into_iter() + .flat_map(|loc| { + let obj = some_or!(self.obj_at_location(&loc), return vec![]); + obj.pointing_locations() + }) + .collect(); + depth -= 1; } } diff --git a/src/relational/semantics.rs b/src/relational/semantics.rs index 14b09eb..d50a399 100644 --- a/src/relational/semantics.rs +++ b/src/relational/semantics.rs @@ -525,15 +525,22 @@ impl AccPath { Self { local, projection } } + #[inline] fn from_place(place: Place<'_>, state: &AbsMem) -> (Self, bool) { - let root = place.local; - let projections = place - .projection + Self::from_local_projection(place.local, place.projection, state) + } + + pub fn from_local_projection( + local: Local, + proj: &[PlaceElem<'_>], + state: &AbsMem, + ) -> (Self, bool) { + let projections = proj .iter() - .filter_map(|e| AccElem::from_elem(e, state)) + .filter_map(|e| AccElem::from_elem(*e, state)) .collect(); - let is_deref = place.is_indirect_first_projection(); - (AccPath::new(root, projections), is_deref) + let is_deref = proj.get(0).map_or(false, |e| matches!(e, PlaceElem::Deref)); + (AccPath::new(local, projections), is_deref) } #[inline] diff --git a/src/relational/tests.rs b/src/relational/tests.rs index 90acb18..a4540d6 100644 --- a/src/relational/tests.rs +++ b/src/relational/tests.rs @@ -4,7 +4,7 @@ use rustc_middle::{ mir::{Location, TerminatorKind}, ty::TyCtxt, }; -use rustc_span::def_id::DefId; +use rustc_span::def_id::LocalDefId; use super::*; use crate::*; @@ -15,7 +15,7 @@ fn run_compiler) + Send>(code: &str, f: F) { compile_util::run_compiler(config, f).unwrap(); } -fn find_fn(name: &str, tcx: TyCtxt<'_>) -> DefId { +fn find_fn(name: &str, tcx: TyCtxt<'_>) -> LocalDefId { let hir = tcx.hir(); hir.items() .find_map(|item_id| { @@ -23,12 +23,12 @@ fn find_fn(name: &str, tcx: TyCtxt<'_>) -> DefId { if item.ident.name.as_str() != name { return None; } - Some(item_id.owner_id.to_def_id()) + Some(item_id.owner_id.def_id) }) .unwrap() } -fn find_return(def_id: DefId, tcx: TyCtxt<'_>) -> Location { +fn find_return(def_id: LocalDefId, tcx: TyCtxt<'_>) -> Location { let body = tcx.optimized_mir(def_id); let (bb, bbd) = body .basic_blocks diff --git a/src/ty_finder.rs b/src/ty_finder.rs new file mode 100644 index 0000000..2414d0e --- /dev/null +++ b/src/ty_finder.rs @@ -0,0 +1,100 @@ +use std::collections::{HashMap, HashSet}; + +use etrace::some_or; +use rustc_hir::{def::Res, intravisit, intravisit::Visitor, ItemKind, Node, QPath, Ty, TyKind}; +use rustc_middle::{hir::nested_filter, ty::TyCtxt}; +use rustc_span::def_id::LocalDefId; + +use crate::graph; + +pub struct TyVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + tys: Vec, + ty_ids: HashMap, + foreign_types: HashSet, + type_graph: HashMap>, +} + +impl std::fmt::Debug for TyVisitor<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("TyVisitor") + .field("tys", &self.tys) + .field("ty_ids", &self.ty_ids) + .field("foreign_types", &self.foreign_types) + .field("type_graph", &self.type_graph) + .finish() + } +} + +impl<'tcx> TyVisitor<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>) -> Self { + Self { + tcx, + tys: Vec::new(), + ty_ids: HashMap::new(), + foreign_types: HashSet::new(), + type_graph: HashMap::new(), + } + } + + pub fn find_foreign_tys(mut self, tcx: TyCtxt<'tcx>) -> HashSet { + tcx.hir().visit_all_item_likes_in_crate(&mut self); + self.foreign_types + .into_iter() + .flat_map(|id| graph::reachable_vertices(&self.type_graph, id, self.tys.len())) + .map(|id| self.tys[id]) + .collect() + } + + fn ty_to_id(&mut self, ty: LocalDefId) -> usize { + self.ty_ids.get(&ty).copied().unwrap_or_else(|| { + let id = self.tys.len(); + self.tys.push(ty); + self.ty_ids.insert(ty, id); + id + }) + } + + fn handle_ty(&mut self, ty: &'tcx Ty<'tcx>) { + let TyKind::Path(QPath::Resolved(_, path)) = ty.kind else { return }; + let Res::Def(_, def_id) = path.res else { return }; + let def_id = some_or!(def_id.as_local(), return); + let id = self.ty_to_id(def_id); + + let hir = self.tcx.hir(); + let mut hir_id = ty.hir_id; + while let Some(parent_id) = hir.opt_parent_id(hir_id) { + let node = hir.get(parent_id); + match node { + Node::ForeignItem(_) => { + self.foreign_types.insert(id); + break; + } + Node::Item(item) => { + if matches!( + item.kind, + ItemKind::Struct(_, _) | ItemKind::Union(_, _) | ItemKind::TyAlias(_, _) + ) { + let item_id = self.ty_to_id(item.owner_id.def_id); + self.type_graph.entry(item_id).or_default().insert(id); + } + break; + } + _ => hir_id = parent_id, + } + } + } +} + +impl<'tcx> Visitor<'tcx> for TyVisitor<'tcx> { + type NestedFilter = nested_filter::OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() + } + + fn visit_ty(&mut self, ty: &'tcx Ty<'tcx>) { + self.handle_ty(ty); + intravisit::walk_ty(self, ty); + } +}