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