1 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
3 #![recursion_limit = "256"]
12 use rustc_ast
::util
::comments
::beautify_doc_string
;
13 use rustc_ast_pretty
::pprust
::attribute_to_string
;
15 use rustc_hir
::def
::{DefKind as HirDefKind, Res}
;
16 use rustc_hir
::def_id
::{DefId, LOCAL_CRATE}
;
17 use rustc_hir
::intravisit
::{self, Visitor}
;
19 use rustc_hir_pretty
::{enum_def_to_string, fn_to_string, ty_to_string}
;
20 use rustc_middle
::hir
::map
::Map
;
21 use rustc_middle
::middle
::cstore
::ExternCrate
;
22 use rustc_middle
::middle
::privacy
::AccessLevels
;
23 use rustc_middle
::ty
::{self, print::with_no_trimmed_paths, DefIdTree, TyCtxt}
;
24 use rustc_middle
::{bug, span_bug}
;
25 use rustc_session
::config
::{CrateType, Input, OutputType}
;
26 use rustc_session
::output
::{filename_for_metadata, out_filename}
;
27 use rustc_span
::source_map
::Spanned
;
28 use rustc_span
::symbol
::Ident
;
32 use std
::default::Default
;
35 use std
::io
::BufWriter
;
36 use std
::path
::{Path, PathBuf}
;
38 use dump_visitor
::DumpVisitor
;
39 use span_utils
::SpanUtils
;
41 use rls_data
::config
::Config
;
43 Analysis
, Def
, DefKind
, ExternalCrateData
, GlobalCrateId
, Impl
, ImplKind
, MacroRef
, Ref
,
44 RefKind
, Relation
, RelationKind
, SpanData
,
47 use tracing
::{debug, error, info}
;
49 pub struct SaveContext
<'tcx
> {
51 maybe_typeck_results
: Option
<&'tcx ty
::TypeckResults
<'tcx
>>,
52 access_levels
: &'tcx AccessLevels
,
53 span_utils
: SpanUtils
<'tcx
>,
55 impl_counter
: Cell
<u32>,
62 RelationData(Relation
, Impl
),
65 impl<'tcx
> SaveContext
<'tcx
> {
66 /// Gets the type-checking results for the current body.
67 /// As this will ICE if called outside bodies, only call when working with
68 /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies).
70 fn typeck_results(&self) -> &'tcx ty
::TypeckResults
<'tcx
> {
71 self.maybe_typeck_results
.expect("`SaveContext::typeck_results` called outside of body")
74 fn span_from_span(&self, span
: Span
) -> SpanData
{
75 use rls_span
::{Column, Row}
;
77 let sm
= self.tcx
.sess
.source_map();
78 let start
= sm
.lookup_char_pos(span
.lo());
79 let end
= sm
.lookup_char_pos(span
.hi());
82 file_name
: start
.file
.name
.prefer_remapped().to_string().into(),
83 byte_start
: span
.lo().0,
84 byte_end
: span
.hi().0,
85 line_start
: Row
::new_one_indexed(start
.line
as u32),
86 line_end
: Row
::new_one_indexed(end
.line
as u32),
87 column_start
: Column
::new_one_indexed(start
.col
.0 as u32 + 1),
88 column_end
: Column
::new_one_indexed(end
.col
.0 as u32 + 1),
92 // Returns path to the compilation output (e.g., libfoo-12345678.rmeta)
93 pub fn compilation_output(&self, crate_name
: &str) -> PathBuf
{
94 let sess
= &self.tcx
.sess
;
95 // Save-analysis is emitted per whole session, not per each crate type
96 let crate_type
= sess
.crate_types()[0];
97 let outputs
= &*self.tcx
.output_filenames(());
99 if outputs
.outputs
.contains_key(&OutputType
::Metadata
) {
100 filename_for_metadata(sess
, crate_name
, outputs
)
101 } else if outputs
.outputs
.should_codegen() {
102 out_filename(sess
, crate_type
, outputs
, crate_name
)
104 // Otherwise it's only a DepInfo, in which case we return early and
105 // not even reach the analysis stage.
110 // List external crates used by the current crate.
111 pub fn get_external_crates(&self) -> Vec
<ExternalCrateData
> {
112 let mut result
= Vec
::with_capacity(self.tcx
.crates(()).len());
114 for &n
in self.tcx
.crates(()).iter() {
115 let span
= match self.tcx
.extern_crate(n
.as_def_id()) {
116 Some(&ExternCrate { span, .. }
) => span
,
118 debug
!("skipping crate {}, no data", n
);
122 let lo_loc
= self.span_utils
.sess
.source_map().lookup_char_pos(span
.lo());
123 result
.push(ExternalCrateData
{
124 // FIXME: change file_name field to PathBuf in rls-data
125 // https://github.com/nrc/rls-data/issues/7
126 file_name
: self.span_utils
.make_filename_string(&lo_loc
.file
),
129 name
: self.tcx
.crate_name(n
).to_string(),
131 self.tcx
.def_path_hash(n
.as_def_id()).stable_crate_id().to_u64(),
141 pub fn get_extern_item_data(&self, item
: &hir
::ForeignItem
<'_
>) -> Option
<Data
> {
142 let def_id
= item
.def_id
.to_def_id();
143 let qualname
= format
!("::{}", self.tcx
.def_path_str(def_id
));
144 let attrs
= self.tcx
.hir().attrs(item
.hir_id());
146 hir
::ForeignItemKind
::Fn(ref decl
, arg_names
, ref generics
) => {
147 filter
!(self.span_utils
, item
.ident
.span
);
149 Some(Data
::DefData(Def
{
150 kind
: DefKind
::ForeignFunction
,
151 id
: id_from_def_id(def_id
),
152 span
: self.span_from_span(item
.ident
.span
),
153 name
: item
.ident
.to_string(),
158 // functions in extern block are implicitly unsafe
159 unsafety
: hir
::Unsafety
::Unsafe
,
160 // functions in extern block cannot be const
161 constness
: hir
::Constness
::NotConst
,
162 abi
: self.tcx
.hir().get_foreign_abi(item
.hir_id()),
163 // functions in extern block cannot be async
164 asyncness
: hir
::IsAsync
::NotAsync
,
166 Some(item
.ident
.name
),
175 docs
: self.docs_for_attrs(attrs
),
176 sig
: sig
::foreign_item_signature(item
, self),
177 attributes
: lower_attributes(attrs
.to_vec(), self),
180 hir
::ForeignItemKind
::Static(ref ty
, _
) => {
181 filter
!(self.span_utils
, item
.ident
.span
);
183 let id
= id_from_def_id(def_id
);
184 let span
= self.span_from_span(item
.ident
.span
);
186 Some(Data
::DefData(Def
{
187 kind
: DefKind
::ForeignStatic
,
190 name
: item
.ident
.to_string(),
192 value
: ty_to_string(ty
),
196 docs
: self.docs_for_attrs(attrs
),
197 sig
: sig
::foreign_item_signature(item
, self),
198 attributes
: lower_attributes(attrs
.to_vec(), self),
201 // FIXME(plietar): needs a new DefKind in rls-data
202 hir
::ForeignItemKind
::Type
=> None
,
206 pub fn get_item_data(&self, item
: &hir
::Item
<'_
>) -> Option
<Data
> {
207 let def_id
= item
.def_id
.to_def_id();
208 let attrs
= self.tcx
.hir().attrs(item
.hir_id());
210 hir
::ItemKind
::Fn(ref sig
, ref generics
, _
) => {
211 let qualname
= format
!("::{}", self.tcx
.def_path_str(def_id
));
212 filter
!(self.span_utils
, item
.ident
.span
);
213 Some(Data
::DefData(Def
{
214 kind
: DefKind
::Function
,
215 id
: id_from_def_id(def_id
),
216 span
: self.span_from_span(item
.ident
.span
),
217 name
: item
.ident
.to_string(),
222 Some(item
.ident
.name
),
231 docs
: self.docs_for_attrs(attrs
),
232 sig
: sig
::item_signature(item
, self),
233 attributes
: lower_attributes(attrs
.to_vec(), self),
236 hir
::ItemKind
::Static(ref typ
, ..) => {
237 let qualname
= format
!("::{}", self.tcx
.def_path_str(def_id
));
239 filter
!(self.span_utils
, item
.ident
.span
);
241 let id
= id_from_def_id(def_id
);
242 let span
= self.span_from_span(item
.ident
.span
);
244 Some(Data
::DefData(Def
{
245 kind
: DefKind
::Static
,
248 name
: item
.ident
.to_string(),
250 value
: ty_to_string(&typ
),
254 docs
: self.docs_for_attrs(attrs
),
255 sig
: sig
::item_signature(item
, self),
256 attributes
: lower_attributes(attrs
.to_vec(), self),
259 hir
::ItemKind
::Const(ref typ
, _
) => {
260 let qualname
= format
!("::{}", self.tcx
.def_path_str(def_id
));
261 filter
!(self.span_utils
, item
.ident
.span
);
263 let id
= id_from_def_id(def_id
);
264 let span
= self.span_from_span(item
.ident
.span
);
266 Some(Data
::DefData(Def
{
267 kind
: DefKind
::Const
,
270 name
: item
.ident
.to_string(),
272 value
: ty_to_string(typ
),
276 docs
: self.docs_for_attrs(attrs
),
277 sig
: sig
::item_signature(item
, self),
278 attributes
: lower_attributes(attrs
.to_vec(), self),
281 hir
::ItemKind
::Mod(ref m
) => {
282 let qualname
= format
!("::{}", self.tcx
.def_path_str(def_id
));
284 let sm
= self.tcx
.sess
.source_map();
285 let filename
= sm
.span_to_filename(m
.inner
);
287 filter
!(self.span_utils
, item
.ident
.span
);
289 Some(Data
::DefData(Def
{
291 id
: id_from_def_id(def_id
),
292 name
: item
.ident
.to_string(),
294 span
: self.span_from_span(item
.ident
.span
),
295 value
: filename
.prefer_remapped().to_string(),
300 .map(|i
| id_from_def_id(i
.def_id
.to_def_id()))
303 docs
: self.docs_for_attrs(attrs
),
304 sig
: sig
::item_signature(item
, self),
305 attributes
: lower_attributes(attrs
.to_vec(), self),
308 hir
::ItemKind
::Enum(ref def
, ref generics
) => {
309 let name
= item
.ident
.to_string();
310 let qualname
= format
!("::{}", self.tcx
.def_path_str(def_id
));
311 filter
!(self.span_utils
, item
.ident
.span
);
313 enum_def_to_string(def
, generics
, item
.ident
.name
, item
.span
, &item
.vis
);
314 Some(Data
::DefData(Def
{
316 id
: id_from_def_id(def_id
),
317 span
: self.span_from_span(item
.ident
.span
),
322 children
: def
.variants
.iter().map(|v
| id_from_hir_id(v
.id
, self)).collect(),
324 docs
: self.docs_for_attrs(attrs
),
325 sig
: sig
::item_signature(item
, self),
326 attributes
: lower_attributes(attrs
.to_vec(), self),
329 hir
::ItemKind
::Impl(hir
::Impl { ref of_trait, ref self_ty, ref items, .. }
) => {
330 if let hir
::TyKind
::Path(hir
::QPath
::Resolved(_
, ref path
)) = self_ty
.kind
{
331 // Common case impl for a struct or something basic.
332 if generated_code(path
.span
) {
335 let sub_span
= path
.segments
.last().unwrap().ident
.span
;
336 filter
!(self.span_utils
, sub_span
);
338 let impl_id
= self.next_impl_id();
339 let span
= self.span_from_span(sub_span
);
341 let type_data
= self.lookup_def_id(self_ty
.hir_id
);
342 type_data
.map(|type_data
| {
345 kind
: RelationKind
::Impl { id: impl_id }
,
347 from
: id_from_def_id(type_data
),
350 .and_then(|t
| self.lookup_def_id(t
.hir_ref_id
))
352 .unwrap_or_else(null_id
),
356 kind
: match *of_trait
{
357 Some(_
) => ImplKind
::Direct
,
358 None
=> ImplKind
::Inherent
,
361 value
: String
::new(),
365 .map(|i
| id_from_def_id(i
.id
.def_id
.to_def_id()))
384 pub fn get_field_data(&self, field
: &hir
::FieldDef
<'_
>, scope
: hir
::HirId
) -> Option
<Def
> {
385 let name
= field
.ident
.to_string();
386 let scope_def_id
= self.tcx
.hir().local_def_id(scope
).to_def_id();
387 let qualname
= format
!("::{}::{}", self.tcx
.def_path_str(scope_def_id
), field
.ident
);
388 filter
!(self.span_utils
, field
.ident
.span
);
389 let field_def_id
= self.tcx
.hir().local_def_id(field
.hir_id
).to_def_id();
390 let typ
= self.tcx
.type_of(field_def_id
).to_string();
392 let id
= id_from_def_id(field_def_id
);
393 let span
= self.span_from_span(field
.ident
.span
);
394 let attrs
= self.tcx
.hir().attrs(field
.hir_id
);
397 kind
: DefKind
::Field
,
403 parent
: Some(id_from_def_id(scope_def_id
)),
406 docs
: self.docs_for_attrs(attrs
),
407 sig
: sig
::field_signature(field
, self),
408 attributes
: lower_attributes(attrs
.to_vec(), self),
412 // FIXME would be nice to take a MethodItem here, but the ast provides both
413 // trait and impl flavours, so the caller must do the disassembly.
414 pub fn get_method_data(&self, hir_id
: hir
::HirId
, ident
: Ident
, span
: Span
) -> Option
<Def
> {
415 // The qualname for a method is the trait name or name of the struct in an impl in
416 // which the method is declared in, followed by the method's name.
417 let def_id
= self.tcx
.hir().local_def_id(hir_id
).to_def_id();
418 let (qualname
, parent_scope
, decl_id
, docs
, attributes
) =
419 match self.tcx
.impl_of_method(def_id
) {
420 Some(impl_id
) => match self.tcx
.hir().get_if_local(impl_id
) {
421 Some(Node
::Item(item
)) => match item
.kind
{
422 hir
::ItemKind
::Impl(hir
::Impl { ref self_ty, .. }
) => {
423 let hir
= self.tcx
.hir();
425 let mut qualname
= String
::from("<");
427 .push_str(&rustc_hir_pretty
::id_to_string(&hir
, self_ty
.hir_id
));
429 let trait_id
= self.tcx
.trait_id_of_impl(impl_id
);
430 let mut docs
= String
::new();
431 let mut attrs
= vec
![];
432 if let Some(Node
::ImplItem(_
)) = hir
.find(hir_id
) {
433 attrs
= self.tcx
.hir().attrs(hir_id
).to_vec();
434 docs
= self.docs_for_attrs(&attrs
);
437 let mut decl_id
= None
;
438 if let Some(def_id
) = trait_id
{
439 // A method in a trait impl.
440 qualname
.push_str(" as ");
441 qualname
.push_str(&self.tcx
.def_path_str(def_id
));
445 .associated_items(def_id
)
446 .filter_by_name_unhygienic(ident
.name
)
448 .map(|item
| item
.def_id
);
452 (qualname
, trait_id
, decl_id
, docs
, attrs
)
457 "Container {:?} for method {} not an impl?",
466 "Container {:?} for method {} is not a node item {:?}",
473 None
=> match self.tcx
.trait_of_item(def_id
) {
475 let mut docs
= String
::new();
476 let mut attrs
= vec
![];
478 if let Some(Node
::TraitItem(_
)) = self.tcx
.hir().find(hir_id
) {
479 attrs
= self.tcx
.hir().attrs(hir_id
).to_vec();
480 docs
= self.docs_for_attrs(&attrs
);
484 format
!("::{}", self.tcx
.def_path_str(def_id
)),
492 debug
!("could not find container for method {} at {:?}", hir_id
, span
);
493 // This is not necessarily a bug, if there was a compilation error,
494 // the typeck results we need might not exist.
500 let qualname
= format
!("{}::{}", qualname
, ident
.name
);
502 filter
!(self.span_utils
, ident
.span
);
505 kind
: DefKind
::Method
,
506 id
: id_from_def_id(def_id
),
507 span
: self.span_from_span(ident
.span
),
508 name
: ident
.name
.to_string(),
510 // FIXME you get better data here by using the visitor.
511 value
: String
::new(),
512 parent
: parent_scope
.map(id_from_def_id
),
514 decl_id
: decl_id
.map(id_from_def_id
),
517 attributes
: lower_attributes(attributes
, self),
521 pub fn get_expr_data(&self, expr
: &hir
::Expr
<'_
>) -> Option
<Data
> {
522 let ty
= self.typeck_results().expr_ty_adjusted_opt(expr
)?
;
523 if matches
!(ty
.kind(), ty
::Error(_
)) {
527 hir
::ExprKind
::Field(ref sub_ex
, ident
) => {
528 match self.typeck_results().expr_ty_adjusted(&sub_ex
).kind() {
529 ty
::Adt(def
, _
) if !def
.is_enum() => {
530 let variant
= &def
.non_enum_variant();
531 filter
!(self.span_utils
, ident
.span
);
532 let span
= self.span_from_span(ident
.span
);
533 Some(Data
::RefData(Ref
{
534 kind
: RefKind
::Variable
,
538 .find_field_index(ident
, variant
)
539 .map(|index
| id_from_def_id(variant
.fields
[index
].did
))
540 .unwrap_or_else(null_id
),
543 ty
::Tuple(..) => None
,
545 debug
!("expected struct or union type, found {:?}", ty
);
550 hir
::ExprKind
::Struct(qpath
, ..) => match ty
.kind() {
552 let sub_span
= qpath
.last_segment_span();
553 filter
!(self.span_utils
, sub_span
);
554 let span
= self.span_from_span(sub_span
);
555 Some(Data
::RefData(Ref
{
558 ref_id
: id_from_def_id(def
.did
),
562 debug
!("expected adt, found {:?}", ty
);
566 hir
::ExprKind
::MethodCall(ref seg
, ..) => {
567 let method_id
= match self.typeck_results().type_dependent_def_id(expr
.hir_id
) {
570 debug
!("could not resolve method id for {:?}", expr
);
574 let (def_id
, decl_id
) = match self.tcx
.associated_item(method_id
).container
{
575 ty
::ImplContainer(_
) => (Some(method_id
), None
),
576 ty
::TraitContainer(_
) => (None
, Some(method_id
)),
578 let sub_span
= seg
.ident
.span
;
579 filter
!(self.span_utils
, sub_span
);
580 let span
= self.span_from_span(sub_span
);
581 Some(Data
::RefData(Ref
{
582 kind
: RefKind
::Function
,
584 ref_id
: def_id
.or(decl_id
).map(id_from_def_id
).unwrap_or_else(null_id
),
587 hir
::ExprKind
::Path(ref path
) => {
588 self.get_path_data(expr
.hir_id
, path
).map(Data
::RefData
)
592 bug
!("invalid expression: {:?}", expr
);
597 pub fn get_path_res(&self, hir_id
: hir
::HirId
) -> Res
{
598 match self.tcx
.hir().get(hir_id
) {
599 Node
::TraitRef(tr
) => tr
.path
.res
,
601 Node
::Item(&hir
::Item { kind: hir::ItemKind::Use(path, _), .. }
) => path
.res
,
602 Node
::Visibility(&Spanned
{
603 node
: hir
::VisibilityKind
::Restricted { ref path, .. }
,
607 Node
::PathSegment(seg
) => match seg
.res
{
608 Some(res
) if res
!= Res
::Err
=> res
,
610 let parent_node
= self.tcx
.hir().get_parent_node(hir_id
);
611 self.get_path_res(parent_node
)
615 Node
::Expr(&hir
::Expr { kind: hir::ExprKind::Struct(ref qpath, ..), .. }
) => {
616 self.typeck_results().qpath_res(qpath
, hir_id
)
619 Node
::Expr(&hir
::Expr { kind: hir::ExprKind::Path(ref qpath), .. }
)
620 | Node
::Pat(&hir
::Pat
{
622 hir
::PatKind
::Path(ref qpath
)
623 | hir
::PatKind
::Struct(ref qpath
, ..)
624 | hir
::PatKind
::TupleStruct(ref qpath
, ..),
627 | Node
::Ty(&hir
::Ty { kind: hir::TyKind::Path(ref qpath), .. }
) => match qpath
{
628 hir
::QPath
::Resolved(_
, path
) => path
.res
,
629 hir
::QPath
::TypeRelative(..) | hir
::QPath
::LangItem(..) => {
630 // #75962: `self.typeck_results` may be different from the `hir_id`'s result.
631 if self.tcx
.has_typeck_results(hir_id
.owner
.to_def_id()) {
632 self.tcx
.typeck(hir_id
.owner
).qpath_res(qpath
, hir_id
)
639 Node
::Binding(&hir
::Pat
{
640 kind
: hir
::PatKind
::Binding(_
, canonical_id
, ..), ..
641 }) => Res
::Local(canonical_id
),
647 pub fn get_path_data(&self, id
: hir
::HirId
, path
: &hir
::QPath
<'_
>) -> Option
<Ref
> {
648 let segment
= match path
{
649 hir
::QPath
::Resolved(_
, path
) => path
.segments
.last(),
650 hir
::QPath
::TypeRelative(_
, segment
) => Some(*segment
),
651 hir
::QPath
::LangItem(..) => None
,
653 segment
.and_then(|seg
| {
654 self.get_path_segment_data(seg
).or_else(|| self.get_path_segment_data_with_id(seg
, id
))
658 pub fn get_path_segment_data(&self, path_seg
: &hir
::PathSegment
<'_
>) -> Option
<Ref
> {
659 self.get_path_segment_data_with_id(path_seg
, path_seg
.hir_id?
)
662 pub fn get_path_segment_data_with_id(
664 path_seg
: &hir
::PathSegment
<'_
>,
667 // Returns true if the path is function type sugar, e.g., `Fn(A) -> B`.
668 fn fn_type(seg
: &hir
::PathSegment
<'_
>) -> bool
{
669 seg
.args
.map_or(false, |args
| args
.parenthesized
)
672 let res
= self.get_path_res(id
);
673 let span
= path_seg
.ident
.span
;
674 filter
!(self.span_utils
, span
);
675 let span
= self.span_from_span(span
);
679 Some(Ref { kind: RefKind::Variable, span, ref_id: id_from_hir_id(id, self) }
)
681 Res
::Def(HirDefKind
::Trait
, def_id
) if fn_type(path_seg
) => {
682 Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(def_id) }
)
686 | HirDefKind
::Variant
689 | HirDefKind
::TyAlias
690 | HirDefKind
::ForeignTy
691 | HirDefKind
::TraitAlias
692 | HirDefKind
::AssocTy
694 | HirDefKind
::OpaqueTy
695 | HirDefKind
::TyParam
,
697 ) => Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(def_id) }
),
698 Res
::Def(HirDefKind
::ConstParam
, def_id
) => {
699 Some(Ref { kind: RefKind::Variable, span, ref_id: id_from_def_id(def_id) }
)
701 Res
::Def(HirDefKind
::Ctor(..), def_id
) => {
702 // This is a reference to a tuple struct or an enum variant where the def_id points
703 // to an invisible constructor function. That is not a very useful
704 // def, so adjust to point to the tuple struct or enum variant itself.
705 let parent_def_id
= self.tcx
.parent(def_id
).unwrap();
706 Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(parent_def_id) }
)
708 Res
::Def(HirDefKind
::Static
| HirDefKind
::Const
| HirDefKind
::AssocConst
, _
) => {
709 Some(Ref { kind: RefKind::Variable, span, ref_id: id_from_def_id(res.def_id()) }
)
711 Res
::Def(HirDefKind
::AssocFn
, decl_id
) => {
712 let def_id
= if decl_id
.is_local() {
713 let ti
= self.tcx
.associated_item(decl_id
);
716 .associated_items(ti
.container
.id())
717 .filter_by_name_unhygienic(ti
.ident
.name
)
718 .find(|item
| item
.defaultness
.has_value())
719 .map(|item
| item
.def_id
)
724 kind
: RefKind
::Function
,
726 ref_id
: id_from_def_id(def_id
.unwrap_or(decl_id
)),
729 Res
::Def(HirDefKind
::Fn
, def_id
) => {
730 Some(Ref { kind: RefKind::Function, span, ref_id: id_from_def_id(def_id) }
)
732 Res
::Def(HirDefKind
::Mod
, def_id
) => {
733 Some(Ref { kind: RefKind::Mod, span, ref_id: id_from_def_id(def_id) }
)
737 HirDefKind
::Macro(..)
738 | HirDefKind
::ExternCrate
739 | HirDefKind
::ForeignMod
740 | HirDefKind
::LifetimeParam
741 | HirDefKind
::AnonConst
744 | HirDefKind
::GlobalAsm
746 | HirDefKind
::Closure
747 | HirDefKind
::Generator
,
753 | Res
::NonMacroAttr(..)
759 pub fn get_field_ref_data(
761 field_ref
: &hir
::ExprField
<'_
>,
762 variant
: &ty
::VariantDef
,
764 filter
!(self.span_utils
, field_ref
.ident
.span
);
765 self.tcx
.find_field_index(field_ref
.ident
, variant
).map(|index
| {
766 let span
= self.span_from_span(field_ref
.ident
.span
);
767 Ref { kind: RefKind::Variable, span, ref_id: id_from_def_id(variant.fields[index].did) }
771 /// Attempt to return MacroRef for any AST node.
773 /// For a given piece of AST defined by the supplied Span and NodeId,
774 /// returns `None` if the node is not macro-generated or the span is malformed,
775 /// else uses the expansion callsite and callee to return some MacroRef.
777 /// FIXME: [`DumpVisitor::process_macro_use`] should actually dump this data
779 fn get_macro_use_data(&self, span
: Span
) -> Option
<MacroRef
> {
780 if !generated_code(span
) {
783 // Note we take care to use the source callsite/callee, to handle
784 // nested expansions and ensure we only generate data for source-visible
786 let callsite
= span
.source_callsite();
787 let callsite_span
= self.span_from_span(callsite
);
788 let callee
= span
.source_callee()?
;
790 let mac_name
= match callee
.kind
{
791 ExpnKind
::Macro(kind
, name
) => match kind
{
792 MacroKind
::Bang
=> name
,
794 // Ignore attribute macros, their spans are usually mangled
795 // FIXME(eddyb) is this really the case anymore?
796 MacroKind
::Attr
| MacroKind
::Derive
=> return None
,
799 // These are not macros.
800 // FIXME(eddyb) maybe there is a way to handle them usefully?
801 ExpnKind
::Inlined
| ExpnKind
::Root
| ExpnKind
::AstPass(_
) | ExpnKind
::Desugaring(_
) => {
806 let callee_span
= self.span_from_span(callee
.def_site
);
809 qualname
: mac_name
.to_string(), // FIXME: generate the real qualname
814 fn lookup_def_id(&self, ref_id
: hir
::HirId
) -> Option
<DefId
> {
815 match self.get_path_res(ref_id
) {
816 Res
::PrimTy(_
) | Res
::SelfTy(..) | Res
::Err
=> None
,
817 def
=> def
.opt_def_id(),
821 fn docs_for_attrs(&self, attrs
: &[ast
::Attribute
]) -> String
{
822 let mut result
= String
::new();
825 if let Some(val
) = attr
.doc_str() {
826 // FIXME: Should save-analysis beautify doc strings itself or leave it to users?
827 result
.push_str(&beautify_doc_string(val
).as_str());
832 if !self.config
.full_docs
{
833 if let Some(index
) = result
.find("\n\n") {
834 result
.truncate(index
);
841 fn next_impl_id(&self) -> u32 {
842 let next
= self.impl_counter
.get();
843 self.impl_counter
.set(next
+ 1);
848 // An AST visitor for collecting paths (e.g., the names of structs) and formal
849 // variables (idents) from patterns.
850 struct PathCollector
<'l
> {
852 collected_paths
: Vec
<(hir
::HirId
, &'l hir
::QPath
<'l
>)>,
853 collected_idents
: Vec
<(hir
::HirId
, Ident
, hir
::Mutability
)>,
856 impl<'l
> PathCollector
<'l
> {
857 fn new(tcx
: TyCtxt
<'l
>) -> PathCollector
<'l
> {
858 PathCollector { tcx, collected_paths: vec![], collected_idents: vec![] }
862 impl<'l
> Visitor
<'l
> for PathCollector
<'l
> {
865 fn nested_visit_map(&mut self) -> intravisit
::NestedVisitorMap
<Self::Map
> {
866 intravisit
::NestedVisitorMap
::All(self.tcx
.hir())
869 fn visit_pat(&mut self, p
: &'l hir
::Pat
<'l
>) {
871 hir
::PatKind
::Struct(ref path
, ..) => {
872 self.collected_paths
.push((p
.hir_id
, path
));
874 hir
::PatKind
::TupleStruct(ref path
, ..) | hir
::PatKind
::Path(ref path
) => {
875 self.collected_paths
.push((p
.hir_id
, path
));
877 hir
::PatKind
::Binding(bm
, _
, ident
, _
) => {
879 "PathCollector, visit ident in pat {}: {:?} {:?}",
880 ident
, p
.span
, ident
.span
882 let immut
= match bm
{
883 // Even if the ref is mut, you can't change the ref, only
884 // the data pointed at, so showing the initialising expression
885 // is still worthwhile.
886 hir
::BindingAnnotation
::Unannotated
| hir
::BindingAnnotation
::Ref
=> {
889 hir
::BindingAnnotation
::Mutable
| hir
::BindingAnnotation
::RefMut
=> {
893 self.collected_idents
.push((p
.hir_id
, ident
, immut
));
897 intravisit
::walk_pat(self, p
);
901 /// Defines what to do with the results of saving the analysis.
902 pub trait SaveHandler
{
903 fn save(&mut self, save_ctxt
: &SaveContext
<'_
>, analysis
: &Analysis
);
906 /// Dump the save-analysis results to a file.
907 pub struct DumpHandler
<'a
> {
908 odir
: Option
<&'a Path
>,
912 impl<'a
> DumpHandler
<'a
> {
913 pub fn new(odir
: Option
<&'a Path
>, cratename
: &str) -> DumpHandler
<'a
> {
914 DumpHandler { odir, cratename: cratename.to_owned() }
917 fn output_file(&self, ctx
: &SaveContext
<'_
>) -> (BufWriter
<File
>, PathBuf
) {
918 let sess
= &ctx
.tcx
.sess
;
919 let file_name
= match ctx
.config
.output_file
{
920 Some(ref s
) => PathBuf
::from(s
),
922 let mut root_path
= match self.odir
{
923 Some(val
) => val
.join("save-analysis"),
924 None
=> PathBuf
::from("save-analysis-temp"),
927 if let Err(e
) = std
::fs
::create_dir_all(&root_path
) {
928 error
!("Could not create directory {}: {}", root_path
.display(), e
);
931 let executable
= sess
.crate_types().iter().any(|ct
| *ct
== CrateType
::Executable
);
932 let mut out_name
= if executable { String::new() }
else { "lib".to_owned() }
;
933 out_name
.push_str(&self.cratename
);
934 out_name
.push_str(&sess
.opts
.cg
.extra_filename
);
935 out_name
.push_str(".json");
936 root_path
.push(&out_name
);
942 info
!("Writing output to {}", file_name
.display());
944 let output_file
= BufWriter
::new(File
::create(&file_name
).unwrap_or_else(|e
| {
945 sess
.fatal(&format
!("Could not open {}: {}", file_name
.display(), e
))
948 (output_file
, file_name
)
952 impl SaveHandler
for DumpHandler
<'_
> {
953 fn save(&mut self, save_ctxt
: &SaveContext
<'_
>, analysis
: &Analysis
) {
954 let sess
= &save_ctxt
.tcx
.sess
;
955 let (output
, file_name
) = self.output_file(&save_ctxt
);
956 if let Err(e
) = serde_json
::to_writer(output
, &analysis
) {
957 error
!("Can't serialize save-analysis: {:?}", e
);
960 if sess
.opts
.json_artifact_notifications
{
961 sess
.parse_sess
.span_diagnostic
.emit_artifact_notification(&file_name
, "save-analysis");
966 /// Call a callback with the results of save-analysis.
967 pub struct CallbackHandler
<'b
> {
968 pub callback
: &'b
mut dyn FnMut(&rls_data
::Analysis
),
971 impl SaveHandler
for CallbackHandler
<'_
> {
972 fn save(&mut self, _
: &SaveContext
<'_
>, analysis
: &Analysis
) {
973 (self.callback
)(analysis
)
977 pub fn process_crate
<'l
, 'tcx
, H
: SaveHandler
>(
981 config
: Option
<Config
>,
984 with_no_trimmed_paths(|| {
985 tcx
.dep_graph
.with_ignore(|| {
986 info
!("Dumping crate {}", cratename
);
988 // Privacy checking requires and is done after type checking; use a
989 // fallback in case the access levels couldn't have been correctly computed.
990 let access_levels
= match tcx
.sess
.compile_status() {
991 Ok(..) => tcx
.privacy_access_levels(()),
992 Err(..) => tcx
.arena
.alloc(AccessLevels
::default()),
995 let save_ctxt
= SaveContext
{
997 maybe_typeck_results
: None
,
998 access_levels
: &access_levels
,
999 span_utils
: SpanUtils
::new(&tcx
.sess
),
1000 config
: find_config(config
),
1001 impl_counter
: Cell
::new(0),
1004 let mut visitor
= DumpVisitor
::new(save_ctxt
);
1006 visitor
.dump_crate_info(cratename
, tcx
.hir().krate());
1007 visitor
.dump_compilation_options(input
, cratename
);
1008 visitor
.process_crate(tcx
.hir().krate());
1010 handler
.save(&visitor
.save_ctxt
, &visitor
.analysis())
1015 fn find_config(supplied
: Option
<Config
>) -> Config
{
1016 if let Some(config
) = supplied
{
1020 match env
::var_os("RUST_SAVE_ANALYSIS_CONFIG") {
1021 None
=> Config
::default(),
1022 Some(config
) => config
1025 .map_err(|_
| error
!("`RUST_SAVE_ANALYSIS_CONFIG` isn't UTF-8"))
1027 serde_json
::from_str(cfg
)
1028 .map_err(|_
| error
!("Could not deserialize save-analysis config"))
1030 .unwrap_or_default(),
1034 // Utility functions for the module.
1036 // Helper function to escape quotes in a string
1037 fn escape(s
: String
) -> String
{
1038 s
.replace("\"", "\"\"")
1041 // Helper function to determine if a span came from a
1042 // macro expansion or syntax extension.
1043 fn generated_code(span
: Span
) -> bool
{
1044 span
.from_expansion() || span
.is_dummy()
1047 // DefId::index is a newtype and so the JSON serialisation is ugly. Therefore
1048 // we use our own Id which is the same, but without the newtype.
1049 fn id_from_def_id(id
: DefId
) -> rls_data
::Id
{
1050 rls_data
::Id { krate: id.krate.as_u32(), index: id.index.as_u32() }
1053 fn id_from_hir_id(id
: hir
::HirId
, scx
: &SaveContext
<'_
>) -> rls_data
::Id
{
1054 let def_id
= scx
.tcx
.hir().opt_local_def_id(id
);
1055 def_id
.map(|id
| id_from_def_id(id
.to_def_id())).unwrap_or_else(|| {
1056 // Create a *fake* `DefId` out of a `HirId` by combining the owner
1057 // `local_def_index` and the `local_id`.
1058 // This will work unless you have *billions* of definitions in a single
1059 // crate (very unlikely to actually happen).
1061 krate
: LOCAL_CRATE
.as_u32(),
1062 index
: id
.owner
.local_def_index
.as_u32() | id
.local_id
.as_u32().reverse_bits(),
1067 fn null_id() -> rls_data
::Id
{
1068 rls_data
::Id { krate: u32::MAX, index: u32::MAX }
1071 fn lower_attributes(attrs
: Vec
<ast
::Attribute
>, scx
: &SaveContext
<'_
>) -> Vec
<rls_data
::Attribute
> {
1074 // Only retain real attributes. Doc comments are lowered separately.
1075 .filter(|attr
| !attr
.has_name(sym
::doc
))
1077 // Remove the surrounding '#[..]' or '#![..]' of the pretty printed
1078 // attribute. First normalize all inner attribute (#![..]) to outer
1079 // ones (#[..]), then remove the two leading and the one trailing character.
1080 attr
.style
= ast
::AttrStyle
::Outer
;
1081 let value
= attribute_to_string(&attr
);
1082 // This str slicing works correctly, because the leading and trailing characters
1083 // are in the ASCII range and thus exactly one byte each.
1084 let value
= value
[2..value
.len() - 1].to_string();
1086 rls_data
::Attribute { value, span: scx.span_from_span(attr.span) }