]>
Commit | Line | Data |
---|---|---|
d9579d0f | 1 | // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT |
1a4d82fc JJ |
2 | // file at the top-level directory of this distribution and at |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
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. | |
10 | ||
54a0048b | 11 | #![crate_name = "rustc_save_analysis"] |
54a0048b SL |
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/")] | |
32a655c1 | 17 | #![deny(warnings)] |
54a0048b SL |
18 | |
19 | #![feature(custom_attribute)] | |
20 | #![allow(unused_attributes)] | |
7cac9316 | 21 | |
54a0048b SL |
22 | #[macro_use] extern crate rustc; |
23 | ||
24 | #[macro_use] extern crate log; | |
25 | #[macro_use] extern crate syntax; | |
cc61c64b | 26 | extern crate rustc_serialize; |
7cac9316 | 27 | extern crate rustc_typeck; |
3157f602 | 28 | extern crate syntax_pos; |
54a0048b | 29 | |
cc61c64b XL |
30 | extern crate rls_data; |
31 | extern crate rls_span; | |
32 | ||
9e0c209e | 33 | |
9e0c209e | 34 | mod json_api_dumper; |
a7813a04 | 35 | mod json_dumper; |
a7813a04 | 36 | mod dump_visitor; |
a7813a04 | 37 | #[macro_use] |
041b39d2 XL |
38 | mod span_utils; |
39 | mod sig; | |
a7813a04 XL |
40 | |
41 | use rustc::hir; | |
041b39d2 XL |
42 | use rustc::hir::def::Def as HirDef; |
43 | use rustc::hir::map::{Node, NodeItem}; | |
54a0048b SL |
44 | use rustc::hir::def_id::DefId; |
45 | use rustc::session::config::CrateType::CrateTypeExecutable; | |
cc61c64b | 46 | use rustc::session::Session; |
54a0048b | 47 | use rustc::ty::{self, TyCtxt}; |
7cac9316 | 48 | use rustc_typeck::hir_ty_to_ty; |
1a4d82fc | 49 | |
85aaf69f | 50 | use std::env; |
476ff2be | 51 | use std::fs::File; |
c34b1796 | 52 | use std::path::{Path, PathBuf}; |
1a4d82fc | 53 | |
9e0c209e SL |
54 | use syntax::ast::{self, NodeId, PatKind, Attribute, CRATE_NODE_ID}; |
55 | use syntax::parse::lexer::comments::strip_doc_comment_decoration; | |
476ff2be | 56 | use syntax::parse::token; |
041b39d2 | 57 | use syntax::print::pprust; |
cc61c64b | 58 | use syntax::symbol::keywords; |
1a4d82fc | 59 | use syntax::visit::{self, Visitor}; |
a7813a04 | 60 | use syntax::print::pprust::{ty_to_string, arg_to_string}; |
3157f602 XL |
61 | use syntax::codemap::MacroAttribute; |
62 | use syntax_pos::*; | |
62682a34 | 63 | |
041b39d2 XL |
64 | pub use json_api_dumper::JsonApiDumper; |
65 | pub use json_dumper::JsonDumper; | |
66 | use dump_visitor::DumpVisitor; | |
67 | use span_utils::SpanUtils; | |
68 | ||
69 | use rls_data::{Ref, RefKind, SpanData, MacroRef, Def, DefKind, Relation, RelationKind, | |
70 | ExternalCrateData, Import, CratePreludeData}; | |
71 | ||
c1a9b12d | 72 | |
54a0048b | 73 | pub struct SaveContext<'l, 'tcx: 'l> { |
a7813a04 | 74 | tcx: TyCtxt<'l, 'tcx, 'tcx>, |
32a655c1 | 75 | tables: &'l ty::TypeckTables<'tcx>, |
8bb4bdeb | 76 | analysis: &'l ty::CrateAnalysis, |
a7813a04 | 77 | span_utils: SpanUtils<'tcx>, |
7453a54e SL |
78 | } |
79 | ||
041b39d2 XL |
80 | #[derive(Debug)] |
81 | pub enum Data { | |
82 | /// Data about a macro use. | |
83 | MacroUseData(MacroRef), | |
84 | RefData(Ref), | |
85 | DefData(Def), | |
86 | RelationData(Relation), | |
87 | } | |
88 | ||
89 | pub trait Dump { | |
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); | |
96 | } | |
97 | ||
7453a54e SL |
98 | macro_rules! option_try( |
99 | ($e:expr) => (match $e { Some(e) => e, None => return None }) | |
100 | ); | |
101 | ||
d9579d0f | 102 | impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { |
041b39d2 XL |
103 | fn span_from_span(&self, span: Span) -> SpanData { |
104 | use rls_span::{Row, Column}; | |
105 | ||
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); | |
109 | ||
110 | SpanData { | |
111 | file_name: start.file.name.clone().into(), | |
112 | byte_start: span.lo.0, | |
113 | byte_end: span.hi.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), | |
118 | } | |
119 | } | |
120 | ||
d9579d0f | 121 | // List external crates used by the current crate. |
041b39d2 | 122 | pub fn get_external_crates(&self) -> Vec<ExternalCrateData> { |
d9579d0f | 123 | let mut result = Vec::new(); |
1a4d82fc | 124 | |
92a42be0 | 125 | for n in self.tcx.sess.cstore.crates() { |
041b39d2 | 126 | let span = match *self.tcx.extern_crate(n.as_def_id()) { |
a7813a04 XL |
127 | Some(ref c) => c.span, |
128 | None => { | |
129 | debug!("Skipping crate {}, no data", n); | |
130 | continue; | |
131 | } | |
132 | }; | |
041b39d2 XL |
133 | let lo_loc = self.span_utils.sess.codemap().lookup_char_pos(span.lo); |
134 | result.push(ExternalCrateData { | |
476ff2be | 135 | name: self.tcx.sess.cstore.crate_name(n).to_string(), |
041b39d2 XL |
136 | num: n.as_u32(), |
137 | file_name: SpanUtils::make_path_string(&lo_loc.file.name), | |
b039eaaf | 138 | }); |
92a42be0 | 139 | } |
1a4d82fc JJ |
140 | |
141 | result | |
142 | } | |
143 | ||
cc61c64b XL |
144 | pub fn get_extern_item_data(&self, item: &ast::ForeignItem) -> Option<Data> { |
145 | let qualname = format!("::{}", self.tcx.node_path_str(item.id)); | |
146 | match item.node { | |
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); | |
041b39d2 XL |
150 | |
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()), | |
cc61c64b | 155 | name: item.ident.to_string(), |
041b39d2 | 156 | qualname, |
cc61c64b | 157 | value: make_signature(decl, generics), |
cc61c64b | 158 | parent: None, |
041b39d2 XL |
159 | children: vec![], |
160 | decl_id: None, | |
cc61c64b | 161 | docs: docs_for_attrs(&item.attrs), |
041b39d2 XL |
162 | sig: sig::foreign_item_signature(item, self), |
163 | attributes: lower_attributes(item.attrs.clone(), self), | |
cc61c64b XL |
164 | })) |
165 | } | |
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); | |
041b39d2 XL |
170 | |
171 | let id = ::id_from_node_id(item.id, self); | |
172 | let span = self.span_from_span(sub_span.unwrap()); | |
173 | ||
174 | Some(Data::DefData(Def { | |
175 | kind: DefKind::Static, | |
176 | id, | |
177 | span, | |
cc61c64b | 178 | name: item.ident.to_string(), |
041b39d2 XL |
179 | qualname, |
180 | value: ty_to_string(ty), | |
cc61c64b | 181 | parent: None, |
041b39d2 XL |
182 | children: vec![], |
183 | decl_id: None, | |
cc61c64b | 184 | docs: docs_for_attrs(&item.attrs), |
041b39d2 XL |
185 | sig: sig::foreign_item_signature(item, self), |
186 | attributes: lower_attributes(item.attrs.clone(), self), | |
cc61c64b XL |
187 | })) |
188 | } | |
189 | } | |
190 | } | |
191 | ||
7453a54e | 192 | pub fn get_item_data(&self, item: &ast::Item) -> Option<Data> { |
d9579d0f | 193 | match item.node { |
9e0c209e | 194 | ast::ItemKind::Fn(ref decl, .., ref generics, _) => { |
3157f602 | 195 | let qualname = format!("::{}", self.tcx.node_path_str(item.id)); |
d9579d0f | 196 | let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Fn); |
7453a54e | 197 | filter!(self.span_utils, sub_span, item.span, None); |
041b39d2 XL |
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()), | |
3157f602 | 202 | name: item.ident.to_string(), |
041b39d2 | 203 | qualname, |
a7813a04 | 204 | value: make_signature(decl, generics), |
9e0c209e | 205 | parent: None, |
041b39d2 XL |
206 | children: vec![], |
207 | decl_id: None, | |
9e0c209e | 208 | docs: docs_for_attrs(&item.attrs), |
041b39d2 XL |
209 | sig: sig::item_signature(item, self), |
210 | attributes: lower_attributes(item.attrs.clone(), self), | |
7453a54e | 211 | })) |
1a4d82fc | 212 | } |
041b39d2 | 213 | ast::ItemKind::Static(ref typ, mt, _) => { |
54a0048b | 214 | let qualname = format!("::{}", self.tcx.node_path_str(item.id)); |
1a4d82fc | 215 | |
041b39d2 XL |
216 | let keyword = match mt { |
217 | ast::Mutability::Mutable => keywords::Mut, | |
218 | ast::Mutability::Immutable => keywords::Static, | |
d9579d0f | 219 | }; |
1a4d82fc | 220 | |
d9579d0f | 221 | let sub_span = self.span_utils.sub_span_after_keyword(item.span, keyword); |
7453a54e | 222 | filter!(self.span_utils, sub_span, item.span, None); |
041b39d2 XL |
223 | |
224 | let id = id_from_node_id(item.id, self); | |
225 | let span = self.span_from_span(sub_span.unwrap()); | |
226 | ||
227 | Some(Data::DefData(Def { | |
228 | kind: DefKind::Static, | |
229 | id, | |
230 | span, | |
c1a9b12d | 231 | name: item.ident.to_string(), |
041b39d2 XL |
232 | qualname, |
233 | value: ty_to_string(&typ), | |
9e0c209e | 234 | parent: None, |
041b39d2 XL |
235 | children: vec![], |
236 | decl_id: None, | |
9e0c209e | 237 | docs: docs_for_attrs(&item.attrs), |
041b39d2 XL |
238 | sig: sig::item_signature(item, self), |
239 | attributes: lower_attributes(item.attrs.clone(), self), | |
7453a54e | 240 | })) |
1a4d82fc | 241 | } |
041b39d2 | 242 | ast::ItemKind::Const(ref typ, _) => { |
54a0048b | 243 | let qualname = format!("::{}", self.tcx.node_path_str(item.id)); |
d9579d0f | 244 | let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Const); |
7453a54e | 245 | filter!(self.span_utils, sub_span, item.span, None); |
041b39d2 XL |
246 | |
247 | let id = id_from_node_id(item.id, self); | |
248 | let span = self.span_from_span(sub_span.unwrap()); | |
249 | ||
250 | Some(Data::DefData(Def { | |
251 | kind: DefKind::Const, | |
252 | id, | |
253 | span, | |
c1a9b12d | 254 | name: item.ident.to_string(), |
041b39d2 XL |
255 | qualname, |
256 | value: ty_to_string(typ), | |
9e0c209e | 257 | parent: None, |
041b39d2 XL |
258 | children: vec![], |
259 | decl_id: None, | |
9e0c209e | 260 | docs: docs_for_attrs(&item.attrs), |
041b39d2 XL |
261 | sig: sig::item_signature(item, self), |
262 | attributes: lower_attributes(item.attrs.clone(), self), | |
7453a54e | 263 | })) |
85aaf69f | 264 | } |
7453a54e | 265 | ast::ItemKind::Mod(ref m) => { |
54a0048b | 266 | let qualname = format!("::{}", self.tcx.node_path_str(item.id)); |
62682a34 SL |
267 | |
268 | let cm = self.tcx.sess.codemap(); | |
269 | let filename = cm.span_to_filename(m.inner); | |
270 | ||
271 | let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Mod); | |
7453a54e | 272 | filter!(self.span_utils, sub_span, item.span, None); |
32a655c1 | 273 | |
041b39d2 XL |
274 | Some(Data::DefData(Def { |
275 | kind: DefKind::Mod, | |
276 | id: id_from_node_id(item.id, self), | |
c1a9b12d | 277 | name: item.ident.to_string(), |
041b39d2 XL |
278 | qualname, |
279 | span: self.span_from_span(sub_span.unwrap()), | |
280 | value: filename, | |
281 | parent: None, | |
282 | children: m.items.iter().map(|i| id_from_node_id(i.id, self)).collect(), | |
283 | decl_id: None, | |
9e0c209e | 284 | docs: docs_for_attrs(&item.attrs), |
041b39d2 XL |
285 | sig: sig::item_signature(item, self), |
286 | attributes: lower_attributes(item.attrs.clone(), self), | |
7453a54e | 287 | })) |
e9174d1e | 288 | } |
a7813a04 XL |
289 | ast::ItemKind::Enum(ref def, _) => { |
290 | let name = item.ident.to_string(); | |
291 | let qualname = format!("::{}", self.tcx.node_path_str(item.id)); | |
62682a34 | 292 | let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Enum); |
7453a54e | 293 | filter!(self.span_utils, sub_span, item.span, None); |
a7813a04 XL |
294 | let variants_str = def.variants.iter() |
295 | .map(|v| v.node.name.to_string()) | |
296 | .collect::<Vec<_>>() | |
297 | .join(", "); | |
041b39d2 XL |
298 | let value = format!("{}::{{{}}}", name, variants_str); |
299 | Some(Data::DefData(Def { | |
300 | kind: DefKind::Enum, | |
301 | id: id_from_node_id(item.id, self), | |
302 | span: self.span_from_span(sub_span.unwrap()), | |
303 | name, | |
304 | qualname, | |
305 | value, | |
306 | parent: None, | |
307 | children: def.variants | |
308 | .iter() | |
309 | .map(|v| id_from_node_id(v.node.data.id(), self)) | |
310 | .collect(), | |
311 | decl_id: None, | |
9e0c209e | 312 | docs: docs_for_attrs(&item.attrs), |
041b39d2 XL |
313 | sig: sig::item_signature(item, self), |
314 | attributes: lower_attributes(item.attrs.to_owned(), self), | |
7453a54e | 315 | })) |
e9174d1e | 316 | } |
9e0c209e | 317 | ast::ItemKind::Impl(.., ref trait_ref, ref typ, _) => { |
041b39d2 | 318 | if let ast::TyKind::Path(None, ref path) = typ.node { |
62682a34 | 319 | // Common case impl for a struct or something basic. |
041b39d2 XL |
320 | if generated_code(path.span) { |
321 | return None; | |
62682a34 | 322 | } |
041b39d2 XL |
323 | let sub_span = self.span_utils.sub_span_for_type_name(path.span); |
324 | filter!(self.span_utils, sub_span, typ.span, None); | |
325 | ||
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)) | |
333 | .map(id_from_def_id) | |
334 | .unwrap_or(null_id()), | |
335 | })) | |
336 | } else { | |
337 | None | |
62682a34 | 338 | } |
62682a34 | 339 | } |
d9579d0f AL |
340 | _ => { |
341 | // FIXME | |
54a0048b | 342 | bug!(); |
1a4d82fc JJ |
343 | } |
344 | } | |
1a4d82fc JJ |
345 | } |
346 | ||
32a655c1 SL |
347 | pub fn get_field_data(&self, |
348 | field: &ast::StructField, | |
349 | scope: NodeId) | |
041b39d2 | 350 | -> Option<Def> { |
54a0048b | 351 | if let Some(ident) = field.ident { |
32a655c1 | 352 | let name = ident.to_string(); |
54a0048b | 353 | let qualname = format!("::{}::{}", self.tcx.node_path_str(scope), ident); |
54a0048b SL |
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); | |
32a655c1 | 356 | let def_id = self.tcx.hir.local_def_id(field.id); |
7cac9316 | 357 | let typ = self.tcx.type_of(def_id).to_string(); |
32a655c1 | 358 | |
041b39d2 XL |
359 | |
360 | let id = id_from_node_id(field.id, self); | |
361 | let span = self.span_from_span(sub_span.unwrap()); | |
362 | ||
363 | Some(Def { | |
364 | kind: DefKind::Field, | |
365 | id, | |
366 | span, | |
367 | name, | |
368 | qualname, | |
369 | value: typ, | |
370 | parent: Some(id_from_node_id(scope, self)), | |
371 | children: vec![], | |
372 | decl_id: None, | |
9e0c209e | 373 | docs: docs_for_attrs(&field.attrs), |
041b39d2 XL |
374 | sig: sig::field_signature(field, self), |
375 | attributes: lower_attributes(field.attrs.clone(), self), | |
54a0048b SL |
376 | }) |
377 | } else { | |
378 | None | |
62682a34 SL |
379 | } |
380 | } | |
381 | ||
c1a9b12d SL |
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. | |
041b39d2 XL |
384 | pub fn get_method_data(&self, |
385 | id: ast::NodeId, | |
386 | name: ast::Name, | |
387 | span: Span) | |
388 | -> Option<Def> { | |
c1a9b12d SL |
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. | |
041b39d2 | 391 | let (qualname, parent_scope, decl_id, docs, attributes) = |
32a655c1 SL |
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) { | |
476ff2be | 394 | Some(Node::NodeItem(item)) => { |
c1a9b12d | 395 | match item.node { |
9e0c209e | 396 | hir::ItemImpl(.., ref ty, _) => { |
c1a9b12d | 397 | let mut result = String::from("<"); |
32a655c1 | 398 | result.push_str(&self.tcx.hir.node_to_pretty_string(ty.id)); |
c1a9b12d | 399 | |
041b39d2 | 400 | let mut trait_id = self.tcx.trait_id_of_impl(impl_id); |
476ff2be | 401 | let mut decl_id = None; |
9e0c209e | 402 | if let Some(def_id) = trait_id { |
3157f602 XL |
403 | result.push_str(" as "); |
404 | result.push_str(&self.tcx.item_path_str(def_id)); | |
476ff2be SL |
405 | self.tcx.associated_items(def_id) |
406 | .find(|item| item.name == name) | |
407 | .map(|item| decl_id = Some(item.def_id)); | |
041b39d2 XL |
408 | } else { |
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); | |
412 | } | |
413 | } | |
c1a9b12d SL |
414 | } |
415 | result.push_str(">"); | |
476ff2be SL |
416 | |
417 | (result, trait_id, decl_id, | |
8bb4bdeb XL |
418 | docs_for_attrs(&item.attrs), |
419 | item.attrs.to_vec()) | |
c1a9b12d SL |
420 | } |
421 | _ => { | |
54a0048b SL |
422 | span_bug!(span, |
423 | "Container {:?} for method {} not an impl?", | |
424 | impl_id, | |
425 | id); | |
e9174d1e | 426 | } |
c1a9b12d | 427 | } |
e9174d1e | 428 | } |
b039eaaf | 429 | r => { |
54a0048b SL |
430 | span_bug!(span, |
431 | "Container {:?} for method {} is not a node item {:?}", | |
432 | impl_id, | |
433 | id, | |
434 | r); | |
e9174d1e | 435 | } |
c1a9b12d | 436 | }, |
32a655c1 | 437 | None => match self.tcx.trait_of_item(self.tcx.hir.local_def_id(id)) { |
c1a9b12d | 438 | Some(def_id) => { |
32a655c1 | 439 | match self.tcx.hir.get_if_local(def_id) { |
476ff2be | 440 | Some(Node::NodeItem(item)) => { |
9e0c209e | 441 | (format!("::{}", self.tcx.item_path_str(def_id)), |
476ff2be | 442 | Some(def_id), None, |
8bb4bdeb XL |
443 | docs_for_attrs(&item.attrs), |
444 | item.attrs.to_vec()) | |
c1a9b12d | 445 | } |
b039eaaf | 446 | r => { |
54a0048b SL |
447 | span_bug!(span, |
448 | "Could not find container {:?} for \ | |
449 | method {}, got {:?}", | |
450 | def_id, | |
451 | id, | |
452 | r); | |
c1a9b12d SL |
453 | } |
454 | } | |
e9174d1e | 455 | } |
c1a9b12d | 456 | None => { |
8bb4bdeb XL |
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. | |
460 | return None; | |
e9174d1e | 461 | } |
c1a9b12d SL |
462 | }, |
463 | }; | |
464 | ||
465 | let qualname = format!("{}::{}", qualname, name); | |
466 | ||
c1a9b12d | 467 | let sub_span = self.span_utils.sub_span_after_keyword(span, keywords::Fn); |
7453a54e | 468 | filter!(self.span_utils, sub_span, span, None); |
32a655c1 | 469 | |
041b39d2 XL |
470 | Some(Def { |
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(), | |
475 | qualname, | |
a7813a04 XL |
476 | // FIXME you get better data here by using the visitor. |
477 | value: String::new(), | |
041b39d2 XL |
478 | parent: parent_scope.map(|id| id_from_def_id(id)), |
479 | children: vec![], | |
480 | decl_id: decl_id.map(|id| id_from_def_id(id)), | |
481 | docs, | |
482 | sig: None, | |
483 | attributes: lower_attributes(attributes, self), | |
7453a54e | 484 | }) |
c1a9b12d SL |
485 | } |
486 | ||
62682a34 | 487 | pub fn get_trait_ref_data(&self, |
041b39d2 XL |
488 | trait_ref: &ast::TraitRef) |
489 | -> Option<Ref> { | |
7453a54e | 490 | self.lookup_ref_id(trait_ref.ref_id).and_then(|def_id| { |
62682a34 | 491 | let span = trait_ref.path.span; |
8bb4bdeb XL |
492 | if generated_code(span) { |
493 | return None; | |
494 | } | |
7453a54e SL |
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); | |
041b39d2 XL |
497 | let span = self.span_from_span(sub_span.unwrap()); |
498 | Some(Ref { | |
499 | kind: RefKind::Type, | |
500 | span, | |
501 | ref_id: id_from_def_id(def_id), | |
7453a54e | 502 | }) |
62682a34 SL |
503 | }) |
504 | } | |
505 | ||
506 | pub fn get_expr_data(&self, expr: &ast::Expr) -> Option<Data> { | |
32a655c1 SL |
507 | let hir_node = self.tcx.hir.expect_expr(expr.id); |
508 | let ty = self.tables.expr_ty_adjusted_opt(&hir_node); | |
7453a54e SL |
509 | if ty.is_none() || ty.unwrap().sty == ty::TyError { |
510 | return None; | |
511 | } | |
62682a34 | 512 | match expr.node { |
7453a54e | 513 | ast::ExprKind::Field(ref sub_ex, ident) => { |
32a655c1 | 514 | let hir_node = match self.tcx.hir.find(sub_ex.id) { |
3157f602 XL |
515 | Some(Node::NodeExpr(expr)) => expr, |
516 | _ => { | |
517 | debug!("Missing or weird node for sub-expression {} in {:?}", | |
518 | sub_ex.id, expr); | |
519 | return None; | |
520 | } | |
521 | }; | |
32a655c1 | 522 | match self.tables.expr_ty_adjusted(&hir_node).sty { |
9e0c209e | 523 | ty::TyAdt(def, _) if !def.is_enum() => { |
e9174d1e SL |
524 | let f = def.struct_variant().field_named(ident.node.name); |
525 | let sub_span = self.span_utils.span_for_last_ident(expr.span); | |
7453a54e | 526 | filter!(self.span_utils, sub_span, expr.span, None); |
041b39d2 XL |
527 | let span = self.span_from_span(sub_span.unwrap()); |
528 | return Some(Data::RefData(Ref { | |
529 | kind: RefKind::Variable, | |
530 | span, | |
531 | ref_id: id_from_def_id(f.did), | |
e9174d1e | 532 | })); |
62682a34 SL |
533 | } |
534 | _ => { | |
9e0c209e | 535 | debug!("Expected struct or union type, found {:?}", ty); |
62682a34 SL |
536 | None |
537 | } | |
538 | } | |
539 | } | |
9e0c209e | 540 | ast::ExprKind::Struct(ref path, ..) => { |
32a655c1 | 541 | match self.tables.expr_ty_adjusted(&hir_node).sty { |
9e0c209e | 542 | ty::TyAdt(def, _) if !def.is_enum() => { |
62682a34 | 543 | let sub_span = self.span_utils.span_for_last_ident(path.span); |
7453a54e | 544 | filter!(self.span_utils, sub_span, path.span, None); |
041b39d2 XL |
545 | let span = self.span_from_span(sub_span.unwrap()); |
546 | Some(Data::RefData(Ref { | |
547 | kind: RefKind::Type, | |
548 | span, | |
549 | ref_id: id_from_def_id(def.did), | |
62682a34 SL |
550 | })) |
551 | } | |
552 | _ => { | |
9e0c209e | 553 | // FIXME ty could legitimately be an enum, but then we will fail |
62682a34 | 554 | // later if we try to look up the fields. |
9e0c209e | 555 | debug!("expected struct or union, found {:?}", ty); |
62682a34 SL |
556 | None |
557 | } | |
558 | } | |
559 | } | |
7453a54e | 560 | ast::ExprKind::MethodCall(..) => { |
7cac9316 | 561 | let method_id = self.tables.type_dependent_defs[&expr.id].def_id(); |
476ff2be | 562 | let (def_id, decl_id) = match self.tcx.associated_item(method_id).container { |
c1a9b12d | 563 | ty::ImplContainer(_) => (Some(method_id), None), |
e9174d1e | 564 | ty::TraitContainer(_) => (None, Some(method_id)), |
c1a9b12d SL |
565 | }; |
566 | let sub_span = self.span_utils.sub_span_for_meth_name(expr.span); | |
7453a54e | 567 | filter!(self.span_utils, sub_span, expr.span, None); |
041b39d2 XL |
568 | let span = self.span_from_span(sub_span.unwrap()); |
569 | Some(Data::RefData(Ref { | |
570 | kind: RefKind::Function, | |
571 | span, | |
572 | ref_id: def_id.or(decl_id).map(|id| id_from_def_id(id)).unwrap_or(null_id()), | |
c1a9b12d SL |
573 | })) |
574 | } | |
7453a54e | 575 | ast::ExprKind::Path(_, ref path) => { |
041b39d2 | 576 | self.get_path_data(expr.id, path).map(|d| Data::RefData(d)) |
c1a9b12d | 577 | } |
62682a34 SL |
578 | _ => { |
579 | // FIXME | |
54a0048b | 580 | bug!(); |
62682a34 SL |
581 | } |
582 | } | |
583 | } | |
584 | ||
041b39d2 | 585 | pub fn get_path_def(&self, id: NodeId) -> HirDef { |
32a655c1 | 586 | match self.tcx.hir.get(id) { |
476ff2be SL |
587 | Node::NodeTraitRef(tr) => tr.path.def, |
588 | ||
589 | Node::NodeItem(&hir::Item { node: hir::ItemUse(ref path, _), .. }) | | |
590 | Node::NodeVisibility(&hir::Visibility::Restricted { ref path, .. }) => path.def, | |
591 | ||
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, ..), .. }) => { | |
32a655c1 | 597 | self.tables.qpath_def(qpath, id) |
476ff2be SL |
598 | } |
599 | ||
600 | Node::NodeLocal(&hir::Pat { node: hir::PatKind::Binding(_, def_id, ..), .. }) => { | |
041b39d2 | 601 | HirDef::Local(def_id) |
476ff2be SL |
602 | } |
603 | ||
7cac9316 XL |
604 | Node::NodeTy(ty) => { |
605 | if let hir::Ty { node: hir::TyPath(ref qpath), .. } = *ty { | |
606 | match *qpath { | |
607 | hir::QPath::Resolved(_, ref path) => path.def, | |
608 | hir::QPath::TypeRelative(..) => { | |
609 | let ty = hir_ty_to_ty(self.tcx, ty); | |
476ff2be | 610 | if let ty::TyProjection(proj) = ty.sty { |
041b39d2 | 611 | return HirDef::AssociatedTy(proj.item_def_id); |
476ff2be | 612 | } |
041b39d2 | 613 | HirDef::Err |
476ff2be | 614 | } |
476ff2be | 615 | } |
7cac9316 | 616 | } else { |
041b39d2 | 617 | HirDef::Err |
476ff2be SL |
618 | } |
619 | } | |
620 | ||
041b39d2 | 621 | _ => HirDef::Err |
476ff2be SL |
622 | } |
623 | } | |
624 | ||
041b39d2 | 625 | pub fn get_path_data(&self, id: NodeId, path: &ast::Path) -> Option<Ref> { |
476ff2be | 626 | let def = self.get_path_def(id); |
c1a9b12d | 627 | let sub_span = self.span_utils.span_for_last_ident(path.span); |
7453a54e | 628 | filter!(self.span_utils, sub_span, path.span, None); |
c1a9b12d | 629 | match def { |
041b39d2 XL |
630 | HirDef::Upvar(..) | |
631 | HirDef::Local(..) | | |
632 | HirDef::Static(..) | | |
633 | HirDef::Const(..) | | |
634 | HirDef::AssociatedConst(..) | | |
635 | HirDef::StructCtor(..) | | |
636 | HirDef::VariantCtor(..) => { | |
637 | let span = self.span_from_span(sub_span.unwrap()); | |
638 | Some(Ref { | |
639 | kind: RefKind::Variable, | |
640 | span, | |
641 | ref_id: id_from_def_id(def.def_id()), | |
642 | }) | |
c1a9b12d | 643 | } |
041b39d2 XL |
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()); | |
653 | Some(Ref { | |
654 | kind: RefKind::Type, | |
655 | span, | |
656 | ref_id: id_from_def_id(def_id), | |
657 | }) | |
c1a9b12d | 658 | } |
041b39d2 | 659 | HirDef::Method(decl_id) => { |
c1a9b12d | 660 | let sub_span = self.span_utils.sub_span_for_meth_name(path.span); |
7453a54e | 661 | filter!(self.span_utils, sub_span, path.span, None); |
e9174d1e | 662 | let def_id = if decl_id.is_local() { |
476ff2be SL |
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) | |
c1a9b12d SL |
667 | } else { |
668 | None | |
669 | }; | |
041b39d2 XL |
670 | let span = self.span_from_span(sub_span.unwrap()); |
671 | Some(Ref { | |
672 | kind: RefKind::Function, | |
673 | span, | |
674 | ref_id: id_from_def_id(def_id.unwrap_or(decl_id)), | |
675 | }) | |
e9174d1e | 676 | } |
041b39d2 XL |
677 | HirDef::Fn(def_id) => { |
678 | let span = self.span_from_span(sub_span.unwrap()); | |
679 | Some(Ref { | |
680 | kind: RefKind::Function, | |
681 | span, | |
682 | ref_id: id_from_def_id(def_id), | |
683 | }) | |
c1a9b12d | 684 | } |
041b39d2 XL |
685 | HirDef::Mod(def_id) => { |
686 | let span = self.span_from_span(sub_span.unwrap()); | |
687 | Some(Ref { | |
688 | kind: RefKind::Mod, | |
689 | span, | |
690 | ref_id: id_from_def_id(def_id), | |
691 | }) | |
c1a9b12d | 692 | } |
041b39d2 XL |
693 | HirDef::PrimTy(..) | |
694 | HirDef::SelfTy(..) | | |
695 | HirDef::Label(..) | | |
696 | HirDef::Macro(..) | | |
697 | HirDef::GlobalAsm(..) | | |
698 | HirDef::Err => None, | |
c1a9b12d SL |
699 | } |
700 | } | |
701 | ||
62682a34 SL |
702 | pub fn get_field_ref_data(&self, |
703 | field_ref: &ast::Field, | |
041b39d2 XL |
704 | variant: &ty::VariantDef) |
705 | -> Option<Ref> { | |
e9174d1e SL |
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); | |
7453a54e | 709 | filter!(self.span_utils, sub_span, field_ref.ident.span, None); |
041b39d2 XL |
710 | let span = self.span_from_span(sub_span.unwrap()); |
711 | Some(Ref { | |
712 | kind: RefKind::Variable, | |
713 | span, | |
714 | ref_id: id_from_def_id(f.did), | |
7453a54e SL |
715 | }) |
716 | } | |
717 | ||
041b39d2 | 718 | /// Attempt to return MacroRef for any AST node. |
7453a54e SL |
719 | /// |
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, | |
041b39d2 XL |
722 | /// else uses the expansion callsite and callee to return some MacroRef. |
723 | pub fn get_macro_use_data(&self, span: Span) -> Option<MacroRef> { | |
7453a54e SL |
724 | if !generated_code(span) { |
725 | return None; | |
726 | } | |
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 | |
729 | // macro uses. | |
cc61c64b | 730 | let callsite = span.source_callsite(); |
041b39d2 | 731 | let callsite_span = self.span_from_span(callsite); |
cc61c64b | 732 | let callee = option_try!(span.source_callee()); |
7453a54e SL |
733 | let callee_span = option_try!(callee.span); |
734 | ||
735 | // Ignore attribute macros, their spans are usually mangled | |
736 | if let MacroAttribute(_) = callee.format { | |
737 | return None; | |
738 | } | |
739 | ||
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; | |
041b39d2 XL |
745 | let mac_span = self.span_from_span(mac_span); |
746 | return Some(MacroRef { | |
747 | span: callsite_span, | |
748 | qualname: mac_name.clone(), // FIXME: generate the real qualname | |
749 | callee_span: mac_span, | |
750 | }); | |
62682a34 | 751 | } |
7453a54e | 752 | |
041b39d2 XL |
753 | let callee_span = self.span_from_span(callee_span); |
754 | Some(MacroRef { | |
755 | span: callsite_span, | |
756 | qualname: callee.name().to_string(), // FIXME: generate the real qualname | |
757 | callee_span, | |
7453a54e | 758 | }) |
62682a34 SL |
759 | } |
760 | ||
62682a34 | 761 | fn lookup_ref_id(&self, ref_id: NodeId) -> Option<DefId> { |
476ff2be | 762 | match self.get_path_def(ref_id) { |
041b39d2 | 763 | HirDef::PrimTy(_) | HirDef::SelfTy(..) | HirDef::Err => None, |
3157f602 | 764 | def => Some(def.def_id()), |
62682a34 SL |
765 | } |
766 | } | |
767 | ||
c1a9b12d | 768 | #[inline] |
e9174d1e | 769 | pub fn enclosing_scope(&self, id: NodeId) -> NodeId { |
32a655c1 | 770 | self.tcx.hir.get_enclosing_scope(id).unwrap_or(CRATE_NODE_ID) |
c1a9b12d | 771 | } |
d9579d0f | 772 | } |
1a4d82fc | 773 | |
a7813a04 | 774 | fn make_signature(decl: &ast::FnDecl, generics: &ast::Generics) -> String { |
9e0c209e | 775 | let mut sig = "fn ".to_owned(); |
a7813a04 XL |
776 | if !generics.lifetimes.is_empty() || !generics.ty_params.is_empty() { |
777 | sig.push('<'); | |
778 | sig.push_str(&generics.lifetimes.iter() | |
7cac9316 | 779 | .map(|l| l.lifetime.ident.name.to_string()) |
a7813a04 XL |
780 | .collect::<Vec<_>>() |
781 | .join(", ")); | |
782 | if !generics.lifetimes.is_empty() { | |
783 | sig.push_str(", "); | |
784 | } | |
785 | sig.push_str(&generics.ty_params.iter() | |
786 | .map(|l| l.ident.to_string()) | |
787 | .collect::<Vec<_>>() | |
788 | .join(", ")); | |
789 | sig.push_str("> "); | |
790 | } | |
791 | sig.push('('); | |
792 | sig.push_str(&decl.inputs.iter().map(arg_to_string).collect::<Vec<_>>().join(", ")); | |
793 | sig.push(')'); | |
794 | match decl.output { | |
9e0c209e | 795 | ast::FunctionRetTy::Default(_) => sig.push_str(" -> ()"), |
a7813a04 XL |
796 | ast::FunctionRetTy::Ty(ref t) => sig.push_str(&format!(" -> {}", ty_to_string(t))), |
797 | } | |
798 | ||
799 | sig | |
800 | } | |
801 | ||
d9579d0f AL |
802 | // An AST visitor for collecting paths from patterns. |
803 | struct PathCollector { | |
804 | // The Row field identifies the kind of pattern. | |
041b39d2 | 805 | collected_paths: Vec<(NodeId, ast::Path, ast::Mutability)>, |
d9579d0f | 806 | } |
1a4d82fc | 807 | |
d9579d0f AL |
808 | impl PathCollector { |
809 | fn new() -> PathCollector { | |
e9174d1e | 810 | PathCollector { collected_paths: vec![] } |
1a4d82fc | 811 | } |
d9579d0f | 812 | } |
1a4d82fc | 813 | |
476ff2be | 814 | impl<'a> Visitor<'a> for PathCollector { |
d9579d0f | 815 | fn visit_pat(&mut self, p: &ast::Pat) { |
1a4d82fc | 816 | match p.node { |
9e0c209e | 817 | PatKind::Struct(ref path, ..) => { |
7453a54e | 818 | self.collected_paths.push((p.id, path.clone(), |
041b39d2 | 819 | ast::Mutability::Mutable)); |
1a4d82fc | 820 | } |
9e0c209e | 821 | PatKind::TupleStruct(ref path, ..) | |
3157f602 | 822 | PatKind::Path(_, ref path) => { |
7453a54e | 823 | self.collected_paths.push((p.id, path.clone(), |
041b39d2 | 824 | ast::Mutability::Mutable)); |
1a4d82fc | 825 | } |
7453a54e | 826 | PatKind::Ident(bm, ref path1, _) => { |
d9579d0f | 827 | debug!("PathCollector, visit ident in pat {}: {:?} {:?}", |
c1a9b12d | 828 | path1.node, |
d9579d0f AL |
829 | p.span, |
830 | path1.span); | |
1a4d82fc JJ |
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. | |
7453a54e | 835 | ast::BindingMode::ByRef(_) => ast::Mutability::Immutable, |
9cc50fc6 | 836 | ast::BindingMode::ByValue(mt) => mt, |
1a4d82fc JJ |
837 | }; |
838 | // collect path for either visit_local or visit_arm | |
54a0048b | 839 | let path = ast::Path::from_ident(path1.span, path1.node); |
041b39d2 | 840 | self.collected_paths.push((p.id, path, immut)); |
1a4d82fc | 841 | } |
d9579d0f | 842 | _ => {} |
1a4d82fc | 843 | } |
d9579d0f | 844 | visit::walk_pat(self, p); |
1a4d82fc JJ |
845 | } |
846 | } | |
847 | ||
9e0c209e | 848 | fn docs_for_attrs(attrs: &[Attribute]) -> String { |
9e0c209e SL |
849 | let mut result = String::new(); |
850 | ||
851 | for attr in attrs { | |
cc61c64b | 852 | if attr.check_name("doc") { |
476ff2be SL |
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())); | |
856 | } else { | |
857 | result.push_str(&val.as_str()); | |
858 | } | |
9e0c209e SL |
859 | result.push('\n'); |
860 | } | |
861 | } | |
862 | } | |
863 | ||
864 | result | |
865 | } | |
866 | ||
867 | #[derive(Clone, Copy, Debug, RustcEncodable)] | |
a7813a04 | 868 | pub enum Format { |
a7813a04 | 869 | Json, |
9e0c209e | 870 | JsonApi, |
a7813a04 XL |
871 | } |
872 | ||
873 | impl Format { | |
874 | fn extension(&self) -> &'static str { | |
041b39d2 | 875 | ".json" |
a7813a04 XL |
876 | } |
877 | } | |
878 | ||
cc61c64b XL |
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>, | |
883 | krate: &ast::Crate, | |
884 | cratename: &str); | |
885 | } | |
9cc50fc6 | 886 | |
cc61c64b XL |
887 | /// Dump the save-analysis results to a file. |
888 | pub struct DumpHandler<'a> { | |
889 | format: Format, | |
890 | odir: Option<&'a Path>, | |
891 | cratename: String | |
892 | } | |
1a4d82fc | 893 | |
cc61c64b XL |
894 | impl<'a> DumpHandler<'a> { |
895 | pub fn new(format: Format, odir: Option<&'a Path>, cratename: &str) -> DumpHandler<'a> { | |
896 | DumpHandler { | |
897 | format: format, | |
898 | odir: odir, | |
899 | cratename: cratename.to_owned() | |
900 | } | |
901 | } | |
1a4d82fc | 902 | |
cc61c64b XL |
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"), | |
909 | }, | |
910 | }; | |
911 | ||
912 | if let Err(e) = std::fs::create_dir_all(&root_path) { | |
913 | error!("Could not create directory {}: {}", root_path.display(), e); | |
914 | } | |
915 | ||
916 | { | |
917 | let disp = root_path.display(); | |
918 | info!("Writing output to {}", disp); | |
919 | } | |
920 | ||
921 | let executable = sess.crate_types.borrow().iter().any(|ct| *ct == CrateTypeExecutable); | |
922 | let mut out_name = if executable { | |
923 | "".to_owned() | |
924 | } else { | |
925 | "lib".to_owned() | |
926 | }; | |
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)); | |
934 | }); | |
935 | root_path.pop(); | |
936 | output_file | |
937 | } | |
938 | } | |
939 | ||
940 | impl<'a> SaveHandler for DumpHandler<'a> { | |
941 | fn save<'l, 'tcx>(&mut self, | |
942 | save_ctxt: SaveContext<'l, 'tcx>, | |
943 | krate: &ast::Crate, | |
944 | cratename: &str) { | |
945 | macro_rules! dump { | |
946 | ($new_dumper: expr) => {{ | |
947 | let mut dumper = $new_dumper; | |
948 | let mut visitor = DumpVisitor::new(save_ctxt, &mut dumper); | |
949 | ||
950 | visitor.dump_crate_info(cratename, krate); | |
951 | visit::walk_crate(&mut visitor, krate); | |
952 | }} | |
953 | } | |
954 | ||
955 | let output = &mut self.output_file(&save_ctxt.tcx.sess); | |
1a4d82fc | 956 | |
cc61c64b | 957 | match self.format { |
cc61c64b XL |
958 | Format::Json => dump!(JsonDumper::new(output)), |
959 | Format::JsonApi => dump!(JsonApiDumper::new(output)), | |
960 | } | |
1a4d82fc | 961 | } |
cc61c64b XL |
962 | } |
963 | ||
964 | /// Call a callback with the results of save-analysis. | |
965 | pub struct CallbackHandler<'b> { | |
966 | pub callback: &'b mut FnMut(&rls_data::Analysis), | |
967 | } | |
968 | ||
969 | impl<'b> SaveHandler for CallbackHandler<'b> { | |
970 | fn save<'l, 'tcx>(&mut self, | |
971 | save_ctxt: SaveContext<'l, 'tcx>, | |
972 | krate: &ast::Crate, | |
973 | cratename: &str) { | |
974 | macro_rules! dump { | |
975 | ($new_dumper: expr) => {{ | |
976 | let mut dumper = $new_dumper; | |
977 | let mut visitor = DumpVisitor::new(save_ctxt, &mut dumper); | |
978 | ||
979 | visitor.dump_crate_info(cratename, krate); | |
980 | visit::walk_crate(&mut visitor, krate); | |
981 | }} | |
982 | } | |
1a4d82fc | 983 | |
cc61c64b XL |
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)) | |
1a4d82fc | 990 | } |
cc61c64b | 991 | } |
1a4d82fc | 992 | |
cc61c64b XL |
993 | pub fn process_crate<'l, 'tcx, H: SaveHandler>(tcx: TyCtxt<'l, 'tcx, 'tcx>, |
994 | krate: &ast::Crate, | |
995 | analysis: &'l ty::CrateAnalysis, | |
996 | cratename: &str, | |
997 | mut handler: H) { | |
998 | let _ignore = tcx.dep_graph.in_ignore(); | |
999 | ||
1000 | assert!(analysis.glob_map.is_some()); | |
1001 | ||
1002 | info!("Dumping crate {}", cratename); | |
1a4d82fc | 1003 | |
32a655c1 SL |
1004 | let save_ctxt = SaveContext { |
1005 | tcx: tcx, | |
1006 | tables: &ty::TypeckTables::empty(), | |
1007 | analysis: analysis, | |
1008 | span_utils: SpanUtils::new(&tcx.sess), | |
1009 | }; | |
1a4d82fc | 1010 | |
cc61c64b | 1011 | handler.save(save_ctxt, krate, cratename) |
1a4d82fc | 1012 | } |
d9579d0f AL |
1013 | |
1014 | // Utility functions for the module. | |
1015 | ||
1016 | // Helper function to escape quotes in a string | |
1017 | fn escape(s: String) -> String { | |
1018 | s.replace("\"", "\"\"") | |
1019 | } | |
1020 | ||
7453a54e SL |
1021 | // Helper function to determine if a span came from a |
1022 | // macro expansion or syntax extension. | |
041b39d2 | 1023 | fn generated_code(span: Span) -> bool { |
cc61c64b | 1024 | span.ctxt != NO_EXPANSION || span == DUMMY_SP |
d9579d0f | 1025 | } |
041b39d2 XL |
1026 | |
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 { | |
1030 | rls_data::Id { | |
1031 | krate: id.krate.as_u32(), | |
1032 | index: id.index.as_u32(), | |
1033 | } | |
1034 | } | |
1035 | ||
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) | |
1039 | } | |
1040 | ||
1041 | fn null_id() -> rls_data::Id { | |
1042 | rls_data::Id { | |
1043 | krate: u32::max_value(), | |
1044 | index: u32::max_value(), | |
1045 | } | |
1046 | } | |
1047 | ||
1048 | fn lower_attributes(attrs: Vec<Attribute>, scx: &SaveContext) -> Vec<rls_data::Attribute> { | |
1049 | attrs.into_iter() | |
1050 | // Only retain real attributes. Doc comments are lowered separately. | |
1051 | .filter(|attr| attr.path != "doc") | |
1052 | .map(|mut attr| { | |
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(); | |
1061 | ||
1062 | rls_data::Attribute { | |
1063 | value: value, | |
1064 | span: scx.span_from_span(attr.span), | |
1065 | } | |
1066 | }).collect() | |
1067 | } |