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