lib.rs 40.4 KB
Newer Older
1
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2 3 4 5 6 7 8 9 10
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

11 12 13 14 15 16
#![crate_name = "rustc_save_analysis"]
#![crate_type = "dylib"]
#![crate_type = "rlib"]
#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
      html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
      html_root_url = "https://doc.rust-lang.org/nightly/")]
17
#![deny(warnings)]
18

19 20
#![feature(custom_attribute)]
#![allow(unused_attributes)]
21 22 23 24

#![cfg_attr(stage0, unstable(feature = "rustc_private", issue = "27812"))]
#![cfg_attr(stage0, feature(rustc_private))]
#![cfg_attr(stage0, feature(staged_api))]
25

26
#[macro_use] extern crate rustc;
27 28 29

#[macro_use] extern crate log;
#[macro_use] extern crate syntax;
N
Nick Cameron 已提交
30
extern crate rustc_serialize;
31
extern crate rustc_typeck;
32
extern crate syntax_pos;
33

34
extern crate rls_data;
35
extern crate rls_span;
36

N
Nick Cameron 已提交
37

38
mod csv_dumper;
N
Nick Cameron 已提交
39
mod json_api_dumper;
40 41 42 43 44 45 46
mod json_dumper;
mod data;
mod dump;
mod dump_visitor;
pub mod external_data;
#[macro_use]
pub mod span_utils;
N
Nick Cameron 已提交
47
mod sig;
48

49
use rustc::hir;
50
use rustc::hir::def::Def;
51
use rustc::hir::map::Node;
52
use rustc::hir::def_id::DefId;
53
use rustc::session::config::CrateType::CrateTypeExecutable;
54
use rustc::session::Session;
55
use rustc::ty::{self, TyCtxt};
56
use rustc_typeck::hir_ty_to_ty;
57 58

use std::env;
59
use std::fs::File;
60
use std::path::{Path, PathBuf};
N
Nick Cameron 已提交
61

62
use syntax::ast::{self, NodeId, PatKind, Attribute, CRATE_NODE_ID};
63
use syntax::parse::lexer::comments::strip_doc_comment_decoration;
64
use syntax::parse::token;
65
use syntax::symbol::keywords;
N
Nick Cameron 已提交
66
use syntax::visit::{self, Visitor};
67
use syntax::print::pprust::{ty_to_string, arg_to_string};
68 69
use syntax::codemap::MacroAttribute;
use syntax_pos::*;
70

A
Adolfo Ochagavía 已提交
71
pub use self::csv_dumper::CsvDumper;
N
Nick Cameron 已提交
72
pub use self::json_api_dumper::JsonApiDumper;
73
pub use self::json_dumper::JsonDumper;
A
Adolfo Ochagavía 已提交
74
pub use self::data::*;
75
pub use self::external_data::make_def_id;
A
Adolfo Ochagavía 已提交
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
pub use self::dump::Dump;
pub use self::dump_visitor::DumpVisitor;
use self::span_utils::SpanUtils;

// FIXME this is legacy code and should be removed
pub mod recorder {
    pub use self::Row::*;

    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
    pub enum Row {
        TypeRef,
        ModRef,
        VarRef,
        FnRef,
    }
}
92

N
Nick Cameron 已提交
93
pub struct SaveContext<'l, 'tcx: 'l> {
94
    tcx: TyCtxt<'l, 'tcx, 'tcx>,
95
    tables: &'l ty::TypeckTables<'tcx>,
96
    analysis: &'l ty::CrateAnalysis,
N
Nick Cameron 已提交
97
    span_utils: SpanUtils<'tcx>,
98 99
}

100 101 102 103
macro_rules! option_try(
    ($e:expr) => (match $e { Some(e) => e, None => return None })
);

N
Nick Cameron 已提交
104
impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
105 106 107 108
    // List external crates used by the current crate.
    pub fn get_external_crates(&self) -> Vec<CrateData> {
        let mut result = Vec::new();

A
Ariel Ben-Yehuda 已提交
109
        for n in self.tcx.sess.cstore.crates() {
110 111 112 113 114 115 116
            let span = match self.tcx.sess.cstore.extern_crate(n) {
                Some(ref c) => c.span,
                None => {
                    debug!("Skipping crate {}, no data", n);
                    continue;
                }
            };
N
Nick Cameron 已提交
117
            result.push(CrateData {
118
                name: self.tcx.sess.cstore.crate_name(n).to_string(),
119
                number: n.as_u32(),
120
                span: span,
N
Nick Cameron 已提交
121
            });
A
Ariel Ben-Yehuda 已提交
122
        }
123 124 125

        result
    }
N
Nick Cameron 已提交
126

127 128 129 130 131
    pub fn get_extern_item_data(&self, item: &ast::ForeignItem) -> Option<Data> {
        let qualname = format!("::{}", self.tcx.node_path_str(item.id));
        match item.node {
            ast::ForeignItemKind::Fn(ref decl, ref generics) => {
                let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Fn);
N
rebased  
Nick Cameron 已提交
132
                filter!(self.span_utils, sub_span, item.span, None);
133 134 135 136 137 138 139 140 141 142 143
                Some(Data::FunctionData(FunctionData {
                    id: item.id,
                    name: item.ident.to_string(),
                    qualname: qualname,
                    declaration: None,
                    span: sub_span.unwrap(),
                    scope: self.enclosing_scope(item.id),
                    value: make_signature(decl, generics),
                    visibility: From::from(&item.vis),
                    parent: None,
                    docs: docs_for_attrs(&item.attrs),
144
                    sig: sig::foreign_item_signature(item, self),
N
rebased  
Nick Cameron 已提交
145
                    attributes: item.attrs.clone(),
146 147 148 149 150
                }))
            }
            ast::ForeignItemKind::Static(ref ty, m) => {
                let keyword = if m { keywords::Mut } else { keywords::Static };
                let sub_span = self.span_utils.sub_span_after_keyword(item.span, keyword);
N
rebased  
Nick Cameron 已提交
151
                filter!(self.span_utils, sub_span, item.span, None);
152 153 154 155 156 157 158 159 160 161 162 163
                Some(Data::VariableData(VariableData {
                    id: item.id,
                    kind: VariableKind::Static,
                    name: item.ident.to_string(),
                    qualname: qualname,
                    span: sub_span.unwrap(),
                    scope: self.enclosing_scope(item.id),
                    parent: None,
                    value: String::new(),
                    type_value: ty_to_string(ty),
                    visibility: From::from(&item.vis),
                    docs: docs_for_attrs(&item.attrs),
164
                    sig: sig::foreign_item_signature(item, self),
N
rebased  
Nick Cameron 已提交
165
                    attributes: item.attrs.clone(),
166 167 168 169 170
                }))
            }
        }
    }

171
    pub fn get_item_data(&self, item: &ast::Item) -> Option<Data> {
N
Nick Cameron 已提交
172
        match item.node {
V
Vadim Petrochenkov 已提交
173
            ast::ItemKind::Fn(ref decl, .., ref generics, _) => {
174
                let qualname = format!("::{}", self.tcx.node_path_str(item.id));
N
Nick Cameron 已提交
175
                let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Fn);
176
                filter!(self.span_utils, sub_span, item.span, None);
177 178


179
                Some(Data::FunctionData(FunctionData {
N
Nick Cameron 已提交
180
                    id: item.id,
181
                    name: item.ident.to_string(),
N
Nick Cameron 已提交
182 183 184
                    qualname: qualname,
                    declaration: None,
                    span: sub_span.unwrap(),
185
                    scope: self.enclosing_scope(item.id),
186
                    value: make_signature(decl, generics),
187
                    visibility: From::from(&item.vis),
188
                    parent: None,
N
Nick Cameron 已提交
189
                    docs: docs_for_attrs(&item.attrs),
N
Nick Cameron 已提交
190
                    sig: sig::item_signature(item, self),
191
                    attributes: item.attrs.clone(),
192
                }))
N
Nick Cameron 已提交
193
            }
194
            ast::ItemKind::Static(ref typ, mt, ref expr) => {
195
                let qualname = format!("::{}", self.tcx.node_path_str(item.id));
196 197

                // If the variable is immutable, save the initialising expression.
198
                let (value, keyword) = match mt {
199 200 201 202
                    ast::Mutability::Mutable => (String::from("<mutable>"), keywords::Mut),
                    ast::Mutability::Immutable => {
                        (self.span_utils.snippet(expr.span), keywords::Static)
                    },
203 204
                };

205
                let sub_span = self.span_utils.sub_span_after_keyword(item.span, keyword);
206 207
                filter!(self.span_utils, sub_span, item.span, None);
                Some(Data::VariableData(VariableData {
208
                    id: item.id,
209
                    kind: VariableKind::Static,
210
                    name: item.ident.to_string(),
211 212
                    qualname: qualname,
                    span: sub_span.unwrap(),
213
                    scope: self.enclosing_scope(item.id),
214
                    parent: None,
215 216
                    value: value,
                    type_value: ty_to_string(&typ),
217
                    visibility: From::from(&item.vis),
N
Nick Cameron 已提交
218
                    docs: docs_for_attrs(&item.attrs),
N
Nick Cameron 已提交
219
                    sig: sig::item_signature(item, self),
220
                    attributes: item.attrs.clone(),
221
                }))
222
            }
223
            ast::ItemKind::Const(ref typ, ref expr) => {
224
                let qualname = format!("::{}", self.tcx.node_path_str(item.id));
225
                let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Const);
226 227
                filter!(self.span_utils, sub_span, item.span, None);
                Some(Data::VariableData(VariableData {
228
                    id: item.id,
229
                    kind: VariableKind::Const,
230
                    name: item.ident.to_string(),
231 232
                    qualname: qualname,
                    span: sub_span.unwrap(),
N
Nick Cameron 已提交
233
                    scope: self.enclosing_scope(item.id),
234
                    parent: None,
235 236
                    value: self.span_utils.snippet(expr.span),
                    type_value: ty_to_string(&typ),
237
                    visibility: From::from(&item.vis),
N
Nick Cameron 已提交
238
                    docs: docs_for_attrs(&item.attrs),
N
Nick Cameron 已提交
239
                    sig: sig::item_signature(item, self),
240
                    attributes: item.attrs.clone(),
241
                }))
242
            }
243
            ast::ItemKind::Mod(ref m) => {
244
                let qualname = format!("::{}", self.tcx.node_path_str(item.id));
245

246
                let cm = self.tcx.sess.codemap();
247 248 249
                let filename = cm.span_to_filename(m.inner);

                let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Mod);
250
                filter!(self.span_utils, sub_span, item.span, None);
251

252
                Some(Data::ModData(ModData {
253
                    id: item.id,
254
                    name: item.ident.to_string(),
255 256
                    qualname: qualname,
                    span: sub_span.unwrap(),
257
                    scope: self.enclosing_scope(item.id),
258
                    filename: filename,
259
                    items: m.items.iter().map(|i| i.id).collect(),
260
                    visibility: From::from(&item.vis),
N
Nick Cameron 已提交
261
                    docs: docs_for_attrs(&item.attrs),
N
Nick Cameron 已提交
262
                    sig: sig::item_signature(item, self),
263
                    attributes: item.attrs.clone(),
264
                }))
N
Nick Cameron 已提交
265
            }
266 267 268
            ast::ItemKind::Enum(ref def, _) => {
                let name = item.ident.to_string();
                let qualname = format!("::{}", self.tcx.node_path_str(item.id));
P
Peter Elmers 已提交
269
                let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Enum);
270
                filter!(self.span_utils, sub_span, item.span, None);
271 272 273 274 275
                let variants_str = def.variants.iter()
                                      .map(|v| v.node.name.to_string())
                                      .collect::<Vec<_>>()
                                      .join(", ");
                let val = format!("{}::{{{}}}", name, variants_str);
276
                Some(Data::EnumData(EnumData {
P
Peter Elmers 已提交
277
                    id: item.id,
278
                    name: name,
P
Peter Elmers 已提交
279 280
                    value: val,
                    span: sub_span.unwrap(),
281
                    qualname: qualname,
282
                    scope: self.enclosing_scope(item.id),
283
                    variants: def.variants.iter().map(|v| v.node.data.id()).collect(),
284
                    visibility: From::from(&item.vis),
N
Nick Cameron 已提交
285
                    docs: docs_for_attrs(&item.attrs),
N
Nick Cameron 已提交
286
                    sig: sig::item_signature(item, self),
287
                    attributes: item.attrs.clone(),
288
                }))
N
Nick Cameron 已提交
289
            }
V
Vadim Petrochenkov 已提交
290
            ast::ItemKind::Impl(.., ref trait_ref, ref typ, _) => {
N
Nick Cameron 已提交
291 292 293
                let mut type_data = None;
                let sub_span;

294
                let parent = self.enclosing_scope(item.id);
N
Nick Cameron 已提交
295 296 297

                match typ.node {
                    // Common case impl for a struct or something basic.
298
                    ast::TyKind::Path(None, ref path) => {
299 300 301
                        if generated_code(path.span) {
                            return None;
                        }
302
                        sub_span = self.span_utils.sub_span_for_type_name(path.span);
N
Nick Cameron 已提交
303 304
                        type_data = self.lookup_ref_id(typ.id).map(|id| {
                            TypeRefData {
305
                                span: sub_span.unwrap(),
N
Nick Cameron 已提交
306
                                scope: parent,
A
Adolfo Ochagavía 已提交
307 308
                                ref_id: Some(id),
                                qualname: String::new() // FIXME: generate the real qualname
N
Nick Cameron 已提交
309
                            }
N
Nick Cameron 已提交
310
                        });
N
Nick Cameron 已提交
311
                    }
N
Nick Cameron 已提交
312 313
                    _ => {
                        // Less useful case, impl for a compound type.
314
                        let span = typ.span;
315
                        sub_span = self.span_utils.sub_span_for_type_name(span).or(Some(span));
N
Nick Cameron 已提交
316 317 318
                    }
                }

N
Nick Cameron 已提交
319 320
                let trait_data = trait_ref.as_ref()
                                          .and_then(|tr| self.get_trait_ref_data(tr, parent));
N
Nick Cameron 已提交
321

322
                filter!(self.span_utils, sub_span, typ.span, None);
A
Adolfo Ochagavía 已提交
323
                Some(Data::ImplData(ImplData2 {
N
Nick Cameron 已提交
324
                    id: item.id,
325
                    span: sub_span.unwrap(),
N
Nick Cameron 已提交
326 327 328
                    scope: parent,
                    trait_ref: trait_data,
                    self_ref: type_data,
329
                }))
N
Nick Cameron 已提交
330
            }
331 332
            _ => {
                // FIXME
333
                bug!();
334 335 336 337
            }
        }
    }

N
Nick Cameron 已提交
338 339 340 341
    pub fn get_field_data(&self,
                          field: &ast::StructField,
                          scope: NodeId)
                          -> Option<VariableData> {
342
        if let Some(ident) = field.ident {
N
Nick Cameron 已提交
343
            let name = ident.to_string();
344
            let qualname = format!("::{}::{}", self.tcx.node_path_str(scope), ident);
345 346
            let sub_span = self.span_utils.sub_span_before_token(field.span, token::Colon);
            filter!(self.span_utils, sub_span, field.span, None);
347
            let def_id = self.tcx.hir.local_def_id(field.id);
348
            let typ = self.tcx.type_of(def_id).to_string();
N
Nick Cameron 已提交
349

350
            Some(VariableData {
351
                id: field.id,
352
                kind: VariableKind::Field,
N
Nick Cameron 已提交
353
                name: name,
354 355 356
                qualname: qualname,
                span: sub_span.unwrap(),
                scope: scope,
357
                parent: Some(make_def_id(scope, &self.tcx.hir)),
358 359
                value: "".to_owned(),
                type_value: typ,
360
                visibility: From::from(&field.vis),
N
Nick Cameron 已提交
361
                docs: docs_for_attrs(&field.attrs),
362
                sig: sig::field_signature(field, self),
363
                attributes: field.attrs.clone(),
364 365 366
            })
        } else {
            None
367 368 369
        }
    }

N
Nick Cameron 已提交
370 371
    // FIXME would be nice to take a MethodItem here, but the ast provides both
    // trait and impl flavours, so the caller must do the disassembly.
N
Nick Cameron 已提交
372 373 374 375 376
    pub fn get_method_data(&self,
                           id: ast::NodeId,
                           name: ast::Name,
                           span: Span)
                           -> Option<FunctionData> {
N
Nick Cameron 已提交
377 378
        // The qualname for a method is the trait name or name of the struct in an impl in
        // which the method is declared in, followed by the method's name.
379
        let (qualname, parent_scope, decl_id, vis, docs, attributes) =
380 381
          match self.tcx.impl_of_method(self.tcx.hir.local_def_id(id)) {
            Some(impl_id) => match self.tcx.hir.get_if_local(impl_id) {
382
                Some(Node::NodeItem(item)) => {
N
Nick Cameron 已提交
383
                    match item.node {
V
Vadim Petrochenkov 已提交
384
                        hir::ItemImpl(.., ref ty, _) => {
N
Nick Cameron 已提交
385
                            let mut result = String::from("<");
386
                            result.push_str(&self.tcx.hir.node_to_pretty_string(ty.id));
N
Nick Cameron 已提交
387

388
                            let trait_id = self.tcx.trait_id_of_impl(impl_id);
389
                            let mut decl_id = None;
390
                            if let Some(def_id) = trait_id {
391 392
                                result.push_str(" as ");
                                result.push_str(&self.tcx.item_path_str(def_id));
393 394 395
                                self.tcx.associated_items(def_id)
                                    .find(|item| item.name == name)
                                    .map(|item| decl_id = Some(item.def_id));
N
Nick Cameron 已提交
396 397
                            }
                            result.push_str(">");
398 399 400

                            (result, trait_id, decl_id,
                             From::from(&item.vis),
401
                             docs_for_attrs(&item.attrs),
402
                             item.attrs.to_vec())
N
Nick Cameron 已提交
403 404
                        }
                        _ => {
405 406 407 408
                            span_bug!(span,
                                      "Container {:?} for method {} not an impl?",
                                      impl_id,
                                      id);
N
Nick Cameron 已提交
409
                        }
N
Nick Cameron 已提交
410
                    }
N
Nick Cameron 已提交
411
                }
412
                r => {
413 414 415 416 417
                    span_bug!(span,
                              "Container {:?} for method {} is not a node item {:?}",
                              impl_id,
                              id,
                              r);
N
Nick Cameron 已提交
418
                }
N
Nick Cameron 已提交
419
            },
420
            None => match self.tcx.trait_of_item(self.tcx.hir.local_def_id(id)) {
N
Nick Cameron 已提交
421
                Some(def_id) => {
422
                    match self.tcx.hir.get_if_local(def_id) {
423
                        Some(Node::NodeItem(item)) => {
N
Nick Cameron 已提交
424
                            (format!("::{}", self.tcx.item_path_str(def_id)),
425
                             Some(def_id), None,
N
Nick Cameron 已提交
426
                             From::from(&item.vis),
427
                             docs_for_attrs(&item.attrs),
428
                             item.attrs.to_vec())
N
Nick Cameron 已提交
429
                        }
430
                        r => {
431 432 433 434 435 436
                            span_bug!(span,
                                      "Could not find container {:?} for \
                                       method {}, got {:?}",
                                      def_id,
                                      id,
                                      r);
N
Nick Cameron 已提交
437 438
                        }
                    }
N
Nick Cameron 已提交
439
                }
N
Nick Cameron 已提交
440
                None => {
441 442 443 444
                    debug!("Could not find container for method {} at {:?}", id, span);
                    // This is not necessarily a bug, if there was a compilation error, the tables
                    // we need might not exist.
                    return None;
N
Nick Cameron 已提交
445
                }
N
Nick Cameron 已提交
446 447 448
            },
        };

449
        let qualname = format!("{}::{}", qualname, name);
N
Nick Cameron 已提交
450 451

        let sub_span = self.span_utils.sub_span_after_keyword(span, keywords::Fn);
452
        filter!(self.span_utils, sub_span, span, None);
453

454
        Some(FunctionData {
N
Nick Cameron 已提交
455
            id: id,
N
Nick Cameron 已提交
456
            name: name.to_string(),
N
Nick Cameron 已提交
457 458 459 460
            qualname: qualname,
            declaration: decl_id,
            span: sub_span.unwrap(),
            scope: self.enclosing_scope(id),
461 462
            // FIXME you get better data here by using the visitor.
            value: String::new(),
463
            visibility: vis,
464
            parent: parent_scope,
N
Nick Cameron 已提交
465
            docs: docs,
N
Nick Cameron 已提交
466
            sig: None,
467
            attributes: attributes,
468
        })
N
Nick Cameron 已提交
469 470
    }

N
Nick Cameron 已提交
471 472 473 474
    pub fn get_trait_ref_data(&self,
                              trait_ref: &ast::TraitRef,
                              parent: NodeId)
                              -> Option<TypeRefData> {
475
        self.lookup_ref_id(trait_ref.ref_id).and_then(|def_id| {
476
            let span = trait_ref.path.span;
477 478 479
            if generated_code(span) {
                return None;
            }
480 481 482 483
            let sub_span = self.span_utils.sub_span_for_type_name(span).or(Some(span));
            filter!(self.span_utils, sub_span, span, None);
            Some(TypeRefData {
                span: sub_span.unwrap(),
N
Nick Cameron 已提交
484
                scope: parent,
A
Adolfo Ochagavía 已提交
485 486
                ref_id: Some(def_id),
                qualname: String::new() // FIXME: generate the real qualname
487
            })
N
Nick Cameron 已提交
488 489 490
        })
    }

N
Nick Cameron 已提交
491
    pub fn get_expr_data(&self, expr: &ast::Expr) -> Option<Data> {
492
        let hir_node = self.tcx.hir.expect_expr(expr.id);
493
        let ty = self.tables.expr_ty_adjusted_opt(&hir_node);
494 495 496
        if ty.is_none() || ty.unwrap().sty == ty::TyError {
            return None;
        }
497
        match expr.node {
498
            ast::ExprKind::Field(ref sub_ex, ident) => {
499
                let hir_node = match self.tcx.hir.find(sub_ex.id) {
500 501 502 503 504 505 506
                    Some(Node::NodeExpr(expr)) => expr,
                    _ => {
                        debug!("Missing or weird node for sub-expression {} in {:?}",
                               sub_ex.id, expr);
                        return None;
                    }
                };
507
                match self.tables.expr_ty_adjusted(&hir_node).sty {
508
                    ty::TyAdt(def, _) if !def.is_enum() => {
509 510
                        let f = def.struct_variant().field_named(ident.node.name);
                        let sub_span = self.span_utils.span_for_last_ident(expr.span);
511
                        filter!(self.span_utils, sub_span, expr.span, None);
512 513 514 515 516 517
                        return Some(Data::VariableRefData(VariableRefData {
                            name: ident.node.to_string(),
                            span: sub_span.unwrap(),
                            scope: self.enclosing_scope(expr.id),
                            ref_id: f.did,
                        }));
518
                    }
N
Nick Cameron 已提交
519
                    _ => {
520
                        debug!("Expected struct or union type, found {:?}", ty);
N
Nick Cameron 已提交
521 522
                        None
                    }
523 524
                }
            }
V
Vadim Petrochenkov 已提交
525
            ast::ExprKind::Struct(ref path, ..) => {
526
                match self.tables.expr_ty_adjusted(&hir_node).sty {
527
                    ty::TyAdt(def, _) if !def.is_enum() => {
528
                        let sub_span = self.span_utils.span_for_last_ident(path.span);
529
                        filter!(self.span_utils, sub_span, path.span, None);
N
Nick Cameron 已提交
530
                        Some(Data::TypeRefData(TypeRefData {
531
                            span: sub_span.unwrap(),
532
                            scope: self.enclosing_scope(expr.id),
A
Adolfo Ochagavía 已提交
533 534
                            ref_id: Some(def.did),
                            qualname: String::new() // FIXME: generate the real qualname
N
Nick Cameron 已提交
535
                        }))
536 537
                    }
                    _ => {
538
                        // FIXME ty could legitimately be an enum, but then we will fail
N
Nick Cameron 已提交
539
                        // later if we try to look up the fields.
540
                        debug!("expected struct or union, found {:?}", ty);
N
Nick Cameron 已提交
541
                        None
542 543 544
                    }
                }
            }
545
            ast::ExprKind::MethodCall(..) => {
546
                let method_id = self.tables.type_dependent_defs[&expr.id].def_id();
547
                let (def_id, decl_id) = match self.tcx.associated_item(method_id).container {
548
                    ty::ImplContainer(_) => (Some(method_id), None),
N
Nick Cameron 已提交
549
                    ty::TraitContainer(_) => (None, Some(method_id)),
550 551
                };
                let sub_span = self.span_utils.sub_span_for_meth_name(expr.span);
552
                filter!(self.span_utils, sub_span, expr.span, None);
553
                let parent = self.enclosing_scope(expr.id);
554 555
                Some(Data::MethodCallData(MethodCallData {
                    span: sub_span.unwrap(),
N
Nick Cameron 已提交
556
                    scope: parent,
557
                    ref_id: def_id,
N
Nick Cameron 已提交
558
                    decl_id: decl_id,
559 560
                }))
            }
561
            ast::ExprKind::Path(_, ref path) => {
562
                self.get_path_data(expr.id, path)
N
Nick Cameron 已提交
563
            }
N
Nick Cameron 已提交
564
            _ => {
565
                // FIXME
566
                bug!();
N
Nick Cameron 已提交
567 568 569 570
            }
        }
    }

571
    pub fn get_path_def(&self, id: NodeId) -> Def {
572
        match self.tcx.hir.get(id) {
573 574 575 576 577 578 579 580 581 582
            Node::NodeTraitRef(tr) => tr.path.def,

            Node::NodeItem(&hir::Item { node: hir::ItemUse(ref path, _), .. }) |
            Node::NodeVisibility(&hir::Visibility::Restricted { ref path, .. }) => path.def,

            Node::NodeExpr(&hir::Expr { node: hir::ExprPath(ref qpath), .. }) |
            Node::NodeExpr(&hir::Expr { node: hir::ExprStruct(ref qpath, ..), .. }) |
            Node::NodePat(&hir::Pat { node: hir::PatKind::Path(ref qpath), .. }) |
            Node::NodePat(&hir::Pat { node: hir::PatKind::Struct(ref qpath, ..), .. }) |
            Node::NodePat(&hir::Pat { node: hir::PatKind::TupleStruct(ref qpath, ..), .. }) => {
583
                self.tables.qpath_def(qpath, id)
584 585 586 587 588 589
            }

            Node::NodeLocal(&hir::Pat { node: hir::PatKind::Binding(_, def_id, ..), .. }) => {
                Def::Local(def_id)
            }

590 591 592 593 594 595
            Node::NodeTy(ty) => {
                if let hir::Ty { node: hir::TyPath(ref qpath), .. } = *ty {
                    match *qpath {
                        hir::QPath::Resolved(_, ref path) => path.def,
                        hir::QPath::TypeRelative(..) => {
                            let ty = hir_ty_to_ty(self.tcx, ty);
596 597 598
                            if let ty::TyProjection(proj) = ty.sty {
                                for item in self.tcx.associated_items(proj.trait_ref.def_id) {
                                    if item.kind == ty::AssociatedKind::Type {
599
                                        if item.name == proj.item_name(self.tcx) {
600 601 602 603 604
                                            return Def::AssociatedTy(item.def_id);
                                        }
                                    }
                                }
                            }
605
                            Def::Err
606
                        }
607
                    }
608 609
                } else {
                    Def::Err
610 611 612 613
                }
            }

            _ => Def::Err
614
        }
615
    }
616

617 618
    pub fn get_path_data(&self, id: NodeId, path: &ast::Path) -> Option<Data> {
        let def = self.get_path_def(id);
N
Nick Cameron 已提交
619
        let sub_span = self.span_utils.span_for_last_ident(path.span);
620
        filter!(self.span_utils, sub_span, path.span, None);
N
Nick Cameron 已提交
621
        match def {
622 623 624 625 626
            Def::Upvar(..) |
            Def::Local(..) |
            Def::Static(..) |
            Def::Const(..) |
            Def::AssociatedConst(..) |
627
            Def::StructCtor(..) |
628
            Def::VariantCtor(..) => {
629
                Some(Data::VariableRefData(VariableRefData {
N
Nick Cameron 已提交
630 631
                    name: self.span_utils.snippet(sub_span.unwrap()),
                    span: sub_span.unwrap(),
632
                    scope: self.enclosing_scope(id),
N
Nick Cameron 已提交
633
                    ref_id: def.def_id(),
634
                }))
N
Nick Cameron 已提交
635
            }
636
            Def::Struct(def_id) |
637
            Def::Variant(def_id, ..) |
638
            Def::Union(def_id) |
639 640
            Def::Enum(def_id) |
            Def::TyAlias(def_id) |
641
            Def::AssociatedTy(def_id) |
642
            Def::Trait(def_id) |
643
            Def::TyParam(def_id) => {
644
                Some(Data::TypeRefData(TypeRefData {
N
Nick Cameron 已提交
645
                    span: sub_span.unwrap(),
A
Adolfo Ochagavía 已提交
646
                    ref_id: Some(def_id),
647
                    scope: self.enclosing_scope(id),
A
Adolfo Ochagavía 已提交
648
                    qualname: String::new() // FIXME: generate the real qualname
649
                }))
N
Nick Cameron 已提交
650
            }
651
            Def::Method(decl_id) => {
N
Nick Cameron 已提交
652
                let sub_span = self.span_utils.sub_span_for_meth_name(path.span);
653
                filter!(self.span_utils, sub_span, path.span, None);
654
                let def_id = if decl_id.is_local() {
655 656
                    let ti = self.tcx.associated_item(decl_id);
                    self.tcx.associated_items(ti.container.id())
657
                        .find(|item| item.name == ti.name && item.defaultness.has_value())
658
                        .map(|item| item.def_id)
N
Nick Cameron 已提交
659 660 661
                } else {
                    None
                };
662
                Some(Data::MethodCallData(MethodCallData {
N
Nick Cameron 已提交
663
                    span: sub_span.unwrap(),
664
                    scope: self.enclosing_scope(id),
N
Nick Cameron 已提交
665 666
                    ref_id: def_id,
                    decl_id: Some(decl_id),
667
                }))
N
Nick Cameron 已提交
668
            }
669
            Def::Fn(def_id) => {
670
                Some(Data::FunctionCallData(FunctionCallData {
N
Nick Cameron 已提交
671 672
                    ref_id: def_id,
                    span: sub_span.unwrap(),
673
                    scope: self.enclosing_scope(id),
674
                }))
N
Nick Cameron 已提交
675
            }
676
            Def::Mod(def_id) => {
677
                Some(Data::ModRefData(ModRefData {
A
Adolfo Ochagavía 已提交
678
                    ref_id: Some(def_id),
679 680
                    span: sub_span.unwrap(),
                    scope: self.enclosing_scope(id),
A
Adolfo Ochagavía 已提交
681
                    qualname: String::new() // FIXME: generate the real qualname
682
                }))
683
            }
684 685 686
            Def::PrimTy(..) |
            Def::SelfTy(..) |
            Def::Label(..) |
687
            Def::Macro(..) |
688
            Def::GlobalAsm(..) |
689
            Def::Err => None,
N
Nick Cameron 已提交
690 691 692
        }
    }

693 694
    pub fn get_field_ref_data(&self,
                              field_ref: &ast::Field,
695
                              variant: &ty::VariantDef,
696
                              parent: NodeId)
697
                              -> Option<VariableRefData> {
698 699 700
        let f = variant.field_named(field_ref.ident.node.name);
        // We don't really need a sub-span here, but no harm done
        let sub_span = self.span_utils.span_for_last_ident(field_ref.ident.span);
701 702
        filter!(self.span_utils, sub_span, field_ref.ident.span, None);
        Some(VariableRefData {
703 704 705 706
            name: field_ref.ident.node.to_string(),
            span: sub_span.unwrap(),
            scope: parent,
            ref_id: f.did,
707
        })
708 709
    }

710 711 712 713 714 715 716 717 718 719 720 721
    /// Attempt to return MacroUseData for any AST node.
    ///
    /// For a given piece of AST defined by the supplied Span and NodeId,
    /// returns None if the node is not macro-generated or the span is malformed,
    /// else uses the expansion callsite and callee to return some MacroUseData.
    pub fn get_macro_use_data(&self, span: Span, id: NodeId) -> Option<MacroUseData> {
        if !generated_code(span) {
            return None;
        }
        // Note we take care to use the source callsite/callee, to handle
        // nested expansions and ensure we only generate data for source-visible
        // macro uses.
722 723
        let callsite = span.source_callsite();
        let callee = option_try!(span.source_callee());
724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741
        let callee_span = option_try!(callee.span);

        // Ignore attribute macros, their spans are usually mangled
        if let MacroAttribute(_) = callee.format {
            return None;
        }

        // If the callee is an imported macro from an external crate, need to get
        // the source span and name from the session, as their spans are localized
        // when read in, and no longer correspond to the source.
        if let Some(mac) = self.tcx.sess.imported_macro_spans.borrow().get(&callee_span) {
            let &(ref mac_name, mac_span) = mac;
            return Some(MacroUseData {
                                        span: callsite,
                                        name: mac_name.clone(),
                                        callee_span: mac_span,
                                        scope: self.enclosing_scope(id),
                                        imported: true,
A
Adolfo Ochagavía 已提交
742
                                        qualname: String::new()// FIXME: generate the real qualname
743 744 745 746 747 748 749 750 751
                                    });
        }

        Some(MacroUseData {
            span: callsite,
            name: callee.name().to_string(),
            callee_span: callee_span,
            scope: self.enclosing_scope(id),
            imported: false,
A
Adolfo Ochagavía 已提交
752
            qualname: String::new() // FIXME: generate the real qualname
753 754 755
        })
    }

756 757
    pub fn get_data_for_id(&self, _id: &NodeId) -> Data {
        // FIXME
758
        bug!();
N
Nick Cameron 已提交
759
    }
N
Nick Cameron 已提交
760 761

    fn lookup_ref_id(&self, ref_id: NodeId) -> Option<DefId> {
762 763
        match self.get_path_def(ref_id) {
            Def::PrimTy(_) | Def::SelfTy(..) | Def::Err => None,
764
            def => Some(def.def_id()),
N
Nick Cameron 已提交
765 766 767
        }
    }

768
    #[inline]
769
    pub fn enclosing_scope(&self, id: NodeId) -> NodeId {
770
        self.tcx.hir.get_enclosing_scope(id).unwrap_or(CRATE_NODE_ID)
771
    }
772 773
}

774
fn make_signature(decl: &ast::FnDecl, generics: &ast::Generics) -> String {
775
    let mut sig = "fn ".to_owned();
776 777 778
    if !generics.lifetimes.is_empty() || !generics.ty_params.is_empty() {
        sig.push('<');
        sig.push_str(&generics.lifetimes.iter()
J
Jeffrey Seyfried 已提交
779
                              .map(|l| l.lifetime.ident.name.to_string())
780 781 782 783 784 785 786 787 788 789 790 791 792 793 794
                              .collect::<Vec<_>>()
                              .join(", "));
        if !generics.lifetimes.is_empty() {
            sig.push_str(", ");
        }
        sig.push_str(&generics.ty_params.iter()
                              .map(|l| l.ident.to_string())
                              .collect::<Vec<_>>()
                              .join(", "));
        sig.push_str("> ");
    }
    sig.push('(');
    sig.push_str(&decl.inputs.iter().map(arg_to_string).collect::<Vec<_>>().join(", "));
    sig.push(')');
    match decl.output {
795
        ast::FunctionRetTy::Default(_) => sig.push_str(" -> ()"),
796 797 798 799 800 801
        ast::FunctionRetTy::Ty(ref t) => sig.push_str(&format!(" -> {}", ty_to_string(t))),
    }

    sig
}

N
Nick Cameron 已提交
802 803
// An AST visitor for collecting paths from patterns.
struct PathCollector {
804 805
    // The Row field identifies the kind of pattern.
    collected_paths: Vec<(NodeId, ast::Path, ast::Mutability, recorder::Row)>,
N
Nick Cameron 已提交
806 807 808 809
}

impl PathCollector {
    fn new() -> PathCollector {
N
Nick Cameron 已提交
810
        PathCollector { collected_paths: vec![] }
N
Nick Cameron 已提交
811 812 813
    }
}

814
impl<'a> Visitor<'a> for PathCollector {
N
Nick Cameron 已提交
815 816
    fn visit_pat(&mut self, p: &ast::Pat) {
        match p.node {
V
Vadim Petrochenkov 已提交
817
            PatKind::Struct(ref path, ..) => {
818
                self.collected_paths.push((p.id, path.clone(),
819
                                           ast::Mutability::Mutable, recorder::TypeRef));
N
Nick Cameron 已提交
820
            }
V
Vadim Petrochenkov 已提交
821
            PatKind::TupleStruct(ref path, ..) |
822
            PatKind::Path(_, ref path) => {
823
                self.collected_paths.push((p.id, path.clone(),
824
                                           ast::Mutability::Mutable, recorder::VarRef));
N
Nick Cameron 已提交
825
            }
826
            PatKind::Ident(bm, ref path1, _) => {
827
                debug!("PathCollector, visit ident in pat {}: {:?} {:?}",
828
                       path1.node,
829 830
                       p.span,
                       path1.span);
N
Nick Cameron 已提交
831 832 833 834
                let immut = match bm {
                    // Even if the ref is mut, you can't change the ref, only
                    // the data pointed at, so showing the initialising expression
                    // is still worthwhile.
835
                    ast::BindingMode::ByRef(_) => ast::Mutability::Immutable,
836
                    ast::BindingMode::ByValue(mt) => mt,
N
Nick Cameron 已提交
837 838
                };
                // collect path for either visit_local or visit_arm
E
Eduard Burtescu 已提交
839
                let path = ast::Path::from_ident(path1.span, path1.node);
N
Nick Cameron 已提交
840 841 842 843 844 845 846 847
                self.collected_paths.push((p.id, path, immut, recorder::VarRef));
            }
            _ => {}
        }
        visit::walk_pat(self, p);
    }
}

N
Nick Cameron 已提交
848 849 850 851
fn docs_for_attrs(attrs: &[Attribute]) -> String {
    let mut result = String::new();

    for attr in attrs {
852
        if attr.check_name("doc") {
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
                }
N
Nick Cameron 已提交
859 860 861 862 863 864 865 866
                result.push('\n');
            }
        }
    }

    result
}

867
#[derive(Clone, Copy, Debug, RustcEncodable)]
868 869 870
pub enum Format {
    Csv,
    Json,
871
    JsonApi,
872 873 874 875 876 877
}

impl Format {
    fn extension(&self) -> &'static str {
        match *self {
            Format::Csv => ".csv",
878
            Format::Json | Format::JsonApi => ".json",
879 880 881 882
        }
    }
}

883 884 885 886 887 888 889
/// Defines what to do with the results of saving the analysis.
pub trait SaveHandler {
    fn save<'l, 'tcx>(&mut self,
                      save_ctxt: SaveContext<'l, 'tcx>,
                      krate: &ast::Crate,
                      cratename: &str);
}
890

891 892 893 894 895 896
/// Dump the save-analysis results to a file.
pub struct DumpHandler<'a> {
    format: Format,
    odir: Option<&'a Path>,
    cratename: String
}
897

898 899 900 901 902 903 904 905
impl<'a> DumpHandler<'a> {
    pub fn new(format: Format, odir: Option<&'a Path>, cratename: &str) -> DumpHandler<'a> {
        DumpHandler {
            format: format,
            odir: odir,
            cratename: cratename.to_owned()
        }
    }
906

907 908 909 910 911 912 913 914
    fn output_file(&self, sess: &Session) -> File {
        let mut root_path = match env::var_os("RUST_SAVE_ANALYSIS_FOLDER") {
            Some(val) => PathBuf::from(val),
            None => match self.odir {
                Some(val) => val.join("save-analysis"),
                None => PathBuf::from("save-analysis-temp"),
            },
        };
915

916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940
        if let Err(e) = std::fs::create_dir_all(&root_path) {
            error!("Could not create directory {}: {}", root_path.display(), e);
        }

        {
            let disp = root_path.display();
            info!("Writing output to {}", disp);
        }

        let executable = sess.crate_types.borrow().iter().any(|ct| *ct == CrateTypeExecutable);
        let mut out_name = if executable {
            "".to_owned()
        } else {
            "lib".to_owned()
        };
        out_name.push_str(&self.cratename);
        out_name.push_str(&sess.opts.cg.extra_filename);
        out_name.push_str(self.format.extension());
        root_path.push(&out_name);
        let output_file = File::create(&root_path).unwrap_or_else(|e| {
            let disp = root_path.display();
            sess.fatal(&format!("Could not open {}: {}", disp, e));
        });
        root_path.pop();
        output_file
941
    }
942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959
}

impl<'a> SaveHandler for DumpHandler<'a> {
    fn save<'l, 'tcx>(&mut self,
                      save_ctxt: SaveContext<'l, 'tcx>,
                      krate: &ast::Crate,
                      cratename: &str) {
        macro_rules! dump {
            ($new_dumper: expr) => {{
                let mut dumper = $new_dumper;
                let mut visitor = DumpVisitor::new(save_ctxt, &mut dumper);

                visitor.dump_crate_info(cratename, krate);
                visit::walk_crate(&mut visitor, krate);
            }}
        }

        let output = &mut self.output_file(&save_ctxt.tcx.sess);
960

961 962 963 964 965
        match self.format {
            Format::Csv => dump!(CsvDumper::new(output)),
            Format::Json => dump!(JsonDumper::new(output)),
            Format::JsonApi => dump!(JsonApiDumper::new(output)),
        }
966
    }
967
}
968

969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007
/// Call a callback with the results of save-analysis.
pub struct CallbackHandler<'b> {
    pub callback: &'b mut FnMut(&rls_data::Analysis),
}

impl<'b> SaveHandler for CallbackHandler<'b> {
    fn save<'l, 'tcx>(&mut self,
                      save_ctxt: SaveContext<'l, 'tcx>,
                      krate: &ast::Crate,
                      cratename: &str) {
        macro_rules! dump {
            ($new_dumper: expr) => {{
                let mut dumper = $new_dumper;
                let mut visitor = DumpVisitor::new(save_ctxt, &mut dumper);

                visitor.dump_crate_info(cratename, krate);
                visit::walk_crate(&mut visitor, krate);
            }}
        }

        // We're using the JsonDumper here because it has the format of the
        // save-analysis results that we will pass to the callback. IOW, we are
        // using the JsonDumper to collect the save-analysis results, but not
        // actually to dump them to a file. This is all a bit convoluted and
        // there is certainly a simpler design here trying to get out (FIXME).
        dump!(JsonDumper::with_callback(self.callback))
    }
}

pub fn process_crate<'l, 'tcx, H: SaveHandler>(tcx: TyCtxt<'l, 'tcx, 'tcx>,
                                               krate: &ast::Crate,
                                               analysis: &'l ty::CrateAnalysis,
                                               cratename: &str,
                                               mut handler: H) {
    let _ignore = tcx.dep_graph.in_ignore();

    assert!(analysis.glob_map.is_some());

    info!("Dumping crate {}", cratename);
1008

1009 1010
    let save_ctxt = SaveContext {
        tcx: tcx,
1011
        tables: &ty::TypeckTables::empty(),
1012 1013 1014
        analysis: analysis,
        span_utils: SpanUtils::new(&tcx.sess),
    };
1015

1016
    handler.save(save_ctxt, krate, cratename)
1017
}
1018 1019 1020 1021 1022 1023 1024 1025

// Utility functions for the module.

// Helper function to escape quotes in a string
fn escape(s: String) -> String {
    s.replace("\"", "\"\"")
}

1026 1027
// Helper function to determine if a span came from a
// macro expansion or syntax extension.
N
Nick Cameron 已提交
1028
fn generated_code(span: Span) -> bool {
1029
    span.ctxt != NO_EXPANSION || span == DUMMY_SP
1030
}
N
Nick Cameron 已提交
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044

// DefId::index is a newtype and so the JSON serialisation is ugly. Therefore
// we use our own Id which is the same, but without the newtype.
fn id_from_def_id(id: DefId) -> rls_data::Id {
    rls_data::Id {
        krate: id.krate.as_u32(),
        index: id.index.as_u32(),
    }
}

fn id_from_node_id(id: NodeId, scx: &SaveContext) -> rls_data::Id {
    let def_id = scx.tcx.hir.local_def_id(id);
    id_from_def_id(def_id)
}