]> git.lizzy.rs Git - rust.git/blob - src/librustc_save_analysis/lib.rs
Simplify SaveHandler trait
[rust.git] / src / librustc_save_analysis / lib.rs
1 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
2 #![feature(nll)]
3 #![deny(rust_2018_idioms)]
4 #![deny(unused_lifetimes)]
5 #![allow(unused_attributes)]
6
7 #![recursion_limit="256"]
8
9
10 mod dumper;
11 mod dump_visitor;
12 #[macro_use]
13 mod span_utils;
14 mod sig;
15
16 use rustc::hir;
17 use rustc::hir::def::{CtorOf, Res, DefKind as HirDefKind};
18 use rustc::hir::Node;
19 use rustc::hir::def_id::{DefId, LOCAL_CRATE};
20 use rustc::middle::privacy::AccessLevels;
21 use rustc::middle::cstore::ExternCrate;
22 use rustc::session::config::{CrateType, Input, OutputType};
23 use rustc::ty::{self, DefIdTree, TyCtxt};
24 use rustc::{bug, span_bug};
25 use rustc_codegen_utils::link::{filename_for_metadata, out_filename};
26
27 use std::cell::Cell;
28 use std::default::Default;
29 use std::env;
30 use std::fs::File;
31 use std::io::BufWriter;
32 use std::path::{Path, PathBuf};
33
34 use syntax::ast::{self, Attribute, DUMMY_NODE_ID, NodeId, PatKind};
35 use syntax::source_map::Spanned;
36 use syntax::parse::lexer::comments::strip_doc_comment_decoration;
37 use syntax::print::pprust;
38 use syntax::visit::{self, Visitor};
39 use syntax::print::pprust::{arg_to_string, ty_to_string};
40 use syntax_pos::*;
41
42 use dump_visitor::DumpVisitor;
43 use span_utils::SpanUtils;
44
45 use rls_data::{Def, DefKind, ExternalCrateData, GlobalCrateId, MacroRef, Ref, RefKind, Relation,
46                RelationKind, SpanData, Impl, ImplKind, Analysis};
47 use rls_data::config::Config;
48
49 use log::{debug, error, info};
50
51
52 pub struct SaveContext<'l, 'tcx> {
53     tcx: TyCtxt<'tcx>,
54     tables: &'l ty::TypeckTables<'tcx>,
55     access_levels: &'l AccessLevels,
56     span_utils: SpanUtils<'tcx>,
57     config: Config,
58     impl_counter: Cell<u32>,
59 }
60
61 #[derive(Debug)]
62 pub enum Data {
63     RefData(Ref),
64     DefData(Def),
65     RelationData(Relation, Impl),
66 }
67
68 impl<'l, 'tcx> SaveContext<'l, 'tcx> {
69     fn span_from_span(&self, span: Span) -> SpanData {
70         use rls_span::{Column, Row};
71
72         let cm = self.tcx.sess.source_map();
73         let start = cm.lookup_char_pos(span.lo());
74         let end = cm.lookup_char_pos(span.hi());
75
76         SpanData {
77             file_name: start.file.name.to_string().into(),
78             byte_start: span.lo().0,
79             byte_end: span.hi().0,
80             line_start: Row::new_one_indexed(start.line as u32),
81             line_end: Row::new_one_indexed(end.line as u32),
82             column_start: Column::new_one_indexed(start.col.0 as u32 + 1),
83             column_end: Column::new_one_indexed(end.col.0 as u32 + 1),
84         }
85     }
86
87     // Returns path to the compilation output (e.g., libfoo-12345678.rmeta)
88     pub fn compilation_output(&self, crate_name: &str) -> PathBuf {
89         let sess = &self.tcx.sess;
90         // Save-analysis is emitted per whole session, not per each crate type
91         let crate_type = sess.crate_types.borrow()[0];
92         let outputs = &*self.tcx.output_filenames(LOCAL_CRATE);
93
94         if outputs.outputs.contains_key(&OutputType::Metadata) {
95             filename_for_metadata(sess, crate_name, outputs)
96         } else if outputs.outputs.should_codegen() {
97             out_filename(sess, crate_type, outputs, crate_name)
98         } else {
99             // Otherwise it's only a DepInfo, in which case we return early and
100             // not even reach the analysis stage.
101             unreachable!()
102         }
103     }
104
105     // List external crates used by the current crate.
106     pub fn get_external_crates(&self) -> Vec<ExternalCrateData> {
107         let mut result = Vec::with_capacity(self.tcx.crates().len());
108
109         for &n in self.tcx.crates().iter() {
110             let span = match self.tcx.extern_crate(n.as_def_id()) {
111                 Some(&ExternCrate { span, .. }) => span,
112                 None => {
113                     debug!("skipping crate {}, no data", n);
114                     continue;
115                 }
116             };
117             let lo_loc = self.span_utils.sess.source_map().lookup_char_pos(span.lo());
118             result.push(ExternalCrateData {
119                 // FIXME: change file_name field to PathBuf in rls-data
120                 // https://github.com/nrc/rls-data/issues/7
121                 file_name: self.span_utils.make_filename_string(&lo_loc.file),
122                 num: n.as_u32(),
123                 id: GlobalCrateId {
124                     name: self.tcx.crate_name(n).to_string(),
125                     disambiguator: self.tcx.crate_disambiguator(n).to_fingerprint().as_value(),
126                 },
127             });
128         }
129
130         result
131     }
132
133     pub fn get_extern_item_data(&self, item: &ast::ForeignItem) -> Option<Data> {
134         let qualname = format!("::{}",
135             self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id)));
136         match item.node {
137             ast::ForeignItemKind::Fn(ref decl, ref generics) => {
138                 filter!(self.span_utils, item.ident.span);
139
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(),
145                     qualname,
146                     value: make_signature(decl, generics),
147                     parent: None,
148                     children: vec![],
149                     decl_id: None,
150                     docs: self.docs_for_attrs(&item.attrs),
151                     sig: sig::foreign_item_signature(item, self),
152                     attributes: lower_attributes(item.attrs.clone(), self),
153                 }))
154             }
155             ast::ForeignItemKind::Static(ref ty, _) => {
156                 filter!(self.span_utils, item.ident.span);
157
158                 let id = id_from_node_id(item.id, self);
159                 let span = self.span_from_span(item.ident.span);
160
161                 Some(Data::DefData(Def {
162                     kind: DefKind::ForeignStatic,
163                     id,
164                     span,
165                     name: item.ident.to_string(),
166                     qualname,
167                     value: ty_to_string(ty),
168                     parent: None,
169                     children: vec![],
170                     decl_id: None,
171                     docs: self.docs_for_attrs(&item.attrs),
172                     sig: sig::foreign_item_signature(item, self),
173                     attributes: lower_attributes(item.attrs.clone(), self),
174                 }))
175             }
176             // FIXME(plietar): needs a new DefKind in rls-data
177             ast::ForeignItemKind::Ty => None,
178             ast::ForeignItemKind::Macro(..) => None,
179         }
180     }
181
182     pub fn get_item_data(&self, item: &ast::Item) -> Option<Data> {
183         match item.node {
184             ast::ItemKind::Fn(ref decl, .., ref generics, _) => {
185                 let qualname = format!("::{}",
186                     self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id)));
187                 filter!(self.span_utils, item.ident.span);
188                 Some(Data::DefData(Def {
189                     kind: DefKind::Function,
190                     id: id_from_node_id(item.id, self),
191                     span: self.span_from_span(item.ident.span),
192                     name: item.ident.to_string(),
193                     qualname,
194                     value: make_signature(decl, generics),
195                     parent: None,
196                     children: vec![],
197                     decl_id: None,
198                     docs: self.docs_for_attrs(&item.attrs),
199                     sig: sig::item_signature(item, self),
200                     attributes: lower_attributes(item.attrs.clone(), self),
201                 }))
202             }
203             ast::ItemKind::Static(ref typ, ..) => {
204                 let qualname = format!("::{}",
205                     self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id)));
206
207                 filter!(self.span_utils, item.ident.span);
208
209                 let id = id_from_node_id(item.id, self);
210                 let span = self.span_from_span(item.ident.span);
211
212                 Some(Data::DefData(Def {
213                     kind: DefKind::Static,
214                     id,
215                     span,
216                     name: item.ident.to_string(),
217                     qualname,
218                     value: ty_to_string(&typ),
219                     parent: None,
220                     children: vec![],
221                     decl_id: None,
222                     docs: self.docs_for_attrs(&item.attrs),
223                     sig: sig::item_signature(item, self),
224                     attributes: lower_attributes(item.attrs.clone(), self),
225                 }))
226             }
227             ast::ItemKind::Const(ref typ, _) => {
228                 let qualname = format!("::{}",
229                     self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id)));
230                 filter!(self.span_utils, item.ident.span);
231
232                 let id = id_from_node_id(item.id, self);
233                 let span = self.span_from_span(item.ident.span);
234
235                 Some(Data::DefData(Def {
236                     kind: DefKind::Const,
237                     id,
238                     span,
239                     name: item.ident.to_string(),
240                     qualname,
241                     value: ty_to_string(typ),
242                     parent: None,
243                     children: vec![],
244                     decl_id: None,
245                     docs: self.docs_for_attrs(&item.attrs),
246                     sig: sig::item_signature(item, self),
247                     attributes: lower_attributes(item.attrs.clone(), self),
248                 }))
249             }
250             ast::ItemKind::Mod(ref m) => {
251                 let qualname = format!("::{}",
252                     self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id)));
253
254                 let cm = self.tcx.sess.source_map();
255                 let filename = cm.span_to_filename(m.inner);
256
257                 filter!(self.span_utils, item.ident.span);
258
259                 Some(Data::DefData(Def {
260                     kind: DefKind::Mod,
261                     id: id_from_node_id(item.id, self),
262                     name: item.ident.to_string(),
263                     qualname,
264                     span: self.span_from_span(item.ident.span),
265                     value: filename.to_string(),
266                     parent: None,
267                     children: m.items
268                         .iter()
269                         .map(|i| id_from_node_id(i.id, self))
270                         .collect(),
271                     decl_id: None,
272                     docs: self.docs_for_attrs(&item.attrs),
273                     sig: sig::item_signature(item, self),
274                     attributes: lower_attributes(item.attrs.clone(), self),
275                 }))
276             }
277             ast::ItemKind::Enum(ref def, _) => {
278                 let name = item.ident.to_string();
279                 let qualname = format!("::{}",
280                     self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id)));
281                 filter!(self.span_utils, item.ident.span);
282                 let variants_str = def.variants
283                     .iter()
284                     .map(|v| v.node.ident.to_string())
285                     .collect::<Vec<_>>()
286                     .join(", ");
287                 let value = format!("{}::{{{}}}", name, variants_str);
288                 Some(Data::DefData(Def {
289                     kind: DefKind::Enum,
290                     id: id_from_node_id(item.id, self),
291                     span: self.span_from_span(item.ident.span),
292                     name,
293                     qualname,
294                     value,
295                     parent: None,
296                     children: def.variants
297                         .iter()
298                         .map(|v| id_from_node_id(v.node.id, self))
299                         .collect(),
300                     decl_id: None,
301                     docs: self.docs_for_attrs(&item.attrs),
302                     sig: sig::item_signature(item, self),
303                     attributes: lower_attributes(item.attrs.clone(), self),
304                 }))
305             }
306             ast::ItemKind::Impl(.., ref trait_ref, ref typ, ref impls) => {
307                 if let ast::TyKind::Path(None, ref path) = typ.node {
308                     // Common case impl for a struct or something basic.
309                     if generated_code(path.span) {
310                         return None;
311                     }
312                     let sub_span = path.segments.last().unwrap().ident.span;
313                     filter!(self.span_utils, sub_span);
314
315                     let impl_id = self.next_impl_id();
316                     let span = self.span_from_span(sub_span);
317
318                     let type_data = self.lookup_ref_id(typ.id);
319                     type_data.map(|type_data| {
320                         Data::RelationData(Relation {
321                             kind: RelationKind::Impl {
322                                 id: impl_id,
323                             },
324                             span: span.clone(),
325                             from: id_from_def_id(type_data),
326                             to: trait_ref
327                                 .as_ref()
328                                 .and_then(|t| self.lookup_ref_id(t.ref_id))
329                                 .map(id_from_def_id)
330                                 .unwrap_or_else(|| null_id()),
331                         },
332                         Impl {
333                             id: impl_id,
334                             kind: match *trait_ref {
335                                 Some(_) => ImplKind::Direct,
336                                 None => ImplKind::Inherent,
337                             },
338                             span: span,
339                             value: String::new(),
340                             parent: None,
341                             children: impls
342                                 .iter()
343                                 .map(|i| id_from_node_id(i.id, self))
344                                 .collect(),
345                             docs: String::new(),
346                             sig: None,
347                             attributes: vec![],
348                         })
349                     })
350                 } else {
351                     None
352                 }
353             }
354             _ => {
355                 // FIXME
356                 bug!();
357             }
358         }
359     }
360
361     pub fn get_field_data(&self, field: &ast::StructField, scope: NodeId) -> Option<Def> {
362         if let Some(ident) = field.ident {
363             let name = ident.to_string();
364             let qualname = format!("::{}::{}",
365                 self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(scope)),
366                 ident);
367             filter!(self.span_utils, ident.span);
368             let def_id = self.tcx.hir().local_def_id_from_node_id(field.id);
369             let typ = self.tcx.type_of(def_id).to_string();
370
371
372             let id = id_from_node_id(field.id, self);
373             let span = self.span_from_span(ident.span);
374
375             Some(Def {
376                 kind: DefKind::Field,
377                 id,
378                 span,
379                 name,
380                 qualname,
381                 value: typ,
382                 parent: Some(id_from_node_id(scope, self)),
383                 children: vec![],
384                 decl_id: None,
385                 docs: self.docs_for_attrs(&field.attrs),
386                 sig: sig::field_signature(field, self),
387                 attributes: lower_attributes(field.attrs.clone(), self),
388             })
389         } else {
390             None
391         }
392     }
393
394     // FIXME would be nice to take a MethodItem here, but the ast provides both
395     // trait and impl flavours, so the caller must do the disassembly.
396     pub fn get_method_data(&self, id: ast::NodeId, ident: ast::Ident, span: Span) -> Option<Def> {
397         // The qualname for a method is the trait name or name of the struct in an impl in
398         // which the method is declared in, followed by the method's name.
399         let (qualname, parent_scope, decl_id, docs, attributes) =
400             match self.tcx.impl_of_method(self.tcx.hir().local_def_id_from_node_id(id)) {
401                 Some(impl_id) => match self.tcx.hir().get_if_local(impl_id) {
402                     Some(Node::Item(item)) => match item.node {
403                         hir::ItemKind::Impl(.., ref ty, _) => {
404                             let mut qualname = String::from("<");
405                             qualname.push_str(&self.tcx.hir().hir_to_pretty_string(ty.hir_id));
406
407                             let trait_id = self.tcx.trait_id_of_impl(impl_id);
408                             let mut decl_id = None;
409                             let mut docs = String::new();
410                             let mut attrs = vec![];
411                             let hir_id = self.tcx.hir().node_to_hir_id(id);
412                             if let Some(Node::ImplItem(item)) =
413                                 self.tcx.hir().find(hir_id)
414                             {
415                                 docs = self.docs_for_attrs(&item.attrs);
416                                 attrs = item.attrs.to_vec();
417                             }
418
419                             if let Some(def_id) = trait_id {
420                                 // A method in a trait impl.
421                                 qualname.push_str(" as ");
422                                 qualname.push_str(&self.tcx.def_path_str(def_id));
423                                 self.tcx
424                                     .associated_items(def_id)
425                                     .find(|item| item.ident.name == ident.name)
426                                     .map(|item| decl_id = Some(item.def_id));
427                             }
428                             qualname.push_str(">");
429
430                             (qualname, trait_id, decl_id, docs, attrs)
431                         }
432                         _ => {
433                             span_bug!(
434                                 span,
435                                 "Container {:?} for method {} not an impl?",
436                                 impl_id,
437                                 id
438                             );
439                         }
440                     },
441                     r => {
442                         span_bug!(
443                             span,
444                             "Container {:?} for method {} is not a node item {:?}",
445                             impl_id,
446                             id,
447                             r
448                         );
449                     }
450                 },
451                 None => match self.tcx.trait_of_item(self.tcx.hir().local_def_id_from_node_id(id)) {
452                     Some(def_id) => {
453                         let mut docs = String::new();
454                         let mut attrs = vec![];
455                         let hir_id = self.tcx.hir().node_to_hir_id(id);
456
457                         if let Some(Node::TraitItem(item)) = self.tcx.hir().find(hir_id) {
458                             docs = self.docs_for_attrs(&item.attrs);
459                             attrs = item.attrs.to_vec();
460                         }
461
462                         (
463                             format!("::{}", self.tcx.def_path_str(def_id)),
464                             Some(def_id),
465                             None,
466                             docs,
467                             attrs,
468                         )
469                     }
470                     None => {
471                         debug!("could not find container for method {} at {:?}", id, span);
472                         // This is not necessarily a bug, if there was a compilation error,
473                         // the tables we need might not exist.
474                         return None;
475                     }
476                 },
477             };
478
479         let qualname = format!("{}::{}", qualname, ident.name);
480
481         filter!(self.span_utils, ident.span);
482
483         Some(Def {
484             kind: DefKind::Method,
485             id: id_from_node_id(id, self),
486             span: self.span_from_span(ident.span),
487             name: ident.name.to_string(),
488             qualname,
489             // FIXME you get better data here by using the visitor.
490             value: String::new(),
491             parent: parent_scope.map(|id| id_from_def_id(id)),
492             children: vec![],
493             decl_id: decl_id.map(|id| id_from_def_id(id)),
494             docs,
495             sig: None,
496             attributes: lower_attributes(attributes, self),
497         })
498     }
499
500     pub fn get_trait_ref_data(&self, trait_ref: &ast::TraitRef) -> Option<Ref> {
501         self.lookup_ref_id(trait_ref.ref_id).and_then(|def_id| {
502             let span = trait_ref.path.span;
503             if generated_code(span) {
504                 return None;
505             }
506             let sub_span = trait_ref.path.segments.last().unwrap().ident.span;
507             filter!(self.span_utils, sub_span);
508             let span = self.span_from_span(sub_span);
509             Some(Ref {
510                 kind: RefKind::Type,
511                 span,
512                 ref_id: id_from_def_id(def_id),
513             })
514         })
515     }
516
517     pub fn get_expr_data(&self, expr: &ast::Expr) -> Option<Data> {
518         let expr_hir_id = self.tcx.hir().node_to_hir_id(expr.id);
519         let hir_node = self.tcx.hir().expect_expr(expr_hir_id);
520         let ty = self.tables.expr_ty_adjusted_opt(&hir_node);
521         if ty.is_none() || ty.unwrap().sty == ty::Error {
522             return None;
523         }
524         match expr.node {
525             ast::ExprKind::Field(ref sub_ex, ident) => {
526                 let sub_ex_hir_id = self.tcx.hir().node_to_hir_id(sub_ex.id);
527                 let hir_node = match self.tcx.hir().find(sub_ex_hir_id) {
528                     Some(Node::Expr(expr)) => expr,
529                     _ => {
530                         debug!(
531                             "Missing or weird node for sub-expression {} in {:?}",
532                             sub_ex.id,
533                             expr
534                         );
535                         return None;
536                     }
537                 };
538                 match self.tables.expr_ty_adjusted(&hir_node).sty {
539                     ty::Adt(def, _) if !def.is_enum() => {
540                         let variant = &def.non_enum_variant();
541                         let index = self.tcx.find_field_index(ident, variant).unwrap();
542                         filter!(self.span_utils, ident.span);
543                         let span = self.span_from_span(ident.span);
544                         return Some(Data::RefData(Ref {
545                             kind: RefKind::Variable,
546                             span,
547                             ref_id: id_from_def_id(variant.fields[index].did),
548                         }));
549                     }
550                     ty::Tuple(..) => None,
551                     _ => {
552                         debug!("expected struct or union type, found {:?}", ty);
553                         None
554                     }
555                 }
556             }
557             ast::ExprKind::Struct(ref path, ..) => {
558                 match self.tables.expr_ty_adjusted(&hir_node).sty {
559                     ty::Adt(def, _) if !def.is_enum() => {
560                         let sub_span = path.segments.last().unwrap().ident.span;
561                         filter!(self.span_utils, sub_span);
562                         let span = self.span_from_span(sub_span);
563                         Some(Data::RefData(Ref {
564                             kind: RefKind::Type,
565                             span,
566                             ref_id: id_from_def_id(def.did),
567                         }))
568                     }
569                     _ => {
570                         // FIXME ty could legitimately be an enum, but then we will fail
571                         // later if we try to look up the fields.
572                         debug!("expected struct or union, found {:?}", ty);
573                         None
574                     }
575                 }
576             }
577             ast::ExprKind::MethodCall(ref seg, ..) => {
578                 let expr_hir_id = self.tcx.hir().definitions().node_to_hir_id(expr.id);
579                 let method_id = match self.tables.type_dependent_def_id(expr_hir_id) {
580                     Some(id) => id,
581                     None => {
582                         debug!("could not resolve method id for {:?}", expr);
583                         return None;
584                     }
585                 };
586                 let (def_id, decl_id) = match self.tcx.associated_item(method_id).container {
587                     ty::ImplContainer(_) => (Some(method_id), None),
588                     ty::TraitContainer(_) => (None, Some(method_id)),
589                 };
590                 let sub_span = seg.ident.span;
591                 filter!(self.span_utils, sub_span);
592                 let span = self.span_from_span(sub_span);
593                 Some(Data::RefData(Ref {
594                     kind: RefKind::Function,
595                     span,
596                     ref_id: def_id
597                         .or(decl_id)
598                         .map(|id| id_from_def_id(id))
599                         .unwrap_or_else(|| null_id()),
600                 }))
601             }
602             ast::ExprKind::Path(_, ref path) => {
603                 self.get_path_data(expr.id, path).map(|d| Data::RefData(d))
604             }
605             _ => {
606                 // FIXME
607                 bug!();
608             }
609         }
610     }
611
612     pub fn get_path_res(&self, id: NodeId) -> Res {
613         let hir_id = self.tcx.hir().node_to_hir_id(id);
614         match self.tcx.hir().get(hir_id) {
615             Node::TraitRef(tr) => tr.path.res,
616
617             Node::Item(&hir::Item {
618                 node: hir::ItemKind::Use(ref path, _),
619                 ..
620             }) |
621             Node::Visibility(&Spanned {
622                 node: hir::VisibilityKind::Restricted { ref path, .. }, .. }) => path.res,
623
624             Node::PathSegment(seg) => {
625                 match seg.res {
626                     Some(res) if res != Res::Err => res,
627                     _ => {
628                         let parent_node = self.tcx.hir().get_parent_node(hir_id);
629                         self.get_path_res(self.tcx.hir().hir_to_node_id(parent_node))
630                     },
631                 }
632             }
633
634             Node::Expr(&hir::Expr {
635                 node: hir::ExprKind::Struct(ref qpath, ..),
636                 ..
637             }) => {
638                 self.tables.qpath_res(qpath, hir_id)
639             }
640
641             Node::Expr(&hir::Expr {
642                 node: hir::ExprKind::Path(ref qpath),
643                 ..
644             }) |
645             Node::Pat(&hir::Pat {
646                 node: hir::PatKind::Path(ref qpath),
647                 ..
648             }) |
649             Node::Pat(&hir::Pat {
650                 node: hir::PatKind::Struct(ref qpath, ..),
651                 ..
652             }) |
653             Node::Pat(&hir::Pat {
654                 node: hir::PatKind::TupleStruct(ref qpath, ..),
655                 ..
656             }) |
657             Node::Ty(&hir::Ty {
658                 node: hir::TyKind::Path(ref qpath),
659                 ..
660             }) => {
661                 self.tables.qpath_res(qpath, hir_id)
662             }
663
664             Node::Binding(&hir::Pat {
665                 node: hir::PatKind::Binding(_, canonical_id, ..),
666                 ..
667             }) => Res::Local(canonical_id),
668
669             _ => Res::Err,
670         }
671     }
672
673     pub fn get_path_data(&self, id: NodeId, path: &ast::Path) -> Option<Ref> {
674         path.segments
675             .last()
676             .and_then(|seg| {
677                 self.get_path_segment_data(seg)
678                     .or_else(|| self.get_path_segment_data_with_id(seg, id))
679             })
680     }
681
682     pub fn get_path_segment_data(&self, path_seg: &ast::PathSegment) -> Option<Ref> {
683         self.get_path_segment_data_with_id(path_seg, path_seg.id)
684     }
685
686     fn get_path_segment_data_with_id(
687         &self,
688         path_seg: &ast::PathSegment,
689         id: NodeId,
690     ) -> Option<Ref> {
691         // Returns true if the path is function type sugar, e.g., `Fn(A) -> B`.
692         fn fn_type(seg: &ast::PathSegment) -> bool {
693             if let Some(ref generic_args) = seg.args {
694                 if let ast::GenericArgs::Parenthesized(_) = **generic_args {
695                     return true;
696                 }
697             }
698             false
699         }
700
701         if id == DUMMY_NODE_ID {
702             return None;
703         }
704
705         let res = self.get_path_res(id);
706         let span = path_seg.ident.span;
707         filter!(self.span_utils, span);
708         let span = self.span_from_span(span);
709
710         match res {
711             Res::Local(id) => {
712                 Some(Ref {
713                     kind: RefKind::Variable,
714                     span,
715                     ref_id: id_from_node_id(self.tcx.hir().hir_to_node_id(id), self),
716                 })
717             }
718             Res::Def(HirDefKind::Trait, def_id) if fn_type(path_seg) => {
719                 Some(Ref {
720                     kind: RefKind::Type,
721                     span,
722                     ref_id: id_from_def_id(def_id),
723                 })
724             }
725             Res::Def(HirDefKind::Struct, def_id) |
726             Res::Def(HirDefKind::Variant, def_id) |
727             Res::Def(HirDefKind::Union, def_id) |
728             Res::Def(HirDefKind::Enum, def_id) |
729             Res::Def(HirDefKind::TyAlias, def_id) |
730             Res::Def(HirDefKind::ForeignTy, def_id) |
731             Res::Def(HirDefKind::TraitAlias, def_id) |
732             Res::Def(HirDefKind::AssocExistential, def_id) |
733             Res::Def(HirDefKind::AssocTy, def_id) |
734             Res::Def(HirDefKind::Trait, def_id) |
735             Res::Def(HirDefKind::Existential, def_id) |
736             Res::Def(HirDefKind::TyParam, def_id) => {
737                 Some(Ref {
738                     kind: RefKind::Type,
739                     span,
740                     ref_id: id_from_def_id(def_id),
741                 })
742             }
743             Res::Def(HirDefKind::ConstParam, def_id) => {
744                 Some(Ref {
745                     kind: RefKind::Variable,
746                     span,
747                     ref_id: id_from_def_id(def_id),
748                 })
749             }
750             Res::Def(HirDefKind::Ctor(CtorOf::Struct, ..), def_id) => {
751                 // This is a reference to a tuple struct where the def_id points
752                 // to an invisible constructor function. That is not a very useful
753                 // def, so adjust to point to the tuple struct itself.
754                 let parent_def_id = self.tcx.parent(def_id).unwrap();
755                 Some(Ref {
756                     kind: RefKind::Type,
757                     span,
758                     ref_id: id_from_def_id(parent_def_id),
759                 })
760             }
761             Res::Def(HirDefKind::Static, _) |
762             Res::Def(HirDefKind::Const, _) |
763             Res::Def(HirDefKind::AssocConst, _) |
764             Res::Def(HirDefKind::Ctor(..), _) => {
765                 Some(Ref {
766                     kind: RefKind::Variable,
767                     span,
768                     ref_id: id_from_def_id(res.def_id()),
769                 })
770             }
771             Res::Def(HirDefKind::Method, decl_id) => {
772                 let def_id = if decl_id.is_local() {
773                     let ti = self.tcx.associated_item(decl_id);
774                     self.tcx
775                         .associated_items(ti.container.id())
776                         .find(|item| item.ident.name == ti.ident.name &&
777                                      item.defaultness.has_value())
778                         .map(|item| item.def_id)
779                 } else {
780                     None
781                 };
782                 Some(Ref {
783                     kind: RefKind::Function,
784                     span,
785                     ref_id: id_from_def_id(def_id.unwrap_or(decl_id)),
786                 })
787             }
788             Res::Def(HirDefKind::Fn, def_id) => {
789                 Some(Ref {
790                     kind: RefKind::Function,
791                     span,
792                     ref_id: id_from_def_id(def_id),
793                 })
794             }
795             Res::Def(HirDefKind::Mod, def_id) => {
796                 Some(Ref {
797                     kind: RefKind::Mod,
798                     span,
799                     ref_id: id_from_def_id(def_id),
800                 })
801             }
802             Res::PrimTy(..) |
803             Res::SelfTy(..) |
804             Res::Def(HirDefKind::Macro(..), _) |
805             Res::ToolMod |
806             Res::NonMacroAttr(..) |
807             Res::SelfCtor(..) |
808             Res::Err => None,
809         }
810     }
811
812     pub fn get_field_ref_data(
813         &self,
814         field_ref: &ast::Field,
815         variant: &ty::VariantDef,
816     ) -> Option<Ref> {
817         filter!(self.span_utils, field_ref.ident.span);
818         self.tcx.find_field_index(field_ref.ident, variant).map(|index| {
819             let span = self.span_from_span(field_ref.ident.span);
820             Ref {
821                 kind: RefKind::Variable,
822                 span,
823                 ref_id: id_from_def_id(variant.fields[index].did),
824             }
825         })
826     }
827
828     /// Attempt to return MacroRef for any AST node.
829     ///
830     /// For a given piece of AST defined by the supplied Span and NodeId,
831     /// returns `None` if the node is not macro-generated or the span is malformed,
832     /// else uses the expansion callsite and callee to return some MacroRef.
833     pub fn get_macro_use_data(&self, span: Span) -> Option<MacroRef> {
834         if !generated_code(span) {
835             return None;
836         }
837         // Note we take care to use the source callsite/callee, to handle
838         // nested expansions and ensure we only generate data for source-visible
839         // macro uses.
840         let callsite = span.source_callsite();
841         let callsite_span = self.span_from_span(callsite);
842         let callee = span.source_callee()?;
843
844         // Ignore attribute macros, their spans are usually mangled
845         if let ExpnKind::Macro(MacroKind::Attr, _) |
846                ExpnKind::Macro(MacroKind::Derive, _) = callee.kind {
847             return None;
848         }
849
850         // If the callee is an imported macro from an external crate, need to get
851         // the source span and name from the session, as their spans are localized
852         // when read in, and no longer correspond to the source.
853         if let Some(mac) = self.tcx
854             .sess
855             .imported_macro_spans
856             .borrow()
857             .get(&callee.def_site)
858         {
859             let &(ref mac_name, mac_span) = mac;
860             let mac_span = self.span_from_span(mac_span);
861             return Some(MacroRef {
862                 span: callsite_span,
863                 qualname: mac_name.clone(), // FIXME: generate the real qualname
864                 callee_span: mac_span,
865             });
866         }
867
868         let callee_span = self.span_from_span(callee.def_site);
869         Some(MacroRef {
870             span: callsite_span,
871             qualname: callee.kind.descr().to_string(), // FIXME: generate the real qualname
872             callee_span,
873         })
874     }
875
876     fn lookup_ref_id(&self, ref_id: NodeId) -> Option<DefId> {
877         match self.get_path_res(ref_id) {
878             Res::PrimTy(_) | Res::SelfTy(..) | Res::Err => None,
879             def => Some(def.def_id()),
880         }
881     }
882
883     fn docs_for_attrs(&self, attrs: &[Attribute]) -> String {
884         let mut result = String::new();
885
886         for attr in attrs {
887             if attr.check_name(sym::doc) {
888                 if let Some(val) = attr.value_str() {
889                     if attr.is_sugared_doc {
890                         result.push_str(&strip_doc_comment_decoration(&val.as_str()));
891                     } else {
892                         result.push_str(&val.as_str());
893                     }
894                     result.push('\n');
895                 } else if let Some(meta_list) = attr.meta_item_list() {
896                     meta_list.into_iter()
897                              .filter(|it| it.check_name(sym::include))
898                              .filter_map(|it| it.meta_item_list().map(|l| l.to_owned()))
899                              .flat_map(|it| it)
900                              .filter(|meta| meta.check_name(sym::contents))
901                              .filter_map(|meta| meta.value_str())
902                              .for_each(|val| {
903                                  result.push_str(&val.as_str());
904                                  result.push('\n');
905                              });
906                 }
907             }
908         }
909
910         if !self.config.full_docs {
911             if let Some(index) = result.find("\n\n") {
912                 result.truncate(index);
913             }
914         }
915
916         result
917     }
918
919     fn next_impl_id(&self) -> u32 {
920         let next = self.impl_counter.get();
921         self.impl_counter.set(next + 1);
922         next
923     }
924 }
925
926 fn make_signature(decl: &ast::FnDecl, generics: &ast::Generics) -> String {
927     let mut sig = "fn ".to_owned();
928     if !generics.params.is_empty() {
929         sig.push('<');
930         sig.push_str(&generics
931             .params
932             .iter()
933             .map(|param| param.ident.to_string())
934             .collect::<Vec<_>>()
935             .join(", "));
936         sig.push_str("> ");
937     }
938     sig.push('(');
939     sig.push_str(&decl.inputs
940         .iter()
941         .map(arg_to_string)
942         .collect::<Vec<_>>()
943         .join(", "));
944     sig.push(')');
945     match decl.output {
946         ast::FunctionRetTy::Default(_) => sig.push_str(" -> ()"),
947         ast::FunctionRetTy::Ty(ref t) => sig.push_str(&format!(" -> {}", ty_to_string(t))),
948     }
949
950     sig
951 }
952
953 // An AST visitor for collecting paths (e.g., the names of structs) and formal
954 // variables (idents) from patterns.
955 struct PathCollector<'l> {
956     collected_paths: Vec<(NodeId, &'l ast::Path)>,
957     collected_idents: Vec<(NodeId, ast::Ident, ast::Mutability)>,
958 }
959
960 impl<'l> PathCollector<'l> {
961     fn new() -> PathCollector<'l> {
962         PathCollector {
963             collected_paths: vec![],
964             collected_idents: vec![],
965         }
966     }
967 }
968
969 impl<'l> Visitor<'l> for PathCollector<'l> {
970     fn visit_pat(&mut self, p: &'l ast::Pat) {
971         match p.node {
972             PatKind::Struct(ref path, ..) => {
973                 self.collected_paths.push((p.id, path));
974             }
975             PatKind::TupleStruct(ref path, ..) | PatKind::Path(_, ref path) => {
976                 self.collected_paths.push((p.id, path));
977             }
978             PatKind::Ident(bm, ident, _) => {
979                 debug!(
980                     "PathCollector, visit ident in pat {}: {:?} {:?}",
981                     ident,
982                     p.span,
983                     ident.span
984                 );
985                 let immut = match bm {
986                     // Even if the ref is mut, you can't change the ref, only
987                     // the data pointed at, so showing the initialising expression
988                     // is still worthwhile.
989                     ast::BindingMode::ByRef(_) => ast::Mutability::Immutable,
990                     ast::BindingMode::ByValue(mt) => mt,
991                 };
992                 self.collected_idents
993                     .push((p.id, ident, immut));
994             }
995             _ => {}
996         }
997         visit::walk_pat(self, p);
998     }
999 }
1000
1001 /// Defines what to do with the results of saving the analysis.
1002 pub trait SaveHandler {
1003     fn save(
1004         &mut self,
1005         save_ctxt: &SaveContext<'_, '_>,
1006         analysis: &Analysis,
1007     );
1008 }
1009
1010 /// Dump the save-analysis results to a file.
1011 pub struct DumpHandler<'a> {
1012     odir: Option<&'a Path>,
1013     cratename: String,
1014 }
1015
1016 impl<'a> DumpHandler<'a> {
1017     pub fn new(odir: Option<&'a Path>, cratename: &str) -> DumpHandler<'a> {
1018         DumpHandler {
1019             odir,
1020             cratename: cratename.to_owned(),
1021         }
1022     }
1023
1024     fn output_file(&self, ctx: &SaveContext<'_, '_>) -> (BufWriter<File>, PathBuf) {
1025         let sess = &ctx.tcx.sess;
1026         let file_name = match ctx.config.output_file {
1027             Some(ref s) => PathBuf::from(s),
1028             None => {
1029                 let mut root_path = match self.odir {
1030                     Some(val) => val.join("save-analysis"),
1031                     None => PathBuf::from("save-analysis-temp"),
1032                 };
1033
1034                 if let Err(e) = std::fs::create_dir_all(&root_path) {
1035                     error!("Could not create directory {}: {}", root_path.display(), e);
1036                 }
1037
1038                 let executable = sess.crate_types
1039                     .borrow()
1040                     .iter()
1041                     .any(|ct| *ct == CrateType::Executable);
1042                 let mut out_name = if executable {
1043                     String::new()
1044                 } else {
1045                     "lib".to_owned()
1046                 };
1047                 out_name.push_str(&self.cratename);
1048                 out_name.push_str(&sess.opts.cg.extra_filename);
1049                 out_name.push_str(".json");
1050                 root_path.push(&out_name);
1051
1052                 root_path
1053             }
1054         };
1055
1056         info!("Writing output to {}", file_name.display());
1057
1058         let output_file = BufWriter::new(File::create(&file_name).unwrap_or_else(
1059             |e| sess.fatal(&format!("Could not open {}: {}", file_name.display(), e)),
1060         ));
1061
1062         (output_file, file_name)
1063     }
1064 }
1065
1066 impl SaveHandler for DumpHandler<'_> {
1067     fn save(
1068         &mut self,
1069         save_ctxt: &SaveContext<'_, '_>,
1070         analysis: &Analysis,
1071     ) {
1072         let sess = &save_ctxt.tcx.sess;
1073         let (output, file_name) = self.output_file(&save_ctxt);
1074         if let Err(e) = serde_json::to_writer(output, &analysis) {
1075             error!("Can't serialize save-analysis: {:?}", e);
1076         }
1077
1078         if sess.opts.debugging_opts.emit_artifact_notifications {
1079             sess.parse_sess.span_diagnostic
1080                 .emit_artifact_notification(&file_name, "save-analysis");
1081         }
1082     }
1083 }
1084
1085 /// Call a callback with the results of save-analysis.
1086 pub struct CallbackHandler<'b> {
1087     pub callback: &'b mut dyn FnMut(&rls_data::Analysis),
1088 }
1089
1090 impl SaveHandler for CallbackHandler<'_> {
1091     fn save(
1092         &mut self,
1093         _: &SaveContext<'_, '_>,
1094         analysis: &Analysis,
1095     ) {
1096         (self.callback)(analysis)
1097     }
1098 }
1099
1100 pub fn process_crate<'l, 'tcx, H: SaveHandler>(
1101     tcx: TyCtxt<'tcx>,
1102     krate: &ast::Crate,
1103     cratename: &str,
1104     input: &'l Input,
1105     config: Option<Config>,
1106     mut handler: H,
1107 ) {
1108     tcx.dep_graph.with_ignore(|| {
1109         info!("Dumping crate {}", cratename);
1110
1111         // Privacy checking requires and is done after type checking; use a
1112         // fallback in case the access levels couldn't have been correctly computed.
1113         let access_levels = match tcx.sess.compile_status() {
1114             Ok(..) => tcx.privacy_access_levels(LOCAL_CRATE),
1115             Err(..) => tcx.arena.alloc(AccessLevels::default()),
1116         };
1117
1118         let save_ctxt = SaveContext {
1119             tcx,
1120             tables: &ty::TypeckTables::empty(None),
1121             access_levels: &access_levels,
1122             span_utils: SpanUtils::new(&tcx.sess),
1123             config: find_config(config),
1124             impl_counter: Cell::new(0),
1125         };
1126
1127         let mut visitor = DumpVisitor::new(save_ctxt);
1128
1129         visitor.dump_crate_info(cratename, krate);
1130         visitor.dump_compilation_options(input, cratename);
1131         visit::walk_crate(&mut visitor, krate);
1132
1133         handler.save(&visitor.save_ctxt, &visitor.analysis())
1134     })
1135 }
1136
1137 fn find_config(supplied: Option<Config>) -> Config {
1138     if let Some(config) = supplied {
1139         return config;
1140     }
1141
1142     match env::var_os("RUST_SAVE_ANALYSIS_CONFIG") {
1143         None => Config::default(),
1144         Some(config) => config.to_str()
1145             .ok_or(())
1146             .map_err(|_| error!("`RUST_SAVE_ANALYSIS_CONFIG` isn't UTF-8"))
1147             .and_then(|cfg|  serde_json::from_str(cfg)
1148                 .map_err(|_| error!("Could not deserialize save-analysis config"))
1149             ).unwrap_or_default()
1150     }
1151 }
1152
1153 // Utility functions for the module.
1154
1155 // Helper function to escape quotes in a string
1156 fn escape(s: String) -> String {
1157     s.replace("\"", "\"\"")
1158 }
1159
1160 // Helper function to determine if a span came from a
1161 // macro expansion or syntax extension.
1162 fn generated_code(span: Span) -> bool {
1163     span.ctxt() != NO_EXPANSION || span.is_dummy()
1164 }
1165
1166 // DefId::index is a newtype and so the JSON serialisation is ugly. Therefore
1167 // we use our own Id which is the same, but without the newtype.
1168 fn id_from_def_id(id: DefId) -> rls_data::Id {
1169     rls_data::Id {
1170         krate: id.krate.as_u32(),
1171         index: id.index.as_u32(),
1172     }
1173 }
1174
1175 fn id_from_node_id(id: NodeId, scx: &SaveContext<'_, '_>) -> rls_data::Id {
1176     let def_id = scx.tcx.hir().opt_local_def_id_from_node_id(id);
1177     def_id.map(|id| id_from_def_id(id)).unwrap_or_else(|| {
1178         // Create a *fake* `DefId` out of a `NodeId` by subtracting the `NodeId`
1179         // out of the maximum u32 value. This will work unless you have *billions*
1180         // of definitions in a single crate (very unlikely to actually happen).
1181         rls_data::Id {
1182             krate: LOCAL_CRATE.as_u32(),
1183             index: !id.as_u32(),
1184         }
1185     })
1186 }
1187
1188 fn null_id() -> rls_data::Id {
1189     rls_data::Id {
1190         krate: u32::max_value(),
1191         index: u32::max_value(),
1192     }
1193 }
1194
1195 fn lower_attributes(attrs: Vec<Attribute>, scx: &SaveContext<'_, '_>) -> Vec<rls_data::Attribute> {
1196     attrs.into_iter()
1197     // Only retain real attributes. Doc comments are lowered separately.
1198     .filter(|attr| attr.path != sym::doc)
1199     .map(|mut attr| {
1200         // Remove the surrounding '#[..]' or '#![..]' of the pretty printed
1201         // attribute. First normalize all inner attribute (#![..]) to outer
1202         // ones (#[..]), then remove the two leading and the one trailing character.
1203         attr.style = ast::AttrStyle::Outer;
1204         let value = pprust::attribute_to_string(&attr);
1205         // This str slicing works correctly, because the leading and trailing characters
1206         // are in the ASCII range and thus exactly one byte each.
1207         let value = value[2..value.len()-1].to_string();
1208
1209         rls_data::Attribute {
1210             value,
1211             span: scx.span_from_span(attr.span),
1212         }
1213     }).collect()
1214 }