1 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
3 #![feature(or_patterns)]
4 #![recursion_limit = "256"]
12 use rustc_ast
::ast
::{self, Attribute, NodeId, PatKind, DUMMY_NODE_ID}
;
13 use rustc_ast
::util
::comments
::strip_doc_comment_decoration
;
14 use rustc_ast
::visit
::{self, Visitor}
;
15 use rustc_ast_pretty
::pprust
::{self, param_to_string, ty_to_string}
;
17 use rustc_hir
::def
::{CtorOf, DefKind as HirDefKind, Res}
;
18 use rustc_hir
::def_id
::{DefId, LOCAL_CRATE}
;
20 use rustc_middle
::middle
::cstore
::ExternCrate
;
21 use rustc_middle
::middle
::privacy
::AccessLevels
;
22 use rustc_middle
::ty
::{self, DefIdTree, TyCtxt}
;
23 use rustc_middle
::{bug, span_bug}
;
24 use rustc_session
::config
::{CrateType, Input, OutputType}
;
25 use rustc_session
::output
::{filename_for_metadata, out_filename}
;
26 use rustc_span
::source_map
::Spanned
;
27 use rustc_span
::symbol
::Ident
;
31 use std
::default::Default
;
34 use std
::io
::BufWriter
;
35 use std
::path
::{Path, PathBuf}
;
37 use dump_visitor
::DumpVisitor
;
38 use span_utils
::SpanUtils
;
40 use rls_data
::config
::Config
;
42 Analysis
, Def
, DefKind
, ExternalCrateData
, GlobalCrateId
, Impl
, ImplKind
, MacroRef
, Ref
,
43 RefKind
, Relation
, RelationKind
, SpanData
,
46 use log
::{debug, error, info}
;
48 pub struct SaveContext
<'l
, 'tcx
> {
50 tables
: &'l ty
::TypeckTables
<'tcx
>,
51 /// Used as a fallback when nesting the typeck tables during item processing
52 /// (if these are not available for that item, e.g. don't own a body)
53 empty_tables
: &'l ty
::TypeckTables
<'tcx
>,
54 access_levels
: &'l AccessLevels
,
55 span_utils
: SpanUtils
<'tcx
>,
57 impl_counter
: Cell
<u32>,
64 RelationData(Relation
, Impl
),
67 impl<'l
, 'tcx
> SaveContext
<'l
, 'tcx
> {
68 fn span_from_span(&self, span
: Span
) -> SpanData
{
69 use rls_span
::{Column, Row}
;
71 let sm
= self.tcx
.sess
.source_map();
72 let start
= sm
.lookup_char_pos(span
.lo());
73 let end
= sm
.lookup_char_pos(span
.hi());
76 file_name
: start
.file
.name
.to_string().into(),
77 byte_start
: span
.lo().0,
78 byte_end
: span
.hi().0,
79 line_start
: Row
::new_one_indexed(start
.line
as u32),
80 line_end
: Row
::new_one_indexed(end
.line
as u32),
81 column_start
: Column
::new_one_indexed(start
.col
.0 as u32 + 1),
82 column_end
: Column
::new_one_indexed(end
.col
.0 as u32 + 1),
86 // Returns path to the compilation output (e.g., libfoo-12345678.rmeta)
87 pub fn compilation_output(&self, crate_name
: &str) -> PathBuf
{
88 let sess
= &self.tcx
.sess
;
89 // Save-analysis is emitted per whole session, not per each crate type
90 let crate_type
= sess
.crate_types()[0];
91 let outputs
= &*self.tcx
.output_filenames(LOCAL_CRATE
);
93 if outputs
.outputs
.contains_key(&OutputType
::Metadata
) {
94 filename_for_metadata(sess
, crate_name
, outputs
)
95 } else if outputs
.outputs
.should_codegen() {
96 out_filename(sess
, crate_type
, outputs
, crate_name
)
98 // Otherwise it's only a DepInfo, in which case we return early and
99 // not even reach the analysis stage.
104 // List external crates used by the current crate.
105 pub fn get_external_crates(&self) -> Vec
<ExternalCrateData
> {
106 let mut result
= Vec
::with_capacity(self.tcx
.crates().len());
108 for &n
in self.tcx
.crates().iter() {
109 let span
= match self.tcx
.extern_crate(n
.as_def_id()) {
110 Some(&ExternCrate { span, .. }
) => span
,
112 debug
!("skipping crate {}, no data", n
);
116 let lo_loc
= self.span_utils
.sess
.source_map().lookup_char_pos(span
.lo());
117 result
.push(ExternalCrateData
{
118 // FIXME: change file_name field to PathBuf in rls-data
119 // https://github.com/nrc/rls-data/issues/7
120 file_name
: self.span_utils
.make_filename_string(&lo_loc
.file
),
123 name
: self.tcx
.crate_name(n
).to_string(),
124 disambiguator
: self.tcx
.crate_disambiguator(n
).to_fingerprint().as_value(),
132 pub fn get_extern_item_data(&self, item
: &ast
::ForeignItem
) -> Option
<Data
> {
133 let qualname
= format
!(
135 self.tcx
.def_path_str(self.tcx
.hir().local_def_id_from_node_id(item
.id
).to_def_id())
138 ast
::ForeignItemKind
::Fn(_
, ref sig
, ref generics
, _
) => {
139 filter
!(self.span_utils
, item
.ident
.span
);
141 Some(Data
::DefData(Def
{
142 kind
: DefKind
::ForeignFunction
,
143 id
: id_from_node_id(item
.id
, self),
144 span
: self.span_from_span(item
.ident
.span
),
145 name
: item
.ident
.to_string(),
147 value
: make_signature(&sig
.decl
, generics
),
151 docs
: self.docs_for_attrs(&item
.attrs
),
152 sig
: sig
::foreign_item_signature(item
, self),
153 attributes
: lower_attributes(item
.attrs
.clone(), self),
156 ast
::ForeignItemKind
::Static(ref ty
, _
, _
) => {
157 filter
!(self.span_utils
, item
.ident
.span
);
159 let id
= id_from_node_id(item
.id
, self);
160 let span
= self.span_from_span(item
.ident
.span
);
162 Some(Data
::DefData(Def
{
163 kind
: DefKind
::ForeignStatic
,
166 name
: item
.ident
.to_string(),
168 value
: ty_to_string(ty
),
172 docs
: self.docs_for_attrs(&item
.attrs
),
173 sig
: sig
::foreign_item_signature(item
, self),
174 attributes
: lower_attributes(item
.attrs
.clone(), self),
177 // FIXME(plietar): needs a new DefKind in rls-data
178 ast
::ForeignItemKind
::TyAlias(..) => None
,
179 ast
::ForeignItemKind
::MacCall(..) => None
,
183 pub fn get_item_data(&self, item
: &ast
::Item
) -> Option
<Data
> {
185 ast
::ItemKind
::Fn(_
, ref sig
, .., ref generics
, _
) => {
186 let qualname
= format
!(
188 self.tcx
.def_path_str(
189 self.tcx
.hir().local_def_id_from_node_id(item
.id
).to_def_id()
192 filter
!(self.span_utils
, item
.ident
.span
);
193 Some(Data
::DefData(Def
{
194 kind
: DefKind
::Function
,
195 id
: id_from_node_id(item
.id
, self),
196 span
: self.span_from_span(item
.ident
.span
),
197 name
: item
.ident
.to_string(),
199 value
: make_signature(&sig
.decl
, generics
),
203 docs
: self.docs_for_attrs(&item
.attrs
),
204 sig
: sig
::item_signature(item
, self),
205 attributes
: lower_attributes(item
.attrs
.clone(), self),
208 ast
::ItemKind
::Static(ref typ
, ..) => {
209 let qualname
= format
!(
211 self.tcx
.def_path_str(
212 self.tcx
.hir().local_def_id_from_node_id(item
.id
).to_def_id()
216 filter
!(self.span_utils
, item
.ident
.span
);
218 let id
= id_from_node_id(item
.id
, self);
219 let span
= self.span_from_span(item
.ident
.span
);
221 Some(Data
::DefData(Def
{
222 kind
: DefKind
::Static
,
225 name
: item
.ident
.to_string(),
227 value
: ty_to_string(&typ
),
231 docs
: self.docs_for_attrs(&item
.attrs
),
232 sig
: sig
::item_signature(item
, self),
233 attributes
: lower_attributes(item
.attrs
.clone(), self),
236 ast
::ItemKind
::Const(_
, ref typ
, _
) => {
237 let qualname
= format
!(
239 self.tcx
.def_path_str(
240 self.tcx
.hir().local_def_id_from_node_id(item
.id
).to_def_id()
243 filter
!(self.span_utils
, item
.ident
.span
);
245 let id
= id_from_node_id(item
.id
, self);
246 let span
= self.span_from_span(item
.ident
.span
);
248 Some(Data
::DefData(Def
{
249 kind
: DefKind
::Const
,
252 name
: item
.ident
.to_string(),
254 value
: ty_to_string(typ
),
258 docs
: self.docs_for_attrs(&item
.attrs
),
259 sig
: sig
::item_signature(item
, self),
260 attributes
: lower_attributes(item
.attrs
.clone(), self),
263 ast
::ItemKind
::Mod(ref m
) => {
264 let qualname
= format
!(
266 self.tcx
.def_path_str(
267 self.tcx
.hir().local_def_id_from_node_id(item
.id
).to_def_id()
271 let sm
= self.tcx
.sess
.source_map();
272 let filename
= sm
.span_to_filename(m
.inner
);
274 filter
!(self.span_utils
, item
.ident
.span
);
276 Some(Data
::DefData(Def
{
278 id
: id_from_node_id(item
.id
, self),
279 name
: item
.ident
.to_string(),
281 span
: self.span_from_span(item
.ident
.span
),
282 value
: filename
.to_string(),
284 children
: m
.items
.iter().map(|i
| id_from_node_id(i
.id
, self)).collect(),
286 docs
: self.docs_for_attrs(&item
.attrs
),
287 sig
: sig
::item_signature(item
, self),
288 attributes
: lower_attributes(item
.attrs
.clone(), self),
291 ast
::ItemKind
::Enum(ref def
, _
) => {
292 let name
= item
.ident
.to_string();
293 let qualname
= format
!(
295 self.tcx
.def_path_str(
296 self.tcx
.hir().local_def_id_from_node_id(item
.id
).to_def_id()
299 filter
!(self.span_utils
, item
.ident
.span
);
301 def
.variants
.iter().map(|v
| v
.ident
.to_string()).collect
::<Vec
<_
>>().join(", ");
302 let value
= format
!("{}::{{{}}}", name
, variants_str
);
303 Some(Data
::DefData(Def
{
305 id
: id_from_node_id(item
.id
, self),
306 span
: self.span_from_span(item
.ident
.span
),
311 children
: def
.variants
.iter().map(|v
| id_from_node_id(v
.id
, self)).collect(),
313 docs
: self.docs_for_attrs(&item
.attrs
),
314 sig
: sig
::item_signature(item
, self),
315 attributes
: lower_attributes(item
.attrs
.clone(), self),
318 ast
::ItemKind
::Impl { ref of_trait, ref self_ty, ref items, .. }
=> {
319 if let ast
::TyKind
::Path(None
, ref path
) = self_ty
.kind
{
320 // Common case impl for a struct or something basic.
321 if generated_code(path
.span
) {
324 let sub_span
= path
.segments
.last().unwrap().ident
.span
;
325 filter
!(self.span_utils
, sub_span
);
327 let impl_id
= self.next_impl_id();
328 let span
= self.span_from_span(sub_span
);
330 let type_data
= self.lookup_def_id(self_ty
.id
);
331 type_data
.map(|type_data
| {
334 kind
: RelationKind
::Impl { id: impl_id }
,
336 from
: id_from_def_id(type_data
),
339 .and_then(|t
| self.lookup_def_id(t
.ref_id
))
341 .unwrap_or_else(null_id
),
345 kind
: match *of_trait
{
346 Some(_
) => ImplKind
::Direct
,
347 None
=> ImplKind
::Inherent
,
350 value
: String
::new(),
354 .map(|i
| id_from_node_id(i
.id
, self))
373 pub fn get_field_data(&self, field
: &ast
::StructField
, scope
: NodeId
) -> Option
<Def
> {
374 if let Some(ident
) = field
.ident
{
375 let name
= ident
.to_string();
376 let qualname
= format
!(
378 self.tcx
.def_path_str(self.tcx
.hir().local_def_id_from_node_id(scope
).to_def_id()),
381 filter
!(self.span_utils
, ident
.span
);
382 let def_id
= self.tcx
.hir().local_def_id_from_node_id(field
.id
).to_def_id();
383 let typ
= self.tcx
.type_of(def_id
).to_string();
385 let id
= id_from_node_id(field
.id
, self);
386 let span
= self.span_from_span(ident
.span
);
389 kind
: DefKind
::Field
,
395 parent
: Some(id_from_node_id(scope
, self)),
398 docs
: self.docs_for_attrs(&field
.attrs
),
399 sig
: sig
::field_signature(field
, self),
400 attributes
: lower_attributes(field
.attrs
.clone(), self),
407 // FIXME would be nice to take a MethodItem here, but the ast provides both
408 // trait and impl flavours, so the caller must do the disassembly.
409 pub fn get_method_data(&self, id
: ast
::NodeId
, ident
: Ident
, span
: Span
) -> Option
<Def
> {
410 // The qualname for a method is the trait name or name of the struct in an impl in
411 // which the method is declared in, followed by the method's name.
412 let (qualname
, parent_scope
, decl_id
, docs
, attributes
) = match self
414 .impl_of_method(self.tcx
.hir().local_def_id_from_node_id(id
).to_def_id())
416 Some(impl_id
) => match self.tcx
.hir().get_if_local(impl_id
) {
417 Some(Node
::Item(item
)) => match item
.kind
{
418 hir
::ItemKind
::Impl { ref self_ty, .. }
=> {
419 let hir
= self.tcx
.hir();
421 let mut qualname
= String
::from("<");
422 qualname
.push_str(&rustc_hir_pretty
::id_to_string(&hir
, self_ty
.hir_id
));
424 let trait_id
= self.tcx
.trait_id_of_impl(impl_id
);
425 let mut docs
= String
::new();
426 let mut attrs
= vec
![];
427 if let Some(Node
::ImplItem(item
)) = hir
.find(hir
.node_id_to_hir_id(id
)) {
428 docs
= self.docs_for_attrs(&item
.attrs
);
429 attrs
= item
.attrs
.to_vec();
432 let mut decl_id
= None
;
433 if let Some(def_id
) = trait_id
{
434 // A method in a trait impl.
435 qualname
.push_str(" as ");
436 qualname
.push_str(&self.tcx
.def_path_str(def_id
));
440 .associated_items(def_id
)
441 .filter_by_name_unhygienic(ident
.name
)
443 .map(|item
| item
.def_id
);
445 qualname
.push_str(">");
447 (qualname
, trait_id
, decl_id
, docs
, attrs
)
450 span_bug
!(span
, "Container {:?} for method {} not an impl?", impl_id
, id
);
456 "Container {:?} for method {} is not a node item {:?}",
465 .trait_of_item(self.tcx
.hir().local_def_id_from_node_id(id
).to_def_id())
468 let mut docs
= String
::new();
469 let mut attrs
= vec
![];
470 let hir_id
= self.tcx
.hir().node_id_to_hir_id(id
);
472 if let Some(Node
::TraitItem(item
)) = self.tcx
.hir().find(hir_id
) {
473 docs
= self.docs_for_attrs(&item
.attrs
);
474 attrs
= item
.attrs
.to_vec();
478 format
!("::{}", self.tcx
.def_path_str(def_id
)),
486 debug
!("could not find container for method {} at {:?}", id
, span
);
487 // This is not necessarily a bug, if there was a compilation error,
488 // the tables we need might not exist.
494 let qualname
= format
!("{}::{}", qualname
, ident
.name
);
496 filter
!(self.span_utils
, ident
.span
);
499 kind
: DefKind
::Method
,
500 id
: id_from_node_id(id
, self),
501 span
: self.span_from_span(ident
.span
),
502 name
: ident
.name
.to_string(),
504 // FIXME you get better data here by using the visitor.
505 value
: String
::new(),
506 parent
: parent_scope
.map(id_from_def_id
),
508 decl_id
: decl_id
.map(id_from_def_id
),
511 attributes
: lower_attributes(attributes
, self),
515 pub fn get_trait_ref_data(&self, trait_ref
: &ast
::TraitRef
) -> Option
<Ref
> {
516 self.lookup_def_id(trait_ref
.ref_id
).and_then(|def_id
| {
517 let span
= trait_ref
.path
.span
;
518 if generated_code(span
) {
521 let sub_span
= trait_ref
.path
.segments
.last().unwrap().ident
.span
;
522 filter
!(self.span_utils
, sub_span
);
523 let span
= self.span_from_span(sub_span
);
524 Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(def_id) }
)
528 pub fn get_expr_data(&self, expr
: &ast
::Expr
) -> Option
<Data
> {
529 let expr_hir_id
= self.tcx
.hir().node_id_to_hir_id(expr
.id
);
530 let hir_node
= self.tcx
.hir().expect_expr(expr_hir_id
);
531 let ty
= self.tables
.expr_ty_adjusted_opt(&hir_node
);
532 if ty
.is_none() || ty
.unwrap().kind
== ty
::Error
{
536 ast
::ExprKind
::Field(ref sub_ex
, ident
) => {
537 let sub_ex_hir_id
= self.tcx
.hir().node_id_to_hir_id(sub_ex
.id
);
538 let hir_node
= match self.tcx
.hir().find(sub_ex_hir_id
) {
539 Some(Node
::Expr(expr
)) => expr
,
542 "Missing or weird node for sub-expression {} in {:?}",
548 match self.tables
.expr_ty_adjusted(&hir_node
).kind
{
549 ty
::Adt(def
, _
) if !def
.is_enum() => {
550 let variant
= &def
.non_enum_variant();
551 filter
!(self.span_utils
, ident
.span
);
552 let span
= self.span_from_span(ident
.span
);
553 Some(Data
::RefData(Ref
{
554 kind
: RefKind
::Variable
,
558 .find_field_index(ident
, variant
)
559 .map(|index
| id_from_def_id(variant
.fields
[index
].did
))
560 .unwrap_or_else(null_id
),
563 ty
::Tuple(..) => None
,
565 debug
!("expected struct or union type, found {:?}", ty
);
570 ast
::ExprKind
::Struct(ref path
, ..) => {
571 match self.tables
.expr_ty_adjusted(&hir_node
).kind
{
572 ty
::Adt(def
, _
) if !def
.is_enum() => {
573 let sub_span
= path
.segments
.last().unwrap().ident
.span
;
574 filter
!(self.span_utils
, sub_span
);
575 let span
= self.span_from_span(sub_span
);
576 Some(Data
::RefData(Ref
{
579 ref_id
: id_from_def_id(def
.did
),
583 // FIXME ty could legitimately be an enum, but then we will fail
584 // later if we try to look up the fields.
585 debug
!("expected struct or union, found {:?}", ty
);
590 ast
::ExprKind
::MethodCall(ref seg
, ..) => {
591 let expr_hir_id
= self.tcx
.hir().definitions().node_id_to_hir_id(expr
.id
);
592 let method_id
= match self.tables
.type_dependent_def_id(expr_hir_id
) {
595 debug
!("could not resolve method id for {:?}", expr
);
599 let (def_id
, decl_id
) = match self.tcx
.associated_item(method_id
).container
{
600 ty
::ImplContainer(_
) => (Some(method_id
), None
),
601 ty
::TraitContainer(_
) => (None
, Some(method_id
)),
603 let sub_span
= seg
.ident
.span
;
604 filter
!(self.span_utils
, sub_span
);
605 let span
= self.span_from_span(sub_span
);
606 Some(Data
::RefData(Ref
{
607 kind
: RefKind
::Function
,
609 ref_id
: def_id
.or(decl_id
).map(id_from_def_id
).unwrap_or_else(null_id
),
612 ast
::ExprKind
::Path(_
, ref path
) => {
613 self.get_path_data(expr
.id
, path
).map(Data
::RefData
)
622 pub fn get_path_res(&self, id
: NodeId
) -> Res
{
624 let hir_id
= match self.tcx
.hir().opt_node_id_to_hir_id(id
) {
626 None
=> return Res
::Err
,
628 match self.tcx
.hir().get(hir_id
) {
629 Node
::TraitRef(tr
) => tr
.path
.res
,
631 Node
::Item(&hir
::Item { kind: hir::ItemKind::Use(path, _), .. }
) => path
.res
,
632 Node
::Visibility(&Spanned
{
633 node
: hir
::VisibilityKind
::Restricted { ref path, .. }
,
637 Node
::PathSegment(seg
) => match seg
.res
{
638 Some(res
) if res
!= Res
::Err
=> res
,
640 let parent_node
= self.tcx
.hir().get_parent_node(hir_id
);
641 self.get_path_res(self.tcx
.hir().hir_id_to_node_id(parent_node
))
645 Node
::Expr(&hir
::Expr { kind: hir::ExprKind::Struct(ref qpath, ..), .. }
) => {
646 self.tables
.qpath_res(qpath
, hir_id
)
649 Node
::Expr(&hir
::Expr { kind: hir::ExprKind::Path(ref qpath), .. }
)
650 | Node
::Pat(&hir
::Pat
{
652 hir
::PatKind
::Path(ref qpath
)
653 | hir
::PatKind
::Struct(ref qpath
, ..)
654 | hir
::PatKind
::TupleStruct(ref qpath
, ..),
657 | Node
::Ty(&hir
::Ty { kind: hir::TyKind::Path(ref qpath), .. }
) => {
658 self.tables
.qpath_res(qpath
, hir_id
)
661 Node
::Binding(&hir
::Pat
{
662 kind
: hir
::PatKind
::Binding(_
, canonical_id
, ..), ..
663 }) => Res
::Local(canonical_id
),
669 pub fn get_path_data(&self, id
: NodeId
, path
: &ast
::Path
) -> Option
<Ref
> {
670 path
.segments
.last().and_then(|seg
| {
671 self.get_path_segment_data(seg
).or_else(|| self.get_path_segment_data_with_id(seg
, id
))
675 pub fn get_path_segment_data(&self, path_seg
: &ast
::PathSegment
) -> Option
<Ref
> {
676 self.get_path_segment_data_with_id(path_seg
, path_seg
.id
)
679 fn get_path_segment_data_with_id(
681 path_seg
: &ast
::PathSegment
,
684 // Returns true if the path is function type sugar, e.g., `Fn(A) -> B`.
685 fn fn_type(seg
: &ast
::PathSegment
) -> bool
{
686 if let Some(ref generic_args
) = seg
.args
{
687 if let ast
::GenericArgs
::Parenthesized(_
) = **generic_args
{
694 if id
== DUMMY_NODE_ID
{
698 let res
= self.get_path_res(id
);
699 let span
= path_seg
.ident
.span
;
700 filter
!(self.span_utils
, span
);
701 let span
= self.span_from_span(span
);
704 Res
::Local(id
) => Some(Ref
{
705 kind
: RefKind
::Variable
,
707 ref_id
: id_from_node_id(self.tcx
.hir().hir_id_to_node_id(id
), self),
709 Res
::Def(HirDefKind
::Trait
, def_id
) if fn_type(path_seg
) => {
710 Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(def_id) }
)
714 | HirDefKind
::Variant
717 | HirDefKind
::TyAlias
718 | HirDefKind
::ForeignTy
719 | HirDefKind
::TraitAlias
720 | HirDefKind
::AssocOpaqueTy
721 | HirDefKind
::AssocTy
723 | HirDefKind
::OpaqueTy
724 | HirDefKind
::TyParam
,
726 ) => Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(def_id) }
),
727 Res
::Def(HirDefKind
::ConstParam
, def_id
) => {
728 Some(Ref { kind: RefKind::Variable, span, ref_id: id_from_def_id(def_id) }
)
730 Res
::Def(HirDefKind
::Ctor(CtorOf
::Struct
, ..), def_id
) => {
731 // This is a reference to a tuple struct where the def_id points
732 // to an invisible constructor function. That is not a very useful
733 // def, so adjust to point to the tuple struct itself.
734 let parent_def_id
= self.tcx
.parent(def_id
).unwrap();
735 Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(parent_def_id) }
)
740 | HirDefKind
::AssocConst
741 | HirDefKind
::Ctor(..),
743 ) => Some(Ref { kind: RefKind::Variable, span, ref_id: id_from_def_id(res.def_id()) }
),
744 Res
::Def(HirDefKind
::AssocFn
, decl_id
) => {
745 let def_id
= if decl_id
.is_local() {
746 let ti
= self.tcx
.associated_item(decl_id
);
749 .associated_items(ti
.container
.id())
750 .filter_by_name_unhygienic(ti
.ident
.name
)
751 .find(|item
| item
.defaultness
.has_value())
752 .map(|item
| item
.def_id
)
757 kind
: RefKind
::Function
,
759 ref_id
: id_from_def_id(def_id
.unwrap_or(decl_id
)),
762 Res
::Def(HirDefKind
::Fn
, def_id
) => {
763 Some(Ref { kind: RefKind::Function, span, ref_id: id_from_def_id(def_id) }
)
765 Res
::Def(HirDefKind
::Mod
, def_id
) => {
766 Some(Ref { kind: RefKind::Mod, span, ref_id: id_from_def_id(def_id) }
)
770 HirDefKind
::Macro(..)
771 | HirDefKind
::ExternCrate
772 | HirDefKind
::ForeignMod
773 | HirDefKind
::LifetimeParam
774 | HirDefKind
::AnonConst
777 | HirDefKind
::GlobalAsm
779 | HirDefKind
::Closure
780 | HirDefKind
::Generator
,
786 | Res
::NonMacroAttr(..)
792 pub fn get_field_ref_data(
794 field_ref
: &ast
::Field
,
795 variant
: &ty
::VariantDef
,
797 filter
!(self.span_utils
, field_ref
.ident
.span
);
798 self.tcx
.find_field_index(field_ref
.ident
, variant
).map(|index
| {
799 let span
= self.span_from_span(field_ref
.ident
.span
);
800 Ref { kind: RefKind::Variable, span, ref_id: id_from_def_id(variant.fields[index].did) }
804 /// Attempt to return MacroRef for any AST node.
806 /// For a given piece of AST defined by the supplied Span and NodeId,
807 /// returns `None` if the node is not macro-generated or the span is malformed,
808 /// else uses the expansion callsite and callee to return some MacroRef.
809 pub fn get_macro_use_data(&self, span
: Span
) -> Option
<MacroRef
> {
810 if !generated_code(span
) {
813 // Note we take care to use the source callsite/callee, to handle
814 // nested expansions and ensure we only generate data for source-visible
816 let callsite
= span
.source_callsite();
817 let callsite_span
= self.span_from_span(callsite
);
818 let callee
= span
.source_callee()?
;
820 let mac_name
= match callee
.kind
{
821 ExpnKind
::Macro(mac_kind
, name
) => match mac_kind
{
822 MacroKind
::Bang
=> name
,
824 // Ignore attribute macros, their spans are usually mangled
825 // FIXME(eddyb) is this really the case anymore?
826 MacroKind
::Attr
| MacroKind
::Derive
=> return None
,
829 // These are not macros.
830 // FIXME(eddyb) maybe there is a way to handle them usefully?
831 ExpnKind
::Root
| ExpnKind
::AstPass(_
) | ExpnKind
::Desugaring(_
) => return None
,
834 let callee_span
= self.span_from_span(callee
.def_site
);
837 qualname
: mac_name
.to_string(), // FIXME: generate the real qualname
842 fn lookup_def_id(&self, ref_id
: NodeId
) -> Option
<DefId
> {
843 match self.get_path_res(ref_id
) {
844 Res
::PrimTy(_
) | Res
::SelfTy(..) | Res
::Err
=> None
,
845 def
=> def
.opt_def_id(),
849 fn docs_for_attrs(&self, attrs
: &[Attribute
]) -> String
{
850 let mut result
= String
::new();
853 if let Some(val
) = attr
.doc_str() {
854 if attr
.is_doc_comment() {
855 result
.push_str(&strip_doc_comment_decoration(&val
.as_str()));
857 result
.push_str(&val
.as_str());
860 } else if attr
.check_name(sym
::doc
) {
861 if let Some(meta_list
) = attr
.meta_item_list() {
864 .filter(|it
| it
.check_name(sym
::include
))
865 .filter_map(|it
| it
.meta_item_list().map(|l
| l
.to_owned()))
867 .filter(|meta
| meta
.check_name(sym
::contents
))
868 .filter_map(|meta
| meta
.value_str())
870 result
.push_str(&val
.as_str());
877 if !self.config
.full_docs
{
878 if let Some(index
) = result
.find("\n\n") {
879 result
.truncate(index
);
886 fn next_impl_id(&self) -> u32 {
887 let next
= self.impl_counter
.get();
888 self.impl_counter
.set(next
+ 1);
893 fn make_signature(decl
: &ast
::FnDecl
, generics
: &ast
::Generics
) -> String
{
894 let mut sig
= "fn ".to_owned();
895 if !generics
.params
.is_empty() {
901 .map(|param
| param
.ident
.to_string())
908 sig
.push_str(&decl
.inputs
.iter().map(param_to_string
).collect
::<Vec
<_
>>().join(", "));
911 ast
::FnRetTy
::Default(_
) => sig
.push_str(" -> ()"),
912 ast
::FnRetTy
::Ty(ref t
) => sig
.push_str(&format
!(" -> {}", ty_to_string(t
))),
918 // An AST visitor for collecting paths (e.g., the names of structs) and formal
919 // variables (idents) from patterns.
920 struct PathCollector
<'l
> {
921 collected_paths
: Vec
<(NodeId
, &'l ast
::Path
)>,
922 collected_idents
: Vec
<(NodeId
, Ident
, ast
::Mutability
)>,
925 impl<'l
> PathCollector
<'l
> {
926 fn new() -> PathCollector
<'l
> {
927 PathCollector { collected_paths: vec![], collected_idents: vec![] }
931 impl<'l
> Visitor
<'l
> for PathCollector
<'l
> {
932 fn visit_pat(&mut self, p
: &'l ast
::Pat
) {
934 PatKind
::Struct(ref path
, ..) => {
935 self.collected_paths
.push((p
.id
, path
));
937 PatKind
::TupleStruct(ref path
, ..) | PatKind
::Path(_
, ref path
) => {
938 self.collected_paths
.push((p
.id
, path
));
940 PatKind
::Ident(bm
, ident
, _
) => {
942 "PathCollector, visit ident in pat {}: {:?} {:?}",
943 ident
, p
.span
, ident
.span
945 let immut
= match bm
{
946 // Even if the ref is mut, you can't change the ref, only
947 // the data pointed at, so showing the initialising expression
948 // is still worthwhile.
949 ast
::BindingMode
::ByRef(_
) => ast
::Mutability
::Not
,
950 ast
::BindingMode
::ByValue(mt
) => mt
,
952 self.collected_idents
.push((p
.id
, ident
, immut
));
956 visit
::walk_pat(self, p
);
960 /// Defines what to do with the results of saving the analysis.
961 pub trait SaveHandler
{
962 fn save(&mut self, save_ctxt
: &SaveContext
<'_
, '_
>, analysis
: &Analysis
);
965 /// Dump the save-analysis results to a file.
966 pub struct DumpHandler
<'a
> {
967 odir
: Option
<&'a Path
>,
971 impl<'a
> DumpHandler
<'a
> {
972 pub fn new(odir
: Option
<&'a Path
>, cratename
: &str) -> DumpHandler
<'a
> {
973 DumpHandler { odir, cratename: cratename.to_owned() }
976 fn output_file(&self, ctx
: &SaveContext
<'_
, '_
>) -> (BufWriter
<File
>, PathBuf
) {
977 let sess
= &ctx
.tcx
.sess
;
978 let file_name
= match ctx
.config
.output_file
{
979 Some(ref s
) => PathBuf
::from(s
),
981 let mut root_path
= match self.odir
{
982 Some(val
) => val
.join("save-analysis"),
983 None
=> PathBuf
::from("save-analysis-temp"),
986 if let Err(e
) = std
::fs
::create_dir_all(&root_path
) {
987 error
!("Could not create directory {}: {}", root_path
.display(), e
);
990 let executable
= sess
.crate_types().iter().any(|ct
| *ct
== CrateType
::Executable
);
991 let mut out_name
= if executable { String::new() }
else { "lib".to_owned() }
;
992 out_name
.push_str(&self.cratename
);
993 out_name
.push_str(&sess
.opts
.cg
.extra_filename
);
994 out_name
.push_str(".json");
995 root_path
.push(&out_name
);
1001 info
!("Writing output to {}", file_name
.display());
1003 let output_file
= BufWriter
::new(File
::create(&file_name
).unwrap_or_else(|e
| {
1004 sess
.fatal(&format
!("Could not open {}: {}", file_name
.display(), e
))
1007 (output_file
, file_name
)
1011 impl SaveHandler
for DumpHandler
<'_
> {
1012 fn save(&mut self, save_ctxt
: &SaveContext
<'_
, '_
>, analysis
: &Analysis
) {
1013 let sess
= &save_ctxt
.tcx
.sess
;
1014 let (output
, file_name
) = self.output_file(&save_ctxt
);
1015 if let Err(e
) = serde_json
::to_writer(output
, &analysis
) {
1016 error
!("Can't serialize save-analysis: {:?}", e
);
1019 if sess
.opts
.json_artifact_notifications
{
1020 sess
.parse_sess
.span_diagnostic
.emit_artifact_notification(&file_name
, "save-analysis");
1025 /// Call a callback with the results of save-analysis.
1026 pub struct CallbackHandler
<'b
> {
1027 pub callback
: &'b
mut dyn FnMut(&rls_data
::Analysis
),
1030 impl SaveHandler
for CallbackHandler
<'_
> {
1031 fn save(&mut self, _
: &SaveContext
<'_
, '_
>, analysis
: &Analysis
) {
1032 (self.callback
)(analysis
)
1036 pub fn process_crate
<'l
, 'tcx
, H
: SaveHandler
>(
1041 config
: Option
<Config
>,
1044 tcx
.dep_graph
.with_ignore(|| {
1045 info
!("Dumping crate {}", cratename
);
1047 // Privacy checking requires and is done after type checking; use a
1048 // fallback in case the access levels couldn't have been correctly computed.
1049 let access_levels
= match tcx
.sess
.compile_status() {
1050 Ok(..) => tcx
.privacy_access_levels(LOCAL_CRATE
),
1051 Err(..) => tcx
.arena
.alloc(AccessLevels
::default()),
1054 let save_ctxt
= SaveContext
{
1056 tables
: &ty
::TypeckTables
::empty(None
),
1057 empty_tables
: &ty
::TypeckTables
::empty(None
),
1058 access_levels
: &access_levels
,
1059 span_utils
: SpanUtils
::new(&tcx
.sess
),
1060 config
: find_config(config
),
1061 impl_counter
: Cell
::new(0),
1064 let mut visitor
= DumpVisitor
::new(save_ctxt
);
1066 visitor
.dump_crate_info(cratename
, krate
);
1067 visitor
.dump_compilation_options(input
, cratename
);
1068 visit
::walk_crate(&mut visitor
, krate
);
1070 handler
.save(&visitor
.save_ctxt
, &visitor
.analysis())
1074 fn find_config(supplied
: Option
<Config
>) -> Config
{
1075 if let Some(config
) = supplied
{
1079 match env
::var_os("RUST_SAVE_ANALYSIS_CONFIG") {
1080 None
=> Config
::default(),
1081 Some(config
) => config
1084 .map_err(|_
| error
!("`RUST_SAVE_ANALYSIS_CONFIG` isn't UTF-8"))
1086 serde_json
::from_str(cfg
)
1087 .map_err(|_
| error
!("Could not deserialize save-analysis config"))
1089 .unwrap_or_default(),
1093 // Utility functions for the module.
1095 // Helper function to escape quotes in a string
1096 fn escape(s
: String
) -> String
{
1097 s
.replace("\"", "\"\"")
1100 // Helper function to determine if a span came from a
1101 // macro expansion or syntax extension.
1102 fn generated_code(span
: Span
) -> bool
{
1103 span
.from_expansion() || span
.is_dummy()
1106 // DefId::index is a newtype and so the JSON serialisation is ugly. Therefore
1107 // we use our own Id which is the same, but without the newtype.
1108 fn id_from_def_id(id
: DefId
) -> rls_data
::Id
{
1109 rls_data
::Id { krate: id.krate.as_u32(), index: id.index.as_u32() }
1112 fn id_from_node_id(id
: NodeId
, scx
: &SaveContext
<'_
, '_
>) -> rls_data
::Id
{
1113 let def_id
= scx
.tcx
.hir().opt_local_def_id_from_node_id(id
);
1114 def_id
.map(|id
| id_from_def_id(id
.to_def_id())).unwrap_or_else(|| {
1115 // Create a *fake* `DefId` out of a `NodeId` by subtracting the `NodeId`
1116 // out of the maximum u32 value. This will work unless you have *billions*
1117 // of definitions in a single crate (very unlikely to actually happen).
1118 rls_data
::Id { krate: LOCAL_CRATE.as_u32(), index: !id.as_u32() }
1122 fn null_id() -> rls_data
::Id
{
1123 rls_data
::Id { krate: u32::max_value(), index: u32::max_value() }
1126 fn lower_attributes(attrs
: Vec
<Attribute
>, scx
: &SaveContext
<'_
, '_
>) -> Vec
<rls_data
::Attribute
> {
1129 // Only retain real attributes. Doc comments are lowered separately.
1130 .filter(|attr
| !attr
.has_name(sym
::doc
))
1132 // Remove the surrounding '#[..]' or '#![..]' of the pretty printed
1133 // attribute. First normalize all inner attribute (#![..]) to outer
1134 // ones (#[..]), then remove the two leading and the one trailing character.
1135 attr
.style
= ast
::AttrStyle
::Outer
;
1136 let value
= pprust
::attribute_to_string(&attr
);
1137 // This str slicing works correctly, because the leading and trailing characters
1138 // are in the ASCII range and thus exactly one byte each.
1139 let value
= value
[2..value
.len() - 1].to_string();
1141 rls_data
::Attribute { value, span: scx.span_from_span(attr.span) }