1 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
3 #![feature(or_patterns)]
4 #![recursion_limit = "256"]
13 use rustc_ast
::util
::comments
::beautify_doc_string
;
14 use rustc_ast_pretty
::pprust
::attribute_to_string
;
16 use rustc_hir
::def
::{DefKind as HirDefKind, Res}
;
17 use rustc_hir
::def_id
::{DefId, LOCAL_CRATE}
;
18 use rustc_hir
::intravisit
::{self, Visitor}
;
20 use rustc_hir_pretty
::{enum_def_to_string, fn_to_string, ty_to_string}
;
21 use rustc_middle
::hir
::map
::Map
;
22 use rustc_middle
::middle
::cstore
::ExternCrate
;
23 use rustc_middle
::middle
::privacy
::AccessLevels
;
24 use rustc_middle
::ty
::{self, print::with_no_trimmed_paths, DefIdTree, TyCtxt}
;
25 use rustc_middle
::{bug, span_bug}
;
26 use rustc_session
::config
::{CrateType, Input, OutputType}
;
27 use rustc_session
::output
::{filename_for_metadata, out_filename}
;
28 use rustc_span
::source_map
::Spanned
;
29 use rustc_span
::symbol
::Ident
;
33 use std
::default::Default
;
36 use std
::io
::BufWriter
;
37 use std
::path
::{Path, PathBuf}
;
39 use dump_visitor
::DumpVisitor
;
40 use span_utils
::SpanUtils
;
42 use rls_data
::config
::Config
;
44 Analysis
, Def
, DefKind
, ExternalCrateData
, GlobalCrateId
, Impl
, ImplKind
, MacroRef
, Ref
,
45 RefKind
, Relation
, RelationKind
, SpanData
,
48 use tracing
::{debug, error, info}
;
50 pub struct SaveContext
<'tcx
> {
52 maybe_typeck_results
: Option
<&'tcx ty
::TypeckResults
<'tcx
>>,
53 access_levels
: &'tcx AccessLevels
,
54 span_utils
: SpanUtils
<'tcx
>,
56 impl_counter
: Cell
<u32>,
63 RelationData(Relation
, Impl
),
66 impl<'tcx
> SaveContext
<'tcx
> {
67 /// Gets the type-checking results for the current body.
68 /// As this will ICE if called outside bodies, only call when working with
69 /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies).
71 fn typeck_results(&self) -> &'tcx ty
::TypeckResults
<'tcx
> {
72 self.maybe_typeck_results
.expect("`SaveContext::typeck_results` called outside of body")
75 fn span_from_span(&self, span
: Span
) -> SpanData
{
76 use rls_span
::{Column, Row}
;
78 let sm
= self.tcx
.sess
.source_map();
79 let start
= sm
.lookup_char_pos(span
.lo());
80 let end
= sm
.lookup_char_pos(span
.hi());
83 file_name
: start
.file
.name
.to_string().into(),
84 byte_start
: span
.lo().0,
85 byte_end
: span
.hi().0,
86 line_start
: Row
::new_one_indexed(start
.line
as u32),
87 line_end
: Row
::new_one_indexed(end
.line
as u32),
88 column_start
: Column
::new_one_indexed(start
.col
.0 as u32 + 1),
89 column_end
: Column
::new_one_indexed(end
.col
.0 as u32 + 1),
93 // Returns path to the compilation output (e.g., libfoo-12345678.rmeta)
94 pub fn compilation_output(&self, crate_name
: &str) -> PathBuf
{
95 let sess
= &self.tcx
.sess
;
96 // Save-analysis is emitted per whole session, not per each crate type
97 let crate_type
= sess
.crate_types()[0];
98 let outputs
= &*self.tcx
.output_filenames(LOCAL_CRATE
);
100 if outputs
.outputs
.contains_key(&OutputType
::Metadata
) {
101 filename_for_metadata(sess
, crate_name
, outputs
)
102 } else if outputs
.outputs
.should_codegen() {
103 out_filename(sess
, crate_type
, outputs
, crate_name
)
105 // Otherwise it's only a DepInfo, in which case we return early and
106 // not even reach the analysis stage.
111 // List external crates used by the current crate.
112 pub fn get_external_crates(&self) -> Vec
<ExternalCrateData
> {
113 let mut result
= Vec
::with_capacity(self.tcx
.crates().len());
115 for &n
in self.tcx
.crates().iter() {
116 let span
= match self.tcx
.extern_crate(n
.as_def_id()) {
117 Some(&ExternCrate { span, .. }
) => span
,
119 debug
!("skipping crate {}, no data", n
);
123 let lo_loc
= self.span_utils
.sess
.source_map().lookup_char_pos(span
.lo());
124 result
.push(ExternalCrateData
{
125 // FIXME: change file_name field to PathBuf in rls-data
126 // https://github.com/nrc/rls-data/issues/7
127 file_name
: self.span_utils
.make_filename_string(&lo_loc
.file
),
130 name
: self.tcx
.crate_name(n
).to_string(),
131 disambiguator
: self.tcx
.crate_disambiguator(n
).to_fingerprint().as_value(),
139 pub fn get_extern_item_data(&self, item
: &hir
::ForeignItem
<'_
>) -> Option
<Data
> {
140 let def_id
= self.tcx
.hir().local_def_id(item
.hir_id
).to_def_id();
141 let qualname
= format
!("::{}", self.tcx
.def_path_str(def_id
));
143 hir
::ForeignItemKind
::Fn(ref decl
, arg_names
, ref generics
) => {
144 filter
!(self.span_utils
, item
.ident
.span
);
146 Some(Data
::DefData(Def
{
147 kind
: DefKind
::ForeignFunction
,
148 id
: id_from_def_id(def_id
),
149 span
: self.span_from_span(item
.ident
.span
),
150 name
: item
.ident
.to_string(),
155 // functions in extern block are implicitly unsafe
156 unsafety
: hir
::Unsafety
::Unsafe
,
157 // functions in extern block cannot be const
158 constness
: hir
::Constness
::NotConst
,
159 abi
: self.tcx
.hir().get_foreign_abi(item
.hir_id
),
160 // functions in extern block cannot be async
161 asyncness
: hir
::IsAsync
::NotAsync
,
163 Some(item
.ident
.name
),
172 docs
: self.docs_for_attrs(&item
.attrs
),
173 sig
: sig
::foreign_item_signature(item
, self),
174 attributes
: lower_attributes(item
.attrs
.to_vec(), self),
177 hir
::ForeignItemKind
::Static(ref ty
, _
) => {
178 filter
!(self.span_utils
, item
.ident
.span
);
180 let id
= id_from_def_id(def_id
);
181 let span
= self.span_from_span(item
.ident
.span
);
183 Some(Data
::DefData(Def
{
184 kind
: DefKind
::ForeignStatic
,
187 name
: item
.ident
.to_string(),
189 value
: ty_to_string(ty
),
193 docs
: self.docs_for_attrs(&item
.attrs
),
194 sig
: sig
::foreign_item_signature(item
, self),
195 attributes
: lower_attributes(item
.attrs
.to_vec(), self),
198 // FIXME(plietar): needs a new DefKind in rls-data
199 hir
::ForeignItemKind
::Type
=> None
,
203 pub fn get_item_data(&self, item
: &hir
::Item
<'_
>) -> Option
<Data
> {
204 let def_id
= self.tcx
.hir().local_def_id(item
.hir_id
).to_def_id();
206 hir
::ItemKind
::Fn(ref sig
, ref generics
, _
) => {
207 let qualname
= format
!("::{}", self.tcx
.def_path_str(def_id
));
208 filter
!(self.span_utils
, item
.ident
.span
);
209 Some(Data
::DefData(Def
{
210 kind
: DefKind
::Function
,
211 id
: id_from_def_id(def_id
),
212 span
: self.span_from_span(item
.ident
.span
),
213 name
: item
.ident
.to_string(),
218 Some(item
.ident
.name
),
227 docs
: self.docs_for_attrs(&item
.attrs
),
228 sig
: sig
::item_signature(item
, self),
229 attributes
: lower_attributes(item
.attrs
.to_vec(), self),
232 hir
::ItemKind
::Static(ref typ
, ..) => {
233 let qualname
= format
!("::{}", self.tcx
.def_path_str(def_id
));
235 filter
!(self.span_utils
, item
.ident
.span
);
237 let id
= id_from_def_id(def_id
);
238 let span
= self.span_from_span(item
.ident
.span
);
240 Some(Data
::DefData(Def
{
241 kind
: DefKind
::Static
,
244 name
: item
.ident
.to_string(),
246 value
: ty_to_string(&typ
),
250 docs
: self.docs_for_attrs(&item
.attrs
),
251 sig
: sig
::item_signature(item
, self),
252 attributes
: lower_attributes(item
.attrs
.to_vec(), self),
255 hir
::ItemKind
::Const(ref typ
, _
) => {
256 let qualname
= format
!("::{}", self.tcx
.def_path_str(def_id
));
257 filter
!(self.span_utils
, item
.ident
.span
);
259 let id
= id_from_def_id(def_id
);
260 let span
= self.span_from_span(item
.ident
.span
);
262 Some(Data
::DefData(Def
{
263 kind
: DefKind
::Const
,
266 name
: item
.ident
.to_string(),
268 value
: ty_to_string(typ
),
272 docs
: self.docs_for_attrs(&item
.attrs
),
273 sig
: sig
::item_signature(item
, self),
274 attributes
: lower_attributes(item
.attrs
.to_vec(), self),
277 hir
::ItemKind
::Mod(ref m
) => {
278 let qualname
= format
!("::{}", self.tcx
.def_path_str(def_id
));
280 let sm
= self.tcx
.sess
.source_map();
281 let filename
= sm
.span_to_filename(m
.inner
);
283 filter
!(self.span_utils
, item
.ident
.span
);
285 Some(Data
::DefData(Def
{
287 id
: id_from_def_id(def_id
),
288 name
: item
.ident
.to_string(),
290 span
: self.span_from_span(item
.ident
.span
),
291 value
: filename
.to_string(),
293 children
: m
.item_ids
.iter().map(|i
| id_from_hir_id(i
.id
, self)).collect(),
295 docs
: self.docs_for_attrs(&item
.attrs
),
296 sig
: sig
::item_signature(item
, self),
297 attributes
: lower_attributes(item
.attrs
.to_vec(), self),
300 hir
::ItemKind
::Enum(ref def
, ref generics
) => {
301 let name
= item
.ident
.to_string();
302 let qualname
= format
!("::{}", self.tcx
.def_path_str(def_id
));
303 filter
!(self.span_utils
, item
.ident
.span
);
305 enum_def_to_string(def
, generics
, item
.ident
.name
, item
.span
, &item
.vis
);
306 Some(Data
::DefData(Def
{
308 id
: id_from_def_id(def_id
),
309 span
: self.span_from_span(item
.ident
.span
),
314 children
: def
.variants
.iter().map(|v
| id_from_hir_id(v
.id
, self)).collect(),
316 docs
: self.docs_for_attrs(&item
.attrs
),
317 sig
: sig
::item_signature(item
, self),
318 attributes
: lower_attributes(item
.attrs
.to_vec(), self),
321 hir
::ItemKind
::Impl { ref of_trait, ref self_ty, ref items, .. }
=> {
322 if let hir
::TyKind
::Path(hir
::QPath
::Resolved(_
, ref path
)) = self_ty
.kind
{
323 // Common case impl for a struct or something basic.
324 if generated_code(path
.span
) {
327 let sub_span
= path
.segments
.last().unwrap().ident
.span
;
328 filter
!(self.span_utils
, sub_span
);
330 let impl_id
= self.next_impl_id();
331 let span
= self.span_from_span(sub_span
);
333 let type_data
= self.lookup_def_id(self_ty
.hir_id
);
334 type_data
.map(|type_data
| {
337 kind
: RelationKind
::Impl { id: impl_id }
,
339 from
: id_from_def_id(type_data
),
342 .and_then(|t
| self.lookup_def_id(t
.hir_ref_id
))
344 .unwrap_or_else(null_id
),
348 kind
: match *of_trait
{
349 Some(_
) => ImplKind
::Direct
,
350 None
=> ImplKind
::Inherent
,
353 value
: String
::new(),
357 .map(|i
| id_from_hir_id(i
.id
.hir_id
, self))
376 pub fn get_field_data(&self, field
: &hir
::StructField
<'_
>, scope
: hir
::HirId
) -> Option
<Def
> {
377 let name
= field
.ident
.to_string();
378 let scope_def_id
= self.tcx
.hir().local_def_id(scope
).to_def_id();
379 let qualname
= format
!("::{}::{}", self.tcx
.def_path_str(scope_def_id
), field
.ident
);
380 filter
!(self.span_utils
, field
.ident
.span
);
381 let field_def_id
= self.tcx
.hir().local_def_id(field
.hir_id
).to_def_id();
382 let typ
= self.tcx
.type_of(field_def_id
).to_string();
384 let id
= id_from_def_id(field_def_id
);
385 let span
= self.span_from_span(field
.ident
.span
);
388 kind
: DefKind
::Field
,
394 parent
: Some(id_from_def_id(scope_def_id
)),
397 docs
: self.docs_for_attrs(&field
.attrs
),
398 sig
: sig
::field_signature(field
, self),
399 attributes
: lower_attributes(field
.attrs
.to_vec(), self),
403 // FIXME would be nice to take a MethodItem here, but the ast provides both
404 // trait and impl flavours, so the caller must do the disassembly.
405 pub fn get_method_data(&self, hir_id
: hir
::HirId
, ident
: Ident
, span
: Span
) -> Option
<Def
> {
406 // The qualname for a method is the trait name or name of the struct in an impl in
407 // which the method is declared in, followed by the method's name.
408 let def_id
= self.tcx
.hir().local_def_id(hir_id
).to_def_id();
409 let (qualname
, parent_scope
, decl_id
, docs
, attributes
) =
410 match self.tcx
.impl_of_method(def_id
) {
411 Some(impl_id
) => match self.tcx
.hir().get_if_local(impl_id
) {
412 Some(Node
::Item(item
)) => match item
.kind
{
413 hir
::ItemKind
::Impl { ref self_ty, .. }
=> {
414 let hir
= self.tcx
.hir();
416 let mut qualname
= String
::from("<");
418 .push_str(&rustc_hir_pretty
::id_to_string(&hir
, self_ty
.hir_id
));
420 let trait_id
= self.tcx
.trait_id_of_impl(impl_id
);
421 let mut docs
= String
::new();
422 let mut attrs
= vec
![];
423 if let Some(Node
::ImplItem(item
)) = hir
.find(hir_id
) {
424 docs
= self.docs_for_attrs(&item
.attrs
);
425 attrs
= item
.attrs
.to_vec();
428 let mut decl_id
= None
;
429 if let Some(def_id
) = trait_id
{
430 // A method in a trait impl.
431 qualname
.push_str(" as ");
432 qualname
.push_str(&self.tcx
.def_path_str(def_id
));
436 .associated_items(def_id
)
437 .filter_by_name_unhygienic(ident
.name
)
439 .map(|item
| item
.def_id
);
443 (qualname
, trait_id
, decl_id
, docs
, attrs
)
448 "Container {:?} for method {} not an impl?",
457 "Container {:?} for method {} is not a node item {:?}",
464 None
=> match self.tcx
.trait_of_item(def_id
) {
466 let mut docs
= String
::new();
467 let mut attrs
= vec
![];
469 if let Some(Node
::TraitItem(item
)) = self.tcx
.hir().find(hir_id
) {
470 docs
= self.docs_for_attrs(&item
.attrs
);
471 attrs
= item
.attrs
.to_vec();
475 format
!("::{}", self.tcx
.def_path_str(def_id
)),
483 debug
!("could not find container for method {} at {:?}", hir_id
, span
);
484 // This is not necessarily a bug, if there was a compilation error,
485 // the typeck results we need might not exist.
491 let qualname
= format
!("{}::{}", qualname
, ident
.name
);
493 filter
!(self.span_utils
, ident
.span
);
496 kind
: DefKind
::Method
,
497 id
: id_from_def_id(def_id
),
498 span
: self.span_from_span(ident
.span
),
499 name
: ident
.name
.to_string(),
501 // FIXME you get better data here by using the visitor.
502 value
: String
::new(),
503 parent
: parent_scope
.map(id_from_def_id
),
505 decl_id
: decl_id
.map(id_from_def_id
),
508 attributes
: lower_attributes(attributes
, self),
512 pub fn get_trait_ref_data(&self, trait_ref
: &hir
::TraitRef
<'_
>) -> Option
<Ref
> {
513 self.lookup_def_id(trait_ref
.hir_ref_id
).and_then(|def_id
| {
514 let span
= trait_ref
.path
.span
;
515 if generated_code(span
) {
518 let sub_span
= trait_ref
.path
.segments
.last().unwrap().ident
.span
;
519 filter
!(self.span_utils
, sub_span
);
520 let span
= self.span_from_span(sub_span
);
521 Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(def_id) }
)
525 pub fn get_expr_data(&self, expr
: &hir
::Expr
<'_
>) -> Option
<Data
> {
526 let ty
= self.typeck_results().expr_ty_adjusted_opt(expr
)?
;
527 if matches
!(ty
.kind(), ty
::Error(_
)) {
531 hir
::ExprKind
::Field(ref sub_ex
, ident
) => {
532 match self.typeck_results().expr_ty_adjusted(&sub_ex
).kind() {
533 ty
::Adt(def
, _
) if !def
.is_enum() => {
534 let variant
= &def
.non_enum_variant();
535 filter
!(self.span_utils
, ident
.span
);
536 let span
= self.span_from_span(ident
.span
);
537 Some(Data
::RefData(Ref
{
538 kind
: RefKind
::Variable
,
542 .find_field_index(ident
, variant
)
543 .map(|index
| id_from_def_id(variant
.fields
[index
].did
))
544 .unwrap_or_else(null_id
),
547 ty
::Tuple(..) => None
,
549 debug
!("expected struct or union type, found {:?}", ty
);
554 hir
::ExprKind
::Struct(qpath
, ..) => match ty
.kind() {
556 let sub_span
= qpath
.last_segment_span();
557 filter
!(self.span_utils
, sub_span
);
558 let span
= self.span_from_span(sub_span
);
559 Some(Data
::RefData(Ref
{
562 ref_id
: id_from_def_id(def
.did
),
566 debug
!("expected adt, found {:?}", ty
);
570 hir
::ExprKind
::MethodCall(ref seg
, ..) => {
571 let method_id
= match self.typeck_results().type_dependent_def_id(expr
.hir_id
) {
574 debug
!("could not resolve method id for {:?}", expr
);
578 let (def_id
, decl_id
) = match self.tcx
.associated_item(method_id
).container
{
579 ty
::ImplContainer(_
) => (Some(method_id
), None
),
580 ty
::TraitContainer(_
) => (None
, Some(method_id
)),
582 let sub_span
= seg
.ident
.span
;
583 filter
!(self.span_utils
, sub_span
);
584 let span
= self.span_from_span(sub_span
);
585 Some(Data
::RefData(Ref
{
586 kind
: RefKind
::Function
,
588 ref_id
: def_id
.or(decl_id
).map(id_from_def_id
).unwrap_or_else(null_id
),
591 hir
::ExprKind
::Path(ref path
) => {
592 self.get_path_data(expr
.hir_id
, path
).map(Data
::RefData
)
596 bug
!("invalid expression: {:?}", expr
);
601 pub fn get_path_res(&self, hir_id
: hir
::HirId
) -> Res
{
602 match self.tcx
.hir().get(hir_id
) {
603 Node
::TraitRef(tr
) => tr
.path
.res
,
605 Node
::Item(&hir
::Item { kind: hir::ItemKind::Use(path, _), .. }
) => path
.res
,
606 Node
::Visibility(&Spanned
{
607 node
: hir
::VisibilityKind
::Restricted { ref path, .. }
,
611 Node
::PathSegment(seg
) => match seg
.res
{
612 Some(res
) if res
!= Res
::Err
=> res
,
614 let parent_node
= self.tcx
.hir().get_parent_node(hir_id
);
615 self.get_path_res(parent_node
)
619 Node
::Expr(&hir
::Expr { kind: hir::ExprKind::Struct(ref qpath, ..), .. }
) => {
620 self.typeck_results().qpath_res(qpath
, hir_id
)
623 Node
::Expr(&hir
::Expr { kind: hir::ExprKind::Path(ref qpath), .. }
)
624 | Node
::Pat(&hir
::Pat
{
626 hir
::PatKind
::Path(ref qpath
)
627 | hir
::PatKind
::Struct(ref qpath
, ..)
628 | hir
::PatKind
::TupleStruct(ref qpath
, ..),
631 | Node
::Ty(&hir
::Ty { kind: hir::TyKind::Path(ref qpath), .. }
) => match qpath
{
632 hir
::QPath
::Resolved(_
, path
) => path
.res
,
633 hir
::QPath
::TypeRelative(..) | hir
::QPath
::LangItem(..) => {
634 // #75962: `self.typeck_results` may be different from the `hir_id`'s result.
635 if self.tcx
.has_typeck_results(hir_id
.owner
.to_def_id()) {
636 self.tcx
.typeck(hir_id
.owner
).qpath_res(qpath
, hir_id
)
643 Node
::Binding(&hir
::Pat
{
644 kind
: hir
::PatKind
::Binding(_
, canonical_id
, ..), ..
645 }) => Res
::Local(canonical_id
),
651 pub fn get_path_data(&self, id
: hir
::HirId
, path
: &hir
::QPath
<'_
>) -> Option
<Ref
> {
652 let segment
= match path
{
653 hir
::QPath
::Resolved(_
, path
) => path
.segments
.last(),
654 hir
::QPath
::TypeRelative(_
, segment
) => Some(*segment
),
655 hir
::QPath
::LangItem(..) => None
,
657 segment
.and_then(|seg
| {
658 self.get_path_segment_data(seg
).or_else(|| self.get_path_segment_data_with_id(seg
, id
))
662 pub fn get_path_segment_data(&self, path_seg
: &hir
::PathSegment
<'_
>) -> Option
<Ref
> {
663 self.get_path_segment_data_with_id(path_seg
, path_seg
.hir_id?
)
666 pub fn get_path_segment_data_with_id(
668 path_seg
: &hir
::PathSegment
<'_
>,
671 // Returns true if the path is function type sugar, e.g., `Fn(A) -> B`.
672 fn fn_type(seg
: &hir
::PathSegment
<'_
>) -> bool
{
673 seg
.args
.map(|args
| args
.parenthesized
).unwrap_or(false)
676 let res
= self.get_path_res(id
);
677 let span
= path_seg
.ident
.span
;
678 filter
!(self.span_utils
, span
);
679 let span
= self.span_from_span(span
);
683 Some(Ref { kind: RefKind::Variable, span, ref_id: id_from_hir_id(id, self) }
)
685 Res
::Def(HirDefKind
::Trait
, def_id
) if fn_type(path_seg
) => {
686 Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(def_id) }
)
690 | HirDefKind
::Variant
693 | HirDefKind
::TyAlias
694 | HirDefKind
::ForeignTy
695 | HirDefKind
::TraitAlias
696 | HirDefKind
::AssocTy
698 | HirDefKind
::OpaqueTy
699 | HirDefKind
::TyParam
,
701 ) => Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(def_id) }
),
702 Res
::Def(HirDefKind
::ConstParam
, def_id
) => {
703 Some(Ref { kind: RefKind::Variable, span, ref_id: id_from_def_id(def_id) }
)
705 Res
::Def(HirDefKind
::Ctor(..), def_id
) => {
706 // This is a reference to a tuple struct or an enum variant where the def_id points
707 // to an invisible constructor function. That is not a very useful
708 // def, so adjust to point to the tuple struct or enum variant itself.
709 let parent_def_id
= self.tcx
.parent(def_id
).unwrap();
710 Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(parent_def_id) }
)
712 Res
::Def(HirDefKind
::Static
| HirDefKind
::Const
| HirDefKind
::AssocConst
, _
) => {
713 Some(Ref { kind: RefKind::Variable, span, ref_id: id_from_def_id(res.def_id()) }
)
715 Res
::Def(HirDefKind
::AssocFn
, decl_id
) => {
716 let def_id
= if decl_id
.is_local() {
717 let ti
= self.tcx
.associated_item(decl_id
);
720 .associated_items(ti
.container
.id())
721 .filter_by_name_unhygienic(ti
.ident
.name
)
722 .find(|item
| item
.defaultness
.has_value())
723 .map(|item
| item
.def_id
)
728 kind
: RefKind
::Function
,
730 ref_id
: id_from_def_id(def_id
.unwrap_or(decl_id
)),
733 Res
::Def(HirDefKind
::Fn
, def_id
) => {
734 Some(Ref { kind: RefKind::Function, span, ref_id: id_from_def_id(def_id) }
)
736 Res
::Def(HirDefKind
::Mod
, def_id
) => {
737 Some(Ref { kind: RefKind::Mod, span, ref_id: id_from_def_id(def_id) }
)
741 HirDefKind
::Macro(..)
742 | HirDefKind
::ExternCrate
743 | HirDefKind
::ForeignMod
744 | HirDefKind
::LifetimeParam
745 | HirDefKind
::AnonConst
748 | HirDefKind
::GlobalAsm
750 | HirDefKind
::Closure
751 | HirDefKind
::Generator
,
757 | Res
::NonMacroAttr(..)
763 pub fn get_field_ref_data(
765 field_ref
: &hir
::Field
<'_
>,
766 variant
: &ty
::VariantDef
,
768 filter
!(self.span_utils
, field_ref
.ident
.span
);
769 self.tcx
.find_field_index(field_ref
.ident
, variant
).map(|index
| {
770 let span
= self.span_from_span(field_ref
.ident
.span
);
771 Ref { kind: RefKind::Variable, span, ref_id: id_from_def_id(variant.fields[index].did) }
775 /// Attempt to return MacroRef for any AST node.
777 /// For a given piece of AST defined by the supplied Span and NodeId,
778 /// returns `None` if the node is not macro-generated or the span is malformed,
779 /// else uses the expansion callsite and callee to return some MacroRef.
780 pub fn get_macro_use_data(&self, span
: Span
) -> Option
<MacroRef
> {
781 if !generated_code(span
) {
784 // Note we take care to use the source callsite/callee, to handle
785 // nested expansions and ensure we only generate data for source-visible
787 let callsite
= span
.source_callsite();
788 let callsite_span
= self.span_from_span(callsite
);
789 let callee
= span
.source_callee()?
;
791 let mac_name
= match callee
.kind
{
792 ExpnKind
::Macro(kind
, name
) => match kind
{
793 MacroKind
::Bang
=> name
,
795 // Ignore attribute macros, their spans are usually mangled
796 // FIXME(eddyb) is this really the case anymore?
797 MacroKind
::Attr
| MacroKind
::Derive
=> return None
,
800 // These are not macros.
801 // FIXME(eddyb) maybe there is a way to handle them usefully?
802 ExpnKind
::Inlined
| ExpnKind
::Root
| ExpnKind
::AstPass(_
) | ExpnKind
::Desugaring(_
) => {
807 let callee_span
= self.span_from_span(callee
.def_site
);
810 qualname
: mac_name
.to_string(), // FIXME: generate the real qualname
815 fn lookup_def_id(&self, ref_id
: hir
::HirId
) -> Option
<DefId
> {
816 match self.get_path_res(ref_id
) {
817 Res
::PrimTy(_
) | Res
::SelfTy(..) | Res
::Err
=> None
,
818 def
=> def
.opt_def_id(),
822 fn docs_for_attrs(&self, attrs
: &[ast
::Attribute
]) -> String
{
823 let mut result
= String
::new();
826 if let Some(val
) = attr
.doc_str() {
827 // FIXME: Should save-analysis beautify doc strings itself or leave it to users?
828 result
.push_str(&beautify_doc_string(val
).as_str());
830 } else if self.tcx
.sess
.check_name(attr
, sym
::doc
) {
831 if let Some(meta_list
) = attr
.meta_item_list() {
834 .filter(|it
| it
.has_name(sym
::include
))
835 .filter_map(|it
| it
.meta_item_list().map(|l
| l
.to_owned()))
837 .filter(|meta
| meta
.has_name(sym
::contents
))
838 .filter_map(|meta
| meta
.value_str())
840 result
.push_str(&val
.as_str());
847 if !self.config
.full_docs
{
848 if let Some(index
) = result
.find("\n\n") {
849 result
.truncate(index
);
856 fn next_impl_id(&self) -> u32 {
857 let next
= self.impl_counter
.get();
858 self.impl_counter
.set(next
+ 1);
863 // An AST visitor for collecting paths (e.g., the names of structs) and formal
864 // variables (idents) from patterns.
865 struct PathCollector
<'l
> {
867 collected_paths
: Vec
<(hir
::HirId
, &'l hir
::QPath
<'l
>)>,
868 collected_idents
: Vec
<(hir
::HirId
, Ident
, hir
::Mutability
)>,
871 impl<'l
> PathCollector
<'l
> {
872 fn new(tcx
: TyCtxt
<'l
>) -> PathCollector
<'l
> {
873 PathCollector { tcx, collected_paths: vec![], collected_idents: vec![] }
877 impl<'l
> Visitor
<'l
> for PathCollector
<'l
> {
880 fn nested_visit_map(&mut self) -> intravisit
::NestedVisitorMap
<Self::Map
> {
881 intravisit
::NestedVisitorMap
::All(self.tcx
.hir())
884 fn visit_pat(&mut self, p
: &'l hir
::Pat
<'l
>) {
886 hir
::PatKind
::Struct(ref path
, ..) => {
887 self.collected_paths
.push((p
.hir_id
, path
));
889 hir
::PatKind
::TupleStruct(ref path
, ..) | hir
::PatKind
::Path(ref path
) => {
890 self.collected_paths
.push((p
.hir_id
, path
));
892 hir
::PatKind
::Binding(bm
, _
, ident
, _
) => {
894 "PathCollector, visit ident in pat {}: {:?} {:?}",
895 ident
, p
.span
, ident
.span
897 let immut
= match bm
{
898 // Even if the ref is mut, you can't change the ref, only
899 // the data pointed at, so showing the initialising expression
900 // is still worthwhile.
901 hir
::BindingAnnotation
::Unannotated
| hir
::BindingAnnotation
::Ref
=> {
904 hir
::BindingAnnotation
::Mutable
| hir
::BindingAnnotation
::RefMut
=> {
908 self.collected_idents
.push((p
.hir_id
, ident
, immut
));
912 intravisit
::walk_pat(self, p
);
916 /// Defines what to do with the results of saving the analysis.
917 pub trait SaveHandler
{
918 fn save(&mut self, save_ctxt
: &SaveContext
<'_
>, analysis
: &Analysis
);
921 /// Dump the save-analysis results to a file.
922 pub struct DumpHandler
<'a
> {
923 odir
: Option
<&'a Path
>,
927 impl<'a
> DumpHandler
<'a
> {
928 pub fn new(odir
: Option
<&'a Path
>, cratename
: &str) -> DumpHandler
<'a
> {
929 DumpHandler { odir, cratename: cratename.to_owned() }
932 fn output_file(&self, ctx
: &SaveContext
<'_
>) -> (BufWriter
<File
>, PathBuf
) {
933 let sess
= &ctx
.tcx
.sess
;
934 let file_name
= match ctx
.config
.output_file
{
935 Some(ref s
) => PathBuf
::from(s
),
937 let mut root_path
= match self.odir
{
938 Some(val
) => val
.join("save-analysis"),
939 None
=> PathBuf
::from("save-analysis-temp"),
942 if let Err(e
) = std
::fs
::create_dir_all(&root_path
) {
943 error
!("Could not create directory {}: {}", root_path
.display(), e
);
946 let executable
= sess
.crate_types().iter().any(|ct
| *ct
== CrateType
::Executable
);
947 let mut out_name
= if executable { String::new() }
else { "lib".to_owned() }
;
948 out_name
.push_str(&self.cratename
);
949 out_name
.push_str(&sess
.opts
.cg
.extra_filename
);
950 out_name
.push_str(".json");
951 root_path
.push(&out_name
);
957 info
!("Writing output to {}", file_name
.display());
959 let output_file
= BufWriter
::new(File
::create(&file_name
).unwrap_or_else(|e
| {
960 sess
.fatal(&format
!("Could not open {}: {}", file_name
.display(), e
))
963 (output_file
, file_name
)
967 impl SaveHandler
for DumpHandler
<'_
> {
968 fn save(&mut self, save_ctxt
: &SaveContext
<'_
>, analysis
: &Analysis
) {
969 let sess
= &save_ctxt
.tcx
.sess
;
970 let (output
, file_name
) = self.output_file(&save_ctxt
);
971 if let Err(e
) = serde_json
::to_writer(output
, &analysis
) {
972 error
!("Can't serialize save-analysis: {:?}", e
);
975 if sess
.opts
.json_artifact_notifications
{
976 sess
.parse_sess
.span_diagnostic
.emit_artifact_notification(&file_name
, "save-analysis");
981 /// Call a callback with the results of save-analysis.
982 pub struct CallbackHandler
<'b
> {
983 pub callback
: &'b
mut dyn FnMut(&rls_data
::Analysis
),
986 impl SaveHandler
for CallbackHandler
<'_
> {
987 fn save(&mut self, _
: &SaveContext
<'_
>, analysis
: &Analysis
) {
988 (self.callback
)(analysis
)
992 pub fn process_crate
<'l
, 'tcx
, H
: SaveHandler
>(
996 config
: Option
<Config
>,
999 with_no_trimmed_paths(|| {
1000 tcx
.dep_graph
.with_ignore(|| {
1001 info
!("Dumping crate {}", cratename
);
1003 // Privacy checking requires and is done after type checking; use a
1004 // fallback in case the access levels couldn't have been correctly computed.
1005 let access_levels
= match tcx
.sess
.compile_status() {
1006 Ok(..) => tcx
.privacy_access_levels(LOCAL_CRATE
),
1007 Err(..) => tcx
.arena
.alloc(AccessLevels
::default()),
1010 let save_ctxt
= SaveContext
{
1012 maybe_typeck_results
: None
,
1013 access_levels
: &access_levels
,
1014 span_utils
: SpanUtils
::new(&tcx
.sess
),
1015 config
: find_config(config
),
1016 impl_counter
: Cell
::new(0),
1019 let mut visitor
= DumpVisitor
::new(save_ctxt
);
1021 visitor
.dump_crate_info(cratename
, tcx
.hir().krate());
1022 visitor
.dump_compilation_options(input
, cratename
);
1023 visitor
.process_crate(tcx
.hir().krate());
1025 handler
.save(&visitor
.save_ctxt
, &visitor
.analysis())
1030 fn find_config(supplied
: Option
<Config
>) -> Config
{
1031 if let Some(config
) = supplied
{
1035 match env
::var_os("RUST_SAVE_ANALYSIS_CONFIG") {
1036 None
=> Config
::default(),
1037 Some(config
) => config
1040 .map_err(|_
| error
!("`RUST_SAVE_ANALYSIS_CONFIG` isn't UTF-8"))
1042 serde_json
::from_str(cfg
)
1043 .map_err(|_
| error
!("Could not deserialize save-analysis config"))
1045 .unwrap_or_default(),
1049 // Utility functions for the module.
1051 // Helper function to escape quotes in a string
1052 fn escape(s
: String
) -> String
{
1053 s
.replace("\"", "\"\"")
1056 // Helper function to determine if a span came from a
1057 // macro expansion or syntax extension.
1058 fn generated_code(span
: Span
) -> bool
{
1059 span
.from_expansion() || span
.is_dummy()
1062 // DefId::index is a newtype and so the JSON serialisation is ugly. Therefore
1063 // we use our own Id which is the same, but without the newtype.
1064 fn id_from_def_id(id
: DefId
) -> rls_data
::Id
{
1065 rls_data
::Id { krate: id.krate.as_u32(), index: id.index.as_u32() }
1068 fn id_from_hir_id(id
: hir
::HirId
, scx
: &SaveContext
<'_
>) -> rls_data
::Id
{
1069 let def_id
= scx
.tcx
.hir().opt_local_def_id(id
);
1070 def_id
.map(|id
| id_from_def_id(id
.to_def_id())).unwrap_or_else(|| {
1071 // Create a *fake* `DefId` out of a `HirId` by combining the owner
1072 // `local_def_index` and the `local_id`.
1073 // This will work unless you have *billions* of definitions in a single
1074 // crate (very unlikely to actually happen).
1076 krate
: LOCAL_CRATE
.as_u32(),
1077 index
: id
.owner
.local_def_index
.as_u32() | id
.local_id
.as_u32().reverse_bits(),
1082 fn null_id() -> rls_data
::Id
{
1083 rls_data
::Id { krate: u32::MAX, index: u32::MAX }
1086 fn lower_attributes(attrs
: Vec
<ast
::Attribute
>, scx
: &SaveContext
<'_
>) -> Vec
<rls_data
::Attribute
> {
1089 // Only retain real attributes. Doc comments are lowered separately.
1090 .filter(|attr
| !attr
.has_name(sym
::doc
))
1092 // Remove the surrounding '#[..]' or '#![..]' of the pretty printed
1093 // attribute. First normalize all inner attribute (#![..]) to outer
1094 // ones (#[..]), then remove the two leading and the one trailing character.
1095 attr
.style
= ast
::AttrStyle
::Outer
;
1096 let value
= attribute_to_string(&attr
);
1097 // This str slicing works correctly, because the leading and trailing characters
1098 // are in the ASCII range and thus exactly one byte each.
1099 let value
= value
[2..value
.len() - 1].to_string();
1101 rls_data
::Attribute { value, span: scx.span_from_span(attr.span) }