]>
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 | ||
d9579d0f | 11 | use middle::ty; |
62682a34 | 12 | use middle::def; |
b039eaaf | 13 | use middle::def_id::DefId; |
1a4d82fc | 14 | |
85aaf69f | 15 | use std::env; |
c34b1796 AL |
16 | use std::fs::{self, File}; |
17 | use std::path::{Path, PathBuf}; | |
1a4d82fc | 18 | |
e9174d1e | 19 | use rustc_front; |
b039eaaf | 20 | use rustc_front::{hir, lowering}; |
92a42be0 SL |
21 | use rustc::front::map::NodeItem; |
22 | use rustc::session::config::CrateType::CrateTypeExecutable; | |
c1a9b12d | 23 | |
e9174d1e | 24 | use syntax::ast::{self, NodeId}; |
d9579d0f | 25 | use syntax::ast_util; |
1a4d82fc | 26 | use syntax::codemap::*; |
c1a9b12d | 27 | use syntax::parse::token::{self, keywords}; |
1a4d82fc | 28 | use syntax::visit::{self, Visitor}; |
d9579d0f | 29 | use syntax::print::pprust::ty_to_string; |
1a4d82fc | 30 | |
d9579d0f | 31 | use self::span_utils::SpanUtils; |
1a4d82fc | 32 | |
62682a34 | 33 | |
e9174d1e SL |
34 | pub mod span_utils; |
35 | pub mod recorder; | |
1a4d82fc | 36 | |
d9579d0f | 37 | mod dump_csv; |
1a4d82fc | 38 | |
d9579d0f | 39 | pub struct SaveContext<'l, 'tcx: 'l> { |
62682a34 | 40 | tcx: &'l ty::ctxt<'tcx>, |
b039eaaf | 41 | lcx: &'l lowering::LoweringContext<'l>, |
d9579d0f AL |
42 | span_utils: SpanUtils<'l>, |
43 | } | |
44 | ||
45 | pub struct CrateData { | |
46 | pub name: String, | |
47 | pub number: u32, | |
48 | } | |
1a4d82fc | 49 | |
d9579d0f AL |
50 | /// Data for any entity in the Rust language. The actual data contained varied |
51 | /// with the kind of entity being queried. See the nested structs for details. | |
62682a34 | 52 | #[derive(Debug)] |
d9579d0f AL |
53 | pub enum Data { |
54 | /// Data for all kinds of functions and methods. | |
55 | FunctionData(FunctionData), | |
62682a34 | 56 | /// Data for local and global variables (consts and statics), and fields. |
d9579d0f | 57 | VariableData(VariableData), |
62682a34 SL |
58 | /// Data for modules. |
59 | ModData(ModData), | |
60 | /// Data for Enums. | |
61 | EnumData(EnumData), | |
62 | /// Data for impls. | |
63 | ImplData(ImplData), | |
64 | ||
65 | /// Data for the use of some variable (e.g., the use of a local variable, which | |
66 | /// will refere to that variables declaration). | |
67 | VariableRefData(VariableRefData), | |
68 | /// Data for a reference to a type or trait. | |
69 | TypeRefData(TypeRefData), | |
c1a9b12d SL |
70 | /// Data for a reference to a module. |
71 | ModRefData(ModRefData), | |
72 | /// Data about a function call. | |
73 | FunctionCallData(FunctionCallData), | |
74 | /// Data about a method call. | |
75 | MethodCallData(MethodCallData), | |
d9579d0f | 76 | } |
1a4d82fc | 77 | |
d9579d0f | 78 | /// Data for all kinds of functions and methods. |
62682a34 | 79 | #[derive(Debug)] |
d9579d0f AL |
80 | pub struct FunctionData { |
81 | pub id: NodeId, | |
82 | pub name: String, | |
83 | pub qualname: String, | |
84 | pub declaration: Option<DefId>, | |
85 | pub span: Span, | |
86 | pub scope: NodeId, | |
87 | } | |
1a4d82fc | 88 | |
d9579d0f | 89 | /// Data for local and global variables (consts and statics). |
62682a34 | 90 | #[derive(Debug)] |
d9579d0f AL |
91 | pub struct VariableData { |
92 | pub id: NodeId, | |
93 | pub name: String, | |
94 | pub qualname: String, | |
95 | pub span: Span, | |
96 | pub scope: NodeId, | |
97 | pub value: String, | |
98 | pub type_value: String, | |
1a4d82fc JJ |
99 | } |
100 | ||
62682a34 SL |
101 | /// Data for modules. |
102 | #[derive(Debug)] | |
103 | pub struct ModData { | |
104 | pub id: NodeId, | |
105 | pub name: String, | |
106 | pub qualname: String, | |
107 | pub span: Span, | |
108 | pub scope: NodeId, | |
109 | pub filename: String, | |
110 | } | |
111 | ||
112 | /// Data for enum declarations. | |
113 | #[derive(Debug)] | |
114 | pub struct EnumData { | |
115 | pub id: NodeId, | |
116 | pub value: String, | |
117 | pub qualname: String, | |
118 | pub span: Span, | |
119 | pub scope: NodeId, | |
120 | } | |
121 | ||
122 | #[derive(Debug)] | |
123 | pub struct ImplData { | |
124 | pub id: NodeId, | |
125 | pub span: Span, | |
126 | pub scope: NodeId, | |
127 | // FIXME: I'm not really sure inline data is the best way to do this. Seems | |
128 | // OK in this case, but generalising leads to returning chunks of AST, which | |
129 | // feels wrong. | |
130 | pub trait_ref: Option<TypeRefData>, | |
131 | pub self_ref: Option<TypeRefData>, | |
132 | } | |
133 | ||
134 | /// Data for the use of some item (e.g., the use of a local variable, which | |
c1a9b12d | 135 | /// will refer to that variables declaration (by ref_id)). |
62682a34 SL |
136 | #[derive(Debug)] |
137 | pub struct VariableRefData { | |
138 | pub name: String, | |
139 | pub span: Span, | |
140 | pub scope: NodeId, | |
141 | pub ref_id: DefId, | |
142 | } | |
143 | ||
144 | /// Data for a reference to a type or trait. | |
145 | #[derive(Debug)] | |
146 | pub struct TypeRefData { | |
147 | pub span: Span, | |
148 | pub scope: NodeId, | |
149 | pub ref_id: DefId, | |
150 | } | |
151 | ||
c1a9b12d SL |
152 | /// Data for a reference to a module. |
153 | #[derive(Debug)] | |
154 | pub struct ModRefData { | |
155 | pub span: Span, | |
156 | pub scope: NodeId, | |
157 | pub ref_id: DefId, | |
158 | } | |
159 | ||
160 | /// Data about a function call. | |
161 | #[derive(Debug)] | |
162 | pub struct FunctionCallData { | |
163 | pub span: Span, | |
164 | pub scope: NodeId, | |
165 | pub ref_id: DefId, | |
166 | } | |
167 | ||
168 | /// Data about a method call. | |
169 | #[derive(Debug)] | |
170 | pub struct MethodCallData { | |
171 | pub span: Span, | |
172 | pub scope: NodeId, | |
173 | pub ref_id: Option<DefId>, | |
174 | pub decl_id: Option<DefId>, | |
175 | } | |
176 | ||
177 | ||
62682a34 | 178 | |
d9579d0f | 179 | impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { |
b039eaaf SL |
180 | pub fn new(tcx: &'l ty::ctxt<'tcx>, |
181 | lcx: &'l lowering::LoweringContext<'l>) | |
182 | -> SaveContext<'l, 'tcx> { | |
c1a9b12d | 183 | let span_utils = SpanUtils::new(&tcx.sess); |
b039eaaf | 184 | SaveContext::from_span_utils(tcx, lcx, span_utils) |
c1a9b12d SL |
185 | } |
186 | ||
187 | pub fn from_span_utils(tcx: &'l ty::ctxt<'tcx>, | |
b039eaaf | 188 | lcx: &'l lowering::LoweringContext<'l>, |
c1a9b12d SL |
189 | span_utils: SpanUtils<'l>) |
190 | -> SaveContext<'l, 'tcx> { | |
b039eaaf SL |
191 | SaveContext { |
192 | tcx: tcx, | |
193 | lcx: lcx, | |
194 | span_utils: span_utils, | |
195 | } | |
1a4d82fc JJ |
196 | } |
197 | ||
d9579d0f AL |
198 | // List external crates used by the current crate. |
199 | pub fn get_external_crates(&self) -> Vec<CrateData> { | |
200 | let mut result = Vec::new(); | |
1a4d82fc | 201 | |
92a42be0 | 202 | for n in self.tcx.sess.cstore.crates() { |
b039eaaf | 203 | result.push(CrateData { |
92a42be0 | 204 | name: self.tcx.sess.cstore.crate_name(n), |
b039eaaf SL |
205 | number: n, |
206 | }); | |
92a42be0 | 207 | } |
1a4d82fc JJ |
208 | |
209 | result | |
210 | } | |
211 | ||
d9579d0f AL |
212 | pub fn get_item_data(&self, item: &ast::Item) -> Data { |
213 | match item.node { | |
62682a34 SL |
214 | ast::ItemFn(..) => { |
215 | let name = self.tcx.map.path_to_string(item.id); | |
d9579d0f AL |
216 | let qualname = format!("::{}", name); |
217 | let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Fn); | |
218 | ||
219 | Data::FunctionData(FunctionData { | |
220 | id: item.id, | |
221 | name: name, | |
222 | qualname: qualname, | |
223 | declaration: None, | |
224 | span: sub_span.unwrap(), | |
c1a9b12d | 225 | scope: self.enclosing_scope(item.id), |
d9579d0f | 226 | }) |
1a4d82fc | 227 | } |
d9579d0f | 228 | ast::ItemStatic(ref typ, mt, ref expr) => { |
62682a34 | 229 | let qualname = format!("::{}", self.tcx.map.path_to_string(item.id)); |
1a4d82fc | 230 | |
d9579d0f AL |
231 | // If the variable is immutable, save the initialising expression. |
232 | let (value, keyword) = match mt { | |
62682a34 | 233 | ast::MutMutable => (String::from("<mutable>"), keywords::Mut), |
d9579d0f AL |
234 | ast::MutImmutable => (self.span_utils.snippet(expr.span), keywords::Static), |
235 | }; | |
1a4d82fc | 236 | |
d9579d0f AL |
237 | let sub_span = self.span_utils.sub_span_after_keyword(item.span, keyword); |
238 | ||
239 | Data::VariableData(VariableData { | |
240 | id: item.id, | |
c1a9b12d | 241 | name: item.ident.to_string(), |
d9579d0f AL |
242 | qualname: qualname, |
243 | span: sub_span.unwrap(), | |
c1a9b12d | 244 | scope: self.enclosing_scope(item.id), |
d9579d0f AL |
245 | value: value, |
246 | type_value: ty_to_string(&typ), | |
247 | }) | |
1a4d82fc | 248 | } |
d9579d0f | 249 | ast::ItemConst(ref typ, ref expr) => { |
62682a34 | 250 | let qualname = format!("::{}", self.tcx.map.path_to_string(item.id)); |
d9579d0f AL |
251 | let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Const); |
252 | ||
253 | Data::VariableData(VariableData { | |
254 | id: item.id, | |
c1a9b12d | 255 | name: item.ident.to_string(), |
d9579d0f AL |
256 | qualname: qualname, |
257 | span: sub_span.unwrap(), | |
c1a9b12d | 258 | scope: self.enclosing_scope(item.id), |
d9579d0f AL |
259 | value: self.span_utils.snippet(expr.span), |
260 | type_value: ty_to_string(&typ), | |
261 | }) | |
85aaf69f | 262 | } |
62682a34 SL |
263 | ast::ItemMod(ref m) => { |
264 | let qualname = format!("::{}", self.tcx.map.path_to_string(item.id)); | |
265 | ||
266 | let cm = self.tcx.sess.codemap(); | |
267 | let filename = cm.span_to_filename(m.inner); | |
268 | ||
269 | let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Mod); | |
270 | ||
271 | Data::ModData(ModData { | |
272 | id: item.id, | |
c1a9b12d | 273 | name: item.ident.to_string(), |
62682a34 SL |
274 | qualname: qualname, |
275 | span: sub_span.unwrap(), | |
c1a9b12d | 276 | scope: self.enclosing_scope(item.id), |
62682a34 SL |
277 | filename: filename, |
278 | }) | |
e9174d1e | 279 | } |
62682a34 SL |
280 | ast::ItemEnum(..) => { |
281 | let enum_name = format!("::{}", self.tcx.map.path_to_string(item.id)); | |
282 | let val = self.span_utils.snippet(item.span); | |
283 | let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Enum); | |
284 | ||
285 | Data::EnumData(EnumData { | |
286 | id: item.id, | |
287 | value: val, | |
288 | span: sub_span.unwrap(), | |
289 | qualname: enum_name, | |
c1a9b12d | 290 | scope: self.enclosing_scope(item.id), |
62682a34 | 291 | }) |
e9174d1e | 292 | } |
62682a34 SL |
293 | ast::ItemImpl(_, _, _, ref trait_ref, ref typ, _) => { |
294 | let mut type_data = None; | |
295 | let sub_span; | |
296 | ||
c1a9b12d | 297 | let parent = self.enclosing_scope(item.id); |
62682a34 SL |
298 | |
299 | match typ.node { | |
300 | // Common case impl for a struct or something basic. | |
301 | ast::TyPath(None, ref path) => { | |
302 | sub_span = self.span_utils.sub_span_for_type_name(path.span).unwrap(); | |
b039eaaf SL |
303 | type_data = self.lookup_ref_id(typ.id).map(|id| { |
304 | TypeRefData { | |
305 | span: sub_span, | |
306 | scope: parent, | |
307 | ref_id: id, | |
308 | } | |
62682a34 | 309 | }); |
e9174d1e | 310 | } |
62682a34 SL |
311 | _ => { |
312 | // Less useful case, impl for a compound type. | |
313 | let span = typ.span; | |
314 | sub_span = self.span_utils.sub_span_for_type_name(span).unwrap_or(span); | |
315 | } | |
316 | } | |
317 | ||
b039eaaf SL |
318 | let trait_data = trait_ref.as_ref() |
319 | .and_then(|tr| self.get_trait_ref_data(tr, parent)); | |
62682a34 SL |
320 | |
321 | Data::ImplData(ImplData { | |
322 | id: item.id, | |
323 | span: sub_span, | |
324 | scope: parent, | |
325 | trait_ref: trait_data, | |
326 | self_ref: type_data, | |
327 | }) | |
328 | } | |
d9579d0f AL |
329 | _ => { |
330 | // FIXME | |
331 | unimplemented!(); | |
1a4d82fc JJ |
332 | } |
333 | } | |
1a4d82fc JJ |
334 | } |
335 | ||
c1a9b12d | 336 | pub fn get_field_data(&self, field: &ast::StructField, scope: NodeId) -> Option<VariableData> { |
62682a34 SL |
337 | match field.node.kind { |
338 | ast::NamedField(ident, _) => { | |
b039eaaf SL |
339 | let qualname = format!("::{}::{}", self.tcx.map.path_to_string(scope), ident); |
340 | let typ = self.tcx.node_types().get(&field.node.id).unwrap().to_string(); | |
62682a34 | 341 | let sub_span = self.span_utils.sub_span_before_token(field.span, token::Colon); |
c1a9b12d | 342 | Some(VariableData { |
62682a34 | 343 | id: field.node.id, |
c1a9b12d | 344 | name: ident.to_string(), |
62682a34 SL |
345 | qualname: qualname, |
346 | span: sub_span.unwrap(), | |
c1a9b12d | 347 | scope: scope, |
62682a34 SL |
348 | value: "".to_owned(), |
349 | type_value: typ, | |
c1a9b12d | 350 | }) |
e9174d1e | 351 | } |
62682a34 SL |
352 | _ => None, |
353 | } | |
354 | } | |
355 | ||
c1a9b12d SL |
356 | // FIXME would be nice to take a MethodItem here, but the ast provides both |
357 | // trait and impl flavours, so the caller must do the disassembly. | |
e9174d1e | 358 | pub fn get_method_data(&self, id: ast::NodeId, name: ast::Name, span: Span) -> FunctionData { |
c1a9b12d SL |
359 | // The qualname for a method is the trait name or name of the struct in an impl in |
360 | // which the method is declared in, followed by the method's name. | |
b039eaaf SL |
361 | let qualname = match self.tcx.impl_of_method(self.tcx.map.local_def_id(id)) { |
362 | Some(impl_id) => match self.tcx.map.get_if_local(impl_id) { | |
363 | Some(NodeItem(item)) => { | |
c1a9b12d | 364 | match item.node { |
e9174d1e | 365 | hir::ItemImpl(_, _, _, _, ref ty, _) => { |
c1a9b12d | 366 | let mut result = String::from("<"); |
e9174d1e | 367 | result.push_str(&rustc_front::print::pprust::ty_to_string(&**ty)); |
c1a9b12d | 368 | |
b039eaaf | 369 | match self.tcx.trait_of_item(self.tcx.map.local_def_id(id)) { |
c1a9b12d SL |
370 | Some(def_id) => { |
371 | result.push_str(" as "); | |
b039eaaf | 372 | result.push_str(&self.tcx.item_path_str(def_id)); |
e9174d1e | 373 | } |
c1a9b12d SL |
374 | None => {} |
375 | } | |
376 | result.push_str(">"); | |
377 | result | |
378 | } | |
379 | _ => { | |
380 | self.tcx.sess.span_bug(span, | |
b039eaaf SL |
381 | &format!("Container {:?} for method {} not \ |
382 | an impl?", | |
383 | impl_id, | |
384 | id)); | |
e9174d1e | 385 | } |
c1a9b12d | 386 | } |
e9174d1e | 387 | } |
b039eaaf | 388 | r => { |
c1a9b12d | 389 | self.tcx.sess.span_bug(span, |
b039eaaf SL |
390 | &format!("Container {:?} for method {} is not a node \ |
391 | item {:?}", | |
392 | impl_id, | |
393 | id, | |
394 | r)); | |
e9174d1e | 395 | } |
c1a9b12d | 396 | }, |
b039eaaf | 397 | None => match self.tcx.trait_of_item(self.tcx.map.local_def_id(id)) { |
c1a9b12d | 398 | Some(def_id) => { |
b039eaaf SL |
399 | match self.tcx.map.get_if_local(def_id) { |
400 | Some(NodeItem(_)) => { | |
c1a9b12d SL |
401 | format!("::{}", self.tcx.item_path_str(def_id)) |
402 | } | |
b039eaaf | 403 | r => { |
c1a9b12d | 404 | self.tcx.sess.span_bug(span, |
b039eaaf SL |
405 | &format!("Could not find container {:?} for \ |
406 | method {}, got {:?}", | |
407 | def_id, | |
408 | id, | |
409 | r)); | |
c1a9b12d SL |
410 | } |
411 | } | |
e9174d1e | 412 | } |
c1a9b12d SL |
413 | None => { |
414 | self.tcx.sess.span_bug(span, | |
b039eaaf | 415 | &format!("Could not find container for method {}", id)); |
e9174d1e | 416 | } |
c1a9b12d SL |
417 | }, |
418 | }; | |
419 | ||
420 | let qualname = format!("{}::{}", qualname, name); | |
421 | ||
b039eaaf SL |
422 | let def_id = self.tcx.map.local_def_id(id); |
423 | let decl_id = self.tcx.trait_item_of_item(def_id).and_then(|new_id| { | |
424 | let new_def_id = new_id.def_id(); | |
425 | if new_def_id != def_id { | |
426 | Some(new_def_id) | |
427 | } else { | |
428 | None | |
429 | } | |
430 | }); | |
c1a9b12d SL |
431 | |
432 | let sub_span = self.span_utils.sub_span_after_keyword(span, keywords::Fn); | |
433 | ||
434 | FunctionData { | |
435 | id: id, | |
436 | name: name.to_string(), | |
437 | qualname: qualname, | |
438 | declaration: decl_id, | |
439 | span: sub_span.unwrap(), | |
440 | scope: self.enclosing_scope(id), | |
441 | } | |
442 | } | |
443 | ||
62682a34 SL |
444 | pub fn get_trait_ref_data(&self, |
445 | trait_ref: &ast::TraitRef, | |
446 | parent: NodeId) | |
447 | -> Option<TypeRefData> { | |
448 | self.lookup_ref_id(trait_ref.ref_id).map(|def_id| { | |
449 | let span = trait_ref.path.span; | |
450 | let sub_span = self.span_utils.sub_span_for_type_name(span).unwrap_or(span); | |
451 | TypeRefData { | |
452 | span: sub_span, | |
453 | scope: parent, | |
454 | ref_id: def_id, | |
455 | } | |
456 | }) | |
457 | } | |
458 | ||
459 | pub fn get_expr_data(&self, expr: &ast::Expr) -> Option<Data> { | |
460 | match expr.node { | |
461 | ast::ExprField(ref sub_ex, ident) => { | |
b039eaaf | 462 | let hir_node = lowering::lower_expr(self.lcx, sub_ex); |
e9174d1e | 463 | let ty = &self.tcx.expr_ty_adjusted(&hir_node).sty; |
62682a34 | 464 | match *ty { |
e9174d1e SL |
465 | ty::TyStruct(def, _) => { |
466 | let f = def.struct_variant().field_named(ident.node.name); | |
467 | let sub_span = self.span_utils.span_for_last_ident(expr.span); | |
468 | return Some(Data::VariableRefData(VariableRefData { | |
469 | name: ident.node.to_string(), | |
470 | span: sub_span.unwrap(), | |
471 | scope: self.enclosing_scope(expr.id), | |
472 | ref_id: f.did, | |
473 | })); | |
62682a34 SL |
474 | } |
475 | _ => { | |
476 | debug!("Expected struct type, found {:?}", ty); | |
477 | None | |
478 | } | |
479 | } | |
480 | } | |
481 | ast::ExprStruct(ref path, _, _) => { | |
b039eaaf SL |
482 | let hir_node = lowering::lower_expr(self.lcx, expr); |
483 | let ty = &self.tcx.expr_ty_adjusted(&hir_node).sty; | |
62682a34 | 484 | match *ty { |
e9174d1e | 485 | ty::TyStruct(def, _) => { |
62682a34 SL |
486 | let sub_span = self.span_utils.span_for_last_ident(path.span); |
487 | Some(Data::TypeRefData(TypeRefData { | |
488 | span: sub_span.unwrap(), | |
c1a9b12d | 489 | scope: self.enclosing_scope(expr.id), |
e9174d1e | 490 | ref_id: def.did, |
62682a34 SL |
491 | })) |
492 | } | |
493 | _ => { | |
494 | // FIXME ty could legitimately be a TyEnum, but then we will fail | |
495 | // later if we try to look up the fields. | |
496 | debug!("expected TyStruct, found {:?}", ty); | |
497 | None | |
498 | } | |
499 | } | |
500 | } | |
c1a9b12d SL |
501 | ast::ExprMethodCall(..) => { |
502 | let method_call = ty::MethodCall::expr(expr.id); | |
503 | let method_id = self.tcx.tables.borrow().method_map[&method_call].def_id; | |
504 | let (def_id, decl_id) = match self.tcx.impl_or_trait_item(method_id).container() { | |
505 | ty::ImplContainer(_) => (Some(method_id), None), | |
e9174d1e | 506 | ty::TraitContainer(_) => (None, Some(method_id)), |
c1a9b12d SL |
507 | }; |
508 | let sub_span = self.span_utils.sub_span_for_meth_name(expr.span); | |
509 | let parent = self.enclosing_scope(expr.id); | |
510 | Some(Data::MethodCallData(MethodCallData { | |
511 | span: sub_span.unwrap(), | |
512 | scope: parent, | |
513 | ref_id: def_id, | |
514 | decl_id: decl_id, | |
515 | })) | |
516 | } | |
517 | ast::ExprPath(_, ref path) => { | |
518 | self.get_path_data(expr.id, path) | |
519 | } | |
62682a34 SL |
520 | _ => { |
521 | // FIXME | |
522 | unimplemented!(); | |
523 | } | |
524 | } | |
525 | } | |
526 | ||
e9174d1e | 527 | pub fn get_path_data(&self, id: NodeId, path: &ast::Path) -> Option<Data> { |
c1a9b12d SL |
528 | let def_map = self.tcx.def_map.borrow(); |
529 | if !def_map.contains_key(&id) { | |
530 | self.tcx.sess.span_bug(path.span, | |
531 | &format!("def_map has no key for {} in visit_expr", id)); | |
532 | } | |
533 | let def = def_map.get(&id).unwrap().full_def(); | |
534 | let sub_span = self.span_utils.span_for_last_ident(path.span); | |
535 | match def { | |
536 | def::DefUpvar(..) | | |
537 | def::DefLocal(..) | | |
538 | def::DefStatic(..) | | |
539 | def::DefConst(..) | | |
540 | def::DefAssociatedConst(..) | | |
541 | def::DefVariant(..) => { | |
542 | Some(Data::VariableRefData(VariableRefData { | |
543 | name: self.span_utils.snippet(sub_span.unwrap()), | |
544 | span: sub_span.unwrap(), | |
545 | scope: self.enclosing_scope(id), | |
546 | ref_id: def.def_id(), | |
547 | })) | |
548 | } | |
549 | def::DefStruct(def_id) | | |
550 | def::DefTy(def_id, _) | | |
551 | def::DefTrait(def_id) | | |
552 | def::DefTyParam(_, _, def_id, _) => { | |
553 | Some(Data::TypeRefData(TypeRefData { | |
554 | span: sub_span.unwrap(), | |
555 | ref_id: def_id, | |
556 | scope: self.enclosing_scope(id), | |
557 | })) | |
558 | } | |
559 | def::DefMethod(decl_id) => { | |
560 | let sub_span = self.span_utils.sub_span_for_meth_name(path.span); | |
e9174d1e | 561 | let def_id = if decl_id.is_local() { |
c1a9b12d SL |
562 | let ti = self.tcx.impl_or_trait_item(decl_id); |
563 | match ti.container() { | |
564 | ty::TraitContainer(def_id) => { | |
b039eaaf SL |
565 | self.tcx |
566 | .trait_items(def_id) | |
c1a9b12d | 567 | .iter() |
b039eaaf | 568 | .find(|mr| mr.name() == ti.name() && self.trait_method_has_body(mr)) |
c1a9b12d SL |
569 | .map(|mr| mr.def_id()) |
570 | } | |
571 | ty::ImplContainer(def_id) => { | |
572 | let impl_items = self.tcx.impl_items.borrow(); | |
573 | Some(impl_items.get(&def_id) | |
574 | .unwrap() | |
575 | .iter() | |
576 | .find(|mr| { | |
b039eaaf SL |
577 | self.tcx.impl_or_trait_item(mr.def_id()).name() == |
578 | ti.name() | |
579 | }) | |
c1a9b12d SL |
580 | .unwrap() |
581 | .def_id()) | |
582 | } | |
583 | } | |
584 | } else { | |
585 | None | |
586 | }; | |
587 | Some(Data::MethodCallData(MethodCallData { | |
588 | span: sub_span.unwrap(), | |
589 | scope: self.enclosing_scope(id), | |
590 | ref_id: def_id, | |
591 | decl_id: Some(decl_id), | |
592 | })) | |
e9174d1e | 593 | } |
c1a9b12d SL |
594 | def::DefFn(def_id, _) => { |
595 | Some(Data::FunctionCallData(FunctionCallData { | |
596 | ref_id: def_id, | |
597 | span: sub_span.unwrap(), | |
598 | scope: self.enclosing_scope(id), | |
599 | })) | |
600 | } | |
601 | def::DefMod(def_id) => { | |
602 | Some(Data::ModRefData(ModRefData { | |
603 | ref_id: def_id, | |
604 | span: sub_span.unwrap(), | |
605 | scope: self.enclosing_scope(id), | |
606 | })) | |
607 | } | |
608 | _ => None, | |
609 | } | |
610 | } | |
611 | ||
612 | fn trait_method_has_body(&self, mr: &ty::ImplOrTraitItem) -> bool { | |
613 | let def_id = mr.def_id(); | |
b039eaaf SL |
614 | if let Some(node_id) = self.tcx.map.as_local_node_id(def_id) { |
615 | let trait_item = self.tcx.map.expect_trait_item(node_id); | |
616 | if let hir::TraitItem_::MethodTraitItem(_, Some(_)) = trait_item.node { | |
617 | true | |
618 | } else { | |
619 | false | |
620 | } | |
c1a9b12d SL |
621 | } else { |
622 | false | |
623 | } | |
624 | } | |
625 | ||
62682a34 SL |
626 | pub fn get_field_ref_data(&self, |
627 | field_ref: &ast::Field, | |
e9174d1e | 628 | variant: ty::VariantDef, |
62682a34 SL |
629 | parent: NodeId) |
630 | -> VariableRefData { | |
e9174d1e SL |
631 | let f = variant.field_named(field_ref.ident.node.name); |
632 | // We don't really need a sub-span here, but no harm done | |
633 | let sub_span = self.span_utils.span_for_last_ident(field_ref.ident.span); | |
634 | VariableRefData { | |
635 | name: field_ref.ident.node.to_string(), | |
636 | span: sub_span.unwrap(), | |
637 | scope: parent, | |
638 | ref_id: f.did, | |
62682a34 | 639 | } |
62682a34 SL |
640 | } |
641 | ||
d9579d0f AL |
642 | pub fn get_data_for_id(&self, _id: &NodeId) -> Data { |
643 | // FIXME | |
644 | unimplemented!(); | |
1a4d82fc | 645 | } |
62682a34 SL |
646 | |
647 | fn lookup_ref_id(&self, ref_id: NodeId) -> Option<DefId> { | |
648 | if !self.tcx.def_map.borrow().contains_key(&ref_id) { | |
649 | self.tcx.sess.bug(&format!("def_map has no key for {} in lookup_type_ref", | |
650 | ref_id)); | |
651 | } | |
652 | let def = self.tcx.def_map.borrow().get(&ref_id).unwrap().full_def(); | |
653 | match def { | |
b039eaaf | 654 | def::DefPrimTy(_) | def::DefSelfTy(..) => None, |
62682a34 SL |
655 | _ => Some(def.def_id()), |
656 | } | |
657 | } | |
658 | ||
c1a9b12d | 659 | #[inline] |
e9174d1e | 660 | pub fn enclosing_scope(&self, id: NodeId) -> NodeId { |
c1a9b12d SL |
661 | self.tcx.map.get_enclosing_scope(id).unwrap_or(0) |
662 | } | |
d9579d0f | 663 | } |
1a4d82fc | 664 | |
d9579d0f AL |
665 | // An AST visitor for collecting paths from patterns. |
666 | struct PathCollector { | |
667 | // The Row field identifies the kind of pattern. | |
668 | collected_paths: Vec<(NodeId, ast::Path, ast::Mutability, recorder::Row)>, | |
669 | } | |
1a4d82fc | 670 | |
d9579d0f AL |
671 | impl PathCollector { |
672 | fn new() -> PathCollector { | |
e9174d1e | 673 | PathCollector { collected_paths: vec![] } |
1a4d82fc | 674 | } |
d9579d0f | 675 | } |
1a4d82fc | 676 | |
d9579d0f AL |
677 | impl<'v> Visitor<'v> for PathCollector { |
678 | fn visit_pat(&mut self, p: &ast::Pat) { | |
1a4d82fc | 679 | if generated_code(p.span) { |
d9579d0f | 680 | return; |
1a4d82fc JJ |
681 | } |
682 | ||
683 | match p.node { | |
d9579d0f | 684 | ast::PatStruct(ref path, _, _) => { |
b039eaaf | 685 | self.collected_paths.push((p.id, path.clone(), ast::MutMutable, recorder::TypeRef)); |
1a4d82fc | 686 | } |
d9579d0f AL |
687 | ast::PatEnum(ref path, _) | |
688 | ast::PatQPath(_, ref path) => { | |
689 | self.collected_paths.push((p.id, path.clone(), ast::MutMutable, recorder::VarRef)); | |
1a4d82fc | 690 | } |
d9579d0f AL |
691 | ast::PatIdent(bm, ref path1, _) => { |
692 | debug!("PathCollector, visit ident in pat {}: {:?} {:?}", | |
c1a9b12d | 693 | path1.node, |
d9579d0f AL |
694 | p.span, |
695 | path1.span); | |
1a4d82fc JJ |
696 | let immut = match bm { |
697 | // Even if the ref is mut, you can't change the ref, only | |
698 | // the data pointed at, so showing the initialising expression | |
699 | // is still worthwhile. | |
d9579d0f AL |
700 | ast::BindByRef(_) => ast::MutImmutable, |
701 | ast::BindByValue(mt) => mt, | |
1a4d82fc JJ |
702 | }; |
703 | // collect path for either visit_local or visit_arm | |
d9579d0f | 704 | let path = ast_util::ident_to_path(path1.span, path1.node); |
1a4d82fc | 705 | self.collected_paths.push((p.id, path, immut, recorder::VarRef)); |
1a4d82fc | 706 | } |
d9579d0f | 707 | _ => {} |
1a4d82fc | 708 | } |
d9579d0f | 709 | visit::walk_pat(self, p); |
1a4d82fc JJ |
710 | } |
711 | } | |
712 | ||
b039eaaf SL |
713 | pub fn process_crate<'l, 'tcx>(tcx: &'l ty::ctxt<'tcx>, |
714 | lcx: &'l lowering::LoweringContext<'l>, | |
715 | krate: &ast::Crate, | |
716 | analysis: &ty::CrateAnalysis, | |
717 | cratename: &str, | |
718 | odir: Option<&Path>) { | |
1a4d82fc JJ |
719 | if generated_code(krate.span) { |
720 | return; | |
721 | } | |
722 | ||
723 | assert!(analysis.glob_map.is_some()); | |
1a4d82fc JJ |
724 | |
725 | info!("Dumping crate {}", cratename); | |
726 | ||
727 | // find a path to dump our data to | |
c34b1796 AL |
728 | let mut root_path = match env::var_os("DXR_RUST_TEMP_FOLDER") { |
729 | Some(val) => PathBuf::from(val), | |
730 | None => match odir { | |
85aaf69f | 731 | Some(val) => val.join("dxr"), |
c34b1796 | 732 | None => PathBuf::from("dxr-temp"), |
1a4d82fc JJ |
733 | }, |
734 | }; | |
735 | ||
62682a34 SL |
736 | if let Err(e) = fs::create_dir_all(&root_path) { |
737 | tcx.sess.err(&format!("Could not create directory {}: {}", | |
b039eaaf SL |
738 | root_path.display(), |
739 | e)); | |
1a4d82fc JJ |
740 | } |
741 | ||
742 | { | |
743 | let disp = root_path.display(); | |
744 | info!("Writing output to {}", disp); | |
745 | } | |
746 | ||
747 | // Create output file. | |
92a42be0 SL |
748 | let executable = tcx.sess.crate_types.borrow().iter().any(|ct| *ct == CrateTypeExecutable); |
749 | let mut out_name = if executable { | |
750 | "".to_owned() | |
751 | } else { | |
752 | "lib".to_owned() | |
753 | }; | |
754 | out_name.push_str(&cratename); | |
755 | out_name.push_str(&tcx.sess.opts.cg.extra_filename); | |
1a4d82fc | 756 | out_name.push_str(".csv"); |
c34b1796 | 757 | root_path.push(&out_name); |
1a4d82fc JJ |
758 | let output_file = match File::create(&root_path) { |
759 | Ok(f) => box f, | |
760 | Err(e) => { | |
761 | let disp = root_path.display(); | |
62682a34 | 762 | tcx.sess.fatal(&format!("Could not open {}: {}", disp, e)); |
1a4d82fc JJ |
763 | } |
764 | }; | |
765 | root_path.pop(); | |
766 | ||
b039eaaf | 767 | let mut visitor = dump_csv::DumpCsvVisitor::new(tcx, lcx, analysis, output_file); |
1a4d82fc | 768 | |
b039eaaf | 769 | visitor.dump_crate_info(cratename, krate); |
1a4d82fc JJ |
770 | visit::walk_crate(&mut visitor, krate); |
771 | } | |
d9579d0f AL |
772 | |
773 | // Utility functions for the module. | |
774 | ||
775 | // Helper function to escape quotes in a string | |
776 | fn escape(s: String) -> String { | |
777 | s.replace("\"", "\"\"") | |
778 | } | |
779 | ||
780 | // If the expression is a macro expansion or other generated code, run screaming | |
781 | // and don't index. | |
c1a9b12d | 782 | pub fn generated_code(span: Span) -> bool { |
e9174d1e | 783 | span.expn_id != NO_EXPANSION || span == DUMMY_SP |
d9579d0f | 784 | } |