1 //! Write the output of rustc's analysis to an implementor of Dump.
3 //! Dumping the analysis is implemented by walking the AST and getting a bunch of
4 //! info out from all over the place. We use `DefId`s to identify objects. The
5 //! tricky part is getting syntactic (span, source text) and semantic (reference
6 //! `DefId`s) information for parts of expressions which the compiler has discarded.
7 //! E.g., in a path `foo::bar::baz`, the compiler only keeps a span for the whole
8 //! path and a reference to `baz`, but we want spans and references for all three
11 //! SpanUtils is used to manipulate spans. In particular, to extract sub-spans
12 //! from spans (e.g., the span for `bar` from the above example path).
13 //! DumpVisitor walks the AST and processes it, and Dumper is used for
14 //! recording the output.
17 use rustc_ast
::walk_list
;
18 use rustc_data_structures
::fx
::FxHashSet
;
20 use rustc_hir
::def
::{DefKind as HirDefKind, Res}
;
21 use rustc_hir
::def_id
::{DefId, LocalDefId}
;
22 use rustc_hir
::intravisit
::{self, Visitor}
;
23 use rustc_hir_pretty
::{bounds_to_string, fn_to_string, generic_params_to_string, ty_to_string}
;
24 use rustc_middle
::hir
::map
::Map
;
25 use rustc_middle
::span_bug
;
26 use rustc_middle
::ty
::{self, DefIdTree, TyCtxt}
;
27 use rustc_session
::config
::Input
;
28 use rustc_span
::source_map
::respan
;
29 use rustc_span
::symbol
::Ident
;
35 use crate::dumper
::{Access, Dumper}
;
37 use crate::span_utils
::SpanUtils
;
39 escape
, generated_code
, id_from_def_id
, id_from_hir_id
, lower_attributes
, PathCollector
,
44 CompilationOptions
, CratePreludeData
, Def
, DefKind
, GlobalCrateId
, Import
, ImportKind
, Ref
,
45 RefKind
, Relation
, RelationKind
, SpanData
,
48 use tracing
::{debug, error}
;
50 macro_rules
! down_cast_data
{
51 ($id
:ident
, $kind
:ident
, $sp
:expr
) => {
52 let $id
= if let super::Data
::$
kind(data
) = $id
{
55 span_bug
!($sp
, "unexpected data kind: {:?}", $id
);
60 macro_rules
! access_from
{
61 ($save_ctxt
:expr
, $item
:expr
, $id
:expr
) => {
63 public
: $item
.vis
.node
.is_pub(),
64 reachable
: $save_ctxt
.access_levels
.is_reachable($id
),
69 macro_rules
! access_from_vis
{
70 ($save_ctxt
:expr
, $vis
:expr
, $id
:expr
) => {
71 Access { public: $vis.node.is_pub(), reachable: $save_ctxt.access_levels.is_reachable($id) }
75 pub struct DumpVisitor
<'tcx
> {
76 pub save_ctxt
: SaveContext
<'tcx
>,
80 span
: SpanUtils
<'tcx
>,
81 // Set of macro definition (callee) spans, and the set
82 // of macro use (callsite) spans. We store these to ensure
83 // we only write one macro def per unique macro definition, and
84 // one macro use per unique callsite span.
85 // mac_defs: FxHashSet<Span>,
86 // macro_calls: FxHashSet<Span>,
89 impl<'tcx
> DumpVisitor
<'tcx
> {
90 pub fn new(save_ctxt
: SaveContext
<'tcx
>) -> DumpVisitor
<'tcx
> {
91 let span_utils
= SpanUtils
::new(&save_ctxt
.tcx
.sess
);
92 let dumper
= Dumper
::new(save_ctxt
.config
.clone());
98 // mac_defs: FxHashSet::default(),
99 // macro_calls: FxHashSet::default(),
103 pub fn analysis(&self) -> &rls_data
::Analysis
{
104 self.dumper
.analysis()
107 fn nest_typeck_results
<F
>(&mut self, item_def_id
: LocalDefId
, f
: F
)
109 F
: FnOnce(&mut Self),
111 let typeck_results
= if self.tcx
.has_typeck_results(item_def_id
) {
112 Some(self.tcx
.typeck(item_def_id
))
117 let old_maybe_typeck_results
= self.save_ctxt
.maybe_typeck_results
;
118 self.save_ctxt
.maybe_typeck_results
= typeck_results
;
120 self.save_ctxt
.maybe_typeck_results
= old_maybe_typeck_results
;
123 fn span_from_span(&self, span
: Span
) -> SpanData
{
124 self.save_ctxt
.span_from_span(span
)
127 fn lookup_def_id(&self, ref_id
: hir
::HirId
) -> Option
<DefId
> {
128 self.save_ctxt
.lookup_def_id(ref_id
)
131 pub fn dump_crate_info(&mut self, name
: &str, krate
: &hir
::Crate
<'_
>) {
132 let source_file
= self.tcx
.sess
.local_crate_source_file
.as_ref();
133 let crate_root
= source_file
.map(|source_file
| {
134 let source_file
= Path
::new(source_file
);
135 match source_file
.file_name() {
136 Some(_
) => source_file
.parent().unwrap().display(),
137 None
=> source_file
.display(),
142 let data
= CratePreludeData
{
143 crate_id
: GlobalCrateId
{
148 .local_crate_disambiguator()
152 crate_root
: crate_root
.unwrap_or_else(|| "<no source>".to_owned()),
153 external_crates
: self.save_ctxt
.get_external_crates(),
154 span
: self.span_from_span(krate
.item
.span
),
157 self.dumper
.crate_prelude(data
);
160 pub fn dump_compilation_options(&mut self, input
: &Input
, crate_name
: &str) {
161 // Apply possible `remap-path-prefix` remapping to the input source file
162 // (and don't include remapping args anymore)
163 let (program
, arguments
) = {
164 let remap_arg_indices
= {
165 let mut indices
= FxHashSet
::default();
166 // Args are guaranteed to be valid UTF-8 (checked early)
167 for (i
, e
) in env
::args().enumerate() {
168 if e
.starts_with("--remap-path-prefix=") {
170 } else if e
== "--remap-path-prefix" {
172 indices
.insert(i
+ 1);
178 let mut args
= env
::args()
180 .filter(|(i
, _
)| !remap_arg_indices
.contains(i
))
181 .map(|(_
, arg
)| match input
{
182 Input
::File(ref path
) if path
== Path
::new(&arg
) => {
183 let mapped
= &self.tcx
.sess
.local_crate_source_file
;
184 mapped
.as_ref().unwrap().to_string_lossy().into()
189 (args
.next().unwrap(), args
.collect())
192 let data
= CompilationOptions
{
193 directory
: self.tcx
.sess
.working_dir
.0.clone(),
196 output
: self.save_ctxt
.compilation_output(crate_name
),
199 self.dumper
.compilation_opts(data
);
202 fn write_segments(&mut self, segments
: impl IntoIterator
<Item
= &'tcx hir
::PathSegment
<'tcx
>>) {
203 for seg
in segments
{
204 if let Some(data
) = self.save_ctxt
.get_path_segment_data(seg
) {
205 self.dumper
.dump_ref(data
);
210 fn write_sub_paths(&mut self, path
: &'tcx hir
::Path
<'tcx
>) {
211 self.write_segments(path
.segments
)
214 // As write_sub_paths, but does not process the last ident in the path (assuming it
215 // will be processed elsewhere). See note on write_sub_paths about global.
216 fn write_sub_paths_truncated(&mut self, path
: &'tcx hir
::Path
<'tcx
>) {
217 if let [segments @
.., _
] = path
.segments
{
218 self.write_segments(segments
)
222 fn process_formals(&mut self, formals
: &'tcx
[hir
::Param
<'tcx
>], qualname
: &str) {
224 self.visit_pat(&arg
.pat
);
225 let mut collector
= PathCollector
::new(self.tcx
);
226 collector
.visit_pat(&arg
.pat
);
228 for (hir_id
, ident
, ..) in collector
.collected_idents
{
229 let typ
= match self.save_ctxt
.typeck_results().node_type_opt(hir_id
) {
230 Some(s
) => s
.to_string(),
233 if !self.span
.filter_generated(ident
.span
) {
234 let id
= id_from_hir_id(hir_id
, &self.save_ctxt
);
235 let span
= self.span_from_span(ident
.span
);
237 self.dumper
.dump_def(
238 &Access { public: false, reachable: false }
,
240 kind
: DefKind
::Local
,
243 name
: ident
.to_string(),
244 qualname
: format
!("{}::{}", qualname
, ident
.to_string()),
261 sig
: &'tcx hir
::FnSig
<'tcx
>,
262 body
: Option
<hir
::BodyId
>,
265 generics
: &'tcx hir
::Generics
<'tcx
>,
266 vis
: &hir
::Visibility
<'tcx
>,
269 debug
!("process_method: {}:{}", hir_id
, ident
);
271 let map
= &self.tcx
.hir();
272 self.nest_typeck_results(map
.local_def_id(hir_id
), |v
| {
273 if let Some(mut method_data
) = v
.save_ctxt
.get_method_data(hir_id
, ident
, span
) {
274 if let Some(body
) = body
{
275 v
.process_formals(map
.body(body
).params
, &method_data
.qualname
);
277 v
.process_generic_params(&generics
, &method_data
.qualname
, hir_id
);
280 fn_to_string(sig
.decl
, sig
.header
, Some(ident
.name
), generics
, vis
, &[], None
);
281 method_data
.sig
= sig
::method_signature(hir_id
, ident
, generics
, sig
, &v
.save_ctxt
);
283 v
.dumper
.dump_def(&access_from_vis
!(v
.save_ctxt
, vis
, hir_id
), method_data
);
286 // walk arg and return types
287 for arg
in sig
.decl
.inputs
{
291 if let hir
::FnRetTy
::Return(ref ret_ty
) = sig
.decl
.output
{
296 if let Some(body
) = body
{
297 v
.visit_expr(&map
.body(body
).value
);
302 fn process_struct_field_def(
304 field
: &'tcx hir
::StructField
<'tcx
>,
305 parent_id
: hir
::HirId
,
307 let field_data
= self.save_ctxt
.get_field_data(field
, parent_id
);
308 if let Some(field_data
) = field_data
{
309 self.dumper
.dump_def(&access_from
!(self.save_ctxt
, field
, field
.hir_id
), field_data
);
313 // Dump generic params bindings, then visit_generics
314 fn process_generic_params(
316 generics
: &'tcx hir
::Generics
<'tcx
>,
320 for param
in generics
.params
{
322 hir
::GenericParamKind
::Lifetime { .. }
=> {}
323 hir
::GenericParamKind
::Type { .. }
=> {
324 let param_ss
= param
.name
.ident().span
;
325 let name
= escape(self.span
.snippet(param_ss
));
326 // Append $id to name to make sure each one is unique.
327 let qualname
= format
!("{}::{}${}", prefix
, name
, id
);
328 if !self.span
.filter_generated(param_ss
) {
329 let id
= id_from_hir_id(param
.hir_id
, &self.save_ctxt
);
330 let span
= self.span_from_span(param_ss
);
332 self.dumper
.dump_def(
333 &Access { public: false, reachable: false }
,
340 value
: String
::new(),
351 hir
::GenericParamKind
::Const { .. }
=> {}
354 self.visit_generics(generics
);
359 item
: &'tcx hir
::Item
<'tcx
>,
360 decl
: &'tcx hir
::FnDecl
<'tcx
>,
361 _header
: &'tcx hir
::FnHeader
,
362 ty_params
: &'tcx hir
::Generics
<'tcx
>,
365 let map
= &self.tcx
.hir();
366 self.nest_typeck_results(map
.local_def_id(item
.hir_id
), |v
| {
367 let body
= map
.body(body
);
368 if let Some(fn_data
) = v
.save_ctxt
.get_item_data(item
) {
369 down_cast_data
!(fn_data
, DefData
, item
.span
);
370 v
.process_formals(body
.params
, &fn_data
.qualname
);
371 v
.process_generic_params(ty_params
, &fn_data
.qualname
, item
.hir_id
);
373 v
.dumper
.dump_def(&access_from
!(v
.save_ctxt
, item
, item
.hir_id
), fn_data
);
376 for arg
in decl
.inputs
{
380 if let hir
::FnRetTy
::Return(ref ret_ty
) = decl
.output
{
384 v
.visit_expr(&body
.value
);
388 fn process_static_or_const_item(
390 item
: &'tcx hir
::Item
<'tcx
>,
391 typ
: &'tcx hir
::Ty
<'tcx
>,
392 expr
: &'tcx hir
::Expr
<'tcx
>,
394 self.nest_typeck_results(self.tcx
.hir().local_def_id(item
.hir_id
), |v
| {
395 if let Some(var_data
) = v
.save_ctxt
.get_item_data(item
) {
396 down_cast_data
!(var_data
, DefData
, item
.span
);
397 v
.dumper
.dump_def(&access_from
!(v
.save_ctxt
, item
, item
.hir_id
), var_data
);
404 fn process_assoc_const(
408 typ
: &'tcx hir
::Ty
<'tcx
>,
409 expr
: Option
<&'tcx hir
::Expr
<'tcx
>>,
411 vis
: &hir
::Visibility
<'tcx
>,
412 attrs
: &'tcx
[ast
::Attribute
],
415 format
!("::{}", self.tcx
.def_path_str(self.tcx
.hir().local_def_id(hir_id
).to_def_id()));
417 if !self.span
.filter_generated(ident
.span
) {
418 let sig
= sig
::assoc_const_signature(hir_id
, ident
.name
, typ
, expr
, &self.save_ctxt
);
419 let span
= self.span_from_span(ident
.span
);
421 self.dumper
.dump_def(
422 &access_from_vis
!(self.save_ctxt
, vis
, hir_id
),
424 kind
: DefKind
::Const
,
425 id
: id_from_hir_id(hir_id
, &self.save_ctxt
),
427 name
: ident
.name
.to_string(),
429 value
: ty_to_string(&typ
),
430 parent
: Some(id_from_def_id(parent_id
)),
433 docs
: self.save_ctxt
.docs_for_attrs(attrs
),
435 attributes
: lower_attributes(attrs
.to_owned(), &self.save_ctxt
),
440 // walk type and init value
441 self.nest_typeck_results(self.tcx
.hir().local_def_id(hir_id
), |v
| {
443 if let Some(expr
) = expr
{
449 // FIXME tuple structs should generate tuple-specific data.
452 item
: &'tcx hir
::Item
<'tcx
>,
453 def
: &'tcx hir
::VariantData
<'tcx
>,
454 ty_params
: &'tcx hir
::Generics
<'tcx
>,
456 debug
!("process_struct {:?} {:?}", item
, item
.span
);
457 let name
= item
.ident
.to_string();
458 let qualname
= format
!(
460 self.tcx
.def_path_str(self.tcx
.hir().local_def_id(item
.hir_id
).to_def_id())
463 let kind
= match item
.kind
{
464 hir
::ItemKind
::Struct(_
, _
) => DefKind
::Struct
,
465 hir
::ItemKind
::Union(_
, _
) => DefKind
::Union
,
469 let (value
, fields
) = match item
.kind
{
470 hir
::ItemKind
::Struct(hir
::VariantData
::Struct(ref fields
, ..), ..)
471 | hir
::ItemKind
::Union(hir
::VariantData
::Struct(ref fields
, ..), ..) => {
472 let include_priv_fields
= !self.save_ctxt
.config
.pub_only
;
473 let fields_str
= fields
476 if include_priv_fields
|| f
.vis
.node
.is_pub() {
477 Some(f
.ident
.to_string())
484 let value
= format
!("{} {{ {} }}", name
, fields_str
);
485 (value
, fields
.iter().map(|f
| id_from_hir_id(f
.hir_id
, &self.save_ctxt
)).collect())
487 _
=> (String
::new(), vec
![]),
490 if !self.span
.filter_generated(item
.ident
.span
) {
491 let span
= self.span_from_span(item
.ident
.span
);
492 self.dumper
.dump_def(
493 &access_from
!(self.save_ctxt
, item
, item
.hir_id
),
496 id
: id_from_hir_id(item
.hir_id
, &self.save_ctxt
),
499 qualname
: qualname
.clone(),
504 docs
: self.save_ctxt
.docs_for_attrs(&item
.attrs
),
505 sig
: sig
::item_signature(item
, &self.save_ctxt
),
506 attributes
: lower_attributes(item
.attrs
.to_vec(), &self.save_ctxt
),
511 self.nest_typeck_results(self.tcx
.hir().local_def_id(item
.hir_id
), |v
| {
512 for field
in def
.fields() {
513 v
.process_struct_field_def(field
, item
.hir_id
);
514 v
.visit_ty(&field
.ty
);
517 v
.process_generic_params(ty_params
, &qualname
, item
.hir_id
);
523 item
: &'tcx hir
::Item
<'tcx
>,
524 enum_definition
: &'tcx hir
::EnumDef
<'tcx
>,
525 ty_params
: &'tcx hir
::Generics
<'tcx
>,
527 let enum_data
= self.save_ctxt
.get_item_data(item
);
528 let enum_data
= match enum_data
{
532 down_cast_data
!(enum_data
, DefData
, item
.span
);
534 let access
= access_from
!(self.save_ctxt
, item
, item
.hir_id
);
536 for variant
in enum_definition
.variants
{
537 let name
= variant
.ident
.name
.to_string();
538 let qualname
= format
!("{}::{}", enum_data
.qualname
, name
);
539 let name_span
= variant
.ident
.span
;
542 hir
::VariantData
::Struct(ref fields
, ..) => {
544 fields
.iter().map(|f
| f
.ident
.to_string()).collect
::<Vec
<_
>>().join(", ");
545 let value
= format
!("{}::{} {{ {} }}", enum_data
.name
, name
, fields_str
);
546 if !self.span
.filter_generated(name_span
) {
547 let span
= self.span_from_span(name_span
);
548 let id
= id_from_hir_id(variant
.id
, &self.save_ctxt
);
549 let parent
= Some(id_from_hir_id(item
.hir_id
, &self.save_ctxt
));
551 self.dumper
.dump_def(
554 kind
: DefKind
::StructVariant
,
563 docs
: self.save_ctxt
.docs_for_attrs(&variant
.attrs
),
564 sig
: sig
::variant_signature(variant
, &self.save_ctxt
),
565 attributes
: lower_attributes(
566 variant
.attrs
.to_vec(),
574 let mut value
= format
!("{}::{}", enum_data
.name
, name
);
575 if let &hir
::VariantData
::Tuple(ref fields
, _
) = v
{
580 .map(|f
| ty_to_string(&f
.ty
))
586 if !self.span
.filter_generated(name_span
) {
587 let span
= self.span_from_span(name_span
);
588 let id
= id_from_hir_id(variant
.id
, &self.save_ctxt
);
589 let parent
= Some(id_from_hir_id(item
.hir_id
, &self.save_ctxt
));
591 self.dumper
.dump_def(
594 kind
: DefKind
::TupleVariant
,
603 docs
: self.save_ctxt
.docs_for_attrs(&variant
.attrs
),
604 sig
: sig
::variant_signature(variant
, &self.save_ctxt
),
605 attributes
: lower_attributes(
606 variant
.attrs
.to_vec(),
615 for field
in variant
.data
.fields() {
616 self.process_struct_field_def(field
, variant
.id
);
617 self.visit_ty(field
.ty
);
620 self.process_generic_params(ty_params
, &enum_data
.qualname
, item
.hir_id
);
621 self.dumper
.dump_def(&access
, enum_data
);
626 item
: &'tcx hir
::Item
<'tcx
>,
627 generics
: &'tcx hir
::Generics
<'tcx
>,
628 trait_ref
: &'tcx Option
<hir
::TraitRef
<'tcx
>>,
629 typ
: &'tcx hir
::Ty
<'tcx
>,
630 impl_items
: &'tcx
[hir
::ImplItemRef
<'tcx
>],
632 if let Some(impl_data
) = self.save_ctxt
.get_item_data(item
) {
633 if !self.span
.filter_generated(item
.span
) {
634 if let super::Data
::RelationData(rel
, imp
) = impl_data
{
635 self.dumper
.dump_relation(rel
);
636 self.dumper
.dump_impl(imp
);
638 span_bug
!(item
.span
, "unexpected data kind: {:?}", impl_data
);
643 let map
= &self.tcx
.hir();
644 self.nest_typeck_results(map
.local_def_id(item
.hir_id
), |v
| {
646 if let &Some(ref trait_ref
) = trait_ref
{
647 v
.process_path(trait_ref
.hir_ref_id
, &hir
::QPath
::Resolved(None
, &trait_ref
.path
));
649 v
.process_generic_params(generics
, "", item
.hir_id
);
650 for impl_item
in impl_items
{
652 map
.impl_item(impl_item
.id
),
653 map
.local_def_id(item
.hir_id
).to_def_id(),
661 item
: &'tcx hir
::Item
<'tcx
>,
662 generics
: &'tcx hir
::Generics
<'tcx
>,
663 trait_refs
: hir
::GenericBounds
<'tcx
>,
664 methods
: &'tcx
[hir
::TraitItemRef
],
666 let name
= item
.ident
.to_string();
667 let qualname
= format
!(
669 self.tcx
.def_path_str(self.tcx
.hir().local_def_id(item
.hir_id
).to_def_id())
671 let mut val
= name
.clone();
672 if !generics
.params
.is_empty() {
673 val
.push_str(&generic_params_to_string(generics
.params
));
675 if !trait_refs
.is_empty() {
677 val
.push_str(&bounds_to_string(trait_refs
));
679 if !self.span
.filter_generated(item
.ident
.span
) {
680 let id
= id_from_hir_id(item
.hir_id
, &self.save_ctxt
);
681 let span
= self.span_from_span(item
.ident
.span
);
683 methods
.iter().map(|i
| id_from_hir_id(i
.id
.hir_id
, &self.save_ctxt
)).collect();
684 self.dumper
.dump_def(
685 &access_from
!(self.save_ctxt
, item
, item
.hir_id
),
687 kind
: DefKind
::Trait
,
691 qualname
: qualname
.clone(),
696 docs
: self.save_ctxt
.docs_for_attrs(&item
.attrs
),
697 sig
: sig
::item_signature(item
, &self.save_ctxt
),
698 attributes
: lower_attributes(item
.attrs
.to_vec(), &self.save_ctxt
),
704 for super_bound
in trait_refs
.iter() {
705 let (def_id
, sub_span
) = match *super_bound
{
706 hir
::GenericBound
::Trait(ref trait_ref
, _
) => (
707 self.lookup_def_id(trait_ref
.trait_ref
.hir_ref_id
),
708 trait_ref
.trait_ref
.path
.segments
.last().unwrap().ident
.span
,
710 hir
::GenericBound
::LangItemTrait(lang_item
, span
, _
, _
) => {
711 (Some(self.tcx
.require_lang_item(lang_item
, Some(span
))), span
)
713 hir
::GenericBound
::Outlives(..) => continue,
716 if let Some(id
) = def_id
{
717 if !self.span
.filter_generated(sub_span
) {
718 let span
= self.span_from_span(sub_span
);
719 self.dumper
.dump_ref(Ref
{
722 ref_id
: id_from_def_id(id
),
725 self.dumper
.dump_relation(Relation
{
726 kind
: RelationKind
::SuperTrait
,
728 from
: id_from_def_id(id
),
729 to
: id_from_hir_id(item
.hir_id
, &self.save_ctxt
),
735 // walk generics and methods
736 self.process_generic_params(generics
, &qualname
, item
.hir_id
);
737 for method
in methods
{
738 let map
= &self.tcx
.hir();
739 self.process_trait_item(
740 map
.trait_item(method
.id
),
741 map
.local_def_id(item
.hir_id
).to_def_id(),
746 // `item` is the module in question, represented as an( item.
747 fn process_mod(&mut self, item
: &'tcx hir
::Item
<'tcx
>) {
748 if let Some(mod_data
) = self.save_ctxt
.get_item_data(item
) {
749 down_cast_data
!(mod_data
, DefData
, item
.span
);
750 self.dumper
.dump_def(&access_from
!(self.save_ctxt
, item
, item
.hir_id
), mod_data
);
754 fn dump_path_ref(&mut self, id
: hir
::HirId
, path
: &hir
::QPath
<'tcx
>) {
755 let path_data
= self.save_ctxt
.get_path_data(id
, path
);
756 if let Some(path_data
) = path_data
{
757 self.dumper
.dump_ref(path_data
);
761 fn dump_path_segment_ref(&mut self, id
: hir
::HirId
, segment
: &hir
::PathSegment
<'tcx
>) {
762 let segment_data
= self.save_ctxt
.get_path_segment_data_with_id(segment
, id
);
763 if let Some(segment_data
) = segment_data
{
764 self.dumper
.dump_ref(segment_data
);
768 fn process_path(&mut self, id
: hir
::HirId
, path
: &hir
::QPath
<'tcx
>) {
769 if self.span
.filter_generated(path
.span()) {
772 self.dump_path_ref(id
, path
);
775 let segments
= match path
{
776 hir
::QPath
::Resolved(ty
, path
) => {
777 if let Some(ty
) = ty
{
782 hir
::QPath
::TypeRelative(ty
, segment
) => {
784 std
::slice
::from_ref(*segment
)
786 hir
::QPath
::LangItem(..) => return,
788 for seg
in segments
{
789 if let Some(ref generic_args
) = seg
.args
{
790 for arg
in generic_args
.args
{
791 if let hir
::GenericArg
::Type(ref ty
) = arg
{
798 if let hir
::QPath
::Resolved(_
, path
) = path
{
799 self.write_sub_paths_truncated(path
);
803 fn process_struct_lit(
805 ex
: &'tcx hir
::Expr
<'tcx
>,
806 path
: &'tcx hir
::QPath
<'tcx
>,
807 fields
: &'tcx
[hir
::Field
<'tcx
>],
808 variant
: &'tcx ty
::VariantDef
,
809 base
: Option
<&'tcx hir
::Expr
<'tcx
>>,
811 if let Some(struct_lit_data
) = self.save_ctxt
.get_expr_data(ex
) {
812 if let hir
::QPath
::Resolved(_
, path
) = path
{
813 self.write_sub_paths_truncated(path
);
815 down_cast_data
!(struct_lit_data
, RefData
, ex
.span
);
816 if !generated_code(ex
.span
) {
817 self.dumper
.dump_ref(struct_lit_data
);
820 for field
in fields
{
821 if let Some(field_data
) = self.save_ctxt
.get_field_ref_data(field
, variant
) {
822 self.dumper
.dump_ref(field_data
);
825 self.visit_expr(&field
.expr
)
829 walk_list
!(self, visit_expr
, base
);
832 fn process_method_call(
834 ex
: &'tcx hir
::Expr
<'tcx
>,
835 seg
: &'tcx hir
::PathSegment
<'tcx
>,
836 args
: &'tcx
[hir
::Expr
<'tcx
>],
838 debug
!("process_method_call {:?} {:?}", ex
, ex
.span
);
839 if let Some(mcd
) = self.save_ctxt
.get_expr_data(ex
) {
840 down_cast_data
!(mcd
, RefData
, ex
.span
);
841 if !generated_code(ex
.span
) {
842 self.dumper
.dump_ref(mcd
);
846 // Explicit types in the turbo-fish.
847 if let Some(generic_args
) = seg
.args
{
848 for arg
in generic_args
.args
{
849 if let hir
::GenericArg
::Type(ty
) = arg
{
855 // walk receiver and args
856 walk_list
!(self, visit_expr
, args
);
859 fn process_pat(&mut self, p
: &'tcx hir
::Pat
<'tcx
>) {
861 hir
::PatKind
::Struct(ref _path
, fields
, _
) => {
862 // FIXME do something with _path?
863 let adt
= match self.save_ctxt
.typeck_results().node_type_opt(p
.hir_id
) {
864 Some(ty
) if ty
.ty_adt_def().is_some() => ty
.ty_adt_def().unwrap(),
866 intravisit
::walk_pat(self, p
);
870 let variant
= adt
.variant_of_res(self.save_ctxt
.get_path_res(p
.hir_id
));
872 for field
in fields
{
873 if let Some(index
) = self.tcx
.find_field_index(field
.ident
, variant
) {
874 if !self.span
.filter_generated(field
.ident
.span
) {
875 let span
= self.span_from_span(field
.ident
.span
);
876 self.dumper
.dump_ref(Ref
{
877 kind
: RefKind
::Variable
,
879 ref_id
: id_from_def_id(variant
.fields
[index
].did
),
883 self.visit_pat(&field
.pat
);
886 _
=> intravisit
::walk_pat(self, p
),
890 fn process_var_decl(&mut self, pat
: &'tcx hir
::Pat
<'tcx
>) {
891 // The pattern could declare multiple new vars,
892 // we must walk the pattern and collect them all.
893 let mut collector
= PathCollector
::new(self.tcx
);
894 collector
.visit_pat(&pat
);
895 self.visit_pat(&pat
);
897 // Process collected paths.
898 for (id
, ident
, _
) in collector
.collected_idents
{
899 let res
= self.save_ctxt
.get_path_res(id
);
901 Res
::Local(hir_id
) => {
905 .node_type_opt(hir_id
)
906 .map(|t
| t
.to_string())
907 .unwrap_or_default();
909 // Rust uses the id of the pattern for var lookups, so we'll use it too.
910 if !self.span
.filter_generated(ident
.span
) {
911 let qualname
= format
!("{}${}", ident
.to_string(), hir_id
);
912 let id
= id_from_hir_id(hir_id
, &self.save_ctxt
);
913 let span
= self.span_from_span(ident
.span
);
915 self.dumper
.dump_def(
916 &Access { public: false, reachable: false }
,
918 kind
: DefKind
::Local
,
921 name
: ident
.to_string(),
937 | HirDefKind
::AssocConst
939 | HirDefKind
::Variant
940 | HirDefKind
::TyAlias
941 | HirDefKind
::AssocTy
,
944 | Res
::SelfTy(..) => {
945 self.dump_path_segment_ref(id
, &hir
::PathSegment
::from_ident(ident
));
948 error
!("unexpected definition kind when processing collected idents: {:?}", def
)
953 for (id
, ref path
) in collector
.collected_paths
{
954 self.process_path(id
, path
);
958 /// Extracts macro use and definition information from the AST node defined
959 /// by the given NodeId, using the expansion information from the node's
962 /// If the span is not macro-generated, do nothing, else use callee and
963 /// callsite spans to record macro definition and use data, using the
964 /// mac_uses and mac_defs sets to prevent multiples.
965 fn process_macro_use(&mut self, _span
: Span
) {
966 // FIXME if we're not dumping the defs (see below), there is no point
967 // dumping refs either.
968 // let source_span = span.source_callsite();
969 // if !self.macro_calls.insert(source_span) {
973 // let data = match self.save_ctxt.get_macro_use_data(span) {
975 // Some(data) => data,
978 // self.dumper.macro_use(data);
980 // FIXME write the macro def
981 // let mut hasher = DefaultHasher::new();
982 // data.callee_span.hash(&mut hasher);
983 // let hash = hasher.finish();
984 // let qualname = format!("{}::{}", data.name, hash);
985 // Don't write macro definition for imported macros
986 // if !self.mac_defs.contains(&data.callee_span)
987 // && !data.imported {
988 // self.mac_defs.insert(data.callee_span);
989 // if let Some(sub_span) = self.span.span_for_macro_def_name(data.callee_span) {
990 // self.dumper.macro_data(MacroData {
992 // name: data.name.clone(),
993 // qualname: qualname.clone(),
994 // // FIXME where do macro docs come from?
995 // docs: String::new(),
996 // }.lower(self.tcx));
1001 fn process_trait_item(&mut self, trait_item
: &'tcx hir
::TraitItem
<'tcx
>, trait_id
: DefId
) {
1002 self.process_macro_use(trait_item
.span
);
1003 let vis_span
= trait_item
.span
.shrink_to_lo();
1004 match trait_item
.kind
{
1005 hir
::TraitItemKind
::Const(ref ty
, body
) => {
1006 let body
= body
.map(|b
| &self.tcx
.hir().body(b
).value
);
1007 let respan
= respan(vis_span
, hir
::VisibilityKind
::Public
);
1008 self.process_assoc_const(
1018 hir
::TraitItemKind
::Fn(ref sig
, ref trait_fn
) => {
1020 if let hir
::TraitFn
::Provided(body
) = trait_fn { Some(*body) }
else { None }
;
1021 let respan
= respan(vis_span
, hir
::VisibilityKind
::Public
);
1022 self.process_method(
1027 &trait_item
.generics
,
1032 hir
::TraitItemKind
::Type(ref bounds
, ref default_ty
) => {
1033 // FIXME do something with _bounds (for type refs)
1034 let name
= trait_item
.ident
.name
.to_string();
1035 let qualname
= format
!(
1038 .def_path_str(self.tcx
.hir().local_def_id(trait_item
.hir_id
).to_def_id())
1041 if !self.span
.filter_generated(trait_item
.ident
.span
) {
1042 let span
= self.span_from_span(trait_item
.ident
.span
);
1043 let id
= id_from_hir_id(trait_item
.hir_id
, &self.save_ctxt
);
1045 self.dumper
.dump_def(
1046 &Access { public: true, reachable: true }
,
1048 kind
: DefKind
::Type
,
1053 value
: self.span
.snippet(trait_item
.span
),
1054 parent
: Some(id_from_def_id(trait_id
)),
1057 docs
: self.save_ctxt
.docs_for_attrs(&trait_item
.attrs
),
1058 sig
: sig
::assoc_type_signature(
1062 default_ty
.as_ref().map(|ty
| &**ty
),
1065 attributes
: lower_attributes(
1066 trait_item
.attrs
.to_vec(),
1073 if let &Some(ref default_ty
) = default_ty
{
1074 self.visit_ty(default_ty
)
1080 fn process_impl_item(&mut self, impl_item
: &'tcx hir
::ImplItem
<'tcx
>, impl_id
: DefId
) {
1081 self.process_macro_use(impl_item
.span
);
1082 match impl_item
.kind
{
1083 hir
::ImplItemKind
::Const(ref ty
, body
) => {
1084 let body
= self.tcx
.hir().body(body
);
1085 self.process_assoc_const(
1095 hir
::ImplItemKind
::Fn(ref sig
, body
) => {
1096 self.process_method(
1101 &impl_item
.generics
,
1106 hir
::ImplItemKind
::TyAlias(ref ty
) => {
1107 // FIXME: uses of the assoc type should ideally point to this
1108 // 'def' and the name here should be a ref to the def in the
1115 pub(crate) fn process_crate(&mut self, krate
: &'tcx hir
::Crate
<'tcx
>) {
1116 let id
= hir
::CRATE_HIR_ID
;
1118 format
!("::{}", self.tcx
.def_path_str(self.tcx
.hir().local_def_id(id
).to_def_id()));
1120 let sm
= self.tcx
.sess
.source_map();
1121 let filename
= sm
.span_to_filename(krate
.item
.span
);
1122 let data_id
= id_from_hir_id(id
, &self.save_ctxt
);
1123 let children
= krate
1128 .map(|i
| id_from_hir_id(i
.id
, &self.save_ctxt
))
1130 let span
= self.span_from_span(krate
.item
.span
);
1132 self.dumper
.dump_def(
1133 &Access { public: true, reachable: true }
,
1137 name
: String
::new(),
1140 value
: filename
.to_string(),
1144 docs
: self.save_ctxt
.docs_for_attrs(krate
.item
.attrs
),
1146 attributes
: lower_attributes(krate
.item
.attrs
.to_owned(), &self.save_ctxt
),
1149 intravisit
::walk_crate(self, krate
);
1152 fn process_bounds(&mut self, bounds
: hir
::GenericBounds
<'tcx
>) {
1153 for bound
in bounds
{
1154 if let hir
::GenericBound
::Trait(ref trait_ref
, _
) = *bound
{
1156 trait_ref
.trait_ref
.hir_ref_id
,
1157 &hir
::QPath
::Resolved(None
, &trait_ref
.trait_ref
.path
),
1164 impl<'tcx
> Visitor
<'tcx
> for DumpVisitor
<'tcx
> {
1165 type Map
= Map
<'tcx
>;
1167 fn nested_visit_map(&mut self) -> intravisit
::NestedVisitorMap
<Self::Map
> {
1168 intravisit
::NestedVisitorMap
::All(self.tcx
.hir())
1171 fn visit_item(&mut self, item
: &'tcx hir
::Item
<'tcx
>) {
1172 self.process_macro_use(item
.span
);
1174 hir
::ItemKind
::Use(path
, hir
::UseKind
::Single
) => {
1175 let sub_span
= path
.segments
.last().unwrap().ident
.span
;
1176 if !self.span
.filter_generated(sub_span
) {
1177 let access
= access_from
!(self.save_ctxt
, item
, item
.hir_id
);
1178 let ref_id
= self.lookup_def_id(item
.hir_id
).map(id_from_def_id
);
1179 let span
= self.span_from_span(sub_span
);
1184 .opt_local_def_id(item
.hir_id
)
1185 .and_then(|id
| self.save_ctxt
.tcx
.parent(id
.to_def_id()))
1186 .map(id_from_def_id
);
1190 kind
: ImportKind
::Use
,
1194 name
: item
.ident
.to_string(),
1195 value
: String
::new(),
1199 self.write_sub_paths_truncated(&path
);
1202 hir
::ItemKind
::Use(path
, hir
::UseKind
::Glob
) => {
1203 // Make a comma-separated list of names of imported modules.
1204 let def_id
= self.tcx
.hir().local_def_id(item
.hir_id
);
1205 let names
= self.tcx
.names_imported_by_glob_use(def_id
);
1206 let names
: Vec
<_
> = names
.iter().map(|n
| n
.to_string()).collect();
1208 // Otherwise it's a span with wrong macro expansion info, which
1209 // we don't want to track anyway, since it's probably macro-internal `use`
1210 if let Some(sub_span
) = self.span
.sub_span_of_star(item
.span
) {
1211 if !self.span
.filter_generated(item
.span
) {
1212 let access
= access_from
!(self.save_ctxt
, item
, item
.hir_id
);
1213 let span
= self.span_from_span(sub_span
);
1218 .opt_local_def_id(item
.hir_id
)
1219 .and_then(|id
| self.save_ctxt
.tcx
.parent(id
.to_def_id()))
1220 .map(id_from_def_id
);
1224 kind
: ImportKind
::GlobUse
,
1228 name
: "*".to_owned(),
1229 value
: names
.join(", "),
1233 self.write_sub_paths(&path
);
1237 hir
::ItemKind
::ExternCrate(_
) => {
1238 let name_span
= item
.ident
.span
;
1239 if !self.span
.filter_generated(name_span
) {
1240 let span
= self.span_from_span(name_span
);
1245 .opt_local_def_id(item
.hir_id
)
1246 .and_then(|id
| self.save_ctxt
.tcx
.parent(id
.to_def_id()))
1247 .map(id_from_def_id
);
1249 &Access { public: false, reachable: false }
,
1251 kind
: ImportKind
::ExternCrate
,
1255 name
: item
.ident
.to_string(),
1256 value
: String
::new(),
1262 hir
::ItemKind
::Fn(ref sig
, ref ty_params
, body
) => {
1263 self.process_fn(item
, sig
.decl
, &sig
.header
, ty_params
, body
)
1265 hir
::ItemKind
::Static(ref typ
, _
, body
) => {
1266 let body
= self.tcx
.hir().body(body
);
1267 self.process_static_or_const_item(item
, typ
, &body
.value
)
1269 hir
::ItemKind
::Const(ref typ
, body
) => {
1270 let body
= self.tcx
.hir().body(body
);
1271 self.process_static_or_const_item(item
, typ
, &body
.value
)
1273 hir
::ItemKind
::Struct(ref def
, ref ty_params
)
1274 | hir
::ItemKind
::Union(ref def
, ref ty_params
) => {
1275 self.process_struct(item
, def
, ty_params
)
1277 hir
::ItemKind
::Enum(ref def
, ref ty_params
) => self.process_enum(item
, def
, ty_params
),
1278 hir
::ItemKind
::Impl { ref generics, ref of_trait, ref self_ty, ref items, .. }
=> {
1279 self.process_impl(item
, generics
, of_trait
, &self_ty
, items
)
1281 hir
::ItemKind
::Trait(_
, _
, ref generics
, ref trait_refs
, methods
) => {
1282 self.process_trait(item
, generics
, trait_refs
, methods
)
1284 hir
::ItemKind
::Mod(ref m
) => {
1285 self.process_mod(item
);
1286 intravisit
::walk_mod(self, m
, item
.hir_id
);
1288 hir
::ItemKind
::TyAlias(ty
, ref generics
) => {
1289 let qualname
= format
!(
1291 self.tcx
.def_path_str(self.tcx
.hir().local_def_id(item
.hir_id
).to_def_id())
1293 let value
= ty_to_string(&ty
);
1294 if !self.span
.filter_generated(item
.ident
.span
) {
1295 let span
= self.span_from_span(item
.ident
.span
);
1296 let id
= id_from_hir_id(item
.hir_id
, &self.save_ctxt
);
1298 self.dumper
.dump_def(
1299 &access_from
!(self.save_ctxt
, item
, item
.hir_id
),
1301 kind
: DefKind
::Type
,
1304 name
: item
.ident
.to_string(),
1305 qualname
: qualname
.clone(),
1310 docs
: self.save_ctxt
.docs_for_attrs(&item
.attrs
),
1311 sig
: sig
::item_signature(item
, &self.save_ctxt
),
1312 attributes
: lower_attributes(item
.attrs
.to_vec(), &self.save_ctxt
),
1318 self.process_generic_params(generics
, &qualname
, item
.hir_id
);
1320 _
=> intravisit
::walk_item(self, item
),
1324 fn visit_generics(&mut self, generics
: &'tcx hir
::Generics
<'tcx
>) {
1325 for param
in generics
.params
{
1327 hir
::GenericParamKind
::Lifetime { .. }
=> {}
1328 hir
::GenericParamKind
::Type { ref default, .. }
=> {
1329 self.process_bounds(param
.bounds
);
1330 if let Some(ref ty
) = default {
1334 hir
::GenericParamKind
::Const { ref ty }
=> {
1335 self.process_bounds(param
.bounds
);
1340 for pred
in generics
.where_clause
.predicates
{
1341 if let hir
::WherePredicate
::BoundPredicate(ref wbp
) = *pred
{
1342 self.process_bounds(wbp
.bounds
);
1343 self.visit_ty(wbp
.bounded_ty
);
1348 fn visit_ty(&mut self, t
: &'tcx hir
::Ty
<'tcx
>) {
1349 self.process_macro_use(t
.span
);
1351 hir
::TyKind
::Path(ref path
) => {
1352 if generated_code(t
.span
) {
1356 if let Some(id
) = self.lookup_def_id(t
.hir_id
) {
1357 let sub_span
= path
.last_segment_span();
1358 let span
= self.span_from_span(sub_span
);
1359 self.dumper
.dump_ref(Ref
{
1360 kind
: RefKind
::Type
,
1362 ref_id
: id_from_def_id(id
),
1366 if let hir
::QPath
::Resolved(_
, path
) = path
{
1367 self.write_sub_paths_truncated(path
);
1369 intravisit
::walk_qpath(self, path
, t
.hir_id
, t
.span
);
1371 hir
::TyKind
::Array(ref ty
, ref anon_const
) => {
1373 let map
= self.tcx
.hir();
1374 self.nest_typeck_results(self.tcx
.hir().local_def_id(anon_const
.hir_id
), |v
| {
1375 v
.visit_expr(&map
.body(anon_const
.body
).value
)
1378 hir
::TyKind
::OpaqueDef(item_id
, _
) => {
1379 let item
= self.tcx
.hir().item(item_id
.id
);
1380 self.nest_typeck_results(self.tcx
.hir().local_def_id(item_id
.id
), |v
| {
1384 _
=> intravisit
::walk_ty(self, t
),
1388 fn visit_expr(&mut self, ex
: &'tcx hir
::Expr
<'tcx
>) {
1389 debug
!("visit_expr {:?}", ex
.kind
);
1390 self.process_macro_use(ex
.span
);
1392 hir
::ExprKind
::Struct(ref path
, ref fields
, ref base
) => {
1393 let hir_expr
= self.save_ctxt
.tcx
.hir().expect_expr(ex
.hir_id
);
1394 let adt
= match self.save_ctxt
.typeck_results().expr_ty_opt(&hir_expr
) {
1395 Some(ty
) if ty
.ty_adt_def().is_some() => ty
.ty_adt_def().unwrap(),
1397 intravisit
::walk_expr(self, ex
);
1401 let res
= self.save_ctxt
.get_path_res(hir_expr
.hir_id
);
1402 self.process_struct_lit(ex
, path
, fields
, adt
.variant_of_res(res
), *base
)
1404 hir
::ExprKind
::MethodCall(ref seg
, _
, args
, _
) => {
1405 self.process_method_call(ex
, seg
, args
)
1407 hir
::ExprKind
::Field(ref sub_ex
, _
) => {
1408 self.visit_expr(&sub_ex
);
1410 if let Some(field_data
) = self.save_ctxt
.get_expr_data(ex
) {
1411 down_cast_data
!(field_data
, RefData
, ex
.span
);
1412 if !generated_code(ex
.span
) {
1413 self.dumper
.dump_ref(field_data
);
1417 hir
::ExprKind
::Closure(_
, ref decl
, body
, _fn_decl_span
, _
) => {
1418 let id
= format
!("${}", ex
.hir_id
);
1420 // walk arg and return types
1421 for ty
in decl
.inputs
{
1425 if let hir
::FnRetTy
::Return(ref ret_ty
) = decl
.output
{
1426 self.visit_ty(ret_ty
);
1430 let map
= self.tcx
.hir();
1431 self.nest_typeck_results(self.tcx
.hir().local_def_id(ex
.hir_id
), |v
| {
1432 let body
= map
.body(body
);
1433 v
.process_formals(body
.params
, &id
);
1434 v
.visit_expr(&body
.value
)
1437 hir
::ExprKind
::Repeat(ref expr
, ref anon_const
) => {
1438 self.visit_expr(expr
);
1439 let map
= self.tcx
.hir();
1440 self.nest_typeck_results(self.tcx
.hir().local_def_id(anon_const
.hir_id
), |v
| {
1441 v
.visit_expr(&map
.body(anon_const
.body
).value
)
1444 // In particular, we take this branch for call and path expressions,
1445 // where we'll index the idents involved just by continuing to walk.
1446 _
=> intravisit
::walk_expr(self, ex
),
1450 fn visit_pat(&mut self, p
: &'tcx hir
::Pat
<'tcx
>) {
1451 self.process_macro_use(p
.span
);
1452 self.process_pat(p
);
1455 fn visit_arm(&mut self, arm
: &'tcx hir
::Arm
<'tcx
>) {
1456 self.process_var_decl(&arm
.pat
);
1457 if let Some(hir
::Guard
::If(expr
)) = &arm
.guard
{
1458 self.visit_expr(expr
);
1460 self.visit_expr(&arm
.body
);
1463 fn visit_qpath(&mut self, path
: &'tcx hir
::QPath
<'tcx
>, id
: hir
::HirId
, _
: Span
) {
1464 self.process_path(id
, path
);
1467 fn visit_stmt(&mut self, s
: &'tcx hir
::Stmt
<'tcx
>) {
1468 self.process_macro_use(s
.span
);
1469 intravisit
::walk_stmt(self, s
)
1472 fn visit_local(&mut self, l
: &'tcx hir
::Local
<'tcx
>) {
1473 self.process_macro_use(l
.span
);
1474 self.process_var_decl(&l
.pat
);
1476 // Just walk the initialiser and type (don't want to walk the pattern again).
1477 walk_list
!(self, visit_ty
, &l
.ty
);
1478 walk_list
!(self, visit_expr
, &l
.init
);
1481 fn visit_foreign_item(&mut self, item
: &'tcx hir
::ForeignItem
<'tcx
>) {
1482 let access
= access_from
!(self.save_ctxt
, item
, item
.hir_id
);
1485 hir
::ForeignItemKind
::Fn(decl
, _
, ref generics
) => {
1486 if let Some(fn_data
) = self.save_ctxt
.get_extern_item_data(item
) {
1487 down_cast_data
!(fn_data
, DefData
, item
.span
);
1489 self.process_generic_params(generics
, &fn_data
.qualname
, item
.hir_id
);
1490 self.dumper
.dump_def(&access
, fn_data
);
1493 for ty
in decl
.inputs
{
1497 if let hir
::FnRetTy
::Return(ref ret_ty
) = decl
.output
{
1498 self.visit_ty(ret_ty
);
1501 hir
::ForeignItemKind
::Static(ref ty
, _
) => {
1502 if let Some(var_data
) = self.save_ctxt
.get_extern_item_data(item
) {
1503 down_cast_data
!(var_data
, DefData
, item
.span
);
1504 self.dumper
.dump_def(&access
, var_data
);
1509 hir
::ForeignItemKind
::Type
=> {
1510 if let Some(var_data
) = self.save_ctxt
.get_extern_item_data(item
) {
1511 down_cast_data
!(var_data
, DefData
, item
.span
);
1512 self.dumper
.dump_def(&access
, var_data
);