]>
Commit | Line | Data |
---|---|---|
d9579d0f | 1 | // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT |
1a4d82fc JJ |
2 | // file at the top-level directory of this distribution and at |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
54a0048b | 11 | #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", |
abe05a73 XL |
12 | html_favicon_url = "https://doc.rust-lang.org/favicon.ico", |
13 | html_root_url = "https://doc.rust-lang.org/nightly/")] | |
54a0048b | 14 | #![feature(custom_attribute)] |
0531ce1d | 15 | #![feature(macro_lifetime_matcher)] |
54a0048b | 16 | #![allow(unused_attributes)] |
7cac9316 | 17 | |
abe05a73 XL |
18 | #[macro_use] |
19 | extern crate rustc; | |
54a0048b | 20 | |
abe05a73 XL |
21 | #[macro_use] |
22 | extern crate log; | |
3b2f2976 | 23 | extern crate rustc_data_structures; |
cc61c64b | 24 | extern crate rustc_serialize; |
83c7162d | 25 | extern crate rustc_target; |
7cac9316 | 26 | extern crate rustc_typeck; |
abe05a73 XL |
27 | #[macro_use] |
28 | extern crate syntax; | |
3157f602 | 29 | extern crate syntax_pos; |
54a0048b | 30 | |
cc61c64b XL |
31 | extern crate rls_data; |
32 | extern crate rls_span; | |
33 | ||
9e0c209e | 34 | |
a7813a04 | 35 | mod json_dumper; |
a7813a04 | 36 | mod dump_visitor; |
a7813a04 | 37 | #[macro_use] |
041b39d2 XL |
38 | mod span_utils; |
39 | mod sig; | |
a7813a04 XL |
40 | |
41 | use rustc::hir; | |
041b39d2 XL |
42 | use rustc::hir::def::Def as HirDef; |
43 | use rustc::hir::map::{Node, NodeItem}; | |
abe05a73 | 44 | use rustc::hir::def_id::{DefId, LOCAL_CRATE}; |
83c7162d | 45 | use rustc::middle::cstore::ExternCrate; |
54a0048b SL |
46 | use rustc::session::config::CrateType::CrateTypeExecutable; |
47 | use rustc::ty::{self, TyCtxt}; | |
7cac9316 | 48 | use rustc_typeck::hir_ty_to_ty; |
1a4d82fc | 49 | |
2c00a5a8 | 50 | use std::cell::Cell; |
3b2f2976 | 51 | use std::default::Default; |
85aaf69f | 52 | use std::env; |
476ff2be | 53 | use std::fs::File; |
c34b1796 | 54 | use std::path::{Path, PathBuf}; |
1a4d82fc | 55 | |
abe05a73 | 56 | use syntax::ast::{self, Attribute, NodeId, PatKind}; |
9e0c209e | 57 | use syntax::parse::lexer::comments::strip_doc_comment_decoration; |
476ff2be | 58 | use syntax::parse::token; |
041b39d2 | 59 | use syntax::print::pprust; |
cc61c64b | 60 | use syntax::symbol::keywords; |
1a4d82fc | 61 | use syntax::visit::{self, Visitor}; |
abe05a73 | 62 | use syntax::print::pprust::{arg_to_string, ty_to_string}; |
3157f602 XL |
63 | use syntax::codemap::MacroAttribute; |
64 | use syntax_pos::*; | |
62682a34 | 65 | |
3b2f2976 | 66 | use json_dumper::JsonDumper; |
041b39d2 XL |
67 | use dump_visitor::DumpVisitor; |
68 | use span_utils::SpanUtils; | |
69 | ||
abe05a73 | 70 | use rls_data::{Def, DefKind, ExternalCrateData, GlobalCrateId, MacroRef, Ref, RefKind, Relation, |
2c00a5a8 | 71 | RelationKind, SpanData, Impl, ImplKind}; |
3b2f2976 | 72 | use rls_data::config::Config; |
041b39d2 | 73 | |
c1a9b12d | 74 | |
54a0048b | 75 | pub struct SaveContext<'l, 'tcx: 'l> { |
a7813a04 | 76 | tcx: TyCtxt<'l, 'tcx, 'tcx>, |
32a655c1 | 77 | tables: &'l ty::TypeckTables<'tcx>, |
8bb4bdeb | 78 | analysis: &'l ty::CrateAnalysis, |
a7813a04 | 79 | span_utils: SpanUtils<'tcx>, |
3b2f2976 | 80 | config: Config, |
2c00a5a8 | 81 | impl_counter: Cell<u32>, |
7453a54e SL |
82 | } |
83 | ||
041b39d2 XL |
84 | #[derive(Debug)] |
85 | pub enum Data { | |
041b39d2 XL |
86 | RefData(Ref), |
87 | DefData(Def), | |
2c00a5a8 | 88 | RelationData(Relation, Impl), |
041b39d2 XL |
89 | } |
90 | ||
d9579d0f | 91 | impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { |
041b39d2 | 92 | fn span_from_span(&self, span: Span) -> SpanData { |
abe05a73 | 93 | use rls_span::{Column, Row}; |
041b39d2 XL |
94 | |
95 | let cm = self.tcx.sess.codemap(); | |
ea8adc8c XL |
96 | let start = cm.lookup_char_pos(span.lo()); |
97 | let end = cm.lookup_char_pos(span.hi()); | |
041b39d2 XL |
98 | |
99 | SpanData { | |
ff7c6d11 | 100 | file_name: start.file.name.clone().to_string().into(), |
ea8adc8c XL |
101 | byte_start: span.lo().0, |
102 | byte_end: span.hi().0, | |
041b39d2 XL |
103 | line_start: Row::new_one_indexed(start.line as u32), |
104 | line_end: Row::new_one_indexed(end.line as u32), | |
105 | column_start: Column::new_one_indexed(start.col.0 as u32 + 1), | |
106 | column_end: Column::new_one_indexed(end.col.0 as u32 + 1), | |
107 | } | |
108 | } | |
109 | ||
d9579d0f | 110 | // List external crates used by the current crate. |
041b39d2 | 111 | pub fn get_external_crates(&self) -> Vec<ExternalCrateData> { |
d9579d0f | 112 | let mut result = Vec::new(); |
1a4d82fc | 113 | |
ea8adc8c | 114 | for &n in self.tcx.crates().iter() { |
041b39d2 | 115 | let span = match *self.tcx.extern_crate(n.as_def_id()) { |
83c7162d | 116 | Some(ExternCrate { span, .. }) => span, |
a7813a04 XL |
117 | None => { |
118 | debug!("Skipping crate {}, no data", n); | |
119 | continue; | |
120 | } | |
121 | }; | |
ea8adc8c | 122 | let lo_loc = self.span_utils.sess.codemap().lookup_char_pos(span.lo()); |
041b39d2 | 123 | result.push(ExternalCrateData { |
ff7c6d11 XL |
124 | // FIXME: change file_name field to PathBuf in rls-data |
125 | // https://github.com/nrc/rls-data/issues/7 | |
041b39d2 | 126 | file_name: SpanUtils::make_path_string(&lo_loc.file.name), |
abe05a73 XL |
127 | num: n.as_u32(), |
128 | id: GlobalCrateId { | |
129 | name: self.tcx.crate_name(n).to_string(), | |
130 | disambiguator: self.tcx.crate_disambiguator(n).to_fingerprint().as_value(), | |
131 | }, | |
b039eaaf | 132 | }); |
92a42be0 | 133 | } |
1a4d82fc JJ |
134 | |
135 | result | |
136 | } | |
137 | ||
cc61c64b XL |
138 | pub fn get_extern_item_data(&self, item: &ast::ForeignItem) -> Option<Data> { |
139 | let qualname = format!("::{}", self.tcx.node_path_str(item.id)); | |
140 | match item.node { | |
141 | ast::ForeignItemKind::Fn(ref decl, ref generics) => { | |
abe05a73 XL |
142 | let sub_span = self.span_utils |
143 | .sub_span_after_keyword(item.span, keywords::Fn); | |
cc61c64b | 144 | filter!(self.span_utils, sub_span, item.span, None); |
041b39d2 XL |
145 | |
146 | Some(Data::DefData(Def { | |
147 | kind: DefKind::Function, | |
148 | id: id_from_node_id(item.id, self), | |
149 | span: self.span_from_span(sub_span.unwrap()), | |
cc61c64b | 150 | name: item.ident.to_string(), |
041b39d2 | 151 | qualname, |
cc61c64b | 152 | value: make_signature(decl, generics), |
cc61c64b | 153 | parent: None, |
041b39d2 XL |
154 | children: vec![], |
155 | decl_id: None, | |
3b2f2976 | 156 | docs: self.docs_for_attrs(&item.attrs), |
041b39d2 XL |
157 | sig: sig::foreign_item_signature(item, self), |
158 | attributes: lower_attributes(item.attrs.clone(), self), | |
cc61c64b XL |
159 | })) |
160 | } | |
161 | ast::ForeignItemKind::Static(ref ty, m) => { | |
162 | let keyword = if m { keywords::Mut } else { keywords::Static }; | |
163 | let sub_span = self.span_utils.sub_span_after_keyword(item.span, keyword); | |
164 | filter!(self.span_utils, sub_span, item.span, None); | |
041b39d2 XL |
165 | |
166 | let id = ::id_from_node_id(item.id, self); | |
167 | let span = self.span_from_span(sub_span.unwrap()); | |
168 | ||
169 | Some(Data::DefData(Def { | |
170 | kind: DefKind::Static, | |
171 | id, | |
172 | span, | |
cc61c64b | 173 | name: item.ident.to_string(), |
041b39d2 XL |
174 | qualname, |
175 | value: ty_to_string(ty), | |
cc61c64b | 176 | parent: None, |
041b39d2 XL |
177 | children: vec![], |
178 | decl_id: None, | |
3b2f2976 | 179 | docs: self.docs_for_attrs(&item.attrs), |
041b39d2 XL |
180 | sig: sig::foreign_item_signature(item, self), |
181 | attributes: lower_attributes(item.attrs.clone(), self), | |
cc61c64b XL |
182 | })) |
183 | } | |
abe05a73 XL |
184 | // FIXME(plietar): needs a new DefKind in rls-data |
185 | ast::ForeignItemKind::Ty => None, | |
83c7162d | 186 | ast::ForeignItemKind::Macro(..) => None, |
cc61c64b XL |
187 | } |
188 | } | |
189 | ||
7453a54e | 190 | pub fn get_item_data(&self, item: &ast::Item) -> Option<Data> { |
d9579d0f | 191 | match item.node { |
9e0c209e | 192 | ast::ItemKind::Fn(ref decl, .., ref generics, _) => { |
3157f602 | 193 | let qualname = format!("::{}", self.tcx.node_path_str(item.id)); |
abe05a73 XL |
194 | let sub_span = self.span_utils |
195 | .sub_span_after_keyword(item.span, keywords::Fn); | |
7453a54e | 196 | filter!(self.span_utils, sub_span, item.span, None); |
041b39d2 XL |
197 | Some(Data::DefData(Def { |
198 | kind: DefKind::Function, | |
199 | id: id_from_node_id(item.id, self), | |
200 | span: self.span_from_span(sub_span.unwrap()), | |
3157f602 | 201 | name: item.ident.to_string(), |
041b39d2 | 202 | qualname, |
a7813a04 | 203 | value: make_signature(decl, generics), |
9e0c209e | 204 | parent: None, |
041b39d2 XL |
205 | children: vec![], |
206 | decl_id: None, | |
3b2f2976 | 207 | docs: self.docs_for_attrs(&item.attrs), |
041b39d2 XL |
208 | sig: sig::item_signature(item, self), |
209 | attributes: lower_attributes(item.attrs.clone(), self), | |
7453a54e | 210 | })) |
1a4d82fc | 211 | } |
041b39d2 | 212 | ast::ItemKind::Static(ref typ, mt, _) => { |
54a0048b | 213 | let qualname = format!("::{}", self.tcx.node_path_str(item.id)); |
1a4d82fc | 214 | |
041b39d2 XL |
215 | let keyword = match mt { |
216 | ast::Mutability::Mutable => keywords::Mut, | |
217 | ast::Mutability::Immutable => keywords::Static, | |
d9579d0f | 218 | }; |
1a4d82fc | 219 | |
d9579d0f | 220 | let sub_span = self.span_utils.sub_span_after_keyword(item.span, keyword); |
7453a54e | 221 | filter!(self.span_utils, sub_span, item.span, None); |
041b39d2 XL |
222 | |
223 | let id = id_from_node_id(item.id, self); | |
224 | let span = self.span_from_span(sub_span.unwrap()); | |
225 | ||
226 | Some(Data::DefData(Def { | |
227 | kind: DefKind::Static, | |
228 | id, | |
229 | span, | |
c1a9b12d | 230 | name: item.ident.to_string(), |
041b39d2 XL |
231 | qualname, |
232 | value: ty_to_string(&typ), | |
9e0c209e | 233 | parent: None, |
041b39d2 XL |
234 | children: vec![], |
235 | decl_id: None, | |
3b2f2976 | 236 | docs: self.docs_for_attrs(&item.attrs), |
041b39d2 XL |
237 | sig: sig::item_signature(item, self), |
238 | attributes: lower_attributes(item.attrs.clone(), self), | |
7453a54e | 239 | })) |
1a4d82fc | 240 | } |
041b39d2 | 241 | ast::ItemKind::Const(ref typ, _) => { |
54a0048b | 242 | let qualname = format!("::{}", self.tcx.node_path_str(item.id)); |
abe05a73 XL |
243 | let sub_span = self.span_utils |
244 | .sub_span_after_keyword(item.span, keywords::Const); | |
7453a54e | 245 | filter!(self.span_utils, sub_span, item.span, None); |
041b39d2 XL |
246 | |
247 | let id = id_from_node_id(item.id, self); | |
248 | let span = self.span_from_span(sub_span.unwrap()); | |
249 | ||
250 | Some(Data::DefData(Def { | |
251 | kind: DefKind::Const, | |
252 | id, | |
253 | span, | |
c1a9b12d | 254 | name: item.ident.to_string(), |
041b39d2 XL |
255 | qualname, |
256 | value: ty_to_string(typ), | |
9e0c209e | 257 | parent: None, |
041b39d2 XL |
258 | children: vec![], |
259 | decl_id: None, | |
3b2f2976 | 260 | docs: self.docs_for_attrs(&item.attrs), |
041b39d2 XL |
261 | sig: sig::item_signature(item, self), |
262 | attributes: lower_attributes(item.attrs.clone(), self), | |
7453a54e | 263 | })) |
85aaf69f | 264 | } |
7453a54e | 265 | ast::ItemKind::Mod(ref m) => { |
54a0048b | 266 | let qualname = format!("::{}", self.tcx.node_path_str(item.id)); |
62682a34 SL |
267 | |
268 | let cm = self.tcx.sess.codemap(); | |
269 | let filename = cm.span_to_filename(m.inner); | |
270 | ||
abe05a73 XL |
271 | let sub_span = self.span_utils |
272 | .sub_span_after_keyword(item.span, keywords::Mod); | |
7453a54e | 273 | filter!(self.span_utils, sub_span, item.span, None); |
32a655c1 | 274 | |
041b39d2 XL |
275 | Some(Data::DefData(Def { |
276 | kind: DefKind::Mod, | |
277 | id: id_from_node_id(item.id, self), | |
c1a9b12d | 278 | name: item.ident.to_string(), |
041b39d2 XL |
279 | qualname, |
280 | span: self.span_from_span(sub_span.unwrap()), | |
ff7c6d11 | 281 | value: filename.to_string(), |
041b39d2 | 282 | parent: None, |
abe05a73 XL |
283 | children: m.items |
284 | .iter() | |
285 | .map(|i| id_from_node_id(i.id, self)) | |
286 | .collect(), | |
041b39d2 | 287 | decl_id: None, |
3b2f2976 | 288 | docs: self.docs_for_attrs(&item.attrs), |
041b39d2 XL |
289 | sig: sig::item_signature(item, self), |
290 | attributes: lower_attributes(item.attrs.clone(), self), | |
7453a54e | 291 | })) |
e9174d1e | 292 | } |
a7813a04 XL |
293 | ast::ItemKind::Enum(ref def, _) => { |
294 | let name = item.ident.to_string(); | |
295 | let qualname = format!("::{}", self.tcx.node_path_str(item.id)); | |
abe05a73 XL |
296 | let sub_span = self.span_utils |
297 | .sub_span_after_keyword(item.span, keywords::Enum); | |
7453a54e | 298 | filter!(self.span_utils, sub_span, item.span, None); |
abe05a73 XL |
299 | let variants_str = def.variants |
300 | .iter() | |
83c7162d | 301 | .map(|v| v.node.ident.to_string()) |
abe05a73 XL |
302 | .collect::<Vec<_>>() |
303 | .join(", "); | |
041b39d2 XL |
304 | let value = format!("{}::{{{}}}", name, variants_str); |
305 | Some(Data::DefData(Def { | |
306 | kind: DefKind::Enum, | |
307 | id: id_from_node_id(item.id, self), | |
308 | span: self.span_from_span(sub_span.unwrap()), | |
309 | name, | |
310 | qualname, | |
311 | value, | |
312 | parent: None, | |
313 | children: def.variants | |
abe05a73 XL |
314 | .iter() |
315 | .map(|v| id_from_node_id(v.node.data.id(), self)) | |
316 | .collect(), | |
041b39d2 | 317 | decl_id: None, |
3b2f2976 | 318 | docs: self.docs_for_attrs(&item.attrs), |
041b39d2 XL |
319 | sig: sig::item_signature(item, self), |
320 | attributes: lower_attributes(item.attrs.to_owned(), self), | |
7453a54e | 321 | })) |
e9174d1e | 322 | } |
2c00a5a8 | 323 | ast::ItemKind::Impl(.., ref trait_ref, ref typ, ref impls) => { |
041b39d2 | 324 | if let ast::TyKind::Path(None, ref path) = typ.node { |
62682a34 | 325 | // Common case impl for a struct or something basic. |
041b39d2 XL |
326 | if generated_code(path.span) { |
327 | return None; | |
62682a34 | 328 | } |
041b39d2 XL |
329 | let sub_span = self.span_utils.sub_span_for_type_name(path.span); |
330 | filter!(self.span_utils, sub_span, typ.span, None); | |
331 | ||
2c00a5a8 XL |
332 | let impl_id = self.next_impl_id(); |
333 | let span = self.span_from_span(sub_span.unwrap()); | |
334 | ||
041b39d2 | 335 | let type_data = self.lookup_ref_id(typ.id); |
abe05a73 XL |
336 | type_data.map(|type_data| { |
337 | Data::RelationData(Relation { | |
2c00a5a8 XL |
338 | kind: RelationKind::Impl { |
339 | id: impl_id, | |
340 | }, | |
341 | span: span.clone(), | |
abe05a73 XL |
342 | from: id_from_def_id(type_data), |
343 | to: trait_ref | |
344 | .as_ref() | |
345 | .and_then(|t| self.lookup_ref_id(t.ref_id)) | |
346 | .map(id_from_def_id) | |
347 | .unwrap_or(null_id()), | |
2c00a5a8 XL |
348 | }, |
349 | Impl { | |
350 | id: impl_id, | |
351 | kind: match *trait_ref { | |
352 | Some(_) => ImplKind::Direct, | |
353 | None => ImplKind::Inherent, | |
354 | }, | |
355 | span: span, | |
356 | value: String::new(), | |
357 | parent: None, | |
358 | children: impls | |
359 | .iter() | |
360 | .map(|i| id_from_node_id(i.id, self)) | |
361 | .collect(), | |
362 | docs: String::new(), | |
363 | sig: None, | |
364 | attributes: vec![], | |
abe05a73 XL |
365 | }) |
366 | }) | |
041b39d2 XL |
367 | } else { |
368 | None | |
62682a34 | 369 | } |
62682a34 | 370 | } |
d9579d0f AL |
371 | _ => { |
372 | // FIXME | |
54a0048b | 373 | bug!(); |
1a4d82fc JJ |
374 | } |
375 | } | |
1a4d82fc JJ |
376 | } |
377 | ||
abe05a73 | 378 | pub fn get_field_data(&self, field: &ast::StructField, scope: NodeId) -> Option<Def> { |
54a0048b | 379 | if let Some(ident) = field.ident { |
32a655c1 | 380 | let name = ident.to_string(); |
54a0048b | 381 | let qualname = format!("::{}::{}", self.tcx.node_path_str(scope), ident); |
abe05a73 XL |
382 | let sub_span = self.span_utils |
383 | .sub_span_before_token(field.span, token::Colon); | |
54a0048b | 384 | filter!(self.span_utils, sub_span, field.span, None); |
32a655c1 | 385 | let def_id = self.tcx.hir.local_def_id(field.id); |
7cac9316 | 386 | let typ = self.tcx.type_of(def_id).to_string(); |
32a655c1 | 387 | |
041b39d2 XL |
388 | |
389 | let id = id_from_node_id(field.id, self); | |
390 | let span = self.span_from_span(sub_span.unwrap()); | |
391 | ||
392 | Some(Def { | |
393 | kind: DefKind::Field, | |
394 | id, | |
395 | span, | |
396 | name, | |
397 | qualname, | |
398 | value: typ, | |
399 | parent: Some(id_from_node_id(scope, self)), | |
400 | children: vec![], | |
401 | decl_id: None, | |
3b2f2976 | 402 | docs: self.docs_for_attrs(&field.attrs), |
041b39d2 XL |
403 | sig: sig::field_signature(field, self), |
404 | attributes: lower_attributes(field.attrs.clone(), self), | |
54a0048b SL |
405 | }) |
406 | } else { | |
407 | None | |
62682a34 SL |
408 | } |
409 | } | |
410 | ||
c1a9b12d SL |
411 | // FIXME would be nice to take a MethodItem here, but the ast provides both |
412 | // trait and impl flavours, so the caller must do the disassembly. | |
abe05a73 | 413 | pub fn get_method_data(&self, id: ast::NodeId, name: ast::Name, span: Span) -> Option<Def> { |
c1a9b12d SL |
414 | // The qualname for a method is the trait name or name of the struct in an impl in |
415 | // which the method is declared in, followed by the method's name. | |
041b39d2 | 416 | let (qualname, parent_scope, decl_id, docs, attributes) = |
abe05a73 XL |
417 | match self.tcx.impl_of_method(self.tcx.hir.local_def_id(id)) { |
418 | Some(impl_id) => match self.tcx.hir.get_if_local(impl_id) { | |
419 | Some(Node::NodeItem(item)) => match item.node { | |
9e0c209e | 420 | hir::ItemImpl(.., ref ty, _) => { |
c1a9b12d | 421 | let mut result = String::from("<"); |
32a655c1 | 422 | result.push_str(&self.tcx.hir.node_to_pretty_string(ty.id)); |
c1a9b12d | 423 | |
041b39d2 | 424 | let mut trait_id = self.tcx.trait_id_of_impl(impl_id); |
476ff2be | 425 | let mut decl_id = None; |
9e0c209e | 426 | if let Some(def_id) = trait_id { |
3157f602 XL |
427 | result.push_str(" as "); |
428 | result.push_str(&self.tcx.item_path_str(def_id)); | |
abe05a73 XL |
429 | self.tcx |
430 | .associated_items(def_id) | |
476ff2be SL |
431 | .find(|item| item.name == name) |
432 | .map(|item| decl_id = Some(item.def_id)); | |
041b39d2 XL |
433 | } else { |
434 | if let Some(NodeItem(item)) = self.tcx.hir.find(id) { | |
435 | if let hir::ItemImpl(_, _, _, _, _, ref ty, _) = item.node { | |
436 | trait_id = self.lookup_ref_id(ty.id); | |
437 | } | |
438 | } | |
c1a9b12d SL |
439 | } |
440 | result.push_str(">"); | |
476ff2be | 441 | |
abe05a73 XL |
442 | ( |
443 | result, | |
444 | trait_id, | |
445 | decl_id, | |
446 | self.docs_for_attrs(&item.attrs), | |
447 | item.attrs.to_vec(), | |
448 | ) | |
c1a9b12d SL |
449 | } |
450 | _ => { | |
abe05a73 XL |
451 | span_bug!( |
452 | span, | |
453 | "Container {:?} for method {} not an impl?", | |
454 | impl_id, | |
455 | id | |
456 | ); | |
e9174d1e | 457 | } |
abe05a73 XL |
458 | }, |
459 | r => { | |
460 | span_bug!( | |
461 | span, | |
462 | "Container {:?} for method {} is not a node item {:?}", | |
463 | impl_id, | |
464 | id, | |
465 | r | |
466 | ); | |
c1a9b12d | 467 | } |
abe05a73 XL |
468 | }, |
469 | None => match self.tcx.trait_of_item(self.tcx.hir.local_def_id(id)) { | |
470 | Some(def_id) => match self.tcx.hir.get_if_local(def_id) { | |
471 | Some(Node::NodeItem(item)) => ( | |
472 | format!("::{}", self.tcx.item_path_str(def_id)), | |
473 | Some(def_id), | |
474 | None, | |
475 | self.docs_for_attrs(&item.attrs), | |
476 | item.attrs.to_vec(), | |
477 | ), | |
b039eaaf | 478 | r => { |
abe05a73 XL |
479 | span_bug!( |
480 | span, | |
481 | "Could not find container {:?} for \ | |
482 | method {}, got {:?}", | |
483 | def_id, | |
484 | id, | |
485 | r | |
486 | ); | |
c1a9b12d | 487 | } |
abe05a73 XL |
488 | }, |
489 | None => { | |
490 | debug!("Could not find container for method {} at {:?}", id, span); | |
491 | // This is not necessarily a bug, if there was a compilation error, | |
492 | // the tables we need might not exist. | |
493 | return None; | |
c1a9b12d | 494 | } |
abe05a73 XL |
495 | }, |
496 | }; | |
c1a9b12d SL |
497 | |
498 | let qualname = format!("{}::{}", qualname, name); | |
499 | ||
c1a9b12d | 500 | let sub_span = self.span_utils.sub_span_after_keyword(span, keywords::Fn); |
7453a54e | 501 | filter!(self.span_utils, sub_span, span, None); |
32a655c1 | 502 | |
041b39d2 XL |
503 | Some(Def { |
504 | kind: DefKind::Method, | |
505 | id: id_from_node_id(id, self), | |
506 | span: self.span_from_span(sub_span.unwrap()), | |
507 | name: name.to_string(), | |
508 | qualname, | |
a7813a04 XL |
509 | // FIXME you get better data here by using the visitor. |
510 | value: String::new(), | |
041b39d2 XL |
511 | parent: parent_scope.map(|id| id_from_def_id(id)), |
512 | children: vec![], | |
513 | decl_id: decl_id.map(|id| id_from_def_id(id)), | |
514 | docs, | |
515 | sig: None, | |
516 | attributes: lower_attributes(attributes, self), | |
7453a54e | 517 | }) |
c1a9b12d SL |
518 | } |
519 | ||
abe05a73 | 520 | pub fn get_trait_ref_data(&self, trait_ref: &ast::TraitRef) -> Option<Ref> { |
7453a54e | 521 | self.lookup_ref_id(trait_ref.ref_id).and_then(|def_id| { |
62682a34 | 522 | let span = trait_ref.path.span; |
8bb4bdeb XL |
523 | if generated_code(span) { |
524 | return None; | |
525 | } | |
7453a54e SL |
526 | let sub_span = self.span_utils.sub_span_for_type_name(span).or(Some(span)); |
527 | filter!(self.span_utils, sub_span, span, None); | |
041b39d2 XL |
528 | let span = self.span_from_span(sub_span.unwrap()); |
529 | Some(Ref { | |
530 | kind: RefKind::Type, | |
531 | span, | |
532 | ref_id: id_from_def_id(def_id), | |
7453a54e | 533 | }) |
62682a34 SL |
534 | }) |
535 | } | |
536 | ||
537 | pub fn get_expr_data(&self, expr: &ast::Expr) -> Option<Data> { | |
32a655c1 SL |
538 | let hir_node = self.tcx.hir.expect_expr(expr.id); |
539 | let ty = self.tables.expr_ty_adjusted_opt(&hir_node); | |
7453a54e SL |
540 | if ty.is_none() || ty.unwrap().sty == ty::TyError { |
541 | return None; | |
542 | } | |
62682a34 | 543 | match expr.node { |
7453a54e | 544 | ast::ExprKind::Field(ref sub_ex, ident) => { |
32a655c1 | 545 | let hir_node = match self.tcx.hir.find(sub_ex.id) { |
3157f602 XL |
546 | Some(Node::NodeExpr(expr)) => expr, |
547 | _ => { | |
abe05a73 XL |
548 | debug!( |
549 | "Missing or weird node for sub-expression {} in {:?}", | |
550 | sub_ex.id, | |
551 | expr | |
552 | ); | |
3157f602 XL |
553 | return None; |
554 | } | |
555 | }; | |
32a655c1 | 556 | match self.tables.expr_ty_adjusted(&hir_node).sty { |
9e0c209e | 557 | ty::TyAdt(def, _) if !def.is_enum() => { |
83c7162d XL |
558 | let variant = &def.non_enum_variant(); |
559 | let index = self.tcx.find_field_index(ident, variant).unwrap(); | |
e9174d1e | 560 | let sub_span = self.span_utils.span_for_last_ident(expr.span); |
7453a54e | 561 | filter!(self.span_utils, sub_span, expr.span, None); |
041b39d2 XL |
562 | let span = self.span_from_span(sub_span.unwrap()); |
563 | return Some(Data::RefData(Ref { | |
564 | kind: RefKind::Variable, | |
565 | span, | |
83c7162d | 566 | ref_id: id_from_def_id(variant.fields[index].did), |
e9174d1e | 567 | })); |
62682a34 | 568 | } |
83c7162d | 569 | ty::TyTuple(..) => None, |
62682a34 | 570 | _ => { |
9e0c209e | 571 | debug!("Expected struct or union type, found {:?}", ty); |
62682a34 SL |
572 | None |
573 | } | |
574 | } | |
575 | } | |
9e0c209e | 576 | ast::ExprKind::Struct(ref path, ..) => { |
32a655c1 | 577 | match self.tables.expr_ty_adjusted(&hir_node).sty { |
9e0c209e | 578 | ty::TyAdt(def, _) if !def.is_enum() => { |
62682a34 | 579 | let sub_span = self.span_utils.span_for_last_ident(path.span); |
7453a54e | 580 | filter!(self.span_utils, sub_span, path.span, None); |
041b39d2 XL |
581 | let span = self.span_from_span(sub_span.unwrap()); |
582 | Some(Data::RefData(Ref { | |
583 | kind: RefKind::Type, | |
584 | span, | |
585 | ref_id: id_from_def_id(def.did), | |
62682a34 SL |
586 | })) |
587 | } | |
588 | _ => { | |
9e0c209e | 589 | // FIXME ty could legitimately be an enum, but then we will fail |
62682a34 | 590 | // later if we try to look up the fields. |
9e0c209e | 591 | debug!("expected struct or union, found {:?}", ty); |
62682a34 SL |
592 | None |
593 | } | |
594 | } | |
595 | } | |
abe05a73 | 596 | ast::ExprKind::MethodCall(ref seg, ..) => { |
3b2f2976 | 597 | let expr_hir_id = self.tcx.hir.definitions().node_to_hir_id(expr.id); |
abe05a73 XL |
598 | let method_id = match self.tables.type_dependent_defs().get(expr_hir_id) { |
599 | Some(id) => id.def_id(), | |
600 | None => { | |
601 | debug!("Could not resolve method id for {:?}", expr); | |
602 | return None; | |
603 | } | |
604 | }; | |
476ff2be | 605 | let (def_id, decl_id) = match self.tcx.associated_item(method_id).container { |
c1a9b12d | 606 | ty::ImplContainer(_) => (Some(method_id), None), |
e9174d1e | 607 | ty::TraitContainer(_) => (None, Some(method_id)), |
c1a9b12d | 608 | }; |
83c7162d | 609 | let sub_span = seg.ident.span; |
abe05a73 XL |
610 | filter!(self.span_utils, Some(sub_span), expr.span, None); |
611 | let span = self.span_from_span(sub_span); | |
041b39d2 XL |
612 | Some(Data::RefData(Ref { |
613 | kind: RefKind::Function, | |
614 | span, | |
abe05a73 XL |
615 | ref_id: def_id |
616 | .or(decl_id) | |
617 | .map(|id| id_from_def_id(id)) | |
618 | .unwrap_or(null_id()), | |
c1a9b12d SL |
619 | })) |
620 | } | |
7453a54e | 621 | ast::ExprKind::Path(_, ref path) => { |
041b39d2 | 622 | self.get_path_data(expr.id, path).map(|d| Data::RefData(d)) |
c1a9b12d | 623 | } |
62682a34 SL |
624 | _ => { |
625 | // FIXME | |
54a0048b | 626 | bug!(); |
62682a34 SL |
627 | } |
628 | } | |
629 | } | |
630 | ||
041b39d2 | 631 | pub fn get_path_def(&self, id: NodeId) -> HirDef { |
32a655c1 | 632 | match self.tcx.hir.get(id) { |
476ff2be SL |
633 | Node::NodeTraitRef(tr) => tr.path.def, |
634 | ||
abe05a73 XL |
635 | Node::NodeItem(&hir::Item { |
636 | node: hir::ItemUse(ref path, _), | |
637 | .. | |
638 | }) | | |
476ff2be SL |
639 | Node::NodeVisibility(&hir::Visibility::Restricted { ref path, .. }) => path.def, |
640 | ||
abe05a73 XL |
641 | Node::NodeExpr(&hir::Expr { |
642 | node: hir::ExprStruct(ref qpath, ..), | |
643 | .. | |
644 | }) | | |
645 | Node::NodeExpr(&hir::Expr { | |
646 | node: hir::ExprPath(ref qpath), | |
647 | .. | |
648 | }) | | |
649 | Node::NodePat(&hir::Pat { | |
650 | node: hir::PatKind::Path(ref qpath), | |
651 | .. | |
652 | }) | | |
653 | Node::NodePat(&hir::Pat { | |
654 | node: hir::PatKind::Struct(ref qpath, ..), | |
655 | .. | |
656 | }) | | |
657 | Node::NodePat(&hir::Pat { | |
658 | node: hir::PatKind::TupleStruct(ref qpath, ..), | |
659 | .. | |
660 | }) => { | |
3b2f2976 XL |
661 | let hir_id = self.tcx.hir.node_to_hir_id(id); |
662 | self.tables.qpath_def(qpath, hir_id) | |
476ff2be SL |
663 | } |
664 | ||
ea8adc8c | 665 | Node::NodeBinding(&hir::Pat { |
abe05a73 XL |
666 | node: hir::PatKind::Binding(_, canonical_id, ..), |
667 | .. | |
ea8adc8c | 668 | }) => HirDef::Local(canonical_id), |
476ff2be | 669 | |
abe05a73 XL |
670 | Node::NodeTy(ty) => if let hir::Ty { |
671 | node: hir::TyPath(ref qpath), | |
672 | .. | |
673 | } = *ty | |
674 | { | |
675 | match *qpath { | |
676 | hir::QPath::Resolved(_, ref path) => path.def, | |
677 | hir::QPath::TypeRelative(..) => { | |
678 | let ty = hir_ty_to_ty(self.tcx, ty); | |
679 | if let ty::TyProjection(proj) = ty.sty { | |
680 | return HirDef::AssociatedTy(proj.item_def_id); | |
476ff2be | 681 | } |
abe05a73 | 682 | HirDef::Err |
476ff2be SL |
683 | } |
684 | } | |
abe05a73 XL |
685 | } else { |
686 | HirDef::Err | |
687 | }, | |
476ff2be | 688 | |
abe05a73 | 689 | _ => HirDef::Err, |
476ff2be SL |
690 | } |
691 | } | |
692 | ||
041b39d2 | 693 | pub fn get_path_data(&self, id: NodeId, path: &ast::Path) -> Option<Ref> { |
abe05a73 XL |
694 | // Returns true if the path is function type sugar, e.g., `Fn(A) -> B`. |
695 | fn fn_type(path: &ast::Path) -> bool { | |
696 | if path.segments.len() != 1 { | |
697 | return false; | |
698 | } | |
699 | if let Some(ref params) = path.segments[0].parameters { | |
700 | if let ast::PathParameters::Parenthesized(_) = **params { | |
701 | return true; | |
702 | } | |
703 | } | |
704 | false | |
705 | } | |
706 | ||
707 | if path.segments.is_empty() { | |
708 | return None; | |
709 | } | |
710 | ||
476ff2be | 711 | let def = self.get_path_def(id); |
abe05a73 | 712 | let last_seg = &path.segments[path.segments.len() - 1]; |
83c7162d | 713 | let sub_span = last_seg.ident.span; |
abe05a73 | 714 | filter!(self.span_utils, Some(sub_span), path.span, None); |
c1a9b12d | 715 | match def { |
abe05a73 XL |
716 | HirDef::Upvar(id, ..) | HirDef::Local(id) => { |
717 | let span = self.span_from_span(sub_span); | |
ea8adc8c XL |
718 | Some(Ref { |
719 | kind: RefKind::Variable, | |
720 | span, | |
721 | ref_id: id_from_node_id(id, self), | |
722 | }) | |
723 | } | |
041b39d2 XL |
724 | HirDef::Static(..) | |
725 | HirDef::Const(..) | | |
726 | HirDef::AssociatedConst(..) | | |
041b39d2 | 727 | HirDef::VariantCtor(..) => { |
abe05a73 | 728 | let span = self.span_from_span(sub_span); |
041b39d2 XL |
729 | Some(Ref { |
730 | kind: RefKind::Variable, | |
731 | span, | |
732 | ref_id: id_from_def_id(def.def_id()), | |
733 | }) | |
c1a9b12d | 734 | } |
abe05a73 XL |
735 | HirDef::Trait(def_id) if fn_type(path) => { |
736 | // Function type bounds are desugared in the parser, so we have to | |
737 | // special case them here. | |
738 | let fn_span = self.span_utils.span_for_first_ident(path.span); | |
739 | fn_span.map(|span| { | |
740 | Ref { | |
741 | kind: RefKind::Type, | |
742 | span: self.span_from_span(span), | |
743 | ref_id: id_from_def_id(def_id), | |
744 | } | |
745 | }) | |
746 | } | |
041b39d2 XL |
747 | HirDef::Struct(def_id) | |
748 | HirDef::Variant(def_id, ..) | | |
749 | HirDef::Union(def_id) | | |
750 | HirDef::Enum(def_id) | | |
751 | HirDef::TyAlias(def_id) | | |
abe05a73 | 752 | HirDef::TyForeign(def_id) | |
ff7c6d11 | 753 | HirDef::TraitAlias(def_id) | |
041b39d2 XL |
754 | HirDef::AssociatedTy(def_id) | |
755 | HirDef::Trait(def_id) | | |
756 | HirDef::TyParam(def_id) => { | |
abe05a73 | 757 | let span = self.span_from_span(sub_span); |
041b39d2 XL |
758 | Some(Ref { |
759 | kind: RefKind::Type, | |
760 | span, | |
761 | ref_id: id_from_def_id(def_id), | |
762 | }) | |
c1a9b12d | 763 | } |
abe05a73 XL |
764 | HirDef::StructCtor(def_id, _) => { |
765 | // This is a reference to a tuple struct where the def_id points | |
766 | // to an invisible constructor function. That is not a very useful | |
767 | // def, so adjust to point to the tuple struct itself. | |
768 | let span = self.span_from_span(sub_span); | |
769 | let parent_def_id = self.tcx.parent_def_id(def_id).unwrap(); | |
770 | Some(Ref { | |
771 | kind: RefKind::Type, | |
772 | span, | |
773 | ref_id: id_from_def_id(parent_def_id), | |
774 | }) | |
775 | } | |
041b39d2 | 776 | HirDef::Method(decl_id) => { |
e9174d1e | 777 | let def_id = if decl_id.is_local() { |
476ff2be | 778 | let ti = self.tcx.associated_item(decl_id); |
abe05a73 XL |
779 | self.tcx |
780 | .associated_items(ti.container.id()) | |
476ff2be SL |
781 | .find(|item| item.name == ti.name && item.defaultness.has_value()) |
782 | .map(|item| item.def_id) | |
c1a9b12d SL |
783 | } else { |
784 | None | |
785 | }; | |
abe05a73 | 786 | let span = self.span_from_span(sub_span); |
041b39d2 XL |
787 | Some(Ref { |
788 | kind: RefKind::Function, | |
789 | span, | |
790 | ref_id: id_from_def_id(def_id.unwrap_or(decl_id)), | |
791 | }) | |
e9174d1e | 792 | } |
041b39d2 | 793 | HirDef::Fn(def_id) => { |
abe05a73 | 794 | let span = self.span_from_span(sub_span); |
041b39d2 XL |
795 | Some(Ref { |
796 | kind: RefKind::Function, | |
797 | span, | |
798 | ref_id: id_from_def_id(def_id), | |
799 | }) | |
c1a9b12d | 800 | } |
041b39d2 | 801 | HirDef::Mod(def_id) => { |
abe05a73 | 802 | let span = self.span_from_span(sub_span); |
041b39d2 XL |
803 | Some(Ref { |
804 | kind: RefKind::Mod, | |
805 | span, | |
806 | ref_id: id_from_def_id(def_id), | |
807 | }) | |
c1a9b12d | 808 | } |
041b39d2 XL |
809 | HirDef::PrimTy(..) | |
810 | HirDef::SelfTy(..) | | |
811 | HirDef::Label(..) | | |
812 | HirDef::Macro(..) | | |
813 | HirDef::GlobalAsm(..) | | |
814 | HirDef::Err => None, | |
c1a9b12d SL |
815 | } |
816 | } | |
817 | ||
abe05a73 XL |
818 | pub fn get_field_ref_data( |
819 | &self, | |
820 | field_ref: &ast::Field, | |
821 | variant: &ty::VariantDef, | |
822 | ) -> Option<Ref> { | |
83c7162d | 823 | let index = self.tcx.find_field_index(field_ref.ident, variant).unwrap(); |
e9174d1e SL |
824 | // We don't really need a sub-span here, but no harm done |
825 | let sub_span = self.span_utils.span_for_last_ident(field_ref.ident.span); | |
7453a54e | 826 | filter!(self.span_utils, sub_span, field_ref.ident.span, None); |
041b39d2 XL |
827 | let span = self.span_from_span(sub_span.unwrap()); |
828 | Some(Ref { | |
829 | kind: RefKind::Variable, | |
830 | span, | |
83c7162d | 831 | ref_id: id_from_def_id(variant.fields[index].did), |
7453a54e SL |
832 | }) |
833 | } | |
834 | ||
041b39d2 | 835 | /// Attempt to return MacroRef for any AST node. |
7453a54e SL |
836 | /// |
837 | /// For a given piece of AST defined by the supplied Span and NodeId, | |
838 | /// returns None if the node is not macro-generated or the span is malformed, | |
041b39d2 XL |
839 | /// else uses the expansion callsite and callee to return some MacroRef. |
840 | pub fn get_macro_use_data(&self, span: Span) -> Option<MacroRef> { | |
7453a54e SL |
841 | if !generated_code(span) { |
842 | return None; | |
843 | } | |
844 | // Note we take care to use the source callsite/callee, to handle | |
845 | // nested expansions and ensure we only generate data for source-visible | |
846 | // macro uses. | |
cc61c64b | 847 | let callsite = span.source_callsite(); |
041b39d2 | 848 | let callsite_span = self.span_from_span(callsite); |
abe05a73 XL |
849 | let callee = span.source_callee()?; |
850 | let callee_span = callee.span?; | |
7453a54e SL |
851 | |
852 | // Ignore attribute macros, their spans are usually mangled | |
853 | if let MacroAttribute(_) = callee.format { | |
854 | return None; | |
855 | } | |
856 | ||
857 | // If the callee is an imported macro from an external crate, need to get | |
858 | // the source span and name from the session, as their spans are localized | |
859 | // when read in, and no longer correspond to the source. | |
abe05a73 XL |
860 | if let Some(mac) = self.tcx |
861 | .sess | |
862 | .imported_macro_spans | |
863 | .borrow() | |
864 | .get(&callee_span) | |
865 | { | |
7453a54e | 866 | let &(ref mac_name, mac_span) = mac; |
041b39d2 XL |
867 | let mac_span = self.span_from_span(mac_span); |
868 | return Some(MacroRef { | |
869 | span: callsite_span, | |
870 | qualname: mac_name.clone(), // FIXME: generate the real qualname | |
871 | callee_span: mac_span, | |
872 | }); | |
62682a34 | 873 | } |
7453a54e | 874 | |
041b39d2 XL |
875 | let callee_span = self.span_from_span(callee_span); |
876 | Some(MacroRef { | |
877 | span: callsite_span, | |
878 | qualname: callee.name().to_string(), // FIXME: generate the real qualname | |
879 | callee_span, | |
7453a54e | 880 | }) |
62682a34 SL |
881 | } |
882 | ||
62682a34 | 883 | fn lookup_ref_id(&self, ref_id: NodeId) -> Option<DefId> { |
476ff2be | 884 | match self.get_path_def(ref_id) { |
041b39d2 | 885 | HirDef::PrimTy(_) | HirDef::SelfTy(..) | HirDef::Err => None, |
3157f602 | 886 | def => Some(def.def_id()), |
62682a34 SL |
887 | } |
888 | } | |
889 | ||
3b2f2976 XL |
890 | fn docs_for_attrs(&self, attrs: &[Attribute]) -> String { |
891 | let mut result = String::new(); | |
892 | ||
893 | for attr in attrs { | |
894 | if attr.check_name("doc") { | |
895 | if let Some(val) = attr.value_str() { | |
896 | if attr.is_sugared_doc { | |
897 | result.push_str(&strip_doc_comment_decoration(&val.as_str())); | |
898 | } else { | |
899 | result.push_str(&val.as_str()); | |
900 | } | |
901 | result.push('\n'); | |
2c00a5a8 XL |
902 | } else if let Some(meta_list) = attr.meta_item_list() { |
903 | meta_list.into_iter() | |
904 | .filter(|it| it.check_name("include")) | |
905 | .filter_map(|it| it.meta_item_list().map(|l| l.to_owned())) | |
906 | .flat_map(|it| it) | |
907 | .filter(|meta| meta.check_name("contents")) | |
908 | .filter_map(|meta| meta.value_str()) | |
909 | .for_each(|val| { | |
910 | result.push_str(&val.as_str()); | |
911 | result.push('\n'); | |
912 | }); | |
3b2f2976 XL |
913 | } |
914 | } | |
915 | } | |
916 | ||
917 | if !self.config.full_docs { | |
918 | if let Some(index) = result.find("\n\n") { | |
919 | result.truncate(index); | |
920 | } | |
921 | } | |
922 | ||
923 | result | |
c1a9b12d | 924 | } |
2c00a5a8 XL |
925 | |
926 | fn next_impl_id(&self) -> u32 { | |
927 | let next = self.impl_counter.get(); | |
928 | self.impl_counter.set(next + 1); | |
929 | next | |
930 | } | |
d9579d0f | 931 | } |
1a4d82fc | 932 | |
a7813a04 | 933 | fn make_signature(decl: &ast::FnDecl, generics: &ast::Generics) -> String { |
9e0c209e | 934 | let mut sig = "fn ".to_owned(); |
ff7c6d11 | 935 | if !generics.params.is_empty() { |
a7813a04 | 936 | sig.push('<'); |
abe05a73 | 937 | sig.push_str(&generics |
ff7c6d11 | 938 | .params |
abe05a73 | 939 | .iter() |
ff7c6d11 XL |
940 | .map(|param| match *param { |
941 | ast::GenericParam::Lifetime(ref l) => l.lifetime.ident.name.to_string(), | |
942 | ast::GenericParam::Type(ref t) => t.ident.to_string(), | |
943 | }) | |
abe05a73 XL |
944 | .collect::<Vec<_>>() |
945 | .join(", ")); | |
a7813a04 XL |
946 | sig.push_str("> "); |
947 | } | |
948 | sig.push('('); | |
abe05a73 XL |
949 | sig.push_str(&decl.inputs |
950 | .iter() | |
951 | .map(arg_to_string) | |
952 | .collect::<Vec<_>>() | |
953 | .join(", ")); | |
a7813a04 XL |
954 | sig.push(')'); |
955 | match decl.output { | |
9e0c209e | 956 | ast::FunctionRetTy::Default(_) => sig.push_str(" -> ()"), |
a7813a04 XL |
957 | ast::FunctionRetTy::Ty(ref t) => sig.push_str(&format!(" -> {}", ty_to_string(t))), |
958 | } | |
959 | ||
960 | sig | |
961 | } | |
962 | ||
abe05a73 XL |
963 | // An AST visitor for collecting paths (e.g., the names of structs) and formal |
964 | // variables (idents) from patterns. | |
965 | struct PathCollector<'l> { | |
966 | collected_paths: Vec<(NodeId, &'l ast::Path)>, | |
83c7162d | 967 | collected_idents: Vec<(NodeId, ast::Ident, ast::Mutability)>, |
d9579d0f | 968 | } |
1a4d82fc | 969 | |
abe05a73 XL |
970 | impl<'l> PathCollector<'l> { |
971 | fn new() -> PathCollector<'l> { | |
972 | PathCollector { | |
973 | collected_paths: vec![], | |
974 | collected_idents: vec![], | |
975 | } | |
1a4d82fc | 976 | } |
d9579d0f | 977 | } |
1a4d82fc | 978 | |
abe05a73 XL |
979 | impl<'l, 'a: 'l> Visitor<'a> for PathCollector<'l> { |
980 | fn visit_pat(&mut self, p: &'a ast::Pat) { | |
1a4d82fc | 981 | match p.node { |
9e0c209e | 982 | PatKind::Struct(ref path, ..) => { |
abe05a73 | 983 | self.collected_paths.push((p.id, path)); |
1a4d82fc | 984 | } |
abe05a73 XL |
985 | PatKind::TupleStruct(ref path, ..) | PatKind::Path(_, ref path) => { |
986 | self.collected_paths.push((p.id, path)); | |
1a4d82fc | 987 | } |
83c7162d | 988 | PatKind::Ident(bm, ident, _) => { |
abe05a73 XL |
989 | debug!( |
990 | "PathCollector, visit ident in pat {}: {:?} {:?}", | |
83c7162d | 991 | ident, |
abe05a73 | 992 | p.span, |
83c7162d | 993 | ident.span |
abe05a73 | 994 | ); |
1a4d82fc JJ |
995 | let immut = match bm { |
996 | // Even if the ref is mut, you can't change the ref, only | |
997 | // the data pointed at, so showing the initialising expression | |
998 | // is still worthwhile. | |
7453a54e | 999 | ast::BindingMode::ByRef(_) => ast::Mutability::Immutable, |
9cc50fc6 | 1000 | ast::BindingMode::ByValue(mt) => mt, |
1a4d82fc | 1001 | }; |
abe05a73 | 1002 | self.collected_idents |
83c7162d | 1003 | .push((p.id, ident, immut)); |
1a4d82fc | 1004 | } |
d9579d0f | 1005 | _ => {} |
1a4d82fc | 1006 | } |
d9579d0f | 1007 | visit::walk_pat(self, p); |
1a4d82fc JJ |
1008 | } |
1009 | } | |
1010 | ||
cc61c64b XL |
1011 | /// Defines what to do with the results of saving the analysis. |
1012 | pub trait SaveHandler { | |
abe05a73 XL |
1013 | fn save<'l, 'tcx>( |
1014 | &mut self, | |
1015 | save_ctxt: SaveContext<'l, 'tcx>, | |
1016 | krate: &ast::Crate, | |
1017 | cratename: &str, | |
1018 | ); | |
cc61c64b | 1019 | } |
9cc50fc6 | 1020 | |
cc61c64b XL |
1021 | /// Dump the save-analysis results to a file. |
1022 | pub struct DumpHandler<'a> { | |
cc61c64b | 1023 | odir: Option<&'a Path>, |
abe05a73 | 1024 | cratename: String, |
cc61c64b | 1025 | } |
1a4d82fc | 1026 | |
cc61c64b | 1027 | impl<'a> DumpHandler<'a> { |
3b2f2976 | 1028 | pub fn new(odir: Option<&'a Path>, cratename: &str) -> DumpHandler<'a> { |
cc61c64b | 1029 | DumpHandler { |
3b2f2976 | 1030 | odir, |
abe05a73 | 1031 | cratename: cratename.to_owned(), |
cc61c64b XL |
1032 | } |
1033 | } | |
1a4d82fc | 1034 | |
3b2f2976 XL |
1035 | fn output_file(&self, ctx: &SaveContext) -> File { |
1036 | let sess = &ctx.tcx.sess; | |
1037 | let file_name = match ctx.config.output_file { | |
1038 | Some(ref s) => PathBuf::from(s), | |
1039 | None => { | |
1040 | let mut root_path = match self.odir { | |
1041 | Some(val) => val.join("save-analysis"), | |
1042 | None => PathBuf::from("save-analysis-temp"), | |
1043 | }; | |
cc61c64b | 1044 | |
3b2f2976 XL |
1045 | if let Err(e) = std::fs::create_dir_all(&root_path) { |
1046 | error!("Could not create directory {}: {}", root_path.display(), e); | |
1047 | } | |
cc61c64b | 1048 | |
abe05a73 XL |
1049 | let executable = sess.crate_types |
1050 | .borrow() | |
1051 | .iter() | |
1052 | .any(|ct| *ct == CrateTypeExecutable); | |
3b2f2976 XL |
1053 | let mut out_name = if executable { |
1054 | "".to_owned() | |
1055 | } else { | |
1056 | "lib".to_owned() | |
1057 | }; | |
1058 | out_name.push_str(&self.cratename); | |
1059 | out_name.push_str(&sess.opts.cg.extra_filename); | |
1060 | out_name.push_str(".json"); | |
1061 | root_path.push(&out_name); | |
cc61c64b | 1062 | |
3b2f2976 XL |
1063 | root_path |
1064 | } | |
cc61c64b | 1065 | }; |
3b2f2976 XL |
1066 | |
1067 | info!("Writing output to {}", file_name.display()); | |
1068 | ||
abe05a73 XL |
1069 | let output_file = File::create(&file_name).unwrap_or_else( |
1070 | |e| sess.fatal(&format!("Could not open {}: {}", file_name.display(), e)), | |
1071 | ); | |
3b2f2976 | 1072 | |
cc61c64b XL |
1073 | output_file |
1074 | } | |
1075 | } | |
1076 | ||
1077 | impl<'a> SaveHandler for DumpHandler<'a> { | |
abe05a73 XL |
1078 | fn save<'l, 'tcx>( |
1079 | &mut self, | |
1080 | save_ctxt: SaveContext<'l, 'tcx>, | |
1081 | krate: &ast::Crate, | |
1082 | cratename: &str, | |
1083 | ) { | |
3b2f2976 XL |
1084 | let output = &mut self.output_file(&save_ctxt); |
1085 | let mut dumper = JsonDumper::new(output, save_ctxt.config.clone()); | |
1086 | let mut visitor = DumpVisitor::new(save_ctxt, &mut dumper); | |
cc61c64b | 1087 | |
3b2f2976 XL |
1088 | visitor.dump_crate_info(cratename, krate); |
1089 | visit::walk_crate(&mut visitor, krate); | |
1a4d82fc | 1090 | } |
cc61c64b XL |
1091 | } |
1092 | ||
1093 | /// Call a callback with the results of save-analysis. | |
1094 | pub struct CallbackHandler<'b> { | |
1095 | pub callback: &'b mut FnMut(&rls_data::Analysis), | |
1096 | } | |
1097 | ||
1098 | impl<'b> SaveHandler for CallbackHandler<'b> { | |
abe05a73 XL |
1099 | fn save<'l, 'tcx>( |
1100 | &mut self, | |
1101 | save_ctxt: SaveContext<'l, 'tcx>, | |
1102 | krate: &ast::Crate, | |
1103 | cratename: &str, | |
1104 | ) { | |
cc61c64b XL |
1105 | // We're using the JsonDumper here because it has the format of the |
1106 | // save-analysis results that we will pass to the callback. IOW, we are | |
1107 | // using the JsonDumper to collect the save-analysis results, but not | |
1108 | // actually to dump them to a file. This is all a bit convoluted and | |
1109 | // there is certainly a simpler design here trying to get out (FIXME). | |
3b2f2976 XL |
1110 | let mut dumper = JsonDumper::with_callback(self.callback, save_ctxt.config.clone()); |
1111 | let mut visitor = DumpVisitor::new(save_ctxt, &mut dumper); | |
1112 | ||
1113 | visitor.dump_crate_info(cratename, krate); | |
1114 | visit::walk_crate(&mut visitor, krate); | |
1a4d82fc | 1115 | } |
cc61c64b | 1116 | } |
1a4d82fc | 1117 | |
abe05a73 XL |
1118 | pub fn process_crate<'l, 'tcx, H: SaveHandler>( |
1119 | tcx: TyCtxt<'l, 'tcx, 'tcx>, | |
1120 | krate: &ast::Crate, | |
1121 | analysis: &'l ty::CrateAnalysis, | |
1122 | cratename: &str, | |
1123 | config: Option<Config>, | |
1124 | mut handler: H, | |
1125 | ) { | |
2c00a5a8 XL |
1126 | tcx.dep_graph.with_ignore(|| { |
1127 | assert!(analysis.glob_map.is_some()); | |
1128 | ||
1129 | info!("Dumping crate {}", cratename); | |
1130 | ||
1131 | let save_ctxt = SaveContext { | |
1132 | tcx, | |
1133 | tables: &ty::TypeckTables::empty(None), | |
1134 | analysis, | |
1135 | span_utils: SpanUtils::new(&tcx.sess), | |
1136 | config: find_config(config), | |
1137 | impl_counter: Cell::new(0), | |
1138 | }; | |
1a4d82fc | 1139 | |
2c00a5a8 XL |
1140 | handler.save(save_ctxt, krate, cratename) |
1141 | }) | |
1a4d82fc | 1142 | } |
d9579d0f | 1143 | |
3b2f2976 XL |
1144 | fn find_config(supplied: Option<Config>) -> Config { |
1145 | if let Some(config) = supplied { | |
1146 | return config; | |
1147 | } | |
1148 | match env::var_os("RUST_SAVE_ANALYSIS_CONFIG") { | |
abe05a73 XL |
1149 | Some(config_string) => rustc_serialize::json::decode(config_string.to_str().unwrap()) |
1150 | .expect("Could not deserialize save-analysis config"), | |
3b2f2976 XL |
1151 | None => Config::default(), |
1152 | } | |
1153 | } | |
1154 | ||
d9579d0f AL |
1155 | // Utility functions for the module. |
1156 | ||
1157 | // Helper function to escape quotes in a string | |
1158 | fn escape(s: String) -> String { | |
1159 | s.replace("\"", "\"\"") | |
1160 | } | |
1161 | ||
7453a54e SL |
1162 | // Helper function to determine if a span came from a |
1163 | // macro expansion or syntax extension. | |
041b39d2 | 1164 | fn generated_code(span: Span) -> bool { |
ea8adc8c | 1165 | span.ctxt() != NO_EXPANSION || span == DUMMY_SP |
d9579d0f | 1166 | } |
041b39d2 XL |
1167 | |
1168 | // DefId::index is a newtype and so the JSON serialisation is ugly. Therefore | |
1169 | // we use our own Id which is the same, but without the newtype. | |
1170 | fn id_from_def_id(id: DefId) -> rls_data::Id { | |
1171 | rls_data::Id { | |
1172 | krate: id.krate.as_u32(), | |
2c00a5a8 | 1173 | index: id.index.as_raw_u32(), |
041b39d2 XL |
1174 | } |
1175 | } | |
1176 | ||
1177 | fn id_from_node_id(id: NodeId, scx: &SaveContext) -> rls_data::Id { | |
1178 | let def_id = scx.tcx.hir.opt_local_def_id(id); | |
ea8adc8c XL |
1179 | def_id.map(|id| id_from_def_id(id)).unwrap_or_else(|| { |
1180 | // Create a *fake* `DefId` out of a `NodeId` by subtracting the `NodeId` | |
1181 | // out of the maximum u32 value. This will work unless you have *billions* | |
1182 | // of definitions in a single crate (very unlikely to actually happen). | |
1183 | rls_data::Id { | |
1184 | krate: LOCAL_CRATE.as_u32(), | |
1185 | index: !id.as_u32(), | |
1186 | } | |
1187 | }) | |
041b39d2 XL |
1188 | } |
1189 | ||
1190 | fn null_id() -> rls_data::Id { | |
1191 | rls_data::Id { | |
1192 | krate: u32::max_value(), | |
1193 | index: u32::max_value(), | |
1194 | } | |
1195 | } | |
1196 | ||
1197 | fn lower_attributes(attrs: Vec<Attribute>, scx: &SaveContext) -> Vec<rls_data::Attribute> { | |
1198 | attrs.into_iter() | |
1199 | // Only retain real attributes. Doc comments are lowered separately. | |
1200 | .filter(|attr| attr.path != "doc") | |
1201 | .map(|mut attr| { | |
1202 | // Remove the surrounding '#[..]' or '#![..]' of the pretty printed | |
1203 | // attribute. First normalize all inner attribute (#![..]) to outer | |
1204 | // ones (#[..]), then remove the two leading and the one trailing character. | |
1205 | attr.style = ast::AttrStyle::Outer; | |
1206 | let value = pprust::attribute_to_string(&attr); | |
1207 | // This str slicing works correctly, because the leading and trailing characters | |
1208 | // are in the ASCII range and thus exactly one byte each. | |
1209 | let value = value[2..value.len()-1].to_string(); | |
1210 | ||
1211 | rls_data::Attribute { | |
3b2f2976 | 1212 | value, |
041b39d2 XL |
1213 | span: scx.span_from_span(attr.span), |
1214 | } | |
1215 | }).collect() | |
1216 | } |