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
;
30 use std
::default::Default
;
33 use std
::io
::BufWriter
;
34 use std
::path
::{Path, PathBuf}
;
36 use dump_visitor
::DumpVisitor
;
37 use span_utils
::SpanUtils
;
39 use rls_data
::config
::Config
;
41 Analysis
, Def
, DefKind
, ExternalCrateData
, GlobalCrateId
, Impl
, ImplKind
, MacroRef
, Ref
,
42 RefKind
, Relation
, RelationKind
, SpanData
,
45 use log
::{debug, error, info}
;
47 pub struct SaveContext
<'l
, 'tcx
> {
49 tables
: &'l ty
::TypeckTables
<'tcx
>,
50 /// Used as a fallback when nesting the typeck tables during item processing
51 /// (if these are not available for that item, e.g. don't own a body)
52 empty_tables
: &'l ty
::TypeckTables
<'tcx
>,
53 access_levels
: &'l AccessLevels
,
54 span_utils
: SpanUtils
<'tcx
>,
56 impl_counter
: Cell
<u32>,
63 RelationData(Relation
, Impl
),
66 impl<'l
, 'tcx
> SaveContext
<'l
, 'tcx
> {
67 fn span_from_span(&self, span
: Span
) -> SpanData
{
68 use rls_span
::{Column, Row}
;
70 let sm
= self.tcx
.sess
.source_map();
71 let start
= sm
.lookup_char_pos(span
.lo());
72 let end
= sm
.lookup_char_pos(span
.hi());
75 file_name
: start
.file
.name
.to_string().into(),
76 byte_start
: span
.lo().0,
77 byte_end
: span
.hi().0,
78 line_start
: Row
::new_one_indexed(start
.line
as u32),
79 line_end
: Row
::new_one_indexed(end
.line
as u32),
80 column_start
: Column
::new_one_indexed(start
.col
.0 as u32 + 1),
81 column_end
: Column
::new_one_indexed(end
.col
.0 as u32 + 1),
85 // Returns path to the compilation output (e.g., libfoo-12345678.rmeta)
86 pub fn compilation_output(&self, crate_name
: &str) -> PathBuf
{
87 let sess
= &self.tcx
.sess
;
88 // Save-analysis is emitted per whole session, not per each crate type
89 let crate_type
= sess
.crate_types
.borrow()[0];
90 let outputs
= &*self.tcx
.output_filenames(LOCAL_CRATE
);
92 if outputs
.outputs
.contains_key(&OutputType
::Metadata
) {
93 filename_for_metadata(sess
, crate_name
, outputs
)
94 } else if outputs
.outputs
.should_codegen() {
95 out_filename(sess
, crate_type
, outputs
, crate_name
)
97 // Otherwise it's only a DepInfo, in which case we return early and
98 // not even reach the analysis stage.
103 // List external crates used by the current crate.
104 pub fn get_external_crates(&self) -> Vec
<ExternalCrateData
> {
105 let mut result
= Vec
::with_capacity(self.tcx
.crates().len());
107 for &n
in self.tcx
.crates().iter() {
108 let span
= match self.tcx
.extern_crate(n
.as_def_id()) {
109 Some(&ExternCrate { span, .. }
) => span
,
111 debug
!("skipping crate {}, no data", n
);
115 let lo_loc
= self.span_utils
.sess
.source_map().lookup_char_pos(span
.lo());
116 result
.push(ExternalCrateData
{
117 // FIXME: change file_name field to PathBuf in rls-data
118 // https://github.com/nrc/rls-data/issues/7
119 file_name
: self.span_utils
.make_filename_string(&lo_loc
.file
),
122 name
: self.tcx
.crate_name(n
).to_string(),
123 disambiguator
: self.tcx
.crate_disambiguator(n
).to_fingerprint().as_value(),
131 pub fn get_extern_item_data(&self, item
: &ast
::ForeignItem
) -> Option
<Data
> {
132 let qualname
= format
!(
134 self.tcx
.def_path_str(self.tcx
.hir().local_def_id_from_node_id(item
.id
).to_def_id())
137 ast
::ForeignItemKind
::Fn(_
, ref sig
, ref generics
, _
) => {
138 filter
!(self.span_utils
, item
.ident
.span
);
140 Some(Data
::DefData(Def
{
141 kind
: DefKind
::ForeignFunction
,
142 id
: id_from_node_id(item
.id
, self),
143 span
: self.span_from_span(item
.ident
.span
),
144 name
: item
.ident
.to_string(),
146 value
: make_signature(&sig
.decl
, generics
),
150 docs
: self.docs_for_attrs(&item
.attrs
),
151 sig
: sig
::foreign_item_signature(item
, self),
152 attributes
: lower_attributes(item
.attrs
.clone(), self),
155 ast
::ForeignItemKind
::Static(ref ty
, _
, _
) => {
156 filter
!(self.span_utils
, item
.ident
.span
);
158 let id
= id_from_node_id(item
.id
, self);
159 let span
= self.span_from_span(item
.ident
.span
);
161 Some(Data
::DefData(Def
{
162 kind
: DefKind
::ForeignStatic
,
165 name
: item
.ident
.to_string(),
167 value
: ty_to_string(ty
),
171 docs
: self.docs_for_attrs(&item
.attrs
),
172 sig
: sig
::foreign_item_signature(item
, self),
173 attributes
: lower_attributes(item
.attrs
.clone(), self),
176 // FIXME(plietar): needs a new DefKind in rls-data
177 ast
::ForeignItemKind
::TyAlias(..) => None
,
178 ast
::ForeignItemKind
::MacCall(..) => None
,
182 pub fn get_item_data(&self, item
: &ast
::Item
) -> Option
<Data
> {
184 ast
::ItemKind
::Fn(_
, ref sig
, .., ref generics
, _
) => {
185 let qualname
= format
!(
187 self.tcx
.def_path_str(
188 self.tcx
.hir().local_def_id_from_node_id(item
.id
).to_def_id()
191 filter
!(self.span_utils
, item
.ident
.span
);
192 Some(Data
::DefData(Def
{
193 kind
: DefKind
::Function
,
194 id
: id_from_node_id(item
.id
, self),
195 span
: self.span_from_span(item
.ident
.span
),
196 name
: item
.ident
.to_string(),
198 value
: make_signature(&sig
.decl
, generics
),
202 docs
: self.docs_for_attrs(&item
.attrs
),
203 sig
: sig
::item_signature(item
, self),
204 attributes
: lower_attributes(item
.attrs
.clone(), self),
207 ast
::ItemKind
::Static(ref typ
, ..) => {
208 let qualname
= format
!(
210 self.tcx
.def_path_str(
211 self.tcx
.hir().local_def_id_from_node_id(item
.id
).to_def_id()
215 filter
!(self.span_utils
, item
.ident
.span
);
217 let id
= id_from_node_id(item
.id
, self);
218 let span
= self.span_from_span(item
.ident
.span
);
220 Some(Data
::DefData(Def
{
221 kind
: DefKind
::Static
,
224 name
: item
.ident
.to_string(),
226 value
: ty_to_string(&typ
),
230 docs
: self.docs_for_attrs(&item
.attrs
),
231 sig
: sig
::item_signature(item
, self),
232 attributes
: lower_attributes(item
.attrs
.clone(), self),
235 ast
::ItemKind
::Const(_
, ref typ
, _
) => {
236 let qualname
= format
!(
238 self.tcx
.def_path_str(
239 self.tcx
.hir().local_def_id_from_node_id(item
.id
).to_def_id()
242 filter
!(self.span_utils
, item
.ident
.span
);
244 let id
= id_from_node_id(item
.id
, self);
245 let span
= self.span_from_span(item
.ident
.span
);
247 Some(Data
::DefData(Def
{
248 kind
: DefKind
::Const
,
251 name
: item
.ident
.to_string(),
253 value
: ty_to_string(typ
),
257 docs
: self.docs_for_attrs(&item
.attrs
),
258 sig
: sig
::item_signature(item
, self),
259 attributes
: lower_attributes(item
.attrs
.clone(), self),
262 ast
::ItemKind
::Mod(ref m
) => {
263 let qualname
= format
!(
265 self.tcx
.def_path_str(
266 self.tcx
.hir().local_def_id_from_node_id(item
.id
).to_def_id()
270 let sm
= self.tcx
.sess
.source_map();
271 let filename
= sm
.span_to_filename(m
.inner
);
273 filter
!(self.span_utils
, item
.ident
.span
);
275 Some(Data
::DefData(Def
{
277 id
: id_from_node_id(item
.id
, self),
278 name
: item
.ident
.to_string(),
280 span
: self.span_from_span(item
.ident
.span
),
281 value
: filename
.to_string(),
283 children
: m
.items
.iter().map(|i
| id_from_node_id(i
.id
, self)).collect(),
285 docs
: self.docs_for_attrs(&item
.attrs
),
286 sig
: sig
::item_signature(item
, self),
287 attributes
: lower_attributes(item
.attrs
.clone(), self),
290 ast
::ItemKind
::Enum(ref def
, _
) => {
291 let name
= item
.ident
.to_string();
292 let qualname
= format
!(
294 self.tcx
.def_path_str(
295 self.tcx
.hir().local_def_id_from_node_id(item
.id
).to_def_id()
298 filter
!(self.span_utils
, item
.ident
.span
);
300 def
.variants
.iter().map(|v
| v
.ident
.to_string()).collect
::<Vec
<_
>>().join(", ");
301 let value
= format
!("{}::{{{}}}", name
, variants_str
);
302 Some(Data
::DefData(Def
{
304 id
: id_from_node_id(item
.id
, self),
305 span
: self.span_from_span(item
.ident
.span
),
310 children
: def
.variants
.iter().map(|v
| id_from_node_id(v
.id
, self)).collect(),
312 docs
: self.docs_for_attrs(&item
.attrs
),
313 sig
: sig
::item_signature(item
, self),
314 attributes
: lower_attributes(item
.attrs
.clone(), self),
317 ast
::ItemKind
::Impl { ref of_trait, ref self_ty, ref items, .. }
=> {
318 if let ast
::TyKind
::Path(None
, ref path
) = self_ty
.kind
{
319 // Common case impl for a struct or something basic.
320 if generated_code(path
.span
) {
323 let sub_span
= path
.segments
.last().unwrap().ident
.span
;
324 filter
!(self.span_utils
, sub_span
);
326 let impl_id
= self.next_impl_id();
327 let span
= self.span_from_span(sub_span
);
329 let type_data
= self.lookup_def_id(self_ty
.id
);
330 type_data
.map(|type_data
| {
333 kind
: RelationKind
::Impl { id: impl_id }
,
335 from
: id_from_def_id(type_data
),
338 .and_then(|t
| self.lookup_def_id(t
.ref_id
))
340 .unwrap_or_else(null_id
),
344 kind
: match *of_trait
{
345 Some(_
) => ImplKind
::Direct
,
346 None
=> ImplKind
::Inherent
,
349 value
: String
::new(),
353 .map(|i
| id_from_node_id(i
.id
, self))
372 pub fn get_field_data(&self, field
: &ast
::StructField
, scope
: NodeId
) -> Option
<Def
> {
373 if let Some(ident
) = field
.ident
{
374 let name
= ident
.to_string();
375 let qualname
= format
!(
377 self.tcx
.def_path_str(self.tcx
.hir().local_def_id_from_node_id(scope
).to_def_id()),
380 filter
!(self.span_utils
, ident
.span
);
381 let def_id
= self.tcx
.hir().local_def_id_from_node_id(field
.id
).to_def_id();
382 let typ
= self.tcx
.type_of(def_id
).to_string();
384 let id
= id_from_node_id(field
.id
, self);
385 let span
= self.span_from_span(ident
.span
);
388 kind
: DefKind
::Field
,
394 parent
: Some(id_from_node_id(scope
, self)),
397 docs
: self.docs_for_attrs(&field
.attrs
),
398 sig
: sig
::field_signature(field
, self),
399 attributes
: lower_attributes(field
.attrs
.clone(), self),
406 // FIXME would be nice to take a MethodItem here, but the ast provides both
407 // trait and impl flavours, so the caller must do the disassembly.
408 pub fn get_method_data(&self, id
: ast
::NodeId
, ident
: ast
::Ident
, span
: Span
) -> Option
<Def
> {
409 // The qualname for a method is the trait name or name of the struct in an impl in
410 // which the method is declared in, followed by the method's name.
411 let (qualname
, parent_scope
, decl_id
, docs
, attributes
) = match self
413 .impl_of_method(self.tcx
.hir().local_def_id_from_node_id(id
).to_def_id())
415 Some(impl_id
) => match self.tcx
.hir().get_if_local(impl_id
) {
416 Some(Node
::Item(item
)) => match item
.kind
{
417 hir
::ItemKind
::Impl { ref self_ty, .. }
=> {
418 let hir
= self.tcx
.hir();
420 let mut qualname
= String
::from("<");
421 qualname
.push_str(&rustc_hir_pretty
::id_to_string(&hir
, self_ty
.hir_id
));
423 let trait_id
= self.tcx
.trait_id_of_impl(impl_id
);
424 let mut docs
= String
::new();
425 let mut attrs
= vec
![];
426 if let Some(Node
::ImplItem(item
)) = hir
.find(hir
.node_id_to_hir_id(id
)) {
427 docs
= self.docs_for_attrs(&item
.attrs
);
428 attrs
= item
.attrs
.to_vec();
431 let mut decl_id
= None
;
432 if let Some(def_id
) = trait_id
{
433 // A method in a trait impl.
434 qualname
.push_str(" as ");
435 qualname
.push_str(&self.tcx
.def_path_str(def_id
));
439 .associated_items(def_id
)
440 .filter_by_name_unhygienic(ident
.name
)
442 .map(|item
| item
.def_id
);
444 qualname
.push_str(">");
446 (qualname
, trait_id
, decl_id
, docs
, attrs
)
449 span_bug
!(span
, "Container {:?} for method {} not an impl?", impl_id
, id
);
455 "Container {:?} for method {} is not a node item {:?}",
464 .trait_of_item(self.tcx
.hir().local_def_id_from_node_id(id
).to_def_id())
467 let mut docs
= String
::new();
468 let mut attrs
= vec
![];
469 let hir_id
= self.tcx
.hir().node_id_to_hir_id(id
);
471 if let Some(Node
::TraitItem(item
)) = self.tcx
.hir().find(hir_id
) {
472 docs
= self.docs_for_attrs(&item
.attrs
);
473 attrs
= item
.attrs
.to_vec();
477 format
!("::{}", self.tcx
.def_path_str(def_id
)),
485 debug
!("could not find container for method {} at {:?}", id
, span
);
486 // This is not necessarily a bug, if there was a compilation error,
487 // the tables we need might not exist.
493 let qualname
= format
!("{}::{}", qualname
, ident
.name
);
495 filter
!(self.span_utils
, ident
.span
);
498 kind
: DefKind
::Method
,
499 id
: id_from_node_id(id
, self),
500 span
: self.span_from_span(ident
.span
),
501 name
: ident
.name
.to_string(),
503 // FIXME you get better data here by using the visitor.
504 value
: String
::new(),
505 parent
: parent_scope
.map(id_from_def_id
),
507 decl_id
: decl_id
.map(id_from_def_id
),
510 attributes
: lower_attributes(attributes
, self),
514 pub fn get_trait_ref_data(&self, trait_ref
: &ast
::TraitRef
) -> Option
<Ref
> {
515 self.lookup_def_id(trait_ref
.ref_id
).and_then(|def_id
| {
516 let span
= trait_ref
.path
.span
;
517 if generated_code(span
) {
520 let sub_span
= trait_ref
.path
.segments
.last().unwrap().ident
.span
;
521 filter
!(self.span_utils
, sub_span
);
522 let span
= self.span_from_span(sub_span
);
523 Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(def_id) }
)
527 pub fn get_expr_data(&self, expr
: &ast
::Expr
) -> Option
<Data
> {
528 let expr_hir_id
= self.tcx
.hir().node_id_to_hir_id(expr
.id
);
529 let hir_node
= self.tcx
.hir().expect_expr(expr_hir_id
);
530 let ty
= self.tables
.expr_ty_adjusted_opt(&hir_node
);
531 if ty
.is_none() || ty
.unwrap().kind
== ty
::Error
{
535 ast
::ExprKind
::Field(ref sub_ex
, ident
) => {
536 let sub_ex_hir_id
= self.tcx
.hir().node_id_to_hir_id(sub_ex
.id
);
537 let hir_node
= match self.tcx
.hir().find(sub_ex_hir_id
) {
538 Some(Node
::Expr(expr
)) => expr
,
541 "Missing or weird node for sub-expression {} in {:?}",
547 match self.tables
.expr_ty_adjusted(&hir_node
).kind
{
548 ty
::Adt(def
, _
) if !def
.is_enum() => {
549 let variant
= &def
.non_enum_variant();
550 filter
!(self.span_utils
, ident
.span
);
551 let span
= self.span_from_span(ident
.span
);
552 Some(Data
::RefData(Ref
{
553 kind
: RefKind
::Variable
,
557 .find_field_index(ident
, variant
)
558 .map(|index
| id_from_def_id(variant
.fields
[index
].did
))
559 .unwrap_or_else(null_id
),
562 ty
::Tuple(..) => None
,
564 debug
!("expected struct or union type, found {:?}", ty
);
569 ast
::ExprKind
::Struct(ref path
, ..) => {
570 match self.tables
.expr_ty_adjusted(&hir_node
).kind
{
571 ty
::Adt(def
, _
) if !def
.is_enum() => {
572 let sub_span
= path
.segments
.last().unwrap().ident
.span
;
573 filter
!(self.span_utils
, sub_span
);
574 let span
= self.span_from_span(sub_span
);
575 Some(Data
::RefData(Ref
{
578 ref_id
: id_from_def_id(def
.did
),
582 // FIXME ty could legitimately be an enum, but then we will fail
583 // later if we try to look up the fields.
584 debug
!("expected struct or union, found {:?}", ty
);
589 ast
::ExprKind
::MethodCall(ref seg
, ..) => {
590 let expr_hir_id
= self.tcx
.hir().definitions().node_id_to_hir_id(expr
.id
);
591 let method_id
= match self.tables
.type_dependent_def_id(expr_hir_id
) {
594 debug
!("could not resolve method id for {:?}", expr
);
598 let (def_id
, decl_id
) = match self.tcx
.associated_item(method_id
).container
{
599 ty
::ImplContainer(_
) => (Some(method_id
), None
),
600 ty
::TraitContainer(_
) => (None
, Some(method_id
)),
602 let sub_span
= seg
.ident
.span
;
603 filter
!(self.span_utils
, sub_span
);
604 let span
= self.span_from_span(sub_span
);
605 Some(Data
::RefData(Ref
{
606 kind
: RefKind
::Function
,
608 ref_id
: def_id
.or(decl_id
).map(id_from_def_id
).unwrap_or_else(null_id
),
611 ast
::ExprKind
::Path(_
, ref path
) => {
612 self.get_path_data(expr
.id
, path
).map(Data
::RefData
)
621 pub fn get_path_res(&self, id
: NodeId
) -> Res
{
622 let hir_id
= self.tcx
.hir().node_id_to_hir_id(id
);
623 match self.tcx
.hir().get(hir_id
) {
624 Node
::TraitRef(tr
) => tr
.path
.res
,
626 Node
::Item(&hir
::Item { kind: hir::ItemKind::Use(path, _), .. }
) => path
.res
,
627 Node
::Visibility(&Spanned
{
628 node
: hir
::VisibilityKind
::Restricted { ref path, .. }
,
632 Node
::PathSegment(seg
) => match seg
.res
{
633 Some(res
) if res
!= Res
::Err
=> res
,
635 let parent_node
= self.tcx
.hir().get_parent_node(hir_id
);
636 self.get_path_res(self.tcx
.hir().hir_id_to_node_id(parent_node
))
640 Node
::Expr(&hir
::Expr { kind: hir::ExprKind::Struct(ref qpath, ..), .. }
) => {
641 self.tables
.qpath_res(qpath
, hir_id
)
644 Node
::Expr(&hir
::Expr { kind: hir::ExprKind::Path(ref qpath), .. }
)
645 | Node
::Pat(&hir
::Pat
{
647 hir
::PatKind
::Path(ref qpath
)
648 | hir
::PatKind
::Struct(ref qpath
, ..)
649 | hir
::PatKind
::TupleStruct(ref qpath
, ..),
652 | Node
::Ty(&hir
::Ty { kind: hir::TyKind::Path(ref qpath), .. }
) => {
653 self.tables
.qpath_res(qpath
, hir_id
)
656 Node
::Binding(&hir
::Pat
{
657 kind
: hir
::PatKind
::Binding(_
, canonical_id
, ..), ..
658 }) => Res
::Local(canonical_id
),
664 pub fn get_path_data(&self, id
: NodeId
, path
: &ast
::Path
) -> Option
<Ref
> {
665 path
.segments
.last().and_then(|seg
| {
666 self.get_path_segment_data(seg
).or_else(|| self.get_path_segment_data_with_id(seg
, id
))
670 pub fn get_path_segment_data(&self, path_seg
: &ast
::PathSegment
) -> Option
<Ref
> {
671 self.get_path_segment_data_with_id(path_seg
, path_seg
.id
)
674 fn get_path_segment_data_with_id(
676 path_seg
: &ast
::PathSegment
,
679 // Returns true if the path is function type sugar, e.g., `Fn(A) -> B`.
680 fn fn_type(seg
: &ast
::PathSegment
) -> bool
{
681 if let Some(ref generic_args
) = seg
.args
{
682 if let ast
::GenericArgs
::Parenthesized(_
) = **generic_args
{
689 if id
== DUMMY_NODE_ID
{
693 let res
= self.get_path_res(id
);
694 let span
= path_seg
.ident
.span
;
695 filter
!(self.span_utils
, span
);
696 let span
= self.span_from_span(span
);
699 Res
::Local(id
) => Some(Ref
{
700 kind
: RefKind
::Variable
,
702 ref_id
: id_from_node_id(self.tcx
.hir().hir_id_to_node_id(id
), self),
704 Res
::Def(HirDefKind
::Trait
, def_id
) if fn_type(path_seg
) => {
705 Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(def_id) }
)
709 | HirDefKind
::Variant
712 | HirDefKind
::TyAlias
713 | HirDefKind
::ForeignTy
714 | HirDefKind
::TraitAlias
715 | HirDefKind
::AssocOpaqueTy
716 | HirDefKind
::AssocTy
718 | HirDefKind
::OpaqueTy
719 | HirDefKind
::TyParam
,
721 ) => Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(def_id) }
),
722 Res
::Def(HirDefKind
::ConstParam
, def_id
) => {
723 Some(Ref { kind: RefKind::Variable, span, ref_id: id_from_def_id(def_id) }
)
725 Res
::Def(HirDefKind
::Ctor(CtorOf
::Struct
, ..), def_id
) => {
726 // This is a reference to a tuple struct where the def_id points
727 // to an invisible constructor function. That is not a very useful
728 // def, so adjust to point to the tuple struct itself.
729 let parent_def_id
= self.tcx
.parent(def_id
).unwrap();
730 Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(parent_def_id) }
)
735 | HirDefKind
::AssocConst
736 | HirDefKind
::Ctor(..),
738 ) => Some(Ref { kind: RefKind::Variable, span, ref_id: id_from_def_id(res.def_id()) }
),
739 Res
::Def(HirDefKind
::AssocFn
, decl_id
) => {
740 let def_id
= if decl_id
.is_local() {
741 let ti
= self.tcx
.associated_item(decl_id
);
744 .associated_items(ti
.container
.id())
745 .filter_by_name_unhygienic(ti
.ident
.name
)
746 .find(|item
| item
.defaultness
.has_value())
747 .map(|item
| item
.def_id
)
752 kind
: RefKind
::Function
,
754 ref_id
: id_from_def_id(def_id
.unwrap_or(decl_id
)),
757 Res
::Def(HirDefKind
::Fn
, def_id
) => {
758 Some(Ref { kind: RefKind::Function, span, ref_id: id_from_def_id(def_id) }
)
760 Res
::Def(HirDefKind
::Mod
, def_id
) => {
761 Some(Ref { kind: RefKind::Mod, span, ref_id: id_from_def_id(def_id) }
)
765 | Res
::Def(HirDefKind
::Macro(..), _
)
767 | Res
::NonMacroAttr(..)
773 pub fn get_field_ref_data(
775 field_ref
: &ast
::Field
,
776 variant
: &ty
::VariantDef
,
778 filter
!(self.span_utils
, field_ref
.ident
.span
);
779 self.tcx
.find_field_index(field_ref
.ident
, variant
).map(|index
| {
780 let span
= self.span_from_span(field_ref
.ident
.span
);
781 Ref { kind: RefKind::Variable, span, ref_id: id_from_def_id(variant.fields[index].did) }
785 /// Attempt to return MacroRef for any AST node.
787 /// For a given piece of AST defined by the supplied Span and NodeId,
788 /// returns `None` if the node is not macro-generated or the span is malformed,
789 /// else uses the expansion callsite and callee to return some MacroRef.
790 pub fn get_macro_use_data(&self, span
: Span
) -> Option
<MacroRef
> {
791 if !generated_code(span
) {
794 // Note we take care to use the source callsite/callee, to handle
795 // nested expansions and ensure we only generate data for source-visible
797 let callsite
= span
.source_callsite();
798 let callsite_span
= self.span_from_span(callsite
);
799 let callee
= span
.source_callee()?
;
801 let mac_name
= match callee
.kind
{
802 ExpnKind
::Macro(mac_kind
, name
) => match mac_kind
{
803 MacroKind
::Bang
=> name
,
805 // Ignore attribute macros, their spans are usually mangled
806 // FIXME(eddyb) is this really the case anymore?
807 MacroKind
::Attr
| MacroKind
::Derive
=> return None
,
810 // These are not macros.
811 // FIXME(eddyb) maybe there is a way to handle them usefully?
812 ExpnKind
::Root
| ExpnKind
::AstPass(_
) | ExpnKind
::Desugaring(_
) => return None
,
815 let callee_span
= self.span_from_span(callee
.def_site
);
818 qualname
: mac_name
.to_string(), // FIXME: generate the real qualname
823 fn lookup_def_id(&self, ref_id
: NodeId
) -> Option
<DefId
> {
824 match self.get_path_res(ref_id
) {
825 Res
::PrimTy(_
) | Res
::SelfTy(..) | Res
::Err
=> None
,
826 def
=> def
.opt_def_id(),
830 fn docs_for_attrs(&self, attrs
: &[Attribute
]) -> String
{
831 let mut result
= String
::new();
834 if let Some(val
) = attr
.doc_str() {
835 if attr
.is_doc_comment() {
836 result
.push_str(&strip_doc_comment_decoration(&val
.as_str()));
838 result
.push_str(&val
.as_str());
841 } else if attr
.check_name(sym
::doc
) {
842 if let Some(meta_list
) = attr
.meta_item_list() {
845 .filter(|it
| it
.check_name(sym
::include
))
846 .filter_map(|it
| it
.meta_item_list().map(|l
| l
.to_owned()))
848 .filter(|meta
| meta
.check_name(sym
::contents
))
849 .filter_map(|meta
| meta
.value_str())
851 result
.push_str(&val
.as_str());
858 if !self.config
.full_docs
{
859 if let Some(index
) = result
.find("\n\n") {
860 result
.truncate(index
);
867 fn next_impl_id(&self) -> u32 {
868 let next
= self.impl_counter
.get();
869 self.impl_counter
.set(next
+ 1);
874 fn make_signature(decl
: &ast
::FnDecl
, generics
: &ast
::Generics
) -> String
{
875 let mut sig
= "fn ".to_owned();
876 if !generics
.params
.is_empty() {
882 .map(|param
| param
.ident
.to_string())
889 sig
.push_str(&decl
.inputs
.iter().map(param_to_string
).collect
::<Vec
<_
>>().join(", "));
892 ast
::FnRetTy
::Default(_
) => sig
.push_str(" -> ()"),
893 ast
::FnRetTy
::Ty(ref t
) => sig
.push_str(&format
!(" -> {}", ty_to_string(t
))),
899 // An AST visitor for collecting paths (e.g., the names of structs) and formal
900 // variables (idents) from patterns.
901 struct PathCollector
<'l
> {
902 collected_paths
: Vec
<(NodeId
, &'l ast
::Path
)>,
903 collected_idents
: Vec
<(NodeId
, ast
::Ident
, ast
::Mutability
)>,
906 impl<'l
> PathCollector
<'l
> {
907 fn new() -> PathCollector
<'l
> {
908 PathCollector { collected_paths: vec![], collected_idents: vec![] }
912 impl<'l
> Visitor
<'l
> for PathCollector
<'l
> {
913 fn visit_pat(&mut self, p
: &'l ast
::Pat
) {
915 PatKind
::Struct(ref path
, ..) => {
916 self.collected_paths
.push((p
.id
, path
));
918 PatKind
::TupleStruct(ref path
, ..) | PatKind
::Path(_
, ref path
) => {
919 self.collected_paths
.push((p
.id
, path
));
921 PatKind
::Ident(bm
, ident
, _
) => {
923 "PathCollector, visit ident in pat {}: {:?} {:?}",
924 ident
, p
.span
, ident
.span
926 let immut
= match bm
{
927 // Even if the ref is mut, you can't change the ref, only
928 // the data pointed at, so showing the initialising expression
929 // is still worthwhile.
930 ast
::BindingMode
::ByRef(_
) => ast
::Mutability
::Not
,
931 ast
::BindingMode
::ByValue(mt
) => mt
,
933 self.collected_idents
.push((p
.id
, ident
, immut
));
937 visit
::walk_pat(self, p
);
941 /// Defines what to do with the results of saving the analysis.
942 pub trait SaveHandler
{
943 fn save(&mut self, save_ctxt
: &SaveContext
<'_
, '_
>, analysis
: &Analysis
);
946 /// Dump the save-analysis results to a file.
947 pub struct DumpHandler
<'a
> {
948 odir
: Option
<&'a Path
>,
952 impl<'a
> DumpHandler
<'a
> {
953 pub fn new(odir
: Option
<&'a Path
>, cratename
: &str) -> DumpHandler
<'a
> {
954 DumpHandler { odir, cratename: cratename.to_owned() }
957 fn output_file(&self, ctx
: &SaveContext
<'_
, '_
>) -> (BufWriter
<File
>, PathBuf
) {
958 let sess
= &ctx
.tcx
.sess
;
959 let file_name
= match ctx
.config
.output_file
{
960 Some(ref s
) => PathBuf
::from(s
),
962 let mut root_path
= match self.odir
{
963 Some(val
) => val
.join("save-analysis"),
964 None
=> PathBuf
::from("save-analysis-temp"),
967 if let Err(e
) = std
::fs
::create_dir_all(&root_path
) {
968 error
!("Could not create directory {}: {}", root_path
.display(), e
);
972 sess
.crate_types
.borrow().iter().any(|ct
| *ct
== CrateType
::Executable
);
973 let mut out_name
= if executable { String::new() }
else { "lib".to_owned() }
;
974 out_name
.push_str(&self.cratename
);
975 out_name
.push_str(&sess
.opts
.cg
.extra_filename
);
976 out_name
.push_str(".json");
977 root_path
.push(&out_name
);
983 info
!("Writing output to {}", file_name
.display());
985 let output_file
= BufWriter
::new(File
::create(&file_name
).unwrap_or_else(|e
| {
986 sess
.fatal(&format
!("Could not open {}: {}", file_name
.display(), e
))
989 (output_file
, file_name
)
993 impl SaveHandler
for DumpHandler
<'_
> {
994 fn save(&mut self, save_ctxt
: &SaveContext
<'_
, '_
>, analysis
: &Analysis
) {
995 let sess
= &save_ctxt
.tcx
.sess
;
996 let (output
, file_name
) = self.output_file(&save_ctxt
);
997 if let Err(e
) = serde_json
::to_writer(output
, &analysis
) {
998 error
!("Can't serialize save-analysis: {:?}", e
);
1001 if sess
.opts
.json_artifact_notifications
{
1002 sess
.parse_sess
.span_diagnostic
.emit_artifact_notification(&file_name
, "save-analysis");
1007 /// Call a callback with the results of save-analysis.
1008 pub struct CallbackHandler
<'b
> {
1009 pub callback
: &'b
mut dyn FnMut(&rls_data
::Analysis
),
1012 impl SaveHandler
for CallbackHandler
<'_
> {
1013 fn save(&mut self, _
: &SaveContext
<'_
, '_
>, analysis
: &Analysis
) {
1014 (self.callback
)(analysis
)
1018 pub fn process_crate
<'l
, 'tcx
, H
: SaveHandler
>(
1023 config
: Option
<Config
>,
1026 tcx
.dep_graph
.with_ignore(|| {
1027 info
!("Dumping crate {}", cratename
);
1029 // Privacy checking requires and is done after type checking; use a
1030 // fallback in case the access levels couldn't have been correctly computed.
1031 let access_levels
= match tcx
.sess
.compile_status() {
1032 Ok(..) => tcx
.privacy_access_levels(LOCAL_CRATE
),
1033 Err(..) => tcx
.arena
.alloc(AccessLevels
::default()),
1036 let save_ctxt
= SaveContext
{
1038 tables
: &ty
::TypeckTables
::empty(None
),
1039 empty_tables
: &ty
::TypeckTables
::empty(None
),
1040 access_levels
: &access_levels
,
1041 span_utils
: SpanUtils
::new(&tcx
.sess
),
1042 config
: find_config(config
),
1043 impl_counter
: Cell
::new(0),
1046 let mut visitor
= DumpVisitor
::new(save_ctxt
);
1048 visitor
.dump_crate_info(cratename
, krate
);
1049 visitor
.dump_compilation_options(input
, cratename
);
1050 visit
::walk_crate(&mut visitor
, krate
);
1052 handler
.save(&visitor
.save_ctxt
, &visitor
.analysis())
1056 fn find_config(supplied
: Option
<Config
>) -> Config
{
1057 if let Some(config
) = supplied
{
1061 match env
::var_os("RUST_SAVE_ANALYSIS_CONFIG") {
1062 None
=> Config
::default(),
1063 Some(config
) => config
1066 .map_err(|_
| error
!("`RUST_SAVE_ANALYSIS_CONFIG` isn't UTF-8"))
1068 serde_json
::from_str(cfg
)
1069 .map_err(|_
| error
!("Could not deserialize save-analysis config"))
1071 .unwrap_or_default(),
1075 // Utility functions for the module.
1077 // Helper function to escape quotes in a string
1078 fn escape(s
: String
) -> String
{
1079 s
.replace("\"", "\"\"")
1082 // Helper function to determine if a span came from a
1083 // macro expansion or syntax extension.
1084 fn generated_code(span
: Span
) -> bool
{
1085 span
.from_expansion() || span
.is_dummy()
1088 // DefId::index is a newtype and so the JSON serialisation is ugly. Therefore
1089 // we use our own Id which is the same, but without the newtype.
1090 fn id_from_def_id(id
: DefId
) -> rls_data
::Id
{
1091 rls_data
::Id { krate: id.krate.as_u32(), index: id.index.as_u32() }
1094 fn id_from_node_id(id
: NodeId
, scx
: &SaveContext
<'_
, '_
>) -> rls_data
::Id
{
1095 let def_id
= scx
.tcx
.hir().opt_local_def_id_from_node_id(id
);
1096 def_id
.map(|id
| id_from_def_id(id
.to_def_id())).unwrap_or_else(|| {
1097 // Create a *fake* `DefId` out of a `NodeId` by subtracting the `NodeId`
1098 // out of the maximum u32 value. This will work unless you have *billions*
1099 // of definitions in a single crate (very unlikely to actually happen).
1100 rls_data
::Id { krate: LOCAL_CRATE.as_u32(), index: !id.as_u32() }
1104 fn null_id() -> rls_data
::Id
{
1105 rls_data
::Id { krate: u32::max_value(), index: u32::max_value() }
1108 fn lower_attributes(attrs
: Vec
<Attribute
>, scx
: &SaveContext
<'_
, '_
>) -> Vec
<rls_data
::Attribute
> {
1111 // Only retain real attributes. Doc comments are lowered separately.
1112 .filter(|attr
| !attr
.has_name(sym
::doc
))
1114 // Remove the surrounding '#[..]' or '#![..]' of the pretty printed
1115 // attribute. First normalize all inner attribute (#![..]) to outer
1116 // ones (#[..]), then remove the two leading and the one trailing character.
1117 attr
.style
= ast
::AttrStyle
::Outer
;
1118 let value
= pprust
::attribute_to_string(&attr
);
1119 // This str slicing works correctly, because the leading and trailing characters
1120 // are in the ASCII range and thus exactly one byte each.
1121 let value
= value
[2..value
.len() - 1].to_string();
1123 rls_data
::Attribute { value, span: scx.span_from_span(attr.span) }