1 // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 #![crate_name = "rustc_save_analysis"]
12 #![crate_type = "dylib"]
13 #![crate_type = "rlib"]
14 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
15 html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
16 html_root_url = "https://doc.rust-lang.org/nightly/")]
19 #![feature(custom_attribute)]
20 #![allow(unused_attributes)]
22 #![cfg_attr(stage0, unstable(feature = "rustc_private", issue = "27812"))]
23 #![cfg_attr(stage0, feature(rustc_private))]
24 #![cfg_attr(stage0, feature(staged_api))]
26 #[macro_use] extern crate rustc;
28 #[macro_use] extern crate log;
29 #[macro_use] extern crate syntax;
30 extern crate rustc_serialize;
31 extern crate rustc_typeck;
32 extern crate syntax_pos;
34 extern crate rls_data;
35 extern crate rls_span;
44 pub mod external_data;
50 use rustc::hir::def::Def;
51 use rustc::hir::map::Node;
52 use rustc::hir::def_id::DefId;
53 use rustc::session::config::CrateType::CrateTypeExecutable;
54 use rustc::session::Session;
55 use rustc::ty::{self, TyCtxt};
56 use rustc_typeck::hir_ty_to_ty;
60 use std::path::{Path, PathBuf};
62 use syntax::ast::{self, NodeId, PatKind, Attribute, CRATE_NODE_ID};
63 use syntax::parse::lexer::comments::strip_doc_comment_decoration;
64 use syntax::parse::token;
65 use syntax::symbol::keywords;
66 use syntax::visit::{self, Visitor};
67 use syntax::print::pprust::{ty_to_string, arg_to_string};
68 use syntax::codemap::MacroAttribute;
71 pub use self::csv_dumper::CsvDumper;
72 pub use self::json_api_dumper::JsonApiDumper;
73 pub use self::json_dumper::JsonDumper;
74 pub use self::data::*;
75 pub use self::external_data::make_def_id;
76 pub use self::dump::Dump;
77 pub use self::dump_visitor::DumpVisitor;
78 use self::span_utils::SpanUtils;
80 // FIXME this is legacy code and should be removed
84 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
93 pub struct SaveContext<'l, 'tcx: 'l> {
94 tcx: TyCtxt<'l, 'tcx, 'tcx>,
95 tables: &'l ty::TypeckTables<'tcx>,
96 analysis: &'l ty::CrateAnalysis,
97 span_utils: SpanUtils<'tcx>,
100 macro_rules! option_try(
101 ($e:expr) => (match $e { Some(e) => e, None => return None })
104 impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
105 // List external crates used by the current crate.
106 pub fn get_external_crates(&self) -> Vec<CrateData> {
107 let mut result = Vec::new();
109 for n in self.tcx.sess.cstore.crates() {
110 let span = match self.tcx.sess.cstore.extern_crate(n) {
111 Some(ref c) => c.span,
113 debug!("Skipping crate {}, no data", n);
117 result.push(CrateData {
118 name: self.tcx.sess.cstore.crate_name(n).to_string(),
127 pub fn get_extern_item_data(&self, item: &ast::ForeignItem) -> Option<Data> {
128 let qualname = format!("::{}", self.tcx.node_path_str(item.id));
130 ast::ForeignItemKind::Fn(ref decl, ref generics) => {
131 let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Fn);
132 filter!(self.span_utils, sub_span, item.span, None);
133 Some(Data::FunctionData(FunctionData {
135 name: item.ident.to_string(),
138 span: sub_span.unwrap(),
139 scope: self.enclosing_scope(item.id),
140 value: make_signature(decl, generics),
141 visibility: From::from(&item.vis),
143 docs: docs_for_attrs(&item.attrs),
144 sig: sig::foreign_item_signature(item, self),
145 attributes: item.attrs.clone(),
148 ast::ForeignItemKind::Static(ref ty, m) => {
149 let keyword = if m { keywords::Mut } else { keywords::Static };
150 let sub_span = self.span_utils.sub_span_after_keyword(item.span, keyword);
151 filter!(self.span_utils, sub_span, item.span, None);
152 Some(Data::VariableData(VariableData {
154 kind: VariableKind::Static,
155 name: item.ident.to_string(),
157 span: sub_span.unwrap(),
158 scope: self.enclosing_scope(item.id),
160 value: String::new(),
161 type_value: ty_to_string(ty),
162 visibility: From::from(&item.vis),
163 docs: docs_for_attrs(&item.attrs),
164 sig: sig::foreign_item_signature(item, self),
165 attributes: item.attrs.clone(),
171 pub fn get_item_data(&self, item: &ast::Item) -> Option<Data> {
173 ast::ItemKind::Fn(ref decl, .., ref generics, _) => {
174 let qualname = format!("::{}", self.tcx.node_path_str(item.id));
175 let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Fn);
176 filter!(self.span_utils, sub_span, item.span, None);
179 Some(Data::FunctionData(FunctionData {
181 name: item.ident.to_string(),
184 span: sub_span.unwrap(),
185 scope: self.enclosing_scope(item.id),
186 value: make_signature(decl, generics),
187 visibility: From::from(&item.vis),
189 docs: docs_for_attrs(&item.attrs),
190 sig: sig::item_signature(item, self),
191 attributes: item.attrs.clone(),
194 ast::ItemKind::Static(ref typ, mt, ref expr) => {
195 let qualname = format!("::{}", self.tcx.node_path_str(item.id));
197 // If the variable is immutable, save the initialising expression.
198 let (value, keyword) = match mt {
199 ast::Mutability::Mutable => (String::from("<mutable>"), keywords::Mut),
200 ast::Mutability::Immutable => {
201 (self.span_utils.snippet(expr.span), keywords::Static)
205 let sub_span = self.span_utils.sub_span_after_keyword(item.span, keyword);
206 filter!(self.span_utils, sub_span, item.span, None);
207 Some(Data::VariableData(VariableData {
209 kind: VariableKind::Static,
210 name: item.ident.to_string(),
212 span: sub_span.unwrap(),
213 scope: self.enclosing_scope(item.id),
216 type_value: ty_to_string(&typ),
217 visibility: From::from(&item.vis),
218 docs: docs_for_attrs(&item.attrs),
219 sig: sig::item_signature(item, self),
220 attributes: item.attrs.clone(),
223 ast::ItemKind::Const(ref typ, ref expr) => {
224 let qualname = format!("::{}", self.tcx.node_path_str(item.id));
225 let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Const);
226 filter!(self.span_utils, sub_span, item.span, None);
227 Some(Data::VariableData(VariableData {
229 kind: VariableKind::Const,
230 name: item.ident.to_string(),
232 span: sub_span.unwrap(),
233 scope: self.enclosing_scope(item.id),
235 value: self.span_utils.snippet(expr.span),
236 type_value: ty_to_string(&typ),
237 visibility: From::from(&item.vis),
238 docs: docs_for_attrs(&item.attrs),
239 sig: sig::item_signature(item, self),
240 attributes: item.attrs.clone(),
243 ast::ItemKind::Mod(ref m) => {
244 let qualname = format!("::{}", self.tcx.node_path_str(item.id));
246 let cm = self.tcx.sess.codemap();
247 let filename = cm.span_to_filename(m.inner);
249 let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Mod);
250 filter!(self.span_utils, sub_span, item.span, None);
252 Some(Data::ModData(ModData {
254 name: item.ident.to_string(),
256 span: sub_span.unwrap(),
257 scope: self.enclosing_scope(item.id),
259 items: m.items.iter().map(|i| i.id).collect(),
260 visibility: From::from(&item.vis),
261 docs: docs_for_attrs(&item.attrs),
262 sig: sig::item_signature(item, self),
263 attributes: item.attrs.clone(),
266 ast::ItemKind::Enum(ref def, _) => {
267 let name = item.ident.to_string();
268 let qualname = format!("::{}", self.tcx.node_path_str(item.id));
269 let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Enum);
270 filter!(self.span_utils, sub_span, item.span, None);
271 let variants_str = def.variants.iter()
272 .map(|v| v.node.name.to_string())
275 let val = format!("{}::{{{}}}", name, variants_str);
276 Some(Data::EnumData(EnumData {
280 span: sub_span.unwrap(),
282 scope: self.enclosing_scope(item.id),
283 variants: def.variants.iter().map(|v| v.node.data.id()).collect(),
284 visibility: From::from(&item.vis),
285 docs: docs_for_attrs(&item.attrs),
286 sig: sig::item_signature(item, self),
287 attributes: item.attrs.clone(),
290 ast::ItemKind::Impl(.., ref trait_ref, ref typ, _) => {
291 let mut type_data = None;
294 let parent = self.enclosing_scope(item.id);
297 // Common case impl for a struct or something basic.
298 ast::TyKind::Path(None, ref path) => {
299 if generated_code(path.span) {
302 sub_span = self.span_utils.sub_span_for_type_name(path.span);
303 type_data = self.lookup_ref_id(typ.id).map(|id| {
305 span: sub_span.unwrap(),
308 qualname: String::new() // FIXME: generate the real qualname
313 // Less useful case, impl for a compound type.
315 sub_span = self.span_utils.sub_span_for_type_name(span).or(Some(span));
319 let trait_data = trait_ref.as_ref()
320 .and_then(|tr| self.get_trait_ref_data(tr, parent));
322 filter!(self.span_utils, sub_span, typ.span, None);
323 Some(Data::ImplData(ImplData2 {
325 span: sub_span.unwrap(),
327 trait_ref: trait_data,
338 pub fn get_field_data(&self,
339 field: &ast::StructField,
341 -> Option<VariableData> {
342 if let Some(ident) = field.ident {
343 let name = ident.to_string();
344 let qualname = format!("::{}::{}", self.tcx.node_path_str(scope), ident);
345 let sub_span = self.span_utils.sub_span_before_token(field.span, token::Colon);
346 filter!(self.span_utils, sub_span, field.span, None);
347 let def_id = self.tcx.hir.local_def_id(field.id);
348 let typ = self.tcx.type_of(def_id).to_string();
352 kind: VariableKind::Field,
355 span: sub_span.unwrap(),
357 parent: Some(make_def_id(scope, &self.tcx.hir)),
358 value: "".to_owned(),
360 visibility: From::from(&field.vis),
361 docs: docs_for_attrs(&field.attrs),
362 sig: sig::field_signature(field, self),
363 attributes: field.attrs.clone(),
370 // FIXME would be nice to take a MethodItem here, but the ast provides both
371 // trait and impl flavours, so the caller must do the disassembly.
372 pub fn get_method_data(&self,
376 -> Option<FunctionData> {
377 // The qualname for a method is the trait name or name of the struct in an impl in
378 // which the method is declared in, followed by the method's name.
379 let (qualname, parent_scope, decl_id, vis, docs, attributes) =
380 match self.tcx.impl_of_method(self.tcx.hir.local_def_id(id)) {
381 Some(impl_id) => match self.tcx.hir.get_if_local(impl_id) {
382 Some(Node::NodeItem(item)) => {
384 hir::ItemImpl(.., ref ty, _) => {
385 let mut result = String::from("<");
386 result.push_str(&self.tcx.hir.node_to_pretty_string(ty.id));
388 let trait_id = self.tcx.trait_id_of_impl(impl_id);
389 let mut decl_id = None;
390 if let Some(def_id) = trait_id {
391 result.push_str(" as ");
392 result.push_str(&self.tcx.item_path_str(def_id));
393 self.tcx.associated_items(def_id)
394 .find(|item| item.name == name)
395 .map(|item| decl_id = Some(item.def_id));
397 result.push_str(">");
399 (result, trait_id, decl_id,
400 From::from(&item.vis),
401 docs_for_attrs(&item.attrs),
406 "Container {:?} for method {} not an impl?",
414 "Container {:?} for method {} is not a node item {:?}",
420 None => match self.tcx.trait_of_item(self.tcx.hir.local_def_id(id)) {
422 match self.tcx.hir.get_if_local(def_id) {
423 Some(Node::NodeItem(item)) => {
424 (format!("::{}", self.tcx.item_path_str(def_id)),
426 From::from(&item.vis),
427 docs_for_attrs(&item.attrs),
432 "Could not find container {:?} for \
433 method {}, got {:?}",
441 debug!("Could not find container for method {} at {:?}", id, span);
442 // This is not necessarily a bug, if there was a compilation error, the tables
443 // we need might not exist.
449 let qualname = format!("{}::{}", qualname, name);
451 let sub_span = self.span_utils.sub_span_after_keyword(span, keywords::Fn);
452 filter!(self.span_utils, sub_span, span, None);
456 name: name.to_string(),
458 declaration: decl_id,
459 span: sub_span.unwrap(),
460 scope: self.enclosing_scope(id),
461 // FIXME you get better data here by using the visitor.
462 value: String::new(),
464 parent: parent_scope,
467 attributes: attributes,
471 pub fn get_trait_ref_data(&self,
472 trait_ref: &ast::TraitRef,
474 -> Option<TypeRefData> {
475 self.lookup_ref_id(trait_ref.ref_id).and_then(|def_id| {
476 let span = trait_ref.path.span;
477 if generated_code(span) {
480 let sub_span = self.span_utils.sub_span_for_type_name(span).or(Some(span));
481 filter!(self.span_utils, sub_span, span, None);
483 span: sub_span.unwrap(),
485 ref_id: Some(def_id),
486 qualname: String::new() // FIXME: generate the real qualname
491 pub fn get_expr_data(&self, expr: &ast::Expr) -> Option<Data> {
492 let hir_node = self.tcx.hir.expect_expr(expr.id);
493 let ty = self.tables.expr_ty_adjusted_opt(&hir_node);
494 if ty.is_none() || ty.unwrap().sty == ty::TyError {
498 ast::ExprKind::Field(ref sub_ex, ident) => {
499 let hir_node = match self.tcx.hir.find(sub_ex.id) {
500 Some(Node::NodeExpr(expr)) => expr,
502 debug!("Missing or weird node for sub-expression {} in {:?}",
507 match self.tables.expr_ty_adjusted(&hir_node).sty {
508 ty::TyAdt(def, _) if !def.is_enum() => {
509 let f = def.struct_variant().field_named(ident.node.name);
510 let sub_span = self.span_utils.span_for_last_ident(expr.span);
511 filter!(self.span_utils, sub_span, expr.span, None);
512 return Some(Data::VariableRefData(VariableRefData {
513 name: ident.node.to_string(),
514 span: sub_span.unwrap(),
515 scope: self.enclosing_scope(expr.id),
520 debug!("Expected struct or union type, found {:?}", ty);
525 ast::ExprKind::Struct(ref path, ..) => {
526 match self.tables.expr_ty_adjusted(&hir_node).sty {
527 ty::TyAdt(def, _) if !def.is_enum() => {
528 let sub_span = self.span_utils.span_for_last_ident(path.span);
529 filter!(self.span_utils, sub_span, path.span, None);
530 Some(Data::TypeRefData(TypeRefData {
531 span: sub_span.unwrap(),
532 scope: self.enclosing_scope(expr.id),
533 ref_id: Some(def.did),
534 qualname: String::new() // FIXME: generate the real qualname
538 // FIXME ty could legitimately be an enum, but then we will fail
539 // later if we try to look up the fields.
540 debug!("expected struct or union, found {:?}", ty);
545 ast::ExprKind::MethodCall(..) => {
546 let method_id = self.tables.type_dependent_defs[&expr.id].def_id();
547 let (def_id, decl_id) = match self.tcx.associated_item(method_id).container {
548 ty::ImplContainer(_) => (Some(method_id), None),
549 ty::TraitContainer(_) => (None, Some(method_id)),
551 let sub_span = self.span_utils.sub_span_for_meth_name(expr.span);
552 filter!(self.span_utils, sub_span, expr.span, None);
553 let parent = self.enclosing_scope(expr.id);
554 Some(Data::MethodCallData(MethodCallData {
555 span: sub_span.unwrap(),
561 ast::ExprKind::Path(_, ref path) => {
562 self.get_path_data(expr.id, path)
571 pub fn get_path_def(&self, id: NodeId) -> Def {
572 match self.tcx.hir.get(id) {
573 Node::NodeTraitRef(tr) => tr.path.def,
575 Node::NodeItem(&hir::Item { node: hir::ItemUse(ref path, _), .. }) |
576 Node::NodeVisibility(&hir::Visibility::Restricted { ref path, .. }) => path.def,
578 Node::NodeExpr(&hir::Expr { node: hir::ExprPath(ref qpath), .. }) |
579 Node::NodeExpr(&hir::Expr { node: hir::ExprStruct(ref qpath, ..), .. }) |
580 Node::NodePat(&hir::Pat { node: hir::PatKind::Path(ref qpath), .. }) |
581 Node::NodePat(&hir::Pat { node: hir::PatKind::Struct(ref qpath, ..), .. }) |
582 Node::NodePat(&hir::Pat { node: hir::PatKind::TupleStruct(ref qpath, ..), .. }) => {
583 self.tables.qpath_def(qpath, id)
586 Node::NodeLocal(&hir::Pat { node: hir::PatKind::Binding(_, def_id, ..), .. }) => {
590 Node::NodeTy(ty) => {
591 if let hir::Ty { node: hir::TyPath(ref qpath), .. } = *ty {
593 hir::QPath::Resolved(_, ref path) => path.def,
594 hir::QPath::TypeRelative(..) => {
595 let ty = hir_ty_to_ty(self.tcx, ty);
596 if let ty::TyProjection(proj) = ty.sty {
597 for item in self.tcx.associated_items(proj.trait_ref.def_id) {
598 if item.kind == ty::AssociatedKind::Type {
599 if item.name == proj.item_name(self.tcx) {
600 return Def::AssociatedTy(item.def_id);
617 pub fn get_path_data(&self, id: NodeId, path: &ast::Path) -> Option<Data> {
618 let def = self.get_path_def(id);
619 let sub_span = self.span_utils.span_for_last_ident(path.span);
620 filter!(self.span_utils, sub_span, path.span, None);
626 Def::AssociatedConst(..) |
627 Def::StructCtor(..) |
628 Def::VariantCtor(..) => {
629 Some(Data::VariableRefData(VariableRefData {
630 name: self.span_utils.snippet(sub_span.unwrap()),
631 span: sub_span.unwrap(),
632 scope: self.enclosing_scope(id),
633 ref_id: def.def_id(),
636 Def::Struct(def_id) |
637 Def::Variant(def_id, ..) |
640 Def::TyAlias(def_id) |
641 Def::AssociatedTy(def_id) |
643 Def::TyParam(def_id) => {
644 Some(Data::TypeRefData(TypeRefData {
645 span: sub_span.unwrap(),
646 ref_id: Some(def_id),
647 scope: self.enclosing_scope(id),
648 qualname: String::new() // FIXME: generate the real qualname
651 Def::Method(decl_id) => {
652 let sub_span = self.span_utils.sub_span_for_meth_name(path.span);
653 filter!(self.span_utils, sub_span, path.span, None);
654 let def_id = if decl_id.is_local() {
655 let ti = self.tcx.associated_item(decl_id);
656 self.tcx.associated_items(ti.container.id())
657 .find(|item| item.name == ti.name && item.defaultness.has_value())
658 .map(|item| item.def_id)
662 Some(Data::MethodCallData(MethodCallData {
663 span: sub_span.unwrap(),
664 scope: self.enclosing_scope(id),
666 decl_id: Some(decl_id),
670 Some(Data::FunctionCallData(FunctionCallData {
672 span: sub_span.unwrap(),
673 scope: self.enclosing_scope(id),
676 Def::Mod(def_id) => {
677 Some(Data::ModRefData(ModRefData {
678 ref_id: Some(def_id),
679 span: sub_span.unwrap(),
680 scope: self.enclosing_scope(id),
681 qualname: String::new() // FIXME: generate the real qualname
693 pub fn get_field_ref_data(&self,
694 field_ref: &ast::Field,
695 variant: &ty::VariantDef,
697 -> Option<VariableRefData> {
698 let f = variant.field_named(field_ref.ident.node.name);
699 // We don't really need a sub-span here, but no harm done
700 let sub_span = self.span_utils.span_for_last_ident(field_ref.ident.span);
701 filter!(self.span_utils, sub_span, field_ref.ident.span, None);
702 Some(VariableRefData {
703 name: field_ref.ident.node.to_string(),
704 span: sub_span.unwrap(),
710 /// Attempt to return MacroUseData for any AST node.
712 /// For a given piece of AST defined by the supplied Span and NodeId,
713 /// returns None if the node is not macro-generated or the span is malformed,
714 /// else uses the expansion callsite and callee to return some MacroUseData.
715 pub fn get_macro_use_data(&self, span: Span, id: NodeId) -> Option<MacroUseData> {
716 if !generated_code(span) {
719 // Note we take care to use the source callsite/callee, to handle
720 // nested expansions and ensure we only generate data for source-visible
722 let callsite = span.source_callsite();
723 let callee = option_try!(span.source_callee());
724 let callee_span = option_try!(callee.span);
726 // Ignore attribute macros, their spans are usually mangled
727 if let MacroAttribute(_) = callee.format {
731 // If the callee is an imported macro from an external crate, need to get
732 // the source span and name from the session, as their spans are localized
733 // when read in, and no longer correspond to the source.
734 if let Some(mac) = self.tcx.sess.imported_macro_spans.borrow().get(&callee_span) {
735 let &(ref mac_name, mac_span) = mac;
736 return Some(MacroUseData {
738 name: mac_name.clone(),
739 callee_span: mac_span,
740 scope: self.enclosing_scope(id),
742 qualname: String::new()// FIXME: generate the real qualname
748 name: callee.name().to_string(),
749 callee_span: callee_span,
750 scope: self.enclosing_scope(id),
752 qualname: String::new() // FIXME: generate the real qualname
756 pub fn get_data_for_id(&self, _id: &NodeId) -> Data {
761 fn lookup_ref_id(&self, ref_id: NodeId) -> Option<DefId> {
762 match self.get_path_def(ref_id) {
763 Def::PrimTy(_) | Def::SelfTy(..) | Def::Err => None,
764 def => Some(def.def_id()),
769 pub fn enclosing_scope(&self, id: NodeId) -> NodeId {
770 self.tcx.hir.get_enclosing_scope(id).unwrap_or(CRATE_NODE_ID)
774 fn make_signature(decl: &ast::FnDecl, generics: &ast::Generics) -> String {
775 let mut sig = "fn ".to_owned();
776 if !generics.lifetimes.is_empty() || !generics.ty_params.is_empty() {
778 sig.push_str(&generics.lifetimes.iter()
779 .map(|l| l.lifetime.ident.name.to_string())
782 if !generics.lifetimes.is_empty() {
785 sig.push_str(&generics.ty_params.iter()
786 .map(|l| l.ident.to_string())
792 sig.push_str(&decl.inputs.iter().map(arg_to_string).collect::<Vec<_>>().join(", "));
795 ast::FunctionRetTy::Default(_) => sig.push_str(" -> ()"),
796 ast::FunctionRetTy::Ty(ref t) => sig.push_str(&format!(" -> {}", ty_to_string(t))),
802 // An AST visitor for collecting paths from patterns.
803 struct PathCollector {
804 // The Row field identifies the kind of pattern.
805 collected_paths: Vec<(NodeId, ast::Path, ast::Mutability, recorder::Row)>,
809 fn new() -> PathCollector {
810 PathCollector { collected_paths: vec![] }
814 impl<'a> Visitor<'a> for PathCollector {
815 fn visit_pat(&mut self, p: &ast::Pat) {
817 PatKind::Struct(ref path, ..) => {
818 self.collected_paths.push((p.id, path.clone(),
819 ast::Mutability::Mutable, recorder::TypeRef));
821 PatKind::TupleStruct(ref path, ..) |
822 PatKind::Path(_, ref path) => {
823 self.collected_paths.push((p.id, path.clone(),
824 ast::Mutability::Mutable, recorder::VarRef));
826 PatKind::Ident(bm, ref path1, _) => {
827 debug!("PathCollector, visit ident in pat {}: {:?} {:?}",
831 let immut = match bm {
832 // Even if the ref is mut, you can't change the ref, only
833 // the data pointed at, so showing the initialising expression
834 // is still worthwhile.
835 ast::BindingMode::ByRef(_) => ast::Mutability::Immutable,
836 ast::BindingMode::ByValue(mt) => mt,
838 // collect path for either visit_local or visit_arm
839 let path = ast::Path::from_ident(path1.span, path1.node);
840 self.collected_paths.push((p.id, path, immut, recorder::VarRef));
844 visit::walk_pat(self, p);
848 fn docs_for_attrs(attrs: &[Attribute]) -> String {
849 let mut result = String::new();
852 if attr.check_name("doc") {
853 if let Some(val) = attr.value_str() {
854 if attr.is_sugared_doc {
855 result.push_str(&strip_doc_comment_decoration(&val.as_str()));
857 result.push_str(&val.as_str());
867 #[derive(Clone, Copy, Debug, RustcEncodable)]
875 fn extension(&self) -> &'static str {
877 Format::Csv => ".csv",
878 Format::Json | Format::JsonApi => ".json",
883 /// Defines what to do with the results of saving the analysis.
884 pub trait SaveHandler {
885 fn save<'l, 'tcx>(&mut self,
886 save_ctxt: SaveContext<'l, 'tcx>,
891 /// Dump the save-analysis results to a file.
892 pub struct DumpHandler<'a> {
894 odir: Option<&'a Path>,
898 impl<'a> DumpHandler<'a> {
899 pub fn new(format: Format, odir: Option<&'a Path>, cratename: &str) -> DumpHandler<'a> {
903 cratename: cratename.to_owned()
907 fn output_file(&self, sess: &Session) -> File {
908 let mut root_path = match env::var_os("RUST_SAVE_ANALYSIS_FOLDER") {
909 Some(val) => PathBuf::from(val),
910 None => match self.odir {
911 Some(val) => val.join("save-analysis"),
912 None => PathBuf::from("save-analysis-temp"),
916 if let Err(e) = std::fs::create_dir_all(&root_path) {
917 error!("Could not create directory {}: {}", root_path.display(), e);
921 let disp = root_path.display();
922 info!("Writing output to {}", disp);
925 let executable = sess.crate_types.borrow().iter().any(|ct| *ct == CrateTypeExecutable);
926 let mut out_name = if executable {
931 out_name.push_str(&self.cratename);
932 out_name.push_str(&sess.opts.cg.extra_filename);
933 out_name.push_str(self.format.extension());
934 root_path.push(&out_name);
935 let output_file = File::create(&root_path).unwrap_or_else(|e| {
936 let disp = root_path.display();
937 sess.fatal(&format!("Could not open {}: {}", disp, e));
944 impl<'a> SaveHandler for DumpHandler<'a> {
945 fn save<'l, 'tcx>(&mut self,
946 save_ctxt: SaveContext<'l, 'tcx>,
950 ($new_dumper: expr) => {{
951 let mut dumper = $new_dumper;
952 let mut visitor = DumpVisitor::new(save_ctxt, &mut dumper);
954 visitor.dump_crate_info(cratename, krate);
955 visit::walk_crate(&mut visitor, krate);
959 let output = &mut self.output_file(&save_ctxt.tcx.sess);
962 Format::Csv => dump!(CsvDumper::new(output)),
963 Format::Json => dump!(JsonDumper::new(output)),
964 Format::JsonApi => dump!(JsonApiDumper::new(output)),
969 /// Call a callback with the results of save-analysis.
970 pub struct CallbackHandler<'b> {
971 pub callback: &'b mut FnMut(&rls_data::Analysis),
974 impl<'b> SaveHandler for CallbackHandler<'b> {
975 fn save<'l, 'tcx>(&mut self,
976 save_ctxt: SaveContext<'l, 'tcx>,
980 ($new_dumper: expr) => {{
981 let mut dumper = $new_dumper;
982 let mut visitor = DumpVisitor::new(save_ctxt, &mut dumper);
984 visitor.dump_crate_info(cratename, krate);
985 visit::walk_crate(&mut visitor, krate);
989 // We're using the JsonDumper here because it has the format of the
990 // save-analysis results that we will pass to the callback. IOW, we are
991 // using the JsonDumper to collect the save-analysis results, but not
992 // actually to dump them to a file. This is all a bit convoluted and
993 // there is certainly a simpler design here trying to get out (FIXME).
994 dump!(JsonDumper::with_callback(self.callback))
998 pub fn process_crate<'l, 'tcx, H: SaveHandler>(tcx: TyCtxt<'l, 'tcx, 'tcx>,
1000 analysis: &'l ty::CrateAnalysis,
1003 let _ignore = tcx.dep_graph.in_ignore();
1005 assert!(analysis.glob_map.is_some());
1007 info!("Dumping crate {}", cratename);
1009 let save_ctxt = SaveContext {
1011 tables: &ty::TypeckTables::empty(),
1013 span_utils: SpanUtils::new(&tcx.sess),
1016 handler.save(save_ctxt, krate, cratename)
1019 // Utility functions for the module.
1021 // Helper function to escape quotes in a string
1022 fn escape(s: String) -> String {
1023 s.replace("\"", "\"\"")
1026 // Helper function to determine if a span came from a
1027 // macro expansion or syntax extension.
1028 fn generated_code(span: Span) -> bool {
1029 span.ctxt != NO_EXPANSION || span == DUMMY_SP
1032 // DefId::index is a newtype and so the JSON serialisation is ugly. Therefore
1033 // we use our own Id which is the same, but without the newtype.
1034 fn id_from_def_id(id: DefId) -> rls_data::Id {
1036 krate: id.krate.as_u32(),
1037 index: id.index.as_u32(),
1041 fn id_from_node_id(id: NodeId, scx: &SaveContext) -> rls_data::Id {
1042 let def_id = scx.tcx.hir.local_def_id(id);
1043 id_from_def_id(def_id)