]>
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 SL |
11 | #![crate_name = "rustc_save_analysis"] |
12 | #![unstable(feature = "rustc_private", issue = "27812")] | |
13 | #![crate_type = "dylib"] | |
14 | #![crate_type = "rlib"] | |
15 | #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", | |
16 | html_favicon_url = "https://doc.rust-lang.org/favicon.ico", | |
17 | html_root_url = "https://doc.rust-lang.org/nightly/")] | |
32a655c1 | 18 | #![deny(warnings)] |
54a0048b SL |
19 | |
20 | #![feature(custom_attribute)] | |
21 | #![allow(unused_attributes)] | |
22 | #![feature(rustc_private)] | |
23 | #![feature(staged_api)] | |
24 | ||
25 | #[macro_use] extern crate rustc; | |
26 | ||
27 | #[macro_use] extern crate log; | |
28 | #[macro_use] extern crate syntax; | |
a7813a04 | 29 | extern crate serialize as rustc_serialize; |
3157f602 | 30 | extern crate syntax_pos; |
54a0048b | 31 | |
9e0c209e | 32 | |
a7813a04 | 33 | mod csv_dumper; |
9e0c209e | 34 | mod json_api_dumper; |
a7813a04 XL |
35 | mod json_dumper; |
36 | mod data; | |
37 | mod dump; | |
38 | mod dump_visitor; | |
39 | pub mod external_data; | |
40 | #[macro_use] | |
41 | pub mod span_utils; | |
42 | ||
43 | use rustc::hir; | |
54a0048b | 44 | use rustc::hir::def::Def; |
476ff2be | 45 | use rustc::hir::map::Node; |
54a0048b SL |
46 | use rustc::hir::def_id::DefId; |
47 | use rustc::session::config::CrateType::CrateTypeExecutable; | |
48 | use rustc::ty::{self, TyCtxt}; | |
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 SL |
56 | use syntax::parse::token; |
57 | use syntax::symbol::{Symbol, keywords}; | |
1a4d82fc | 58 | use syntax::visit::{self, Visitor}; |
a7813a04 | 59 | use syntax::print::pprust::{ty_to_string, arg_to_string}; |
3157f602 XL |
60 | use syntax::codemap::MacroAttribute; |
61 | use syntax_pos::*; | |
62682a34 | 62 | |
54a0048b | 63 | pub use self::csv_dumper::CsvDumper; |
9e0c209e | 64 | pub use self::json_api_dumper::JsonApiDumper; |
a7813a04 | 65 | pub use self::json_dumper::JsonDumper; |
54a0048b | 66 | pub use self::data::*; |
9e0c209e | 67 | pub use self::external_data::make_def_id; |
54a0048b SL |
68 | pub use self::dump::Dump; |
69 | pub use self::dump_visitor::DumpVisitor; | |
70 | use self::span_utils::SpanUtils; | |
c1a9b12d | 71 | |
54a0048b SL |
72 | // FIXME this is legacy code and should be removed |
73 | pub mod recorder { | |
74 | pub use self::Row::*; | |
c1a9b12d | 75 | |
54a0048b SL |
76 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] |
77 | pub enum Row { | |
78 | TypeRef, | |
79 | ModRef, | |
80 | VarRef, | |
81 | FnRef, | |
82 | } | |
c1a9b12d SL |
83 | } |
84 | ||
54a0048b | 85 | pub struct SaveContext<'l, 'tcx: 'l> { |
a7813a04 | 86 | tcx: TyCtxt<'l, 'tcx, 'tcx>, |
32a655c1 | 87 | tables: &'l ty::TypeckTables<'tcx>, |
8bb4bdeb | 88 | analysis: &'l ty::CrateAnalysis, |
a7813a04 | 89 | span_utils: SpanUtils<'tcx>, |
7453a54e SL |
90 | } |
91 | ||
92 | macro_rules! option_try( | |
93 | ($e:expr) => (match $e { Some(e) => e, None => return None }) | |
94 | ); | |
95 | ||
d9579d0f | 96 | impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { |
d9579d0f AL |
97 | // List external crates used by the current crate. |
98 | pub fn get_external_crates(&self) -> Vec<CrateData> { | |
99 | let mut result = Vec::new(); | |
1a4d82fc | 100 | |
92a42be0 | 101 | for n in self.tcx.sess.cstore.crates() { |
a7813a04 XL |
102 | let span = match self.tcx.sess.cstore.extern_crate(n) { |
103 | Some(ref c) => c.span, | |
104 | None => { | |
105 | debug!("Skipping crate {}, no data", n); | |
106 | continue; | |
107 | } | |
108 | }; | |
b039eaaf | 109 | result.push(CrateData { |
476ff2be | 110 | name: self.tcx.sess.cstore.crate_name(n).to_string(), |
9e0c209e | 111 | number: n.as_u32(), |
a7813a04 | 112 | span: span, |
b039eaaf | 113 | }); |
92a42be0 | 114 | } |
1a4d82fc JJ |
115 | |
116 | result | |
117 | } | |
118 | ||
7453a54e | 119 | pub fn get_item_data(&self, item: &ast::Item) -> Option<Data> { |
d9579d0f | 120 | match item.node { |
9e0c209e | 121 | ast::ItemKind::Fn(ref decl, .., ref generics, _) => { |
3157f602 | 122 | let qualname = format!("::{}", self.tcx.node_path_str(item.id)); |
d9579d0f | 123 | let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Fn); |
7453a54e | 124 | filter!(self.span_utils, sub_span, item.span, None); |
a7813a04 XL |
125 | |
126 | ||
7453a54e | 127 | Some(Data::FunctionData(FunctionData { |
d9579d0f | 128 | id: item.id, |
3157f602 | 129 | name: item.ident.to_string(), |
d9579d0f AL |
130 | qualname: qualname, |
131 | declaration: None, | |
132 | span: sub_span.unwrap(), | |
c1a9b12d | 133 | scope: self.enclosing_scope(item.id), |
a7813a04 | 134 | value: make_signature(decl, generics), |
9e0c209e SL |
135 | visibility: From::from(&item.vis), |
136 | parent: None, | |
137 | docs: docs_for_attrs(&item.attrs), | |
32a655c1 | 138 | sig: self.sig_base(item), |
8bb4bdeb | 139 | attributes: item.attrs.clone(), |
7453a54e | 140 | })) |
1a4d82fc | 141 | } |
7453a54e | 142 | ast::ItemKind::Static(ref typ, mt, ref expr) => { |
54a0048b | 143 | let qualname = format!("::{}", self.tcx.node_path_str(item.id)); |
1a4d82fc | 144 | |
d9579d0f AL |
145 | // If the variable is immutable, save the initialising expression. |
146 | let (value, keyword) = match mt { | |
7453a54e SL |
147 | ast::Mutability::Mutable => (String::from("<mutable>"), keywords::Mut), |
148 | ast::Mutability::Immutable => { | |
149 | (self.span_utils.snippet(expr.span), keywords::Static) | |
150 | }, | |
d9579d0f | 151 | }; |
1a4d82fc | 152 | |
d9579d0f | 153 | let sub_span = self.span_utils.sub_span_after_keyword(item.span, keyword); |
7453a54e SL |
154 | filter!(self.span_utils, sub_span, item.span, None); |
155 | Some(Data::VariableData(VariableData { | |
d9579d0f | 156 | id: item.id, |
3157f602 | 157 | kind: VariableKind::Static, |
c1a9b12d | 158 | name: item.ident.to_string(), |
d9579d0f AL |
159 | qualname: qualname, |
160 | span: sub_span.unwrap(), | |
c1a9b12d | 161 | scope: self.enclosing_scope(item.id), |
9e0c209e | 162 | parent: None, |
d9579d0f AL |
163 | value: value, |
164 | type_value: ty_to_string(&typ), | |
9e0c209e SL |
165 | visibility: From::from(&item.vis), |
166 | docs: docs_for_attrs(&item.attrs), | |
32a655c1 | 167 | sig: Some(self.sig_base(item)), |
8bb4bdeb | 168 | attributes: item.attrs.clone(), |
7453a54e | 169 | })) |
1a4d82fc | 170 | } |
7453a54e | 171 | ast::ItemKind::Const(ref typ, ref expr) => { |
54a0048b | 172 | let qualname = format!("::{}", self.tcx.node_path_str(item.id)); |
d9579d0f | 173 | let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Const); |
7453a54e SL |
174 | filter!(self.span_utils, sub_span, item.span, None); |
175 | Some(Data::VariableData(VariableData { | |
d9579d0f | 176 | id: item.id, |
3157f602 | 177 | kind: VariableKind::Const, |
c1a9b12d | 178 | name: item.ident.to_string(), |
d9579d0f AL |
179 | qualname: qualname, |
180 | span: sub_span.unwrap(), | |
c1a9b12d | 181 | scope: self.enclosing_scope(item.id), |
9e0c209e | 182 | parent: None, |
d9579d0f AL |
183 | value: self.span_utils.snippet(expr.span), |
184 | type_value: ty_to_string(&typ), | |
9e0c209e SL |
185 | visibility: From::from(&item.vis), |
186 | docs: docs_for_attrs(&item.attrs), | |
32a655c1 | 187 | sig: Some(self.sig_base(item)), |
8bb4bdeb | 188 | attributes: item.attrs.clone(), |
7453a54e | 189 | })) |
85aaf69f | 190 | } |
7453a54e | 191 | ast::ItemKind::Mod(ref m) => { |
54a0048b | 192 | let qualname = format!("::{}", self.tcx.node_path_str(item.id)); |
62682a34 SL |
193 | |
194 | let cm = self.tcx.sess.codemap(); | |
195 | let filename = cm.span_to_filename(m.inner); | |
196 | ||
197 | let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Mod); | |
7453a54e | 198 | filter!(self.span_utils, sub_span, item.span, None); |
32a655c1 | 199 | |
7453a54e | 200 | Some(Data::ModData(ModData { |
62682a34 | 201 | id: item.id, |
c1a9b12d | 202 | name: item.ident.to_string(), |
62682a34 SL |
203 | qualname: qualname, |
204 | span: sub_span.unwrap(), | |
c1a9b12d | 205 | scope: self.enclosing_scope(item.id), |
62682a34 | 206 | filename: filename, |
3157f602 | 207 | items: m.items.iter().map(|i| i.id).collect(), |
9e0c209e SL |
208 | visibility: From::from(&item.vis), |
209 | docs: docs_for_attrs(&item.attrs), | |
32a655c1 | 210 | sig: self.sig_base(item), |
8bb4bdeb | 211 | attributes: item.attrs.clone(), |
7453a54e | 212 | })) |
e9174d1e | 213 | } |
a7813a04 XL |
214 | ast::ItemKind::Enum(ref def, _) => { |
215 | let name = item.ident.to_string(); | |
216 | let qualname = format!("::{}", self.tcx.node_path_str(item.id)); | |
62682a34 | 217 | let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Enum); |
7453a54e | 218 | filter!(self.span_utils, sub_span, item.span, None); |
a7813a04 XL |
219 | let variants_str = def.variants.iter() |
220 | .map(|v| v.node.name.to_string()) | |
221 | .collect::<Vec<_>>() | |
222 | .join(", "); | |
223 | let val = format!("{}::{{{}}}", name, variants_str); | |
7453a54e | 224 | Some(Data::EnumData(EnumData { |
62682a34 | 225 | id: item.id, |
a7813a04 | 226 | name: name, |
62682a34 SL |
227 | value: val, |
228 | span: sub_span.unwrap(), | |
a7813a04 | 229 | qualname: qualname, |
c1a9b12d | 230 | scope: self.enclosing_scope(item.id), |
3157f602 | 231 | variants: def.variants.iter().map(|v| v.node.data.id()).collect(), |
9e0c209e SL |
232 | visibility: From::from(&item.vis), |
233 | docs: docs_for_attrs(&item.attrs), | |
32a655c1 | 234 | sig: self.sig_base(item), |
8bb4bdeb | 235 | attributes: item.attrs.clone(), |
7453a54e | 236 | })) |
e9174d1e | 237 | } |
9e0c209e | 238 | ast::ItemKind::Impl(.., ref trait_ref, ref typ, _) => { |
62682a34 SL |
239 | let mut type_data = None; |
240 | let sub_span; | |
241 | ||
c1a9b12d | 242 | let parent = self.enclosing_scope(item.id); |
62682a34 SL |
243 | |
244 | match typ.node { | |
245 | // Common case impl for a struct or something basic. | |
7453a54e | 246 | ast::TyKind::Path(None, ref path) => { |
8bb4bdeb XL |
247 | if generated_code(path.span) { |
248 | return None; | |
249 | } | |
7453a54e | 250 | sub_span = self.span_utils.sub_span_for_type_name(path.span); |
b039eaaf SL |
251 | type_data = self.lookup_ref_id(typ.id).map(|id| { |
252 | TypeRefData { | |
7453a54e | 253 | span: sub_span.unwrap(), |
b039eaaf | 254 | scope: parent, |
54a0048b SL |
255 | ref_id: Some(id), |
256 | qualname: String::new() // FIXME: generate the real qualname | |
b039eaaf | 257 | } |
62682a34 | 258 | }); |
e9174d1e | 259 | } |
62682a34 SL |
260 | _ => { |
261 | // Less useful case, impl for a compound type. | |
262 | let span = typ.span; | |
7453a54e | 263 | sub_span = self.span_utils.sub_span_for_type_name(span).or(Some(span)); |
62682a34 SL |
264 | } |
265 | } | |
266 | ||
b039eaaf SL |
267 | let trait_data = trait_ref.as_ref() |
268 | .and_then(|tr| self.get_trait_ref_data(tr, parent)); | |
62682a34 | 269 | |
7453a54e | 270 | filter!(self.span_utils, sub_span, typ.span, None); |
54a0048b | 271 | Some(Data::ImplData(ImplData2 { |
62682a34 | 272 | id: item.id, |
7453a54e | 273 | span: sub_span.unwrap(), |
62682a34 SL |
274 | scope: parent, |
275 | trait_ref: trait_data, | |
276 | self_ref: type_data, | |
7453a54e | 277 | })) |
62682a34 | 278 | } |
d9579d0f AL |
279 | _ => { |
280 | // FIXME | |
54a0048b | 281 | bug!(); |
1a4d82fc JJ |
282 | } |
283 | } | |
1a4d82fc JJ |
284 | } |
285 | ||
32a655c1 SL |
286 | pub fn get_field_data(&self, |
287 | field: &ast::StructField, | |
288 | scope: NodeId) | |
289 | -> Option<VariableData> { | |
54a0048b | 290 | if let Some(ident) = field.ident { |
32a655c1 | 291 | let name = ident.to_string(); |
54a0048b | 292 | let qualname = format!("::{}::{}", self.tcx.node_path_str(scope), ident); |
54a0048b SL |
293 | let sub_span = self.span_utils.sub_span_before_token(field.span, token::Colon); |
294 | filter!(self.span_utils, sub_span, field.span, None); | |
32a655c1 SL |
295 | let def_id = self.tcx.hir.local_def_id(field.id); |
296 | let typ = self.tcx.item_type(def_id).to_string(); | |
297 | ||
298 | let span = field.span; | |
299 | let text = self.span_utils.snippet(field.span); | |
300 | let ident_start = text.find(&name).unwrap(); | |
301 | let ident_end = ident_start + name.len(); | |
302 | let sig = Signature { | |
303 | span: span, | |
304 | text: text, | |
305 | ident_start: ident_start, | |
306 | ident_end: ident_end, | |
307 | defs: vec![], | |
308 | refs: vec![], | |
309 | }; | |
54a0048b SL |
310 | Some(VariableData { |
311 | id: field.id, | |
3157f602 | 312 | kind: VariableKind::Field, |
32a655c1 | 313 | name: name, |
54a0048b SL |
314 | qualname: qualname, |
315 | span: sub_span.unwrap(), | |
316 | scope: scope, | |
32a655c1 | 317 | parent: Some(make_def_id(scope, &self.tcx.hir)), |
54a0048b SL |
318 | value: "".to_owned(), |
319 | type_value: typ, | |
9e0c209e SL |
320 | visibility: From::from(&field.vis), |
321 | docs: docs_for_attrs(&field.attrs), | |
32a655c1 | 322 | sig: Some(sig), |
8bb4bdeb | 323 | attributes: field.attrs.clone(), |
54a0048b SL |
324 | }) |
325 | } else { | |
326 | None | |
62682a34 SL |
327 | } |
328 | } | |
329 | ||
c1a9b12d SL |
330 | // FIXME would be nice to take a MethodItem here, but the ast provides both |
331 | // trait and impl flavours, so the caller must do the disassembly. | |
7453a54e SL |
332 | pub fn get_method_data(&self, id: ast::NodeId, |
333 | name: ast::Name, span: Span) -> Option<FunctionData> { | |
c1a9b12d SL |
334 | // The qualname for a method is the trait name or name of the struct in an impl in |
335 | // which the method is declared in, followed by the method's name. | |
8bb4bdeb | 336 | let (qualname, parent_scope, decl_id, vis, docs, attributes) = |
32a655c1 SL |
337 | match self.tcx.impl_of_method(self.tcx.hir.local_def_id(id)) { |
338 | Some(impl_id) => match self.tcx.hir.get_if_local(impl_id) { | |
476ff2be | 339 | Some(Node::NodeItem(item)) => { |
c1a9b12d | 340 | match item.node { |
9e0c209e | 341 | hir::ItemImpl(.., ref ty, _) => { |
c1a9b12d | 342 | let mut result = String::from("<"); |
32a655c1 | 343 | result.push_str(&self.tcx.hir.node_to_pretty_string(ty.id)); |
c1a9b12d | 344 | |
9e0c209e | 345 | let trait_id = self.tcx.trait_id_of_impl(impl_id); |
476ff2be | 346 | let mut decl_id = None; |
9e0c209e | 347 | if let Some(def_id) = trait_id { |
3157f602 XL |
348 | result.push_str(" as "); |
349 | result.push_str(&self.tcx.item_path_str(def_id)); | |
476ff2be SL |
350 | self.tcx.associated_items(def_id) |
351 | .find(|item| item.name == name) | |
352 | .map(|item| decl_id = Some(item.def_id)); | |
c1a9b12d SL |
353 | } |
354 | result.push_str(">"); | |
476ff2be SL |
355 | |
356 | (result, trait_id, decl_id, | |
357 | From::from(&item.vis), | |
8bb4bdeb XL |
358 | docs_for_attrs(&item.attrs), |
359 | item.attrs.to_vec()) | |
c1a9b12d SL |
360 | } |
361 | _ => { | |
54a0048b SL |
362 | span_bug!(span, |
363 | "Container {:?} for method {} not an impl?", | |
364 | impl_id, | |
365 | id); | |
e9174d1e | 366 | } |
c1a9b12d | 367 | } |
e9174d1e | 368 | } |
b039eaaf | 369 | r => { |
54a0048b SL |
370 | span_bug!(span, |
371 | "Container {:?} for method {} is not a node item {:?}", | |
372 | impl_id, | |
373 | id, | |
374 | r); | |
e9174d1e | 375 | } |
c1a9b12d | 376 | }, |
32a655c1 | 377 | None => match self.tcx.trait_of_item(self.tcx.hir.local_def_id(id)) { |
c1a9b12d | 378 | Some(def_id) => { |
32a655c1 | 379 | match self.tcx.hir.get_if_local(def_id) { |
476ff2be | 380 | Some(Node::NodeItem(item)) => { |
9e0c209e | 381 | (format!("::{}", self.tcx.item_path_str(def_id)), |
476ff2be | 382 | Some(def_id), None, |
9e0c209e | 383 | From::from(&item.vis), |
8bb4bdeb XL |
384 | docs_for_attrs(&item.attrs), |
385 | item.attrs.to_vec()) | |
c1a9b12d | 386 | } |
b039eaaf | 387 | r => { |
54a0048b SL |
388 | span_bug!(span, |
389 | "Could not find container {:?} for \ | |
390 | method {}, got {:?}", | |
391 | def_id, | |
392 | id, | |
393 | r); | |
c1a9b12d SL |
394 | } |
395 | } | |
e9174d1e | 396 | } |
c1a9b12d | 397 | None => { |
8bb4bdeb XL |
398 | debug!("Could not find container for method {} at {:?}", id, span); |
399 | // This is not necessarily a bug, if there was a compilation error, the tables | |
400 | // we need might not exist. | |
401 | return None; | |
e9174d1e | 402 | } |
c1a9b12d SL |
403 | }, |
404 | }; | |
405 | ||
406 | let qualname = format!("{}::{}", qualname, name); | |
407 | ||
c1a9b12d | 408 | let sub_span = self.span_utils.sub_span_after_keyword(span, keywords::Fn); |
7453a54e | 409 | filter!(self.span_utils, sub_span, span, None); |
32a655c1 SL |
410 | |
411 | let name = name.to_string(); | |
412 | let text = self.span_utils.signature_string_for_span(span); | |
413 | let ident_start = text.find(&name).unwrap(); | |
414 | let ident_end = ident_start + name.len(); | |
415 | let sig = Signature { | |
416 | span: span, | |
417 | text: text, | |
418 | ident_start: ident_start, | |
419 | ident_end: ident_end, | |
420 | defs: vec![], | |
421 | refs: vec![], | |
422 | }; | |
423 | ||
7453a54e | 424 | Some(FunctionData { |
c1a9b12d | 425 | id: id, |
32a655c1 | 426 | name: name, |
c1a9b12d SL |
427 | qualname: qualname, |
428 | declaration: decl_id, | |
429 | span: sub_span.unwrap(), | |
430 | scope: self.enclosing_scope(id), | |
a7813a04 XL |
431 | // FIXME you get better data here by using the visitor. |
432 | value: String::new(), | |
9e0c209e SL |
433 | visibility: vis, |
434 | parent: parent_scope, | |
435 | docs: docs, | |
32a655c1 | 436 | sig: sig, |
8bb4bdeb | 437 | attributes: attributes, |
7453a54e | 438 | }) |
c1a9b12d SL |
439 | } |
440 | ||
62682a34 SL |
441 | pub fn get_trait_ref_data(&self, |
442 | trait_ref: &ast::TraitRef, | |
443 | parent: NodeId) | |
444 | -> Option<TypeRefData> { | |
7453a54e | 445 | self.lookup_ref_id(trait_ref.ref_id).and_then(|def_id| { |
62682a34 | 446 | let span = trait_ref.path.span; |
8bb4bdeb XL |
447 | if generated_code(span) { |
448 | return None; | |
449 | } | |
7453a54e SL |
450 | let sub_span = self.span_utils.sub_span_for_type_name(span).or(Some(span)); |
451 | filter!(self.span_utils, sub_span, span, None); | |
452 | Some(TypeRefData { | |
453 | span: sub_span.unwrap(), | |
62682a34 | 454 | scope: parent, |
54a0048b SL |
455 | ref_id: Some(def_id), |
456 | qualname: String::new() // FIXME: generate the real qualname | |
7453a54e | 457 | }) |
62682a34 SL |
458 | }) |
459 | } | |
460 | ||
461 | pub fn get_expr_data(&self, expr: &ast::Expr) -> Option<Data> { | |
32a655c1 SL |
462 | let hir_node = self.tcx.hir.expect_expr(expr.id); |
463 | let ty = self.tables.expr_ty_adjusted_opt(&hir_node); | |
7453a54e SL |
464 | if ty.is_none() || ty.unwrap().sty == ty::TyError { |
465 | return None; | |
466 | } | |
62682a34 | 467 | match expr.node { |
7453a54e | 468 | ast::ExprKind::Field(ref sub_ex, ident) => { |
32a655c1 | 469 | let hir_node = match self.tcx.hir.find(sub_ex.id) { |
3157f602 XL |
470 | Some(Node::NodeExpr(expr)) => expr, |
471 | _ => { | |
472 | debug!("Missing or weird node for sub-expression {} in {:?}", | |
473 | sub_ex.id, expr); | |
474 | return None; | |
475 | } | |
476 | }; | |
32a655c1 | 477 | match self.tables.expr_ty_adjusted(&hir_node).sty { |
9e0c209e | 478 | ty::TyAdt(def, _) if !def.is_enum() => { |
e9174d1e SL |
479 | let f = def.struct_variant().field_named(ident.node.name); |
480 | let sub_span = self.span_utils.span_for_last_ident(expr.span); | |
7453a54e | 481 | filter!(self.span_utils, sub_span, expr.span, None); |
e9174d1e SL |
482 | return Some(Data::VariableRefData(VariableRefData { |
483 | name: ident.node.to_string(), | |
484 | span: sub_span.unwrap(), | |
485 | scope: self.enclosing_scope(expr.id), | |
486 | ref_id: f.did, | |
487 | })); | |
62682a34 SL |
488 | } |
489 | _ => { | |
9e0c209e | 490 | debug!("Expected struct or union type, found {:?}", ty); |
62682a34 SL |
491 | None |
492 | } | |
493 | } | |
494 | } | |
9e0c209e | 495 | ast::ExprKind::Struct(ref path, ..) => { |
32a655c1 | 496 | match self.tables.expr_ty_adjusted(&hir_node).sty { |
9e0c209e | 497 | ty::TyAdt(def, _) if !def.is_enum() => { |
62682a34 | 498 | let sub_span = self.span_utils.span_for_last_ident(path.span); |
7453a54e | 499 | filter!(self.span_utils, sub_span, path.span, None); |
62682a34 SL |
500 | Some(Data::TypeRefData(TypeRefData { |
501 | span: sub_span.unwrap(), | |
c1a9b12d | 502 | scope: self.enclosing_scope(expr.id), |
54a0048b SL |
503 | ref_id: Some(def.did), |
504 | qualname: String::new() // FIXME: generate the real qualname | |
62682a34 SL |
505 | })) |
506 | } | |
507 | _ => { | |
9e0c209e | 508 | // FIXME ty could legitimately be an enum, but then we will fail |
62682a34 | 509 | // later if we try to look up the fields. |
9e0c209e | 510 | debug!("expected struct or union, found {:?}", ty); |
62682a34 SL |
511 | None |
512 | } | |
513 | } | |
514 | } | |
7453a54e | 515 | ast::ExprKind::MethodCall(..) => { |
c1a9b12d | 516 | let method_call = ty::MethodCall::expr(expr.id); |
32a655c1 | 517 | let method_id = self.tables.method_map[&method_call].def_id; |
476ff2be | 518 | let (def_id, decl_id) = match self.tcx.associated_item(method_id).container { |
c1a9b12d | 519 | ty::ImplContainer(_) => (Some(method_id), None), |
e9174d1e | 520 | ty::TraitContainer(_) => (None, Some(method_id)), |
c1a9b12d SL |
521 | }; |
522 | let sub_span = self.span_utils.sub_span_for_meth_name(expr.span); | |
7453a54e | 523 | filter!(self.span_utils, sub_span, expr.span, None); |
c1a9b12d SL |
524 | let parent = self.enclosing_scope(expr.id); |
525 | Some(Data::MethodCallData(MethodCallData { | |
526 | span: sub_span.unwrap(), | |
527 | scope: parent, | |
528 | ref_id: def_id, | |
529 | decl_id: decl_id, | |
530 | })) | |
531 | } | |
7453a54e | 532 | ast::ExprKind::Path(_, ref path) => { |
c1a9b12d SL |
533 | self.get_path_data(expr.id, path) |
534 | } | |
62682a34 SL |
535 | _ => { |
536 | // FIXME | |
54a0048b | 537 | bug!(); |
62682a34 SL |
538 | } |
539 | } | |
540 | } | |
541 | ||
476ff2be | 542 | pub fn get_path_def(&self, id: NodeId) -> Def { |
32a655c1 | 543 | match self.tcx.hir.get(id) { |
476ff2be SL |
544 | Node::NodeTraitRef(tr) => tr.path.def, |
545 | ||
546 | Node::NodeItem(&hir::Item { node: hir::ItemUse(ref path, _), .. }) | | |
547 | Node::NodeVisibility(&hir::Visibility::Restricted { ref path, .. }) => path.def, | |
548 | ||
549 | Node::NodeExpr(&hir::Expr { node: hir::ExprPath(ref qpath), .. }) | | |
550 | Node::NodeExpr(&hir::Expr { node: hir::ExprStruct(ref qpath, ..), .. }) | | |
551 | Node::NodePat(&hir::Pat { node: hir::PatKind::Path(ref qpath), .. }) | | |
552 | Node::NodePat(&hir::Pat { node: hir::PatKind::Struct(ref qpath, ..), .. }) | | |
553 | Node::NodePat(&hir::Pat { node: hir::PatKind::TupleStruct(ref qpath, ..), .. }) => { | |
32a655c1 | 554 | self.tables.qpath_def(qpath, id) |
476ff2be SL |
555 | } |
556 | ||
557 | Node::NodeLocal(&hir::Pat { node: hir::PatKind::Binding(_, def_id, ..), .. }) => { | |
558 | Def::Local(def_id) | |
559 | } | |
560 | ||
561 | Node::NodeTy(&hir::Ty { node: hir::TyPath(ref qpath), .. }) => { | |
562 | match *qpath { | |
563 | hir::QPath::Resolved(_, ref path) => path.def, | |
564 | hir::QPath::TypeRelative(..) => { | |
8bb4bdeb | 565 | if let Some(ty) = self.tcx.ast_ty_to_ty_cache.borrow().get(&id) { |
476ff2be SL |
566 | if let ty::TyProjection(proj) = ty.sty { |
567 | for item in self.tcx.associated_items(proj.trait_ref.def_id) { | |
568 | if item.kind == ty::AssociatedKind::Type { | |
569 | if item.name == proj.item_name { | |
570 | return Def::AssociatedTy(item.def_id); | |
571 | } | |
572 | } | |
573 | } | |
574 | } | |
575 | } | |
576 | Def::Err | |
577 | } | |
578 | } | |
579 | } | |
580 | ||
581 | _ => Def::Err | |
582 | } | |
583 | } | |
584 | ||
e9174d1e | 585 | pub fn get_path_data(&self, id: NodeId, path: &ast::Path) -> Option<Data> { |
476ff2be | 586 | let def = self.get_path_def(id); |
c1a9b12d | 587 | let sub_span = self.span_utils.span_for_last_ident(path.span); |
7453a54e | 588 | filter!(self.span_utils, sub_span, path.span, None); |
c1a9b12d | 589 | match def { |
7453a54e SL |
590 | Def::Upvar(..) | |
591 | Def::Local(..) | | |
592 | Def::Static(..) | | |
593 | Def::Const(..) | | |
594 | Def::AssociatedConst(..) | | |
c30ab7b3 SL |
595 | Def::StructCtor(..) | |
596 | Def::VariantCtor(..) => { | |
c1a9b12d SL |
597 | Some(Data::VariableRefData(VariableRefData { |
598 | name: self.span_utils.snippet(sub_span.unwrap()), | |
599 | span: sub_span.unwrap(), | |
600 | scope: self.enclosing_scope(id), | |
601 | ref_id: def.def_id(), | |
602 | })) | |
603 | } | |
7453a54e | 604 | Def::Struct(def_id) | |
c30ab7b3 | 605 | Def::Variant(def_id, ..) | |
9e0c209e | 606 | Def::Union(def_id) | |
7453a54e SL |
607 | Def::Enum(def_id) | |
608 | Def::TyAlias(def_id) | | |
c30ab7b3 | 609 | Def::AssociatedTy(def_id) | |
7453a54e | 610 | Def::Trait(def_id) | |
9e0c209e | 611 | Def::TyParam(def_id) => { |
c1a9b12d SL |
612 | Some(Data::TypeRefData(TypeRefData { |
613 | span: sub_span.unwrap(), | |
54a0048b | 614 | ref_id: Some(def_id), |
c1a9b12d | 615 | scope: self.enclosing_scope(id), |
54a0048b | 616 | qualname: String::new() // FIXME: generate the real qualname |
c1a9b12d SL |
617 | })) |
618 | } | |
7453a54e | 619 | Def::Method(decl_id) => { |
c1a9b12d | 620 | let sub_span = self.span_utils.sub_span_for_meth_name(path.span); |
7453a54e | 621 | filter!(self.span_utils, sub_span, path.span, None); |
e9174d1e | 622 | let def_id = if decl_id.is_local() { |
476ff2be SL |
623 | let ti = self.tcx.associated_item(decl_id); |
624 | self.tcx.associated_items(ti.container.id()) | |
625 | .find(|item| item.name == ti.name && item.defaultness.has_value()) | |
626 | .map(|item| item.def_id) | |
c1a9b12d SL |
627 | } else { |
628 | None | |
629 | }; | |
630 | Some(Data::MethodCallData(MethodCallData { | |
631 | span: sub_span.unwrap(), | |
632 | scope: self.enclosing_scope(id), | |
633 | ref_id: def_id, | |
634 | decl_id: Some(decl_id), | |
635 | })) | |
e9174d1e | 636 | } |
7453a54e | 637 | Def::Fn(def_id) => { |
c1a9b12d SL |
638 | Some(Data::FunctionCallData(FunctionCallData { |
639 | ref_id: def_id, | |
640 | span: sub_span.unwrap(), | |
641 | scope: self.enclosing_scope(id), | |
642 | })) | |
643 | } | |
7453a54e | 644 | Def::Mod(def_id) => { |
c1a9b12d | 645 | Some(Data::ModRefData(ModRefData { |
54a0048b | 646 | ref_id: Some(def_id), |
c1a9b12d SL |
647 | span: sub_span.unwrap(), |
648 | scope: self.enclosing_scope(id), | |
54a0048b | 649 | qualname: String::new() // FIXME: generate the real qualname |
c1a9b12d SL |
650 | })) |
651 | } | |
c30ab7b3 SL |
652 | Def::PrimTy(..) | |
653 | Def::SelfTy(..) | | |
654 | Def::Label(..) | | |
476ff2be | 655 | Def::Macro(..) | |
c30ab7b3 | 656 | Def::Err => None, |
c1a9b12d SL |
657 | } |
658 | } | |
659 | ||
62682a34 SL |
660 | pub fn get_field_ref_data(&self, |
661 | field_ref: &ast::Field, | |
476ff2be | 662 | variant: &ty::VariantDef, |
62682a34 | 663 | parent: NodeId) |
7453a54e | 664 | -> Option<VariableRefData> { |
e9174d1e SL |
665 | let f = variant.field_named(field_ref.ident.node.name); |
666 | // We don't really need a sub-span here, but no harm done | |
667 | let sub_span = self.span_utils.span_for_last_ident(field_ref.ident.span); | |
7453a54e SL |
668 | filter!(self.span_utils, sub_span, field_ref.ident.span, None); |
669 | Some(VariableRefData { | |
e9174d1e SL |
670 | name: field_ref.ident.node.to_string(), |
671 | span: sub_span.unwrap(), | |
672 | scope: parent, | |
673 | ref_id: f.did, | |
7453a54e SL |
674 | }) |
675 | } | |
676 | ||
677 | /// Attempt to return MacroUseData for any AST node. | |
678 | /// | |
679 | /// For a given piece of AST defined by the supplied Span and NodeId, | |
680 | /// returns None if the node is not macro-generated or the span is malformed, | |
681 | /// else uses the expansion callsite and callee to return some MacroUseData. | |
682 | pub fn get_macro_use_data(&self, span: Span, id: NodeId) -> Option<MacroUseData> { | |
683 | if !generated_code(span) { | |
684 | return None; | |
685 | } | |
686 | // Note we take care to use the source callsite/callee, to handle | |
687 | // nested expansions and ensure we only generate data for source-visible | |
688 | // macro uses. | |
689 | let callsite = self.tcx.sess.codemap().source_callsite(span); | |
690 | let callee = self.tcx.sess.codemap().source_callee(span); | |
691 | let callee = option_try!(callee); | |
692 | let callee_span = option_try!(callee.span); | |
693 | ||
694 | // Ignore attribute macros, their spans are usually mangled | |
695 | if let MacroAttribute(_) = callee.format { | |
696 | return None; | |
697 | } | |
698 | ||
699 | // If the callee is an imported macro from an external crate, need to get | |
700 | // the source span and name from the session, as their spans are localized | |
701 | // when read in, and no longer correspond to the source. | |
702 | if let Some(mac) = self.tcx.sess.imported_macro_spans.borrow().get(&callee_span) { | |
703 | let &(ref mac_name, mac_span) = mac; | |
704 | return Some(MacroUseData { | |
705 | span: callsite, | |
706 | name: mac_name.clone(), | |
707 | callee_span: mac_span, | |
708 | scope: self.enclosing_scope(id), | |
709 | imported: true, | |
54a0048b | 710 | qualname: String::new()// FIXME: generate the real qualname |
7453a54e | 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, | |
54a0048b | 720 | qualname: String::new() // FIXME: generate the real qualname |
7453a54e | 721 | }) |
62682a34 SL |
722 | } |
723 | ||
d9579d0f AL |
724 | pub fn get_data_for_id(&self, _id: &NodeId) -> Data { |
725 | // FIXME | |
54a0048b | 726 | bug!(); |
1a4d82fc | 727 | } |
62682a34 SL |
728 | |
729 | fn lookup_ref_id(&self, ref_id: NodeId) -> Option<DefId> { | |
476ff2be SL |
730 | match self.get_path_def(ref_id) { |
731 | Def::PrimTy(_) | Def::SelfTy(..) | Def::Err => None, | |
3157f602 | 732 | def => Some(def.def_id()), |
62682a34 SL |
733 | } |
734 | } | |
735 | ||
32a655c1 SL |
736 | fn sig_base(&self, item: &ast::Item) -> Signature { |
737 | let text = self.span_utils.signature_string_for_span(item.span); | |
738 | let name = item.ident.to_string(); | |
739 | let ident_start = text.find(&name).expect("Name not in signature?"); | |
740 | let ident_end = ident_start + name.len(); | |
741 | Signature { | |
742 | span: mk_sp(item.span.lo, item.span.lo + BytePos(text.len() as u32)), | |
743 | text: text, | |
744 | ident_start: ident_start, | |
745 | ident_end: ident_end, | |
746 | defs: vec![], | |
747 | refs: vec![], | |
748 | } | |
749 | } | |
750 | ||
c1a9b12d | 751 | #[inline] |
e9174d1e | 752 | pub fn enclosing_scope(&self, id: NodeId) -> NodeId { |
32a655c1 | 753 | self.tcx.hir.get_enclosing_scope(id).unwrap_or(CRATE_NODE_ID) |
c1a9b12d | 754 | } |
d9579d0f | 755 | } |
1a4d82fc | 756 | |
a7813a04 | 757 | fn make_signature(decl: &ast::FnDecl, generics: &ast::Generics) -> String { |
9e0c209e | 758 | let mut sig = "fn ".to_owned(); |
a7813a04 XL |
759 | if !generics.lifetimes.is_empty() || !generics.ty_params.is_empty() { |
760 | sig.push('<'); | |
761 | sig.push_str(&generics.lifetimes.iter() | |
762 | .map(|l| l.lifetime.name.to_string()) | |
763 | .collect::<Vec<_>>() | |
764 | .join(", ")); | |
765 | if !generics.lifetimes.is_empty() { | |
766 | sig.push_str(", "); | |
767 | } | |
768 | sig.push_str(&generics.ty_params.iter() | |
769 | .map(|l| l.ident.to_string()) | |
770 | .collect::<Vec<_>>() | |
771 | .join(", ")); | |
772 | sig.push_str("> "); | |
773 | } | |
774 | sig.push('('); | |
775 | sig.push_str(&decl.inputs.iter().map(arg_to_string).collect::<Vec<_>>().join(", ")); | |
776 | sig.push(')'); | |
777 | match decl.output { | |
9e0c209e | 778 | ast::FunctionRetTy::Default(_) => sig.push_str(" -> ()"), |
a7813a04 XL |
779 | ast::FunctionRetTy::Ty(ref t) => sig.push_str(&format!(" -> {}", ty_to_string(t))), |
780 | } | |
781 | ||
782 | sig | |
783 | } | |
784 | ||
d9579d0f AL |
785 | // An AST visitor for collecting paths from patterns. |
786 | struct PathCollector { | |
787 | // The Row field identifies the kind of pattern. | |
788 | collected_paths: Vec<(NodeId, ast::Path, ast::Mutability, recorder::Row)>, | |
789 | } | |
1a4d82fc | 790 | |
d9579d0f AL |
791 | impl PathCollector { |
792 | fn new() -> PathCollector { | |
e9174d1e | 793 | PathCollector { collected_paths: vec![] } |
1a4d82fc | 794 | } |
d9579d0f | 795 | } |
1a4d82fc | 796 | |
476ff2be | 797 | impl<'a> Visitor<'a> for PathCollector { |
d9579d0f | 798 | fn visit_pat(&mut self, p: &ast::Pat) { |
1a4d82fc | 799 | match p.node { |
9e0c209e | 800 | PatKind::Struct(ref path, ..) => { |
7453a54e SL |
801 | self.collected_paths.push((p.id, path.clone(), |
802 | ast::Mutability::Mutable, recorder::TypeRef)); | |
1a4d82fc | 803 | } |
9e0c209e | 804 | PatKind::TupleStruct(ref path, ..) | |
3157f602 | 805 | PatKind::Path(_, ref path) => { |
7453a54e SL |
806 | self.collected_paths.push((p.id, path.clone(), |
807 | ast::Mutability::Mutable, recorder::VarRef)); | |
1a4d82fc | 808 | } |
7453a54e | 809 | PatKind::Ident(bm, ref path1, _) => { |
d9579d0f | 810 | debug!("PathCollector, visit ident in pat {}: {:?} {:?}", |
c1a9b12d | 811 | path1.node, |
d9579d0f AL |
812 | p.span, |
813 | path1.span); | |
1a4d82fc JJ |
814 | let immut = match bm { |
815 | // Even if the ref is mut, you can't change the ref, only | |
816 | // the data pointed at, so showing the initialising expression | |
817 | // is still worthwhile. | |
7453a54e | 818 | ast::BindingMode::ByRef(_) => ast::Mutability::Immutable, |
9cc50fc6 | 819 | ast::BindingMode::ByValue(mt) => mt, |
1a4d82fc JJ |
820 | }; |
821 | // collect path for either visit_local or visit_arm | |
54a0048b | 822 | let path = ast::Path::from_ident(path1.span, path1.node); |
1a4d82fc | 823 | self.collected_paths.push((p.id, path, immut, recorder::VarRef)); |
1a4d82fc | 824 | } |
d9579d0f | 825 | _ => {} |
1a4d82fc | 826 | } |
d9579d0f | 827 | visit::walk_pat(self, p); |
1a4d82fc JJ |
828 | } |
829 | } | |
830 | ||
9e0c209e | 831 | fn docs_for_attrs(attrs: &[Attribute]) -> String { |
476ff2be | 832 | let doc = Symbol::intern("doc"); |
9e0c209e SL |
833 | let mut result = String::new(); |
834 | ||
835 | for attr in attrs { | |
836 | if attr.name() == doc { | |
476ff2be SL |
837 | if let Some(val) = attr.value_str() { |
838 | if attr.is_sugared_doc { | |
839 | result.push_str(&strip_doc_comment_decoration(&val.as_str())); | |
840 | } else { | |
841 | result.push_str(&val.as_str()); | |
842 | } | |
9e0c209e SL |
843 | result.push('\n'); |
844 | } | |
845 | } | |
846 | } | |
847 | ||
848 | result | |
849 | } | |
850 | ||
851 | #[derive(Clone, Copy, Debug, RustcEncodable)] | |
a7813a04 XL |
852 | pub enum Format { |
853 | Csv, | |
854 | Json, | |
9e0c209e | 855 | JsonApi, |
a7813a04 XL |
856 | } |
857 | ||
858 | impl Format { | |
859 | fn extension(&self) -> &'static str { | |
860 | match *self { | |
861 | Format::Csv => ".csv", | |
9e0c209e | 862 | Format::Json | Format::JsonApi => ".json", |
a7813a04 XL |
863 | } |
864 | } | |
865 | } | |
866 | ||
867 | pub fn process_crate<'l, 'tcx>(tcx: TyCtxt<'l, 'tcx, 'tcx>, | |
b039eaaf | 868 | krate: &ast::Crate, |
8bb4bdeb | 869 | analysis: &'l ty::CrateAnalysis, |
b039eaaf | 870 | cratename: &str, |
a7813a04 XL |
871 | odir: Option<&Path>, |
872 | format: Format) { | |
9cc50fc6 SL |
873 | let _ignore = tcx.dep_graph.in_ignore(); |
874 | ||
1a4d82fc | 875 | assert!(analysis.glob_map.is_some()); |
1a4d82fc JJ |
876 | |
877 | info!("Dumping crate {}", cratename); | |
878 | ||
879 | // find a path to dump our data to | |
a7813a04 | 880 | let mut root_path = match env::var_os("RUST_SAVE_ANALYSIS_FOLDER") { |
c34b1796 AL |
881 | Some(val) => PathBuf::from(val), |
882 | None => match odir { | |
a7813a04 XL |
883 | Some(val) => val.join("save-analysis"), |
884 | None => PathBuf::from("save-analysis-temp"), | |
1a4d82fc JJ |
885 | }, |
886 | }; | |
887 | ||
476ff2be | 888 | if let Err(e) = rustc::util::fs::create_dir_racy(&root_path) { |
62682a34 | 889 | tcx.sess.err(&format!("Could not create directory {}: {}", |
b039eaaf SL |
890 | root_path.display(), |
891 | e)); | |
1a4d82fc JJ |
892 | } |
893 | ||
894 | { | |
895 | let disp = root_path.display(); | |
896 | info!("Writing output to {}", disp); | |
897 | } | |
898 | ||
899 | // Create output file. | |
92a42be0 SL |
900 | let executable = tcx.sess.crate_types.borrow().iter().any(|ct| *ct == CrateTypeExecutable); |
901 | let mut out_name = if executable { | |
902 | "".to_owned() | |
903 | } else { | |
904 | "lib".to_owned() | |
905 | }; | |
906 | out_name.push_str(&cratename); | |
907 | out_name.push_str(&tcx.sess.opts.cg.extra_filename); | |
a7813a04 | 908 | out_name.push_str(format.extension()); |
c34b1796 | 909 | root_path.push(&out_name); |
54a0048b SL |
910 | let mut output_file = File::create(&root_path).unwrap_or_else(|e| { |
911 | let disp = root_path.display(); | |
912 | tcx.sess.fatal(&format!("Could not open {}: {}", disp, e)); | |
913 | }); | |
1a4d82fc | 914 | root_path.pop(); |
a7813a04 | 915 | let output = &mut output_file; |
1a4d82fc | 916 | |
32a655c1 SL |
917 | let save_ctxt = SaveContext { |
918 | tcx: tcx, | |
919 | tables: &ty::TypeckTables::empty(), | |
920 | analysis: analysis, | |
921 | span_utils: SpanUtils::new(&tcx.sess), | |
922 | }; | |
1a4d82fc | 923 | |
a7813a04 XL |
924 | macro_rules! dump { |
925 | ($new_dumper: expr) => {{ | |
926 | let mut dumper = $new_dumper; | |
476ff2be | 927 | let mut visitor = DumpVisitor::new(save_ctxt, &mut dumper); |
a7813a04 XL |
928 | |
929 | visitor.dump_crate_info(cratename, krate); | |
930 | visit::walk_crate(&mut visitor, krate); | |
931 | }} | |
932 | } | |
933 | ||
934 | match format { | |
935 | Format::Csv => dump!(CsvDumper::new(output)), | |
936 | Format::Json => dump!(JsonDumper::new(output)), | |
9e0c209e | 937 | Format::JsonApi => dump!(JsonApiDumper::new(output)), |
a7813a04 | 938 | } |
1a4d82fc | 939 | } |
d9579d0f AL |
940 | |
941 | // Utility functions for the module. | |
942 | ||
943 | // Helper function to escape quotes in a string | |
944 | fn escape(s: String) -> String { | |
945 | s.replace("\"", "\"\"") | |
946 | } | |
947 | ||
7453a54e SL |
948 | // Helper function to determine if a span came from a |
949 | // macro expansion or syntax extension. | |
c1a9b12d | 950 | pub fn generated_code(span: Span) -> bool { |
e9174d1e | 951 | span.expn_id != NO_EXPANSION || span == DUMMY_SP |
d9579d0f | 952 | } |