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 #[macro_use] extern crate rustc;
24 #[macro_use] extern crate log;
25 #[macro_use] extern crate syntax;
26 extern crate rustc_serialize
;
27 extern crate rustc_typeck
;
28 extern crate syntax_pos
;
30 extern crate rls_data
;
31 extern crate rls_span
;
42 use rustc
::hir
::def
::Def
as HirDef
;
43 use rustc
::hir
::map
::{Node, NodeItem}
;
44 use rustc
::hir
::def_id
::DefId
;
45 use rustc
::session
::config
::CrateType
::CrateTypeExecutable
;
46 use rustc
::session
::Session
;
47 use rustc
::ty
::{self, TyCtxt}
;
48 use rustc_typeck
::hir_ty_to_ty
;
52 use std
::path
::{Path, PathBuf}
;
54 use syntax
::ast
::{self, NodeId, PatKind, Attribute, CRATE_NODE_ID}
;
55 use syntax
::parse
::lexer
::comments
::strip_doc_comment_decoration
;
56 use syntax
::parse
::token
;
57 use syntax
::print
::pprust
;
58 use syntax
::symbol
::keywords
;
59 use syntax
::visit
::{self, Visitor}
;
60 use syntax
::print
::pprust
::{ty_to_string, arg_to_string}
;
61 use syntax
::codemap
::MacroAttribute
;
64 pub use json_api_dumper
::JsonApiDumper
;
65 pub use json_dumper
::JsonDumper
;
66 use dump_visitor
::DumpVisitor
;
67 use span_utils
::SpanUtils
;
69 use rls_data
::{Ref
, RefKind
, SpanData
, MacroRef
, Def
, DefKind
, Relation
, RelationKind
,
70 ExternalCrateData
, Import
, CratePreludeData
};
73 pub struct SaveContext
<'l
, 'tcx
: 'l
> {
74 tcx
: TyCtxt
<'l
, 'tcx
, 'tcx
>,
75 tables
: &'l ty
::TypeckTables
<'tcx
>,
76 analysis
: &'l ty
::CrateAnalysis
,
77 span_utils
: SpanUtils
<'tcx
>,
82 /// Data about a macro use.
83 MacroUseData(MacroRef
),
86 RelationData(Relation
),
90 fn crate_prelude(&mut self, _
: CratePreludeData
);
91 fn macro_use(&mut self, _
: MacroRef
) {}
92 fn import(&mut self, _
: bool
, _
: Import
);
93 fn dump_ref(&mut self, _
: Ref
) {}
94 fn dump_def(&mut self, _
: bool
, _
: Def
);
95 fn dump_relation(&mut self, data
: Relation
);
98 macro_rules
! option_try(
99 ($e
:expr
) => (match $e { Some(e) => e, None => return None }
)
102 impl<'l
, 'tcx
: 'l
> SaveContext
<'l
, 'tcx
> {
103 fn span_from_span(&self, span
: Span
) -> SpanData
{
104 use rls_span
::{Row, Column}
;
106 let cm
= self.tcx
.sess
.codemap();
107 let start
= cm
.lookup_char_pos(span
.lo
);
108 let end
= cm
.lookup_char_pos(span
.hi
);
111 file_name
: start
.file
.name
.clone().into(),
112 byte_start
: span
.lo
.0,
114 line_start
: Row
::new_one_indexed(start
.line
as u32),
115 line_end
: Row
::new_one_indexed(end
.line
as u32),
116 column_start
: Column
::new_one_indexed(start
.col
.0 as u32 + 1),
117 column_end
: Column
::new_one_indexed(end
.col
.0 as u32 + 1),
121 // List external crates used by the current crate.
122 pub fn get_external_crates(&self) -> Vec
<ExternalCrateData
> {
123 let mut result
= Vec
::new();
125 for n
in self.tcx
.sess
.cstore
.crates() {
126 let span
= match *self.tcx
.extern_crate(n
.as_def_id()) {
127 Some(ref c
) => c
.span
,
129 debug
!("Skipping crate {}, no data", n
);
133 let lo_loc
= self.span_utils
.sess
.codemap().lookup_char_pos(span
.lo
);
134 result
.push(ExternalCrateData
{
135 name
: self.tcx
.sess
.cstore
.crate_name(n
).to_string(),
137 file_name
: SpanUtils
::make_path_string(&lo_loc
.file
.name
),
144 pub fn get_extern_item_data(&self, item
: &ast
::ForeignItem
) -> Option
<Data
> {
145 let qualname
= format
!("::{}", self.tcx
.node_path_str(item
.id
));
147 ast
::ForeignItemKind
::Fn(ref decl
, ref generics
) => {
148 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Fn
);
149 filter
!(self.span_utils
, sub_span
, item
.span
, None
);
151 Some(Data
::DefData(Def
{
152 kind
: DefKind
::Function
,
153 id
: id_from_node_id(item
.id
, self),
154 span
: self.span_from_span(sub_span
.unwrap()),
155 name
: item
.ident
.to_string(),
157 value
: make_signature(decl
, generics
),
161 docs
: docs_for_attrs(&item
.attrs
),
162 sig
: sig
::foreign_item_signature(item
, self),
163 attributes
: lower_attributes(item
.attrs
.clone(), self),
166 ast
::ForeignItemKind
::Static(ref ty
, m
) => {
167 let keyword
= if m { keywords::Mut }
else { keywords::Static }
;
168 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keyword
);
169 filter
!(self.span_utils
, sub_span
, item
.span
, None
);
171 let id
= ::id_from_node_id(item
.id
, self);
172 let span
= self.span_from_span(sub_span
.unwrap());
174 Some(Data
::DefData(Def
{
175 kind
: DefKind
::Static
,
178 name
: item
.ident
.to_string(),
180 value
: ty_to_string(ty
),
184 docs
: docs_for_attrs(&item
.attrs
),
185 sig
: sig
::foreign_item_signature(item
, self),
186 attributes
: lower_attributes(item
.attrs
.clone(), self),
192 pub fn get_item_data(&self, item
: &ast
::Item
) -> Option
<Data
> {
194 ast
::ItemKind
::Fn(ref decl
, .., ref generics
, _
) => {
195 let qualname
= format
!("::{}", self.tcx
.node_path_str(item
.id
));
196 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Fn
);
197 filter
!(self.span_utils
, sub_span
, item
.span
, None
);
198 Some(Data
::DefData(Def
{
199 kind
: DefKind
::Function
,
200 id
: id_from_node_id(item
.id
, self),
201 span
: self.span_from_span(sub_span
.unwrap()),
202 name
: item
.ident
.to_string(),
204 value
: make_signature(decl
, generics
),
208 docs
: docs_for_attrs(&item
.attrs
),
209 sig
: sig
::item_signature(item
, self),
210 attributes
: lower_attributes(item
.attrs
.clone(), self),
213 ast
::ItemKind
::Static(ref typ
, mt
, _
) => {
214 let qualname
= format
!("::{}", self.tcx
.node_path_str(item
.id
));
216 let keyword
= match mt
{
217 ast
::Mutability
::Mutable
=> keywords
::Mut
,
218 ast
::Mutability
::Immutable
=> keywords
::Static
,
221 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keyword
);
222 filter
!(self.span_utils
, sub_span
, item
.span
, None
);
224 let id
= id_from_node_id(item
.id
, self);
225 let span
= self.span_from_span(sub_span
.unwrap());
227 Some(Data
::DefData(Def
{
228 kind
: DefKind
::Static
,
231 name
: item
.ident
.to_string(),
233 value
: ty_to_string(&typ
),
237 docs
: docs_for_attrs(&item
.attrs
),
238 sig
: sig
::item_signature(item
, self),
239 attributes
: lower_attributes(item
.attrs
.clone(), self),
242 ast
::ItemKind
::Const(ref typ
, _
) => {
243 let qualname
= format
!("::{}", self.tcx
.node_path_str(item
.id
));
244 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Const
);
245 filter
!(self.span_utils
, sub_span
, item
.span
, None
);
247 let id
= id_from_node_id(item
.id
, self);
248 let span
= self.span_from_span(sub_span
.unwrap());
250 Some(Data
::DefData(Def
{
251 kind
: DefKind
::Const
,
254 name
: item
.ident
.to_string(),
256 value
: ty_to_string(typ
),
260 docs
: docs_for_attrs(&item
.attrs
),
261 sig
: sig
::item_signature(item
, self),
262 attributes
: lower_attributes(item
.attrs
.clone(), self),
265 ast
::ItemKind
::Mod(ref m
) => {
266 let qualname
= format
!("::{}", self.tcx
.node_path_str(item
.id
));
268 let cm
= self.tcx
.sess
.codemap();
269 let filename
= cm
.span_to_filename(m
.inner
);
271 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Mod
);
272 filter
!(self.span_utils
, sub_span
, item
.span
, None
);
274 Some(Data
::DefData(Def
{
276 id
: id_from_node_id(item
.id
, self),
277 name
: item
.ident
.to_string(),
279 span
: self.span_from_span(sub_span
.unwrap()),
282 children
: m
.items
.iter().map(|i
| id_from_node_id(i
.id
, self)).collect(),
284 docs
: docs_for_attrs(&item
.attrs
),
285 sig
: sig
::item_signature(item
, self),
286 attributes
: lower_attributes(item
.attrs
.clone(), self),
289 ast
::ItemKind
::Enum(ref def
, _
) => {
290 let name
= item
.ident
.to_string();
291 let qualname
= format
!("::{}", self.tcx
.node_path_str(item
.id
));
292 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Enum
);
293 filter
!(self.span_utils
, sub_span
, item
.span
, None
);
294 let variants_str
= def
.variants
.iter()
295 .map(|v
| v
.node
.name
.to_string())
298 let value
= format
!("{}::{{{}}}", name
, variants_str
);
299 Some(Data
::DefData(Def
{
301 id
: id_from_node_id(item
.id
, self),
302 span
: self.span_from_span(sub_span
.unwrap()),
307 children
: def
.variants
309 .map(|v
| id_from_node_id(v
.node
.data
.id(), self))
312 docs
: docs_for_attrs(&item
.attrs
),
313 sig
: sig
::item_signature(item
, self),
314 attributes
: lower_attributes(item
.attrs
.to_owned(), self),
317 ast
::ItemKind
::Impl(.., ref trait_ref
, ref typ
, _
) => {
318 if let ast
::TyKind
::Path(None
, ref path
) = typ
.node
{
319 // Common case impl for a struct or something basic.
320 if generated_code(path
.span
) {
323 let sub_span
= self.span_utils
.sub_span_for_type_name(path
.span
);
324 filter
!(self.span_utils
, sub_span
, typ
.span
, None
);
326 let type_data
= self.lookup_ref_id(typ
.id
);
327 type_data
.map(|type_data
| Data
::RelationData(Relation
{
328 kind
: RelationKind
::Impl
,
329 span
: self.span_from_span(sub_span
.unwrap()),
330 from
: id_from_def_id(type_data
),
331 to
: trait_ref
.as_ref()
332 .and_then(|t
| self.lookup_ref_id(t
.ref_id
))
334 .unwrap_or(null_id()),
347 pub fn get_field_data(&self,
348 field
: &ast
::StructField
,
351 if let Some(ident
) = field
.ident
{
352 let name
= ident
.to_string();
353 let qualname
= format
!("::{}::{}", self.tcx
.node_path_str(scope
), ident
);
354 let sub_span
= self.span_utils
.sub_span_before_token(field
.span
, token
::Colon
);
355 filter
!(self.span_utils
, sub_span
, field
.span
, None
);
356 let def_id
= self.tcx
.hir
.local_def_id(field
.id
);
357 let typ
= self.tcx
.type_of(def_id
).to_string();
360 let id
= id_from_node_id(field
.id
, self);
361 let span
= self.span_from_span(sub_span
.unwrap());
364 kind
: DefKind
::Field
,
370 parent
: Some(id_from_node_id(scope
, self)),
373 docs
: docs_for_attrs(&field
.attrs
),
374 sig
: sig
::field_signature(field
, self),
375 attributes
: lower_attributes(field
.attrs
.clone(), self),
382 // FIXME would be nice to take a MethodItem here, but the ast provides both
383 // trait and impl flavours, so the caller must do the disassembly.
384 pub fn get_method_data(&self,
389 // The qualname for a method is the trait name or name of the struct in an impl in
390 // which the method is declared in, followed by the method's name.
391 let (qualname
, parent_scope
, decl_id
, docs
, attributes
) =
392 match self.tcx
.impl_of_method(self.tcx
.hir
.local_def_id(id
)) {
393 Some(impl_id
) => match self.tcx
.hir
.get_if_local(impl_id
) {
394 Some(Node
::NodeItem(item
)) => {
396 hir
::ItemImpl(.., ref ty
, _
) => {
397 let mut result
= String
::from("<");
398 result
.push_str(&self.tcx
.hir
.node_to_pretty_string(ty
.id
));
400 let mut trait_id
= self.tcx
.trait_id_of_impl(impl_id
);
401 let mut decl_id
= None
;
402 if let Some(def_id
) = trait_id
{
403 result
.push_str(" as ");
404 result
.push_str(&self.tcx
.item_path_str(def_id
));
405 self.tcx
.associated_items(def_id
)
406 .find(|item
| item
.name
== name
)
407 .map(|item
| decl_id
= Some(item
.def_id
));
409 if let Some(NodeItem(item
)) = self.tcx
.hir
.find(id
) {
410 if let hir
::ItemImpl(_
, _
, _
, _
, _
, ref ty
, _
) = item
.node
{
411 trait_id
= self.lookup_ref_id(ty
.id
);
415 result
.push_str(">");
417 (result
, trait_id
, decl_id
,
418 docs_for_attrs(&item
.attrs
),
423 "Container {:?} for method {} not an impl?",
431 "Container {:?} for method {} is not a node item {:?}",
437 None
=> match self.tcx
.trait_of_item(self.tcx
.hir
.local_def_id(id
)) {
439 match self.tcx
.hir
.get_if_local(def_id
) {
440 Some(Node
::NodeItem(item
)) => {
441 (format
!("::{}", self.tcx
.item_path_str(def_id
)),
443 docs_for_attrs(&item
.attrs
),
448 "Could not find container {:?} for \
449 method {}, got {:?}",
457 debug
!("Could not find container for method {} at {:?}", id
, span
);
458 // This is not necessarily a bug, if there was a compilation error, the tables
459 // we need might not exist.
465 let qualname
= format
!("{}::{}", qualname
, name
);
467 let sub_span
= self.span_utils
.sub_span_after_keyword(span
, keywords
::Fn
);
468 filter
!(self.span_utils
, sub_span
, span
, None
);
471 kind
: DefKind
::Method
,
472 id
: id_from_node_id(id
, self),
473 span
: self.span_from_span(sub_span
.unwrap()),
474 name
: name
.to_string(),
476 // FIXME you get better data here by using the visitor.
477 value
: String
::new(),
478 parent
: parent_scope
.map(|id
| id_from_def_id(id
)),
480 decl_id
: decl_id
.map(|id
| id_from_def_id(id
)),
483 attributes
: lower_attributes(attributes
, self),
487 pub fn get_trait_ref_data(&self,
488 trait_ref
: &ast
::TraitRef
)
490 self.lookup_ref_id(trait_ref
.ref_id
).and_then(|def_id
| {
491 let span
= trait_ref
.path
.span
;
492 if generated_code(span
) {
495 let sub_span
= self.span_utils
.sub_span_for_type_name(span
).or(Some(span
));
496 filter
!(self.span_utils
, sub_span
, span
, None
);
497 let span
= self.span_from_span(sub_span
.unwrap());
501 ref_id
: id_from_def_id(def_id
),
506 pub fn get_expr_data(&self, expr
: &ast
::Expr
) -> Option
<Data
> {
507 let hir_node
= self.tcx
.hir
.expect_expr(expr
.id
);
508 let ty
= self.tables
.expr_ty_adjusted_opt(&hir_node
);
509 if ty
.is_none() || ty
.unwrap().sty
== ty
::TyError
{
513 ast
::ExprKind
::Field(ref sub_ex
, ident
) => {
514 let hir_node
= match self.tcx
.hir
.find(sub_ex
.id
) {
515 Some(Node
::NodeExpr(expr
)) => expr
,
517 debug
!("Missing or weird node for sub-expression {} in {:?}",
522 match self.tables
.expr_ty_adjusted(&hir_node
).sty
{
523 ty
::TyAdt(def
, _
) if !def
.is_enum() => {
524 let f
= def
.struct_variant().field_named(ident
.node
.name
);
525 let sub_span
= self.span_utils
.span_for_last_ident(expr
.span
);
526 filter
!(self.span_utils
, sub_span
, expr
.span
, None
);
527 let span
= self.span_from_span(sub_span
.unwrap());
528 return Some(Data
::RefData(Ref
{
529 kind
: RefKind
::Variable
,
531 ref_id
: id_from_def_id(f
.did
),
535 debug
!("Expected struct or union type, found {:?}", ty
);
540 ast
::ExprKind
::Struct(ref path
, ..) => {
541 match self.tables
.expr_ty_adjusted(&hir_node
).sty
{
542 ty
::TyAdt(def
, _
) if !def
.is_enum() => {
543 let sub_span
= self.span_utils
.span_for_last_ident(path
.span
);
544 filter
!(self.span_utils
, sub_span
, path
.span
, None
);
545 let span
= self.span_from_span(sub_span
.unwrap());
546 Some(Data
::RefData(Ref
{
549 ref_id
: id_from_def_id(def
.did
),
553 // FIXME ty could legitimately be an enum, but then we will fail
554 // later if we try to look up the fields.
555 debug
!("expected struct or union, found {:?}", ty
);
560 ast
::ExprKind
::MethodCall(..) => {
561 let method_id
= self.tables
.type_dependent_defs
[&expr
.id
].def_id();
562 let (def_id
, decl_id
) = match self.tcx
.associated_item(method_id
).container
{
563 ty
::ImplContainer(_
) => (Some(method_id
), None
),
564 ty
::TraitContainer(_
) => (None
, Some(method_id
)),
566 let sub_span
= self.span_utils
.sub_span_for_meth_name(expr
.span
);
567 filter
!(self.span_utils
, sub_span
, expr
.span
, None
);
568 let span
= self.span_from_span(sub_span
.unwrap());
569 Some(Data
::RefData(Ref
{
570 kind
: RefKind
::Function
,
572 ref_id
: def_id
.or(decl_id
).map(|id
| id_from_def_id(id
)).unwrap_or(null_id()),
575 ast
::ExprKind
::Path(_
, ref path
) => {
576 self.get_path_data(expr
.id
, path
).map(|d
| Data
::RefData(d
))
585 pub fn get_path_def(&self, id
: NodeId
) -> HirDef
{
586 match self.tcx
.hir
.get(id
) {
587 Node
::NodeTraitRef(tr
) => tr
.path
.def
,
589 Node
::NodeItem(&hir
::Item { node: hir::ItemUse(ref path, _), .. }
) |
590 Node
::NodeVisibility(&hir
::Visibility
::Restricted { ref path, .. }
) => path
.def
,
592 Node
::NodeExpr(&hir
::Expr { node: hir::ExprPath(ref qpath), .. }
) |
593 Node
::NodeExpr(&hir
::Expr { node: hir::ExprStruct(ref qpath, ..), .. }
) |
594 Node
::NodePat(&hir
::Pat { node: hir::PatKind::Path(ref qpath), .. }
) |
595 Node
::NodePat(&hir
::Pat { node: hir::PatKind::Struct(ref qpath, ..), .. }
) |
596 Node
::NodePat(&hir
::Pat { node: hir::PatKind::TupleStruct(ref qpath, ..), .. }
) => {
597 self.tables
.qpath_def(qpath
, id
)
600 Node
::NodeLocal(&hir
::Pat { node: hir::PatKind::Binding(_, def_id, ..), .. }
) => {
601 HirDef
::Local(def_id
)
604 Node
::NodeTy(ty
) => {
605 if let hir
::Ty { node: hir::TyPath(ref qpath), .. }
= *ty
{
607 hir
::QPath
::Resolved(_
, ref path
) => path
.def
,
608 hir
::QPath
::TypeRelative(..) => {
609 let ty
= hir_ty_to_ty(self.tcx
, ty
);
610 if let ty
::TyProjection(proj
) = ty
.sty
{
611 return HirDef
::AssociatedTy(proj
.item_def_id
);
625 pub fn get_path_data(&self, id
: NodeId
, path
: &ast
::Path
) -> Option
<Ref
> {
626 let def
= self.get_path_def(id
);
627 let sub_span
= self.span_utils
.span_for_last_ident(path
.span
);
628 filter
!(self.span_utils
, sub_span
, path
.span
, None
);
634 HirDef
::AssociatedConst(..) |
635 HirDef
::StructCtor(..) |
636 HirDef
::VariantCtor(..) => {
637 let span
= self.span_from_span(sub_span
.unwrap());
639 kind
: RefKind
::Variable
,
641 ref_id
: id_from_def_id(def
.def_id()),
644 HirDef
::Struct(def_id
) |
645 HirDef
::Variant(def_id
, ..) |
646 HirDef
::Union(def_id
) |
647 HirDef
::Enum(def_id
) |
648 HirDef
::TyAlias(def_id
) |
649 HirDef
::AssociatedTy(def_id
) |
650 HirDef
::Trait(def_id
) |
651 HirDef
::TyParam(def_id
) => {
652 let span
= self.span_from_span(sub_span
.unwrap());
656 ref_id
: id_from_def_id(def_id
),
659 HirDef
::Method(decl_id
) => {
660 let sub_span
= self.span_utils
.sub_span_for_meth_name(path
.span
);
661 filter
!(self.span_utils
, sub_span
, path
.span
, None
);
662 let def_id
= if decl_id
.is_local() {
663 let ti
= self.tcx
.associated_item(decl_id
);
664 self.tcx
.associated_items(ti
.container
.id())
665 .find(|item
| item
.name
== ti
.name
&& item
.defaultness
.has_value())
666 .map(|item
| item
.def_id
)
670 let span
= self.span_from_span(sub_span
.unwrap());
672 kind
: RefKind
::Function
,
674 ref_id
: id_from_def_id(def_id
.unwrap_or(decl_id
)),
677 HirDef
::Fn(def_id
) => {
678 let span
= self.span_from_span(sub_span
.unwrap());
680 kind
: RefKind
::Function
,
682 ref_id
: id_from_def_id(def_id
),
685 HirDef
::Mod(def_id
) => {
686 let span
= self.span_from_span(sub_span
.unwrap());
690 ref_id
: id_from_def_id(def_id
),
697 HirDef
::GlobalAsm(..) |
702 pub fn get_field_ref_data(&self,
703 field_ref
: &ast
::Field
,
704 variant
: &ty
::VariantDef
)
706 let f
= variant
.field_named(field_ref
.ident
.node
.name
);
707 // We don't really need a sub-span here, but no harm done
708 let sub_span
= self.span_utils
.span_for_last_ident(field_ref
.ident
.span
);
709 filter
!(self.span_utils
, sub_span
, field_ref
.ident
.span
, None
);
710 let span
= self.span_from_span(sub_span
.unwrap());
712 kind
: RefKind
::Variable
,
714 ref_id
: id_from_def_id(f
.did
),
718 /// Attempt to return MacroRef for any AST node.
720 /// For a given piece of AST defined by the supplied Span and NodeId,
721 /// returns None if the node is not macro-generated or the span is malformed,
722 /// else uses the expansion callsite and callee to return some MacroRef.
723 pub fn get_macro_use_data(&self, span
: Span
) -> Option
<MacroRef
> {
724 if !generated_code(span
) {
727 // Note we take care to use the source callsite/callee, to handle
728 // nested expansions and ensure we only generate data for source-visible
730 let callsite
= span
.source_callsite();
731 let callsite_span
= self.span_from_span(callsite
);
732 let callee
= option_try
!(span
.source_callee());
733 let callee_span
= option_try
!(callee
.span
);
735 // Ignore attribute macros, their spans are usually mangled
736 if let MacroAttribute(_
) = callee
.format
{
740 // If the callee is an imported macro from an external crate, need to get
741 // the source span and name from the session, as their spans are localized
742 // when read in, and no longer correspond to the source.
743 if let Some(mac
) = self.tcx
.sess
.imported_macro_spans
.borrow().get(&callee_span
) {
744 let &(ref mac_name
, mac_span
) = mac
;
745 let mac_span
= self.span_from_span(mac_span
);
746 return Some(MacroRef
{
748 qualname
: mac_name
.clone(), // FIXME: generate the real qualname
749 callee_span
: mac_span
,
753 let callee_span
= self.span_from_span(callee_span
);
756 qualname
: callee
.name().to_string(), // FIXME: generate the real qualname
761 fn lookup_ref_id(&self, ref_id
: NodeId
) -> Option
<DefId
> {
762 match self.get_path_def(ref_id
) {
763 HirDef
::PrimTy(_
) | HirDef
::SelfTy(..) | HirDef
::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
)>,
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
));
821 PatKind
::TupleStruct(ref path
, ..) |
822 PatKind
::Path(_
, ref path
) => {
823 self.collected_paths
.push((p
.id
, path
.clone(),
824 ast
::Mutability
::Mutable
));
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
));
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)]
874 fn extension(&self) -> &'
static str {
879 /// Defines what to do with the results of saving the analysis.
880 pub trait SaveHandler
{
881 fn save
<'l
, 'tcx
>(&mut self,
882 save_ctxt
: SaveContext
<'l
, 'tcx
>,
887 /// Dump the save-analysis results to a file.
888 pub struct DumpHandler
<'a
> {
890 odir
: Option
<&'a Path
>,
894 impl<'a
> DumpHandler
<'a
> {
895 pub fn new(format
: Format
, odir
: Option
<&'a Path
>, cratename
: &str) -> DumpHandler
<'a
> {
899 cratename
: cratename
.to_owned()
903 fn output_file(&self, sess
: &Session
) -> File
{
904 let mut root_path
= match env
::var_os("RUST_SAVE_ANALYSIS_FOLDER") {
905 Some(val
) => PathBuf
::from(val
),
906 None
=> match self.odir
{
907 Some(val
) => val
.join("save-analysis"),
908 None
=> PathBuf
::from("save-analysis-temp"),
912 if let Err(e
) = std
::fs
::create_dir_all(&root_path
) {
913 error
!("Could not create directory {}: {}", root_path
.display(), e
);
917 let disp
= root_path
.display();
918 info
!("Writing output to {}", disp
);
921 let executable
= sess
.crate_types
.borrow().iter().any(|ct
| *ct
== CrateTypeExecutable
);
922 let mut out_name
= if executable
{
927 out_name
.push_str(&self.cratename
);
928 out_name
.push_str(&sess
.opts
.cg
.extra_filename
);
929 out_name
.push_str(self.format
.extension());
930 root_path
.push(&out_name
);
931 let output_file
= File
::create(&root_path
).unwrap_or_else(|e
| {
932 let disp
= root_path
.display();
933 sess
.fatal(&format
!("Could not open {}: {}", disp
, e
));
940 impl<'a
> SaveHandler
for DumpHandler
<'a
> {
941 fn save
<'l
, 'tcx
>(&mut self,
942 save_ctxt
: SaveContext
<'l
, 'tcx
>,
946 ($new_dumper
: expr
) => {{
947 let mut dumper
= $new_dumper
;
948 let mut visitor
= DumpVisitor
::new(save_ctxt
, &mut dumper
);
950 visitor
.dump_crate_info(cratename
, krate
);
951 visit
::walk_crate(&mut visitor
, krate
);
955 let output
= &mut self.output_file(&save_ctxt
.tcx
.sess
);
958 Format
::Json
=> dump
!(JsonDumper
::new(output
)),
959 Format
::JsonApi
=> dump
!(JsonApiDumper
::new(output
)),
964 /// Call a callback with the results of save-analysis.
965 pub struct CallbackHandler
<'b
> {
966 pub callback
: &'b
mut FnMut(&rls_data
::Analysis
),
969 impl<'b
> SaveHandler
for CallbackHandler
<'b
> {
970 fn save
<'l
, 'tcx
>(&mut self,
971 save_ctxt
: SaveContext
<'l
, 'tcx
>,
975 ($new_dumper
: expr
) => {{
976 let mut dumper
= $new_dumper
;
977 let mut visitor
= DumpVisitor
::new(save_ctxt
, &mut dumper
);
979 visitor
.dump_crate_info(cratename
, krate
);
980 visit
::walk_crate(&mut visitor
, krate
);
984 // We're using the JsonDumper here because it has the format of the
985 // save-analysis results that we will pass to the callback. IOW, we are
986 // using the JsonDumper to collect the save-analysis results, but not
987 // actually to dump them to a file. This is all a bit convoluted and
988 // there is certainly a simpler design here trying to get out (FIXME).
989 dump
!(JsonDumper
::with_callback(self.callback
))
993 pub fn process_crate
<'l
, 'tcx
, H
: SaveHandler
>(tcx
: TyCtxt
<'l
, 'tcx
, 'tcx
>,
995 analysis
: &'l ty
::CrateAnalysis
,
998 let _ignore
= tcx
.dep_graph
.in_ignore();
1000 assert
!(analysis
.glob_map
.is_some());
1002 info
!("Dumping crate {}", cratename
);
1004 let save_ctxt
= SaveContext
{
1006 tables
: &ty
::TypeckTables
::empty(),
1008 span_utils
: SpanUtils
::new(&tcx
.sess
),
1011 handler
.save(save_ctxt
, krate
, cratename
)
1014 // Utility functions for the module.
1016 // Helper function to escape quotes in a string
1017 fn escape(s
: String
) -> String
{
1018 s
.replace("\"", "\"\"")
1021 // Helper function to determine if a span came from a
1022 // macro expansion or syntax extension.
1023 fn generated_code(span
: Span
) -> bool
{
1024 span
.ctxt
!= NO_EXPANSION
|| span
== DUMMY_SP
1027 // DefId::index is a newtype and so the JSON serialisation is ugly. Therefore
1028 // we use our own Id which is the same, but without the newtype.
1029 fn id_from_def_id(id
: DefId
) -> rls_data
::Id
{
1031 krate
: id
.krate
.as_u32(),
1032 index
: id
.index
.as_u32(),
1036 fn id_from_node_id(id
: NodeId
, scx
: &SaveContext
) -> rls_data
::Id
{
1037 let def_id
= scx
.tcx
.hir
.opt_local_def_id(id
);
1038 def_id
.map(|id
| id_from_def_id(id
)).unwrap_or_else(null_id
)
1041 fn null_id() -> rls_data
::Id
{
1043 krate
: u32::max_value(),
1044 index
: u32::max_value(),
1048 fn lower_attributes(attrs
: Vec
<Attribute
>, scx
: &SaveContext
) -> Vec
<rls_data
::Attribute
> {
1050 // Only retain real attributes. Doc comments are lowered separately.
1051 .filter(|attr
| attr
.path
!= "doc")
1053 // Remove the surrounding '#[..]' or '#![..]' of the pretty printed
1054 // attribute. First normalize all inner attribute (#![..]) to outer
1055 // ones (#[..]), then remove the two leading and the one trailing character.
1056 attr
.style
= ast
::AttrStyle
::Outer
;
1057 let value
= pprust
::attribute_to_string(&attr
);
1058 // This str slicing works correctly, because the leading and trailing characters
1059 // are in the ASCII range and thus exactly one byte each.
1060 let value
= value
[2..value
.len()-1].to_string();
1062 rls_data
::Attribute
{
1064 span
: scx
.span_from_span(attr
.span
),