1 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
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, 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
);
441 qualname
.push_str(">");
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(..) => self
634 .maybe_typeck_results
635 .map_or(Res
::Err
, |typeck_results
| typeck_results
.qpath_res(qpath
, hir_id
)),
638 Node
::Binding(&hir
::Pat
{
639 kind
: hir
::PatKind
::Binding(_
, canonical_id
, ..), ..
640 }) => Res
::Local(canonical_id
),
646 pub fn get_path_data(&self, id
: hir
::HirId
, path
: &hir
::QPath
<'_
>) -> Option
<Ref
> {
647 let segment
= match path
{
648 hir
::QPath
::Resolved(_
, path
) => path
.segments
.last(),
649 hir
::QPath
::TypeRelative(_
, segment
) => Some(*segment
),
650 hir
::QPath
::LangItem(..) => None
,
652 segment
.and_then(|seg
| {
653 self.get_path_segment_data(seg
).or_else(|| self.get_path_segment_data_with_id(seg
, id
))
657 pub fn get_path_segment_data(&self, path_seg
: &hir
::PathSegment
<'_
>) -> Option
<Ref
> {
658 self.get_path_segment_data_with_id(path_seg
, path_seg
.hir_id?
)
661 pub fn get_path_segment_data_with_id(
663 path_seg
: &hir
::PathSegment
<'_
>,
666 // Returns true if the path is function type sugar, e.g., `Fn(A) -> B`.
667 fn fn_type(seg
: &hir
::PathSegment
<'_
>) -> bool
{
668 seg
.args
.map(|args
| args
.parenthesized
).unwrap_or(false)
671 let res
= self.get_path_res(id
);
672 let span
= path_seg
.ident
.span
;
673 filter
!(self.span_utils
, span
);
674 let span
= self.span_from_span(span
);
678 Some(Ref { kind: RefKind::Variable, span, ref_id: id_from_hir_id(id, self) }
)
680 Res
::Def(HirDefKind
::Trait
, def_id
) if fn_type(path_seg
) => {
681 Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(def_id) }
)
685 | HirDefKind
::Variant
688 | HirDefKind
::TyAlias
689 | HirDefKind
::ForeignTy
690 | HirDefKind
::TraitAlias
691 | HirDefKind
::AssocTy
693 | HirDefKind
::OpaqueTy
694 | HirDefKind
::TyParam
,
696 ) => Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(def_id) }
),
697 Res
::Def(HirDefKind
::ConstParam
, def_id
) => {
698 Some(Ref { kind: RefKind::Variable, span, ref_id: id_from_def_id(def_id) }
)
700 Res
::Def(HirDefKind
::Ctor(..), def_id
) => {
701 // This is a reference to a tuple struct or an enum variant where the def_id points
702 // to an invisible constructor function. That is not a very useful
703 // def, so adjust to point to the tuple struct or enum variant itself.
704 let parent_def_id
= self.tcx
.parent(def_id
).unwrap();
705 Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(parent_def_id) }
)
707 Res
::Def(HirDefKind
::Static
| HirDefKind
::Const
| HirDefKind
::AssocConst
, _
) => {
708 Some(Ref { kind: RefKind::Variable, span, ref_id: id_from_def_id(res.def_id()) }
)
710 Res
::Def(HirDefKind
::AssocFn
, decl_id
) => {
711 let def_id
= if decl_id
.is_local() {
712 let ti
= self.tcx
.associated_item(decl_id
);
715 .associated_items(ti
.container
.id())
716 .filter_by_name_unhygienic(ti
.ident
.name
)
717 .find(|item
| item
.defaultness
.has_value())
718 .map(|item
| item
.def_id
)
723 kind
: RefKind
::Function
,
725 ref_id
: id_from_def_id(def_id
.unwrap_or(decl_id
)),
728 Res
::Def(HirDefKind
::Fn
, def_id
) => {
729 Some(Ref { kind: RefKind::Function, span, ref_id: id_from_def_id(def_id) }
)
731 Res
::Def(HirDefKind
::Mod
, def_id
) => {
732 Some(Ref { kind: RefKind::Mod, span, ref_id: id_from_def_id(def_id) }
)
736 HirDefKind
::Macro(..)
737 | HirDefKind
::ExternCrate
738 | HirDefKind
::ForeignMod
739 | HirDefKind
::LifetimeParam
740 | HirDefKind
::AnonConst
743 | HirDefKind
::GlobalAsm
745 | HirDefKind
::Closure
746 | HirDefKind
::Generator
,
752 | Res
::NonMacroAttr(..)
758 pub fn get_field_ref_data(
760 field_ref
: &hir
::Field
<'_
>,
761 variant
: &ty
::VariantDef
,
763 filter
!(self.span_utils
, field_ref
.ident
.span
);
764 self.tcx
.find_field_index(field_ref
.ident
, variant
).map(|index
| {
765 let span
= self.span_from_span(field_ref
.ident
.span
);
766 Ref { kind: RefKind::Variable, span, ref_id: id_from_def_id(variant.fields[index].did) }
770 /// Attempt to return MacroRef for any AST node.
772 /// For a given piece of AST defined by the supplied Span and NodeId,
773 /// returns `None` if the node is not macro-generated or the span is malformed,
774 /// else uses the expansion callsite and callee to return some MacroRef.
775 pub fn get_macro_use_data(&self, span
: Span
) -> Option
<MacroRef
> {
776 if !generated_code(span
) {
779 // Note we take care to use the source callsite/callee, to handle
780 // nested expansions and ensure we only generate data for source-visible
782 let callsite
= span
.source_callsite();
783 let callsite_span
= self.span_from_span(callsite
);
784 let callee
= span
.source_callee()?
;
786 let mac_name
= match callee
.kind
{
787 ExpnKind
::Macro(kind
, name
) => match kind
{
788 MacroKind
::Bang
=> name
,
790 // Ignore attribute macros, their spans are usually mangled
791 // FIXME(eddyb) is this really the case anymore?
792 MacroKind
::Attr
| MacroKind
::Derive
=> return None
,
795 // These are not macros.
796 // FIXME(eddyb) maybe there is a way to handle them usefully?
797 ExpnKind
::Root
| ExpnKind
::AstPass(_
) | ExpnKind
::Desugaring(_
) => return None
,
800 let callee_span
= self.span_from_span(callee
.def_site
);
803 qualname
: mac_name
.to_string(), // FIXME: generate the real qualname
808 fn lookup_def_id(&self, ref_id
: hir
::HirId
) -> Option
<DefId
> {
809 match self.get_path_res(ref_id
) {
810 Res
::PrimTy(_
) | Res
::SelfTy(..) | Res
::Err
=> None
,
811 def
=> def
.opt_def_id(),
815 fn docs_for_attrs(&self, attrs
: &[ast
::Attribute
]) -> String
{
816 let mut result
= String
::new();
819 if let Some(val
) = attr
.doc_str() {
820 // FIXME: Should save-analysis beautify doc strings itself or leave it to users?
821 result
.push_str(&beautify_doc_string(val
));
823 } else if self.tcx
.sess
.check_name(attr
, sym
::doc
) {
824 if let Some(meta_list
) = attr
.meta_item_list() {
827 .filter(|it
| it
.has_name(sym
::include
))
828 .filter_map(|it
| it
.meta_item_list().map(|l
| l
.to_owned()))
830 .filter(|meta
| meta
.has_name(sym
::contents
))
831 .filter_map(|meta
| meta
.value_str())
833 result
.push_str(&val
.as_str());
840 if !self.config
.full_docs
{
841 if let Some(index
) = result
.find("\n\n") {
842 result
.truncate(index
);
849 fn next_impl_id(&self) -> u32 {
850 let next
= self.impl_counter
.get();
851 self.impl_counter
.set(next
+ 1);
856 // An AST visitor for collecting paths (e.g., the names of structs) and formal
857 // variables (idents) from patterns.
858 struct PathCollector
<'l
> {
860 collected_paths
: Vec
<(hir
::HirId
, &'l hir
::QPath
<'l
>)>,
861 collected_idents
: Vec
<(hir
::HirId
, Ident
, hir
::Mutability
)>,
864 impl<'l
> PathCollector
<'l
> {
865 fn new(tcx
: TyCtxt
<'l
>) -> PathCollector
<'l
> {
866 PathCollector { tcx, collected_paths: vec![], collected_idents: vec![] }
870 impl<'l
> Visitor
<'l
> for PathCollector
<'l
> {
873 fn nested_visit_map(&mut self) -> intravisit
::NestedVisitorMap
<Self::Map
> {
874 intravisit
::NestedVisitorMap
::All(self.tcx
.hir())
877 fn visit_pat(&mut self, p
: &'l hir
::Pat
<'l
>) {
879 hir
::PatKind
::Struct(ref path
, ..) => {
880 self.collected_paths
.push((p
.hir_id
, path
));
882 hir
::PatKind
::TupleStruct(ref path
, ..) | hir
::PatKind
::Path(ref path
) => {
883 self.collected_paths
.push((p
.hir_id
, path
));
885 hir
::PatKind
::Binding(bm
, _
, ident
, _
) => {
887 "PathCollector, visit ident in pat {}: {:?} {:?}",
888 ident
, p
.span
, ident
.span
890 let immut
= match bm
{
891 // Even if the ref is mut, you can't change the ref, only
892 // the data pointed at, so showing the initialising expression
893 // is still worthwhile.
894 hir
::BindingAnnotation
::Unannotated
| hir
::BindingAnnotation
::Ref
=> {
897 hir
::BindingAnnotation
::Mutable
| hir
::BindingAnnotation
::RefMut
=> {
901 self.collected_idents
.push((p
.hir_id
, ident
, immut
));
905 intravisit
::walk_pat(self, p
);
909 /// Defines what to do with the results of saving the analysis.
910 pub trait SaveHandler
{
911 fn save(&mut self, save_ctxt
: &SaveContext
<'_
>, analysis
: &Analysis
);
914 /// Dump the save-analysis results to a file.
915 pub struct DumpHandler
<'a
> {
916 odir
: Option
<&'a Path
>,
920 impl<'a
> DumpHandler
<'a
> {
921 pub fn new(odir
: Option
<&'a Path
>, cratename
: &str) -> DumpHandler
<'a
> {
922 DumpHandler { odir, cratename: cratename.to_owned() }
925 fn output_file(&self, ctx
: &SaveContext
<'_
>) -> (BufWriter
<File
>, PathBuf
) {
926 let sess
= &ctx
.tcx
.sess
;
927 let file_name
= match ctx
.config
.output_file
{
928 Some(ref s
) => PathBuf
::from(s
),
930 let mut root_path
= match self.odir
{
931 Some(val
) => val
.join("save-analysis"),
932 None
=> PathBuf
::from("save-analysis-temp"),
935 if let Err(e
) = std
::fs
::create_dir_all(&root_path
) {
936 error
!("Could not create directory {}: {}", root_path
.display(), e
);
939 let executable
= sess
.crate_types().iter().any(|ct
| *ct
== CrateType
::Executable
);
940 let mut out_name
= if executable { String::new() }
else { "lib".to_owned() }
;
941 out_name
.push_str(&self.cratename
);
942 out_name
.push_str(&sess
.opts
.cg
.extra_filename
);
943 out_name
.push_str(".json");
944 root_path
.push(&out_name
);
950 info
!("Writing output to {}", file_name
.display());
952 let output_file
= BufWriter
::new(File
::create(&file_name
).unwrap_or_else(|e
| {
953 sess
.fatal(&format
!("Could not open {}: {}", file_name
.display(), e
))
956 (output_file
, file_name
)
960 impl SaveHandler
for DumpHandler
<'_
> {
961 fn save(&mut self, save_ctxt
: &SaveContext
<'_
>, analysis
: &Analysis
) {
962 let sess
= &save_ctxt
.tcx
.sess
;
963 let (output
, file_name
) = self.output_file(&save_ctxt
);
964 if let Err(e
) = serde_json
::to_writer(output
, &analysis
) {
965 error
!("Can't serialize save-analysis: {:?}", e
);
968 if sess
.opts
.json_artifact_notifications
{
969 sess
.parse_sess
.span_diagnostic
.emit_artifact_notification(&file_name
, "save-analysis");
974 /// Call a callback with the results of save-analysis.
975 pub struct CallbackHandler
<'b
> {
976 pub callback
: &'b
mut dyn FnMut(&rls_data
::Analysis
),
979 impl SaveHandler
for CallbackHandler
<'_
> {
980 fn save(&mut self, _
: &SaveContext
<'_
>, analysis
: &Analysis
) {
981 (self.callback
)(analysis
)
985 pub fn process_crate
<'l
, 'tcx
, H
: SaveHandler
>(
989 config
: Option
<Config
>,
992 tcx
.dep_graph
.with_ignore(|| {
993 info
!("Dumping crate {}", cratename
);
995 // Privacy checking requires and is done after type checking; use a
996 // fallback in case the access levels couldn't have been correctly computed.
997 let access_levels
= match tcx
.sess
.compile_status() {
998 Ok(..) => tcx
.privacy_access_levels(LOCAL_CRATE
),
999 Err(..) => tcx
.arena
.alloc(AccessLevels
::default()),
1002 let save_ctxt
= SaveContext
{
1004 maybe_typeck_results
: None
,
1005 access_levels
: &access_levels
,
1006 span_utils
: SpanUtils
::new(&tcx
.sess
),
1007 config
: find_config(config
),
1008 impl_counter
: Cell
::new(0),
1011 let mut visitor
= DumpVisitor
::new(save_ctxt
);
1013 visitor
.dump_crate_info(cratename
, tcx
.hir().krate());
1014 visitor
.dump_compilation_options(input
, cratename
);
1015 visitor
.process_crate(tcx
.hir().krate());
1017 handler
.save(&visitor
.save_ctxt
, &visitor
.analysis())
1021 fn find_config(supplied
: Option
<Config
>) -> Config
{
1022 if let Some(config
) = supplied
{
1026 match env
::var_os("RUST_SAVE_ANALYSIS_CONFIG") {
1027 None
=> Config
::default(),
1028 Some(config
) => config
1031 .map_err(|_
| error
!("`RUST_SAVE_ANALYSIS_CONFIG` isn't UTF-8"))
1033 serde_json
::from_str(cfg
)
1034 .map_err(|_
| error
!("Could not deserialize save-analysis config"))
1036 .unwrap_or_default(),
1040 // Utility functions for the module.
1042 // Helper function to escape quotes in a string
1043 fn escape(s
: String
) -> String
{
1044 s
.replace("\"", "\"\"")
1047 // Helper function to determine if a span came from a
1048 // macro expansion or syntax extension.
1049 fn generated_code(span
: Span
) -> bool
{
1050 span
.from_expansion() || span
.is_dummy()
1053 // DefId::index is a newtype and so the JSON serialisation is ugly. Therefore
1054 // we use our own Id which is the same, but without the newtype.
1055 fn id_from_def_id(id
: DefId
) -> rls_data
::Id
{
1056 rls_data
::Id { krate: id.krate.as_u32(), index: id.index.as_u32() }
1059 fn id_from_hir_id(id
: hir
::HirId
, scx
: &SaveContext
<'_
>) -> rls_data
::Id
{
1060 let def_id
= scx
.tcx
.hir().opt_local_def_id(id
);
1061 def_id
.map(|id
| id_from_def_id(id
.to_def_id())).unwrap_or_else(|| {
1062 // Create a *fake* `DefId` out of a `HirId` by combining the owner
1063 // `local_def_index` and the `local_id`.
1064 // This will work unless you have *billions* of definitions in a single
1065 // crate (very unlikely to actually happen).
1067 krate
: LOCAL_CRATE
.as_u32(),
1068 index
: id
.owner
.local_def_index
.as_u32() | id
.local_id
.as_u32().reverse_bits(),
1073 fn null_id() -> rls_data
::Id
{
1074 rls_data
::Id { krate: u32::MAX, index: u32::MAX }
1077 fn lower_attributes(attrs
: Vec
<ast
::Attribute
>, scx
: &SaveContext
<'_
>) -> Vec
<rls_data
::Attribute
> {
1080 // Only retain real attributes. Doc comments are lowered separately.
1081 .filter(|attr
| !attr
.has_name(sym
::doc
))
1083 // Remove the surrounding '#[..]' or '#![..]' of the pretty printed
1084 // attribute. First normalize all inner attribute (#![..]) to outer
1085 // ones (#[..]), then remove the two leading and the one trailing character.
1086 attr
.style
= ast
::AttrStyle
::Outer
;
1087 let value
= attribute_to_string(&attr
);
1088 // This str slicing works correctly, because the leading and trailing characters
1089 // are in the ASCII range and thus exactly one byte each.
1090 let value
= value
[2..value
.len() - 1].to_string();
1092 rls_data
::Attribute { value, span: scx.span_from_span(attr.span) }