--- /dev/null
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// 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.
+
+use std::io::Write;
+
+use middle::def_id::{DefId, DefIndex};
+use syntax::codemap::Span;
+
+use super::data::*;
+use super::dump::Dump;
+use super::span_utils::SpanUtils;
+
+pub struct CsvDumper<'a, 'b, W: 'b> {
+ output: &'b mut W,
+ dump_spans: bool,
+ span: SpanUtils<'a>
+}
+
+impl<'a, 'b, W: Write> CsvDumper<'a, 'b, W> {
+ pub fn new(writer: &'b mut W, span: SpanUtils<'a>) -> CsvDumper<'a, 'b, W> {
+ CsvDumper { output: writer, dump_spans: false, span: span }
+ }
+
+ fn record(&mut self, kind: &str, span: Span, values: String) {
+ let span_str = self.span.extent_str(span);
+ if let Err(_) = write!(self.output, "{},{}{}\n", kind, span_str, values) {
+ error!("Error writing output");
+ }
+ }
+
+ fn record_raw(&mut self, info: &str) {
+ if let Err(_) = write!(self.output, "{}", info) {
+ error!("Error writing output '{}'", info);
+ }
+ }
+
+ pub fn dump_span(&mut self, kind: &str, span: Span) {
+ assert!(self.dump_spans);
+ let result = format!("span,kind,{},{},text,\"{}\"\n",
+ kind,
+ self.span.extent_str(span),
+ escape(self.span.snippet(span)));
+ self.record_raw(&result);
+ }
+}
+
+impl<'a, 'b, W: Write + 'b> Dump for CsvDumper<'a, 'b, W> {
+ fn crate_prelude(&mut self, span: Span, data: CratePreludeData) {
+ let crate_root = data.crate_root.unwrap_or("<no source>".to_owned());
+
+ let values = make_values_str(&[
+ ("name", &data.crate_name),
+ ("crate_root", &crate_root)
+ ]);
+
+ self.record("crate", span, values);
+
+ for c in data.external_crates {
+ let num = c.num.to_string();
+ let lo_loc = self.span.sess.codemap().lookup_char_pos(span.lo);
+ let file_name = SpanUtils::make_path_string(&lo_loc.file.name);
+ let values = make_values_str(&[
+ ("name", &c.name),
+ ("crate", &num),
+ ("file_name", &file_name)
+ ]);
+
+ self.record_raw(&format!("external_crate{}\n", values));
+ }
+
+ self.record_raw("end_external_crates\n");
+ }
+
+ fn enum_data(&mut self, span: Span, data: EnumData) {
+ if self.dump_spans {
+ self.dump_span("enum", span);
+ return;
+ }
+
+ let id = data.id.to_string();
+ let scope = data.scope.to_string();
+ let values = make_values_str(&[
+ ("id", &id),
+ ("qualname", &data.qualname),
+ ("scopeid", &scope),
+ ("value", &data.value)
+ ]);
+
+ self.record("enum", data.span, values);
+ }
+
+ fn extern_crate(&mut self, span: Span, data: ExternCrateData) {
+ if self.dump_spans {
+ self.dump_span("extern_crate", span);
+ return;
+ }
+
+ let id = data.id.to_string();
+ let crate_num = data.crate_num.to_string();
+ let scope = data.scope.to_string();
+ let values = make_values_str(&[
+ ("id", &id),
+ ("name", &data.name),
+ ("location", &data.location),
+ ("crate", &crate_num),
+ ("scopeid", &scope)
+ ]);
+
+ self.record("extern_crate", data.span, values);
+ }
+
+ fn impl_data(&mut self, span: Span, data: ImplData) {
+ if self.dump_spans {
+ self.dump_span("impl", span);
+ return;
+ }
+
+ let self_ref = data.self_ref.unwrap_or(null_def_id());
+ let trait_ref = data.trait_ref.unwrap_or(null_def_id());
+
+ let id = data.id.to_string();
+ let ref_id = self_ref.index.as_usize().to_string();
+ let ref_id_crate = self_ref.krate.to_string();
+ let trait_id = trait_ref.index.as_usize().to_string();
+ let trait_id_crate = trait_ref.krate.to_string();
+ let scope = data.scope.to_string();
+ let values = make_values_str(&[
+ ("id", &id),
+ ("refid", &ref_id),
+ ("refidcrate", &ref_id_crate),
+ ("traitid", &trait_id),
+ ("traitidcrate", &trait_id_crate),
+ ("scopeid", &scope)
+ ]);
+
+ self.record("impl", data.span, values);
+ }
+
+ fn inheritance(&mut self, data: InheritanceData) {
+ if self.dump_spans {
+ return;
+ }
+
+ let base_id = data.base_id.index.as_usize().to_string();
+ let base_crate = data.base_id.krate.to_string();
+ let deriv_id = data.deriv_id.to_string();
+ let deriv_crate = 0.to_string();
+ let values = make_values_str(&[
+ ("base", &base_id),
+ ("basecrate", &base_crate),
+ ("derived", &deriv_id),
+ ("derivedcrate", &deriv_crate)
+ ]);
+
+ self.record("inheritance", data.span, values);
+ }
+
+ fn function(&mut self, span: Span, data: FunctionData) {
+ if self.dump_spans {
+ self.dump_span("function", span);
+ return;
+ }
+
+ let (decl_id, decl_crate) = match data.declaration {
+ Some(id) => (id.index.as_usize().to_string(), id.krate.to_string()),
+ None => (String::new(), String::new())
+ };
+
+ let id = data.id.to_string();
+ let scope = data.scope.to_string();
+ let values = make_values_str(&[
+ ("id", &id),
+ ("qualname", &data.qualname),
+ ("declid", &decl_id),
+ ("declidcrate", &decl_crate),
+ ("scopeid", &scope)
+ ]);
+
+ self.record("function", data.span, values);
+ }
+
+ fn function_ref(&mut self, span: Span, data: FunctionRefData) {
+ if self.dump_spans {
+ self.dump_span("fn_ref", span);
+ return;
+ }
+
+ let ref_id = data.ref_id.index.as_usize().to_string();
+ let ref_crate = data.ref_id.krate.to_string();
+ let scope = data.scope.to_string();
+ let values = make_values_str(&[
+ ("refid", &ref_id),
+ ("refidcrate", &ref_crate),
+ ("qualname", ""),
+ ("scopeid", &scope)
+ ]);
+
+ self.record("fn_ref", data.span, values);
+ }
+
+ fn function_call(&mut self, span: Span, data: FunctionCallData) {
+ if self.dump_spans {
+ self.dump_span("fn_call", span);
+ return;
+ }
+
+ let ref_id = data.ref_id.index.as_usize().to_string();
+ let ref_crate = data.ref_id.krate.to_string();
+ let qualname = String::new();
+ let scope = data.scope.to_string();
+ let values = make_values_str(&[
+ ("refid", &ref_id),
+ ("refidcrate", &ref_crate),
+ ("qualname", &qualname),
+ ("scopeid", &scope)
+ ]);
+
+ self.record("fn_call", data.span, values);
+ }
+
+ fn method(&mut self, span: Span, data: MethodData) {
+ if self.dump_spans {
+ self.dump_span("method_decl", span);
+ return;
+ }
+
+ let id = data.id.to_string();
+ let scope = data.scope.to_string();
+ let values = make_values_str(&[
+ ("id", &id),
+ ("qualname", &data.qualname),
+ ("scopeid", &scope)
+ ]);
+
+ self.record("method_decl", span, values);
+ }
+
+ fn method_call(&mut self, span: Span, data: MethodCallData) {
+ if self.dump_spans {
+ self.dump_span("method_call", span);
+ return;
+ }
+
+ let (dcn, dck) = match data.decl_id {
+ Some(declid) => (declid.index.as_usize().to_string(), declid.krate.to_string()),
+ None => (String::new(), String::new()),
+ };
+
+ let ref_id = data.ref_id.unwrap_or(null_def_id());
+
+ let def_id = ref_id.index.as_usize().to_string();
+ let def_crate = ref_id.krate.to_string();
+ let scope = data.scope.to_string();
+ let values = make_values_str(&[
+ ("refid", &def_id),
+ ("refidcrate", &def_crate),
+ ("declid", &dcn),
+ ("declidcrate", &dck),
+ ("scopeid", &scope)
+ ]);
+
+ self.record("method_call", data.span, values);
+ }
+
+ fn macro_data(&mut self, span: Span, data: MacroData) {
+ if self.dump_spans {
+ self.dump_span("macro", span);
+ return;
+ }
+
+ let values = make_values_str(&[
+ ("name", &data.name),
+ ("qualname", &data.qualname)
+ ]);
+
+ self.record("macro", data.span, values);
+ }
+
+ fn macro_use(&mut self, span: Span, data: MacroUseData) {
+ if self.dump_spans {
+ self.dump_span("macro_use", span);
+ return;
+ }
+
+ let scope = data.scope.to_string();
+ let values = make_values_str(&[
+ ("callee_name", &data.name),
+ ("qualname", &data.qualname),
+ ("scopeid", &scope)
+ ]);
+
+ self.record("macro_use", data.span, values);
+ }
+
+ fn mod_data(&mut self, data: ModData) {
+ if self.dump_spans {
+ return;
+ }
+
+ let id = data.id.to_string();
+ let scope = data.scope.to_string();
+ let values = make_values_str(&[
+ ("id", &id),
+ ("qualname", &data.qualname),
+ ("scopeid", &scope),
+ ("def_file", &data.filename)
+ ]);
+
+ self.record("module", data.span, values);
+ }
+
+ fn mod_ref(&mut self, span: Span, data: ModRefData) {
+ if self.dump_spans {
+ self.dump_span("mod_ref", span);
+ return;
+ }
+
+ let (ref_id, ref_crate) = match data.ref_id {
+ Some(rid) => (rid.index.as_usize().to_string(), rid.krate.to_string()),
+ None => (0.to_string(), 0.to_string())
+ };
+
+ let scope = data.scope.to_string();
+ let values = make_values_str(&[
+ ("refid", &ref_id),
+ ("refidcrate", &ref_crate),
+ ("qualname", &data.qualname),
+ ("scopeid", &scope)
+ ]);
+
+ self.record("mod_ref", data.span, values);
+ }
+
+ fn struct_data(&mut self, span: Span, data: StructData) {
+ if self.dump_spans {
+ self.dump_span("struct", span);
+ return;
+ }
+
+ let id = data.id.to_string();
+ let ctor_id = data.ctor_id.to_string();
+ let scope = data.scope.to_string();
+ let values = make_values_str(&[
+ ("id", &id),
+ ("ctor_id", &ctor_id),
+ ("qualname", &data.qualname),
+ ("scopeid", &scope),
+ ("value", &data.value)
+ ]);
+
+ self.record("struct", data.span, values);
+ }
+
+ fn struct_variant(&mut self, span: Span, data: StructVariantData) {
+ if self.dump_spans {
+ self.dump_span("variant_struct", span);
+ return;
+ }
+
+ let id = data.id.to_string();
+ let scope = data.scope.to_string();
+ let values = make_values_str(&[
+ ("id", &id),
+ ("ctor_id", &id),
+ ("qualname", &data.qualname),
+ ("type", &data.type_value),
+ ("value", &data.value),
+ ("scopeid", &scope)
+ ]);
+
+ self.record("variant_struct", data.span, values);
+ }
+
+ fn trait_data(&mut self, span: Span, data: TraitData) {
+ if self.dump_spans {
+ self.dump_span("trait", span);
+ return;
+ }
+
+ let id = data.id.to_string();
+ let scope = data.scope.to_string();
+ let values = make_values_str(&[
+ ("id", &id),
+ ("qualname", &data.qualname),
+ ("scopeid", &scope),
+ ("value", &data.value)
+ ]);
+
+ self.record("trait", data.span, values);
+ }
+
+ fn tuple_variant(&mut self, span: Span, data: TupleVariantData) {
+ if self.dump_spans {
+ self.dump_span("variant", span);
+ return;
+ }
+
+ let id = data.id.to_string();
+ let scope = data.scope.to_string();
+ let values = make_values_str(&[
+ ("id", &id),
+ ("name", &data.name),
+ ("qualname", &data.qualname),
+ ("type", &data.type_value),
+ ("value", &data.value),
+ ("scopeid", &scope)
+ ]);
+
+ self.record("variant", data.span, values);
+ }
+
+ fn type_ref(&mut self, span: Span, data: TypeRefData) {
+ if self.dump_spans {
+ self.dump_span("type_ref", span);
+ return;
+ }
+
+ let (ref_id, ref_crate) = match data.ref_id {
+ Some(id) => (id.index.as_usize().to_string(), id.krate.to_string()),
+ None => (0.to_string(), 0.to_string())
+ };
+
+ let scope = data.scope.to_string();
+ let values = make_values_str(&[
+ ("refid", &ref_id),
+ ("refidcrate", &ref_crate),
+ ("qualname", &data.qualname),
+ ("scopeid", &scope)
+ ]);
+
+ self.record("type_ref", data.span, values);
+ }
+
+ fn typedef(&mut self, span: Span, data: TypedefData) {
+ if self.dump_spans {
+ self.dump_span("typedef", span);
+ return;
+ }
+
+ let id = data.id.to_string();
+ let values = make_values_str(&[
+ ("id", &id),
+ ("qualname", &data.qualname),
+ ("value", &data.value)
+ ]);
+
+ self.record("typedef", data.span, values);
+ }
+
+ fn use_data(&mut self, span: Span, data: UseData) {
+ if self.dump_spans {
+ self.dump_span("use_alias", span);
+ return;
+ }
+
+ let mod_id = data.mod_id.unwrap_or(null_def_id());
+
+ let id = data.id.to_string();
+ let ref_id = mod_id.index.as_usize().to_string();
+ let ref_crate = mod_id.krate.to_string();
+ let scope = data.scope.to_string();
+ let values = make_values_str(&[
+ ("id", &id),
+ ("refid", &ref_id),
+ ("refidcrate", &ref_crate),
+ ("name", &data.name),
+ ("scopeid", &scope)
+ ]);
+
+ self.record("use_alias", data.span, values);
+ }
+
+ fn use_glob(&mut self, span: Span, data: UseGlobData) {
+ if self.dump_spans {
+ self.dump_span("use_glob", span);
+ return;
+ }
+
+ let names = data.names.join(", ");
+
+ let id = data.id.to_string();
+ let scope = data.scope.to_string();
+ let values = make_values_str(&[
+ ("id", &id),
+ ("value", &names),
+ ("scopeid", &scope)
+ ]);
+
+ self.record("use_glob", data.span, values);
+ }
+
+ fn variable(&mut self, span: Span, data: VariableData) {
+ if self.dump_spans {
+ self.dump_span("variable", span);
+ return;
+ }
+
+ let id = data.id.to_string();
+ let scope = data.scope.to_string();
+ let values = make_values_str(&[
+ ("id", &id),
+ ("name", &data.name),
+ ("qualname", &data.qualname),
+ ("value", &data.value),
+ ("type", &data.type_value),
+ ("scopeid", &scope)
+ ]);
+
+ self.record("variable", data.span, values);
+ }
+
+ fn variable_ref(&mut self, span: Span, data: VariableRefData) {
+ if self.dump_spans {
+ self.dump_span("var_ref", span);
+ return;
+ }
+
+ let ref_id = data.ref_id.index.as_usize().to_string();
+ let ref_crate = data.ref_id.krate.to_string();
+ let scope = data.scope.to_string();
+ let values = make_values_str(&[
+ ("refid", &ref_id),
+ ("refidcrate", &ref_crate),
+ ("qualname", ""),
+ ("scopeid", &scope)
+ ]);
+
+ self.record("var_ref", data.span, values)
+ }
+}
+
+// Helper function to escape quotes in a string
+fn escape(s: String) -> String {
+ s.replace("\"", "\"\"")
+}
+
+fn make_values_str(pairs: &[(&'static str, &str)]) -> String {
+ let pairs = pairs.into_iter().map(|&(f, v)| {
+ // Never take more than 1020 chars
+ if v.len() > 1020 {
+ (f, &v[..1020])
+ } else {
+ (f, v)
+ }
+ });
+
+ let strs = pairs.map(|(f, v)| format!(",{},\"{}\"", f, escape(String::from(v))));
+ strs.fold(String::new(), |mut s, ss| {
+ s.push_str(&ss[..]);
+ s
+ })
+}
+
+fn null_def_id() -> DefId {
+ DefId {
+ krate: 0,
+ index: DefIndex::new(0),
+ }
+}
--- /dev/null
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// 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.
+
+//! Structs representing the analysis data from a crate.
+//!
+//! The `Dump` trait can be used together with `DumpVisitor` in order to
+//! retrieve the data from a crate.
+
+use std::hash::Hasher;
+
+use middle::def_id::DefId;
+use middle::ty;
+use syntax::ast::{CrateNum, NodeId};
+use syntax::codemap::Span;
+
+#[macro_export]
+macro_rules! down_cast_data {
+ ($id:ident, $kind:ident, $this:ident, $sp:expr) => {
+ let $id = if let super::Data::$kind(data) = $id {
+ data
+ } else {
+ $this.sess.span_bug($sp, &format!("unexpected data kind: {:?}", $id));
+ }
+ };
+}
+
+pub struct CrateData {
+ pub name: String,
+ pub number: u32,
+}
+
+/// Data for any entity in the Rust language. The actual data contained varies
+/// with the kind of entity being queried. See the nested structs for details.
+#[derive(Debug)]
+pub enum Data {
+ /// Data for Enums.
+ EnumData(EnumData),
+ /// Data for extern crates.
+ ExternCrateData(ExternCrateData),
+ /// Data about a function call.
+ FunctionCallData(FunctionCallData),
+ /// Data for all kinds of functions and methods.
+ FunctionData(FunctionData),
+ /// Data about a function ref.
+ FunctionRefData(FunctionRefData),
+ /// Data for impls.
+ ImplData(ImplData2),
+ /// Data for trait inheritance.
+ InheritanceData(InheritanceData),
+ /// Data about a macro declaration.
+ MacroData(MacroData),
+ /// Data about a macro use.
+ MacroUseData(MacroUseData),
+ /// Data about a method call.
+ MethodCallData(MethodCallData),
+ /// Data for method declarations (methods with a body are treated as functions).
+ MethodData(MethodData),
+ /// Data for modules.
+ ModData(ModData),
+ /// Data for a reference to a module.
+ ModRefData(ModRefData),
+ /// Data for a struct declaration.
+ StructData(StructData),
+ /// Data for a struct variant.
+ StructVariantDat(StructVariantData),
+ /// Data for a trait declaration.
+ TraitData(TraitData),
+ /// Data for a tuple variant.
+ TupleVariantData(TupleVariantData),
+ /// Data for a typedef.
+ TypeDefData(TypedefData),
+ /// Data for a reference to a type or trait.
+ TypeRefData(TypeRefData),
+ /// Data for a use statement.
+ UseData(UseData),
+ /// Data for a global use statement.
+ UseGlobData(UseGlobData),
+ /// Data for local and global variables (consts and statics), and fields.
+ VariableData(VariableData),
+ /// Data for the use of some variable (e.g., the use of a local variable, which
+ /// will refere to that variables declaration).
+ VariableRefData(VariableRefData),
+}
+
+/// Data for the prelude of a crate.
+#[derive(Debug)]
+pub struct CratePreludeData {
+ pub crate_name: String,
+ pub crate_root: Option<String>,
+ pub external_crates: Vec<ExternalCrateData>
+}
+
+/// Data for external crates in the prelude of a crate.
+#[derive(Debug)]
+pub struct ExternalCrateData {
+ pub name: String,
+ pub num: CrateNum
+}
+
+/// Data for enum declarations.
+#[derive(Clone, Debug)]
+pub struct EnumData {
+ pub id: NodeId,
+ pub value: String,
+ pub qualname: String,
+ pub span: Span,
+ pub scope: NodeId,
+}
+
+/// Data for extern crates.
+#[derive(Debug)]
+pub struct ExternCrateData {
+ pub id: NodeId,
+ pub name: String,
+ pub crate_num: CrateNum,
+ pub location: String,
+ pub span: Span,
+ pub scope: NodeId,
+}
+
+/// Data about a function call.
+#[derive(Debug)]
+pub struct FunctionCallData {
+ pub span: Span,
+ pub scope: NodeId,
+ pub ref_id: DefId,
+}
+
+/// Data for all kinds of functions and methods.
+#[derive(Clone, Debug)]
+pub struct FunctionData {
+ pub id: NodeId,
+ pub name: String,
+ pub qualname: String,
+ pub declaration: Option<DefId>,
+ pub span: Span,
+ pub scope: NodeId,
+}
+
+/// Data about a function call.
+#[derive(Debug)]
+pub struct FunctionRefData {
+ pub span: Span,
+ pub scope: NodeId,
+ pub ref_id: DefId,
+}
+
+#[derive(Debug)]
+pub struct ImplData {
+ pub id: NodeId,
+ pub span: Span,
+ pub scope: NodeId,
+ pub trait_ref: Option<DefId>,
+ pub self_ref: Option<DefId>,
+}
+
+#[derive(Debug)]
+// FIXME: this struct should not exist. However, removing it requires heavy
+// refactoring of dump_visitor.rs. See PR 31838 for more info.
+pub struct ImplData2 {
+ pub id: NodeId,
+ pub span: Span,
+ pub scope: NodeId,
+ // FIXME: I'm not really sure inline data is the best way to do this. Seems
+ // OK in this case, but generalising leads to returning chunks of AST, which
+ // feels wrong.
+ pub trait_ref: Option<TypeRefData>,
+ pub self_ref: Option<TypeRefData>,
+}
+
+#[derive(Debug)]
+pub struct InheritanceData {
+ pub span: Span,
+ pub base_id: DefId,
+ pub deriv_id: NodeId
+}
+
+/// Data about a macro declaration.
+#[derive(Debug)]
+pub struct MacroData {
+ pub span: Span,
+ pub name: String,
+ pub qualname: String,
+}
+
+/// Data about a macro use.
+#[derive(Debug)]
+pub struct MacroUseData {
+ pub span: Span,
+ pub name: String,
+ pub qualname: String,
+ // Because macro expansion happens before ref-ids are determined,
+ // we use the callee span to reference the associated macro definition.
+ pub callee_span: Span,
+ pub scope: NodeId,
+ pub imported: bool,
+}
+
+/// Data about a method call.
+#[derive(Debug)]
+pub struct MethodCallData {
+ pub span: Span,
+ pub scope: NodeId,
+ pub ref_id: Option<DefId>,
+ pub decl_id: Option<DefId>,
+}
+
+/// Data for method declarations (methods with a body are treated as functions).
+#[derive(Clone, Debug)]
+pub struct MethodData {
+ pub id: NodeId,
+ pub qualname: String,
+ pub span: Span,
+ pub scope: NodeId,
+}
+
+/// Data for modules.
+#[derive(Debug)]
+pub struct ModData {
+ pub id: NodeId,
+ pub name: String,
+ pub qualname: String,
+ pub span: Span,
+ pub scope: NodeId,
+ pub filename: String,
+}
+
+/// Data for a reference to a module.
+#[derive(Debug)]
+pub struct ModRefData {
+ pub span: Span,
+ pub scope: NodeId,
+ pub ref_id: Option<DefId>,
+ pub qualname: String
+}
+
+#[derive(Debug)]
+pub struct StructData {
+ pub span: Span,
+ pub id: NodeId,
+ pub ctor_id: NodeId,
+ pub qualname: String,
+ pub scope: NodeId,
+ pub value: String
+}
+
+#[derive(Debug)]
+pub struct StructVariantData {
+ pub span: Span,
+ pub id: NodeId,
+ pub qualname: String,
+ pub type_value: String,
+ pub value: String,
+ pub scope: NodeId
+}
+
+#[derive(Debug)]
+pub struct TraitData {
+ pub span: Span,
+ pub id: NodeId,
+ pub qualname: String,
+ pub scope: NodeId,
+ pub value: String
+}
+
+#[derive(Debug)]
+pub struct TupleVariantData {
+ pub span: Span,
+ pub id: NodeId,
+ pub name: String,
+ pub qualname: String,
+ pub type_value: String,
+ pub value: String,
+ pub scope: NodeId
+}
+
+/// Data for a typedef.
+#[derive(Debug)]
+pub struct TypedefData {
+ pub id: NodeId,
+ pub span: Span,
+ pub qualname: String,
+ pub value: String,
+}
+
+/// Data for a reference to a type or trait.
+#[derive(Clone, Debug)]
+pub struct TypeRefData {
+ pub span: Span,
+ pub scope: NodeId,
+ pub ref_id: Option<DefId>,
+ pub qualname: String,
+}
+
+#[derive(Debug)]
+pub struct UseData {
+ pub id: NodeId,
+ pub span: Span,
+ pub name: String,
+ pub mod_id: Option<DefId>,
+ pub scope: NodeId
+}
+
+#[derive(Debug)]
+pub struct UseGlobData {
+ pub id: NodeId,
+ pub span: Span,
+ pub names: Vec<String>,
+ pub scope: NodeId
+}
+
+/// Data for local and global variables (consts and statics).
+#[derive(Debug)]
+pub struct VariableData {
+ pub id: NodeId,
+ pub name: String,
+ pub qualname: String,
+ pub span: Span,
+ pub scope: NodeId,
+ pub value: String,
+ pub type_value: String,
+}
+
+/// Data for the use of some item (e.g., the use of a local variable, which
+/// will refer to that variables declaration (by ref_id)).
+#[derive(Debug)]
+pub struct VariableRefData {
+ pub name: String,
+ pub span: Span,
+ pub scope: NodeId,
+ pub ref_id: DefId,
+}
+
+// Emitted ids are used to cross-reference items across crates. DefIds and
+// NodeIds do not usually correspond in any way. The strategy is to use the
+// index from the DefId as a crate-local id. However, within a crate, DefId
+// indices and NodeIds can overlap. So, we must adjust the NodeIds. If an
+// item can be identified by a DefId as well as a NodeId, then we use the
+// DefId index as the id. If it can't, then we have to use the NodeId, but
+// need to adjust it so it will not clash with any possible DefId index.
+pub fn normalize_node_id<'a>(tcx: &ty::TyCtxt<'a>, id: NodeId) -> usize {
+ match tcx.map.opt_local_def_id(id) {
+ Some(id) => id.index.as_usize(),
+ None => id as usize + tcx.map.num_local_def_ids()
+ }
+}
+
+// Macro to implement a normalize() function (see below for usage)
+macro_rules! impl_normalize {
+ ($($t:ty => $($field:ident),*);*) => {
+ $(
+ impl $t {
+ pub fn normalize<'a>(mut self, tcx: &ty::TyCtxt<'a>) -> $t {
+ $(
+ self.$field = normalize_node_id(tcx, self.$field) as u32;
+ )*
+ self
+ }
+ }
+ )*
+ }
+}
+
+impl_normalize! {
+ EnumData => id, scope;
+ ExternCrateData => id, scope;
+ FunctionCallData => scope;
+ FunctionData => id, scope;
+ FunctionRefData => scope;
+ ImplData => id, scope;
+ InheritanceData => deriv_id;
+ MacroUseData => scope;
+ MethodCallData => scope;
+ MethodData => id, scope;
+ ModData => id, scope;
+ ModRefData => scope;
+ StructData => ctor_id, id, scope;
+ StructVariantData => id, scope;
+ TupleVariantData => id, scope;
+ TraitData => id, scope;
+ TypedefData => id;
+ TypeRefData => scope;
+ UseData => id, scope;
+ UseGlobData => id, scope;
+ VariableData => id;
+ VariableRefData => scope
+}
--- /dev/null
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// 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.
+
+use syntax::codemap::Span;
+
+use super::data::*;
+
+pub trait Dump {
+ fn crate_prelude(&mut self, _: Span, _: CratePreludeData) {}
+ fn enum_data(&mut self, _: Span, _: EnumData) {}
+ fn extern_crate(&mut self, _: Span, _: ExternCrateData) {}
+ fn impl_data(&mut self, _: Span, _: ImplData) {}
+ fn inheritance(&mut self, _: InheritanceData) {}
+ fn function(&mut self, _: Span, _: FunctionData) {}
+ fn function_ref(&mut self, _: Span, _: FunctionRefData) {}
+ fn function_call(&mut self, _: Span, _: FunctionCallData) {}
+ fn method(&mut self, _: Span, _: MethodData) {}
+ fn method_call(&mut self, _: Span, _: MethodCallData) {}
+ fn macro_data(&mut self, _: Span, _: MacroData) {}
+ fn macro_use(&mut self, _: Span, _: MacroUseData) {}
+ fn mod_data(&mut self, _: ModData) {}
+ fn mod_ref(&mut self, _: Span, _: ModRefData) {}
+ fn struct_data(&mut self, _: Span, _: StructData) {}
+ fn struct_variant(&mut self, _: Span, _: StructVariantData) {}
+ fn trait_data(&mut self, _: Span, _: TraitData) {}
+ fn tuple_variant(&mut self, _: Span, _: TupleVariantData) {}
+ fn type_ref(&mut self, _: Span, _: TypeRefData) {}
+ fn typedef(&mut self, _: Span, _: TypedefData) {}
+ fn use_data(&mut self, _: Span, _: UseData) {}
+ fn use_glob(&mut self, _: Span, _: UseGlobData) {}
+ fn variable(&mut self, _: Span, _: VariableData) {}
+ fn variable_ref(&mut self, _: Span, _: VariableRefData) {}
+}
+++ /dev/null
-// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
-// 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.
-
-//! Output a CSV file containing the output from rustc's analysis. The data is
-//! primarily designed to be used as input to the DXR tool, specifically its
-//! Rust plugin. It could also be used by IDEs or other code browsing, search, or
-//! cross-referencing tools.
-//!
-//! Dumping the analysis is implemented by walking the AST and getting a bunch of
-//! info out from all over the place. We use Def IDs to identify objects. The
-//! tricky part is getting syntactic (span, source text) and semantic (reference
-//! Def IDs) information for parts of expressions which the compiler has discarded.
-//! E.g., in a path `foo::bar::baz`, the compiler only keeps a span for the whole
-//! path and a reference to `baz`, but we want spans and references for all three
-//! idents.
-//!
-//! SpanUtils is used to manipulate spans. In particular, to extract sub-spans
-//! from spans (e.g., the span for `bar` from the above example path).
-//! Recorder is used for recording the output in csv format. FmtStrs separates
-//! the format of the output away from extracting it from the compiler.
-//! DumpCsvVisitor walks the AST and processes it.
-
-
-use super::{escape, generated_code, recorder, SaveContext, PathCollector, Data};
-
-use session::Session;
-
-use middle::def::Def;
-use middle::def_id::DefId;
-use middle::ty::{self, TyCtxt};
-
-use std::fs::File;
-use std::hash::*;
-use std::collections::HashSet;
-
-use syntax::ast::{self, NodeId, PatKind};
-use syntax::codemap::*;
-use syntax::parse::token::{self, keywords};
-use syntax::visit::{self, Visitor};
-use syntax::print::pprust::{path_to_string, ty_to_string};
-use syntax::ptr::P;
-
-use rustc_front::lowering::{lower_expr, LoweringContext};
-
-use super::span_utils::SpanUtils;
-use super::recorder::{Recorder, FmtStrs};
-
-macro_rules! down_cast_data {
- ($id:ident, $kind:ident, $this:ident, $sp:expr) => {
- let $id = if let super::Data::$kind(data) = $id {
- data
- } else {
- $this.sess.span_bug($sp, &format!("unexpected data kind: {:?}", $id));
- }
- };
-}
-
-pub struct DumpCsvVisitor<'l, 'tcx: 'l> {
- save_ctxt: SaveContext<'l, 'tcx>,
- sess: &'l Session,
- tcx: &'l TyCtxt<'tcx>,
- analysis: &'l ty::CrateAnalysis<'l>,
-
- span: SpanUtils<'l>,
- fmt: FmtStrs<'l, 'tcx>,
-
- cur_scope: NodeId,
-
- // Set of macro definition (callee) spans, and the set
- // of macro use (callsite) spans. We store these to ensure
- // we only write one macro def per unique macro definition, and
- // one macro use per unique callsite span.
- mac_defs: HashSet<Span>,
- mac_uses: HashSet<Span>,
-
-}
-
-impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> {
- pub fn new(tcx: &'l TyCtxt<'tcx>,
- lcx: &'l LoweringContext<'l>,
- analysis: &'l ty::CrateAnalysis<'l>,
- output_file: Box<File>)
- -> DumpCsvVisitor<'l, 'tcx> {
- let span_utils = SpanUtils::new(&tcx.sess);
- DumpCsvVisitor {
- sess: &tcx.sess,
- tcx: tcx,
- save_ctxt: SaveContext::from_span_utils(tcx, lcx, span_utils.clone()),
- analysis: analysis,
- span: span_utils.clone(),
- fmt: FmtStrs::new(box Recorder {
- out: output_file,
- dump_spans: false,
- },
- span_utils,
- tcx),
- cur_scope: 0,
- mac_defs: HashSet::new(),
- mac_uses: HashSet::new(),
- }
- }
-
- fn nest<F>(&mut self, scope_id: NodeId, f: F)
- where F: FnOnce(&mut DumpCsvVisitor<'l, 'tcx>)
- {
- let parent_scope = self.cur_scope;
- self.cur_scope = scope_id;
- f(self);
- self.cur_scope = parent_scope;
- }
-
- pub fn dump_crate_info(&mut self, name: &str, krate: &ast::Crate) {
- let source_file = self.tcx.sess.local_crate_source_file.as_ref();
- let crate_root = match source_file {
- Some(source_file) => match source_file.file_name() {
- Some(_) => source_file.parent().unwrap().display().to_string(),
- None => source_file.display().to_string(),
- },
- None => "<no source>".to_owned(),
- };
-
- // The current crate.
- self.fmt.crate_str(krate.span, name, &crate_root);
-
- // Dump info about all the external crates referenced from this crate.
- for c in &self.save_ctxt.get_external_crates() {
- self.fmt.external_crate_str(krate.span, &c.name, c.number);
- }
- self.fmt.recorder.record("end_external_crates\n");
- }
-
- // Return all non-empty prefixes of a path.
- // For each prefix, we return the span for the last segment in the prefix and
- // a str representation of the entire prefix.
- fn process_path_prefixes(&self, path: &ast::Path) -> Vec<(Span, String)> {
- let spans = self.span.spans_for_path_segments(path);
-
- // Paths to enums seem to not match their spans - the span includes all the
- // variants too. But they seem to always be at the end, so I hope we can cope with
- // always using the first ones. So, only error out if we don't have enough spans.
- // What could go wrong...?
- if spans.len() < path.segments.len() {
- if generated_code(path.span) {
- return vec!();
- }
- error!("Mis-calculated spans for path '{}'. Found {} spans, expected {}. Found spans:",
- path_to_string(path),
- spans.len(),
- path.segments.len());
- for s in &spans {
- let loc = self.sess.codemap().lookup_char_pos(s.lo);
- error!(" '{}' in {}, line {}",
- self.span.snippet(*s),
- loc.file.name,
- loc.line);
- }
- return vec!();
- }
-
- let mut result: Vec<(Span, String)> = vec!();
-
- let mut segs = vec!();
- for (i, (seg, span)) in path.segments.iter().zip(&spans).enumerate() {
- segs.push(seg.clone());
- let sub_path = ast::Path {
- span: *span, // span for the last segment
- global: path.global,
- segments: segs,
- };
- let qualname = if i == 0 && path.global {
- format!("::{}", path_to_string(&sub_path))
- } else {
- path_to_string(&sub_path)
- };
- result.push((*span, qualname));
- segs = sub_path.segments;
- }
-
- result
- }
-
- // The global arg allows us to override the global-ness of the path (which
- // actually means 'does the path start with `::`', rather than 'is the path
- // semantically global). We use the override for `use` imports (etc.) where
- // the syntax is non-global, but the semantics are global.
- fn write_sub_paths(&mut self, path: &ast::Path, global: bool) {
- let sub_paths = self.process_path_prefixes(path);
- for (i, &(ref span, ref qualname)) in sub_paths.iter().enumerate() {
- let qualname = if i == 0 && global && !path.global {
- format!("::{}", qualname)
- } else {
- qualname.clone()
- };
- self.fmt.sub_mod_ref_str(path.span, *span, &qualname, self.cur_scope);
- }
- }
-
- // As write_sub_paths, but does not process the last ident in the path (assuming it
- // will be processed elsewhere). See note on write_sub_paths about global.
- fn write_sub_paths_truncated(&mut self, path: &ast::Path, global: bool) {
- let sub_paths = self.process_path_prefixes(path);
- let len = sub_paths.len();
- if len <= 1 {
- return;
- }
-
- let sub_paths = &sub_paths[..len-1];
- for (i, &(ref span, ref qualname)) in sub_paths.iter().enumerate() {
- let qualname = if i == 0 && global && !path.global {
- format!("::{}", qualname)
- } else {
- qualname.clone()
- };
- self.fmt.sub_mod_ref_str(path.span, *span, &qualname, self.cur_scope);
- }
- }
-
- // As write_sub_paths, but expects a path of the form module_path::trait::method
- // Where trait could actually be a struct too.
- fn write_sub_path_trait_truncated(&mut self, path: &ast::Path) {
- let sub_paths = self.process_path_prefixes(path);
- let len = sub_paths.len();
- if len <= 1 {
- return;
- }
- let sub_paths = &sub_paths[.. (len-1)];
-
- // write the trait part of the sub-path
- let (ref span, ref qualname) = sub_paths[len-2];
- self.fmt.sub_type_ref_str(path.span, *span, &qualname);
-
- // write the other sub-paths
- if len <= 2 {
- return;
- }
- let sub_paths = &sub_paths[..len-2];
- for &(ref span, ref qualname) in sub_paths {
- self.fmt.sub_mod_ref_str(path.span, *span, &qualname, self.cur_scope);
- }
- }
-
- // looks up anything, not just a type
- fn lookup_type_ref(&self, ref_id: NodeId) -> Option<DefId> {
- if !self.tcx.def_map.borrow().contains_key(&ref_id) {
- self.sess.bug(&format!("def_map has no key for {} in lookup_type_ref",
- ref_id));
- }
- let def = self.tcx.def_map.borrow().get(&ref_id).unwrap().full_def();
- match def {
- Def::PrimTy(..) => None,
- Def::SelfTy(..) => None,
- _ => Some(def.def_id()),
- }
- }
-
- fn lookup_def_kind(&self, ref_id: NodeId, span: Span) -> Option<recorder::Row> {
- let def_map = self.tcx.def_map.borrow();
- if !def_map.contains_key(&ref_id) {
- self.sess.span_bug(span,
- &format!("def_map has no key for {} in lookup_def_kind",
- ref_id));
- }
- let def = def_map.get(&ref_id).unwrap().full_def();
- match def {
- Def::Mod(_) |
- Def::ForeignMod(_) => Some(recorder::ModRef),
- Def::Struct(..) => Some(recorder::TypeRef),
- Def::Enum(..) |
- Def::TyAlias(..) |
- Def::AssociatedTy(..) |
- Def::Trait(_) => Some(recorder::TypeRef),
- Def::Static(_, _) |
- Def::Const(_) |
- Def::AssociatedConst(..) |
- Def::Local(..) |
- Def::Variant(..) |
- Def::Upvar(..) => Some(recorder::VarRef),
-
- Def::Fn(..) => Some(recorder::FnRef),
-
- Def::SelfTy(..) |
- Def::Label(_) |
- Def::TyParam(..) |
- Def::Method(..) |
- Def::PrimTy(_) |
- Def::Err => {
- self.sess.span_bug(span,
- &format!("lookup_def_kind for unexpected item: {:?}", def));
- }
- }
- }
-
- fn process_formals(&mut self, formals: &Vec<ast::Arg>, qualname: &str) {
- for arg in formals {
- self.visit_pat(&arg.pat);
- let mut collector = PathCollector::new();
- collector.visit_pat(&arg.pat);
- let span_utils = self.span.clone();
- for &(id, ref p, _, _) in &collector.collected_paths {
- let typ = self.tcx.node_types().get(&id).unwrap().to_string();
- // get the span only for the name of the variable (I hope the path is only ever a
- // variable name, but who knows?)
- self.fmt.formal_str(p.span,
- span_utils.span_for_last_ident(p.span),
- id,
- qualname,
- &path_to_string(p),
- &typ);
- }
- }
- }
-
- fn process_method(&mut self,
- sig: &ast::MethodSig,
- body: Option<&ast::Block>,
- id: ast::NodeId,
- name: ast::Name,
- span: Span) {
- debug!("process_method: {}:{}", id, name);
-
- if let Some(method_data) = self.save_ctxt.get_method_data(id, name, span) {
-
- if body.is_some() {
- self.fmt.method_str(span,
- Some(method_data.span),
- method_data.id,
- &method_data.qualname,
- method_data.declaration,
- method_data.scope);
- self.process_formals(&sig.decl.inputs, &method_data.qualname);
- } else {
- self.fmt.method_decl_str(span,
- Some(method_data.span),
- method_data.id,
- &method_data.qualname,
- method_data.scope);
- }
- self.process_generic_params(&sig.generics, span, &method_data.qualname, id);
- }
-
- // walk arg and return types
- for arg in &sig.decl.inputs {
- self.visit_ty(&arg.ty);
- }
-
- if let ast::FunctionRetTy::Ty(ref ret_ty) = sig.decl.output {
- self.visit_ty(ret_ty);
- }
-
- // walk the fn body
- if let Some(body) = body {
- self.nest(id, |v| v.visit_block(body));
- }
- }
-
- fn process_trait_ref(&mut self, trait_ref: &ast::TraitRef) {
- let trait_ref_data = self.save_ctxt.get_trait_ref_data(trait_ref, self.cur_scope);
- if let Some(trait_ref_data) = trait_ref_data {
- self.fmt.ref_str(recorder::TypeRef,
- trait_ref.path.span,
- Some(trait_ref_data.span),
- trait_ref_data.ref_id,
- trait_ref_data.scope);
- visit::walk_path(self, &trait_ref.path);
- }
- }
-
- fn process_struct_field_def(&mut self, field: &ast::StructField, parent_id: NodeId) {
- let field_data = self.save_ctxt.get_field_data(field, parent_id);
- if let Some(field_data) = field_data {
- self.fmt.field_str(field.span,
- Some(field_data.span),
- field_data.id,
- &field_data.name,
- &field_data.qualname,
- &field_data.type_value,
- field_data.scope);
- }
- }
-
- // Dump generic params bindings, then visit_generics
- fn process_generic_params(&mut self,
- generics: &ast::Generics,
- full_span: Span,
- prefix: &str,
- id: NodeId) {
- // We can't only use visit_generics since we don't have spans for param
- // bindings, so we reparse the full_span to get those sub spans.
- // However full span is the entire enum/fn/struct block, so we only want
- // the first few to match the number of generics we're looking for.
- let param_sub_spans = self.span.spans_for_ty_params(full_span,
- (generics.ty_params.len() as isize));
- for (param, param_ss) in generics.ty_params.iter().zip(param_sub_spans) {
- // Append $id to name to make sure each one is unique
- let name = format!("{}::{}${}",
- prefix,
- escape(self.span.snippet(param_ss)),
- id);
- self.fmt.typedef_str(full_span, Some(param_ss), param.id, &name, "");
- }
- self.visit_generics(generics);
- }
-
- fn process_fn(&mut self,
- item: &ast::Item,
- decl: &ast::FnDecl,
- ty_params: &ast::Generics,
- body: &ast::Block) {
- if let Some(fn_data) = self.save_ctxt.get_item_data(item) {
- down_cast_data!(fn_data, FunctionData, self, item.span);
- self.fmt.fn_str(item.span,
- Some(fn_data.span),
- fn_data.id,
- &fn_data.qualname,
- fn_data.scope);
-
- self.process_formals(&decl.inputs, &fn_data.qualname);
- self.process_generic_params(ty_params, item.span, &fn_data.qualname, item.id);
- }
-
- for arg in &decl.inputs {
- self.visit_ty(&arg.ty);
- }
-
- if let ast::FunctionRetTy::Ty(ref ret_ty) = decl.output {
- self.visit_ty(&ret_ty);
- }
-
- self.nest(item.id, |v| v.visit_block(&body));
- }
-
- fn process_static_or_const_item(&mut self, item: &ast::Item, typ: &ast::Ty, expr: &ast::Expr) {
- if let Some(var_data) = self.save_ctxt.get_item_data(item) {
- down_cast_data!(var_data, VariableData, self, item.span);
- self.fmt.static_str(item.span,
- Some(var_data.span),
- var_data.id,
- &var_data.name,
- &var_data.qualname,
- &var_data.value,
- &var_data.type_value,
- var_data.scope);
- }
- self.visit_ty(&typ);
- self.visit_expr(expr);
- }
-
- fn process_const(&mut self,
- id: ast::NodeId,
- name: ast::Name,
- span: Span,
- typ: &ast::Ty,
- expr: &ast::Expr) {
- let qualname = format!("::{}", self.tcx.map.path_to_string(id));
-
- let sub_span = self.span.sub_span_after_keyword(span, keywords::Const);
-
- self.fmt.static_str(span,
- sub_span,
- id,
- &name.as_str(),
- &qualname,
- &self.span.snippet(expr.span),
- &ty_to_string(&typ),
- self.cur_scope);
-
- // walk type and init value
- self.visit_ty(typ);
- self.visit_expr(expr);
- }
-
- fn process_struct(&mut self,
- item: &ast::Item,
- def: &ast::VariantData,
- ty_params: &ast::Generics) {
- let qualname = format!("::{}", self.tcx.map.path_to_string(item.id));
-
- let val = self.span.snippet(item.span);
- let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Struct);
- self.fmt.struct_str(item.span,
- sub_span,
- item.id,
- def.id(),
- &qualname,
- self.cur_scope,
- &val);
-
- // fields
- for field in def.fields() {
- self.process_struct_field_def(field, item.id);
- self.visit_ty(&field.node.ty);
- }
-
- self.process_generic_params(ty_params, item.span, &qualname, item.id);
- }
-
- fn process_enum(&mut self,
- item: &ast::Item,
- enum_definition: &ast::EnumDef,
- ty_params: &ast::Generics) {
- let enum_data = self.save_ctxt.get_item_data(item);
- let enum_data = match enum_data {
- None => return,
- Some(data) => data,
- };
- down_cast_data!(enum_data, EnumData, self, item.span);
- self.fmt.enum_str(item.span,
- Some(enum_data.span),
- enum_data.id,
- &enum_data.qualname,
- enum_data.scope,
- &enum_data.value);
-
- for variant in &enum_definition.variants {
- let name = &variant.node.name.name.as_str();
- let mut qualname = enum_data.qualname.clone();
- qualname.push_str("::");
- qualname.push_str(name);
- let val = self.span.snippet(variant.span);
-
- match variant.node.data {
- ast::VariantData::Struct(..) => {
- self.fmt.struct_variant_str(variant.span,
- self.span.span_for_first_ident(variant.span),
- variant.node.data.id(),
- &qualname,
- &enum_data.qualname,
- &val,
- enum_data.scope);
- }
- _ => {
- self.fmt.tuple_variant_str(variant.span,
- self.span.span_for_first_ident(variant.span),
- variant.node.data.id(),
- name,
- &qualname,
- &enum_data.qualname,
- &val,
- enum_data.scope);
- }
- }
-
-
- for field in variant.node.data.fields() {
- self.process_struct_field_def(field, variant.node.data.id());
- self.visit_ty(&field.node.ty);
- }
- }
- self.process_generic_params(ty_params, item.span, &enum_data.qualname, enum_data.id);
- }
-
- fn process_impl(&mut self,
- item: &ast::Item,
- type_parameters: &ast::Generics,
- trait_ref: &Option<ast::TraitRef>,
- typ: &ast::Ty,
- impl_items: &[ast::ImplItem]) {
- let mut has_self_ref = false;
- if let Some(impl_data) = self.save_ctxt.get_item_data(item) {
- down_cast_data!(impl_data, ImplData, self, item.span);
- if let Some(ref self_ref) = impl_data.self_ref {
- has_self_ref = true;
- self.fmt.ref_str(recorder::TypeRef,
- item.span,
- Some(self_ref.span),
- self_ref.ref_id,
- self_ref.scope);
- }
- if let Some(ref trait_ref_data) = impl_data.trait_ref {
- self.fmt.ref_str(recorder::TypeRef,
- item.span,
- Some(trait_ref_data.span),
- trait_ref_data.ref_id,
- trait_ref_data.scope);
- visit::walk_path(self, &trait_ref.as_ref().unwrap().path);
- }
-
- self.fmt.impl_str(item.span,
- Some(impl_data.span),
- impl_data.id,
- impl_data.self_ref.map(|data| data.ref_id),
- impl_data.trait_ref.map(|data| data.ref_id),
- impl_data.scope);
- }
- if !has_self_ref {
- self.visit_ty(&typ);
- }
- self.process_generic_params(type_parameters, item.span, "", item.id);
- for impl_item in impl_items {
- self.visit_impl_item(impl_item);
- }
- }
-
- fn process_trait(&mut self,
- item: &ast::Item,
- generics: &ast::Generics,
- trait_refs: &ast::TyParamBounds,
- methods: &[ast::TraitItem]) {
- let qualname = format!("::{}", self.tcx.map.path_to_string(item.id));
- let val = self.span.snippet(item.span);
- let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Trait);
- self.fmt.trait_str(item.span,
- sub_span,
- item.id,
- &qualname,
- self.cur_scope,
- &val);
-
- // super-traits
- for super_bound in trait_refs.iter() {
- let trait_ref = match *super_bound {
- ast::TraitTyParamBound(ref trait_ref, _) => {
- trait_ref
- }
- ast::RegionTyParamBound(..) => {
- continue;
- }
- };
-
- let trait_ref = &trait_ref.trait_ref;
- match self.lookup_type_ref(trait_ref.ref_id) {
- Some(id) => {
- let sub_span = self.span.sub_span_for_type_name(trait_ref.path.span);
- self.fmt.ref_str(recorder::TypeRef,
- trait_ref.path.span,
- sub_span,
- id,
- self.cur_scope);
- self.fmt.inherit_str(trait_ref.path.span, sub_span, id, item.id);
- }
- None => (),
- }
- }
-
- // walk generics and methods
- self.process_generic_params(generics, item.span, &qualname, item.id);
- for method in methods {
- self.visit_trait_item(method)
- }
- }
-
- // `item` is the module in question, represented as an item.
- fn process_mod(&mut self, item: &ast::Item) {
- if let Some(mod_data) = self.save_ctxt.get_item_data(item) {
- down_cast_data!(mod_data, ModData, self, item.span);
- self.fmt.mod_str(item.span,
- Some(mod_data.span),
- mod_data.id,
- &mod_data.qualname,
- mod_data.scope,
- &mod_data.filename);
- }
- }
-
- fn process_path(&mut self, id: NodeId, path: &ast::Path, ref_kind: Option<recorder::Row>) {
- let path_data = self.save_ctxt.get_path_data(id, path);
- if generated_code(path.span) && path_data.is_none() {
- return;
- }
-
- let path_data = match path_data {
- Some(pd) => pd,
- None => {
- self.tcx.sess.span_bug(path.span,
- &format!("Unexpected def kind while looking up path in \
- `{}`",
- self.span.snippet(path.span)))
- }
- };
- match path_data {
- Data::VariableRefData(ref vrd) => {
- self.fmt.ref_str(ref_kind.unwrap_or(recorder::VarRef),
- path.span,
- Some(vrd.span),
- vrd.ref_id,
- vrd.scope);
-
- }
- Data::TypeRefData(ref trd) => {
- self.fmt.ref_str(recorder::TypeRef,
- path.span,
- Some(trd.span),
- trd.ref_id,
- trd.scope);
- }
- Data::MethodCallData(ref mcd) => {
- self.fmt.meth_call_str(path.span,
- Some(mcd.span),
- mcd.ref_id,
- mcd.decl_id,
- mcd.scope);
- }
- Data::FunctionCallData(fcd) => {
- self.fmt.fn_call_str(path.span, Some(fcd.span), fcd.ref_id, fcd.scope);
- }
- _ => {
- self.sess.span_bug(path.span,
- &format!("Unexpected data: {:?}", path_data));
- }
- }
-
- // Modules or types in the path prefix.
- let def_map = self.tcx.def_map.borrow();
- let def = def_map.get(&id).unwrap().full_def();
- match def {
- Def::Method(did) => {
- let ti = self.tcx.impl_or_trait_item(did);
- if let ty::MethodTraitItem(m) = ti {
- if m.explicit_self == ty::ExplicitSelfCategory::Static {
- self.write_sub_path_trait_truncated(path);
- }
- }
- }
- Def::Local(..) |
- Def::Static(_,_) |
- Def::Const(..) |
- Def::AssociatedConst(..) |
- Def::Struct(..) |
- Def::Variant(..) |
- Def::Fn(..) => self.write_sub_paths_truncated(path, false),
- _ => {}
- }
- }
-
- fn process_struct_lit(&mut self,
- ex: &ast::Expr,
- path: &ast::Path,
- fields: &Vec<ast::Field>,
- variant: ty::VariantDef,
- base: &Option<P<ast::Expr>>) {
- self.write_sub_paths_truncated(path, false);
-
- if let Some(struct_lit_data) = self.save_ctxt.get_expr_data(ex) {
- down_cast_data!(struct_lit_data, TypeRefData, self, ex.span);
- self.fmt.ref_str(recorder::TypeRef,
- ex.span,
- Some(struct_lit_data.span),
- struct_lit_data.ref_id,
- struct_lit_data.scope);
- let scope = self.save_ctxt.enclosing_scope(ex.id);
-
- for field in fields {
- if let Some(field_data) = self.save_ctxt
- .get_field_ref_data(field, variant, scope) {
-
- self.fmt.ref_str(recorder::VarRef,
- field.ident.span,
- Some(field_data.span),
- field_data.ref_id,
- field_data.scope);
- }
-
- self.visit_expr(&field.expr)
- }
- }
-
- walk_list!(self, visit_expr, base);
- }
-
- fn process_method_call(&mut self, ex: &ast::Expr, args: &Vec<P<ast::Expr>>) {
- if let Some(call_data) = self.save_ctxt.get_expr_data(ex) {
- down_cast_data!(call_data, MethodCallData, self, ex.span);
- self.fmt.meth_call_str(ex.span,
- Some(call_data.span),
- call_data.ref_id,
- call_data.decl_id,
- call_data.scope);
- }
-
- // walk receiver and args
- walk_list!(self, visit_expr, args);
- }
-
- fn process_pat(&mut self, p: &ast::Pat) {
- match p.node {
- PatKind::Struct(ref path, ref fields, _) => {
- visit::walk_path(self, path);
- let adt = self.tcx.node_id_to_type(p.id).ty_adt_def().unwrap();
- let def = self.tcx.def_map.borrow()[&p.id].full_def();
- let variant = adt.variant_of_def(def);
-
- for &Spanned { node: ref field, span } in fields {
- let sub_span = self.span.span_for_first_ident(span);
- if let Some(f) = variant.find_field_named(field.ident.name) {
- self.fmt.ref_str(recorder::VarRef, span, sub_span, f.did, self.cur_scope);
- }
- self.visit_pat(&field.pat);
- }
- }
- _ => visit::walk_pat(self, p),
- }
- }
-
-
- fn process_var_decl(&mut self, p: &ast::Pat, value: String) {
- // The local could declare multiple new vars, we must walk the
- // pattern and collect them all.
- let mut collector = PathCollector::new();
- collector.visit_pat(&p);
- self.visit_pat(&p);
-
- for &(id, ref p, immut, _) in &collector.collected_paths {
- let value = if immut == ast::Mutability::Immutable {
- value.to_string()
- } else {
- "<mutable>".to_string()
- };
- let types = self.tcx.node_types();
- let typ = types.get(&id).map(|t| t.to_string()).unwrap_or(String::new());
- // Get the span only for the name of the variable (I hope the path
- // is only ever a variable name, but who knows?).
- let sub_span = self.span.span_for_last_ident(p.span);
- // Rust uses the id of the pattern for var lookups, so we'll use it too.
- self.fmt.variable_str(p.span,
- sub_span,
- id,
- &path_to_string(p),
- &value,
- &typ);
- }
- }
-
- /// Extract macro use and definition information from the AST node defined
- /// by the given NodeId, using the expansion information from the node's
- /// span.
- ///
- /// If the span is not macro-generated, do nothing, else use callee and
- /// callsite spans to record macro definition and use data, using the
- /// mac_uses and mac_defs sets to prevent multiples.
- fn process_macro_use(&mut self, span: Span, id: NodeId) {
- let data = match self.save_ctxt.get_macro_use_data(span, id) {
- None => return,
- Some(data) => data,
- };
- let mut hasher = SipHasher::new();
- data.callee_span.hash(&mut hasher);
- let hash = hasher.finish();
- let qualname = format!("{}::{}", data.name, hash);
- // Don't write macro definition for imported macros
- if !self.mac_defs.contains(&data.callee_span)
- && !data.imported {
- self.mac_defs.insert(data.callee_span);
- if let Some(sub_span) = self.span.span_for_macro_def_name(data.callee_span) {
- self.fmt.macro_str(data.callee_span, sub_span,
- data.name.clone(), qualname.clone());
- }
- }
- if !self.mac_uses.contains(&data.span) {
- self.mac_uses.insert(data.span);
- if let Some(sub_span) = self.span.span_for_macro_use_name(data.span) {
- self.fmt.macro_use_str(data.span, sub_span, data.name,
- qualname, data.scope);
- }
- }
- }
-}
-
-impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> {
- fn visit_item(&mut self, item: &ast::Item) {
- use syntax::ast::ItemKind::*;
- self.process_macro_use(item.span, item.id);
- match item.node {
- Use(ref use_item) => {
- match use_item.node {
- ast::ViewPathSimple(ident, ref path) => {
- let sub_span = self.span.span_for_last_ident(path.span);
- let mod_id = match self.lookup_type_ref(item.id) {
- Some(def_id) => {
- match self.lookup_def_kind(item.id, path.span) {
- Some(kind) => self.fmt.ref_str(kind,
- path.span,
- sub_span,
- def_id,
- self.cur_scope),
- None => {}
- }
- Some(def_id)
- }
- None => None,
- };
-
- // 'use' always introduces an alias, if there is not an explicit
- // one, there is an implicit one.
- let sub_span = match self.span.sub_span_after_keyword(use_item.span,
- keywords::As) {
- Some(sub_span) => Some(sub_span),
- None => sub_span,
- };
-
- self.fmt.use_alias_str(path.span,
- sub_span,
- item.id,
- mod_id,
- &ident.name.as_str(),
- self.cur_scope);
- self.write_sub_paths_truncated(path, true);
- }
- ast::ViewPathGlob(ref path) => {
- // Make a comma-separated list of names of imported modules.
- let mut name_string = String::new();
- let glob_map = &self.analysis.glob_map;
- let glob_map = glob_map.as_ref().unwrap();
- if glob_map.contains_key(&item.id) {
- for n in glob_map.get(&item.id).unwrap() {
- if !name_string.is_empty() {
- name_string.push_str(", ");
- }
- name_string.push_str(&n.as_str());
- }
- }
-
- let sub_span = self.span
- .sub_span_of_token(path.span, token::BinOp(token::Star));
- self.fmt.use_glob_str(path.span,
- sub_span,
- item.id,
- &name_string,
- self.cur_scope);
- self.write_sub_paths(path, true);
- }
- ast::ViewPathList(ref path, ref list) => {
- for plid in list {
- match plid.node {
- ast::PathListItemKind::Ident { id, .. } => {
- match self.lookup_type_ref(id) {
- Some(def_id) => match self.lookup_def_kind(id, plid.span) {
- Some(kind) => {
- self.fmt.ref_str(kind,
- plid.span,
- Some(plid.span),
- def_id,
- self.cur_scope);
- }
- None => (),
- },
- None => (),
- }
- }
- ast::PathListItemKind::Mod { .. } => (),
- }
- }
-
- self.write_sub_paths(path, true);
- }
- }
- }
- ExternCrate(ref s) => {
- let location = match *s {
- Some(s) => s.to_string(),
- None => item.ident.to_string(),
- };
- let alias_span = self.span.span_for_last_ident(item.span);
- let cnum = match self.sess.cstore.extern_mod_stmt_cnum(item.id) {
- Some(cnum) => cnum,
- None => 0,
- };
- self.fmt.extern_crate_str(item.span,
- alias_span,
- item.id,
- cnum,
- &item.ident.name.as_str(),
- &location,
- self.cur_scope);
- }
- Fn(ref decl, _, _, _, ref ty_params, ref body) =>
- self.process_fn(item, &decl, ty_params, &body),
- Static(ref typ, _, ref expr) =>
- self.process_static_or_const_item(item, typ, expr),
- Const(ref typ, ref expr) =>
- self.process_static_or_const_item(item, &typ, &expr),
- Struct(ref def, ref ty_params) => self.process_struct(item, def, ty_params),
- Enum(ref def, ref ty_params) => self.process_enum(item, def, ty_params),
- Impl(_, _,
- ref ty_params,
- ref trait_ref,
- ref typ,
- ref impl_items) => {
- self.process_impl(item, ty_params, trait_ref, &typ, impl_items)
- }
- Trait(_, ref generics, ref trait_refs, ref methods) =>
- self.process_trait(item, generics, trait_refs, methods),
- Mod(ref m) => {
- self.process_mod(item);
- self.nest(item.id, |v| visit::walk_mod(v, m));
- }
- Ty(ref ty, ref ty_params) => {
- let qualname = format!("::{}", self.tcx.map.path_to_string(item.id));
- let value = ty_to_string(&ty);
- let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Type);
- self.fmt.typedef_str(item.span, sub_span, item.id, &qualname, &value);
-
- self.visit_ty(&ty);
- self.process_generic_params(ty_params, item.span, &qualname, item.id);
- }
- Mac(_) => (),
- _ => visit::walk_item(self, item),
- }
- }
-
- fn visit_generics(&mut self, generics: &ast::Generics) {
- for param in generics.ty_params.iter() {
- for bound in param.bounds.iter() {
- if let ast::TraitTyParamBound(ref trait_ref, _) = *bound {
- self.process_trait_ref(&trait_ref.trait_ref);
- }
- }
- if let Some(ref ty) = param.default {
- self.visit_ty(&ty);
- }
- }
- }
-
- fn visit_trait_item(&mut self, trait_item: &ast::TraitItem) {
- self.process_macro_use(trait_item.span, trait_item.id);
- match trait_item.node {
- ast::TraitItemKind::Const(ref ty, Some(ref expr)) => {
- self.process_const(trait_item.id,
- trait_item.ident.name,
- trait_item.span,
- &ty,
- &expr);
- }
- ast::TraitItemKind::Method(ref sig, ref body) => {
- self.process_method(sig,
- body.as_ref().map(|x| &**x),
- trait_item.id,
- trait_item.ident.name,
- trait_item.span);
- }
- ast::TraitItemKind::Const(_, None) |
- ast::TraitItemKind::Type(..) => {}
- }
- }
-
- fn visit_impl_item(&mut self, impl_item: &ast::ImplItem) {
- self.process_macro_use(impl_item.span, impl_item.id);
- match impl_item.node {
- ast::ImplItemKind::Const(ref ty, ref expr) => {
- self.process_const(impl_item.id,
- impl_item.ident.name,
- impl_item.span,
- &ty,
- &expr);
- }
- ast::ImplItemKind::Method(ref sig, ref body) => {
- self.process_method(sig,
- Some(body),
- impl_item.id,
- impl_item.ident.name,
- impl_item.span);
- }
- ast::ImplItemKind::Type(_) |
- ast::ImplItemKind::Macro(_) => {}
- }
- }
-
- fn visit_ty(&mut self, t: &ast::Ty) {
- self.process_macro_use(t.span, t.id);
- match t.node {
- ast::TyKind::Path(_, ref path) => {
- match self.lookup_type_ref(t.id) {
- Some(id) => {
- let sub_span = self.span.sub_span_for_type_name(t.span);
- self.fmt.ref_str(recorder::TypeRef, t.span, sub_span, id, self.cur_scope);
- }
- None => (),
- }
-
- self.write_sub_paths_truncated(path, false);
-
- visit::walk_path(self, path);
- }
- _ => visit::walk_ty(self, t),
- }
- }
-
- fn visit_expr(&mut self, ex: &ast::Expr) {
- self.process_macro_use(ex.span, ex.id);
- match ex.node {
- ast::ExprKind::Call(ref _f, ref _args) => {
- // Don't need to do anything for function calls,
- // because just walking the callee path does what we want.
- visit::walk_expr(self, ex);
- }
- ast::ExprKind::Path(_, ref path) => {
- self.process_path(ex.id, path, None);
- visit::walk_expr(self, ex);
- }
- ast::ExprKind::Struct(ref path, ref fields, ref base) => {
- let hir_expr = lower_expr(self.save_ctxt.lcx, ex);
- let adt = self.tcx.expr_ty(&hir_expr).ty_adt_def().unwrap();
- let def = self.tcx.resolve_expr(&hir_expr);
- self.process_struct_lit(ex, path, fields, adt.variant_of_def(def), base)
- }
- ast::ExprKind::MethodCall(_, _, ref args) => self.process_method_call(ex, args),
- ast::ExprKind::Field(ref sub_ex, _) => {
- self.visit_expr(&sub_ex);
-
- if let Some(field_data) = self.save_ctxt.get_expr_data(ex) {
- down_cast_data!(field_data, VariableRefData, self, ex.span);
- self.fmt.ref_str(recorder::VarRef,
- ex.span,
- Some(field_data.span),
- field_data.ref_id,
- field_data.scope);
- }
- }
- ast::ExprKind::TupField(ref sub_ex, idx) => {
- self.visit_expr(&sub_ex);
-
- let hir_node = lower_expr(self.save_ctxt.lcx, sub_ex);
- let ty = &self.tcx.expr_ty_adjusted(&hir_node).sty;
- match *ty {
- ty::TyStruct(def, _) => {
- let sub_span = self.span.sub_span_after_token(ex.span, token::Dot);
- self.fmt.ref_str(recorder::VarRef,
- ex.span,
- sub_span,
- def.struct_variant().fields[idx.node].did,
- self.cur_scope);
- }
- ty::TyTuple(_) => {}
- _ => self.sess.span_bug(ex.span,
- &format!("Expected struct or tuple type, found {:?}",
- ty)),
- }
- }
- ast::ExprKind::Closure(_, ref decl, ref body) => {
- let mut id = String::from("$");
- id.push_str(&ex.id.to_string());
- self.process_formals(&decl.inputs, &id);
-
- // walk arg and return types
- for arg in &decl.inputs {
- self.visit_ty(&arg.ty);
- }
-
- if let ast::FunctionRetTy::Ty(ref ret_ty) = decl.output {
- self.visit_ty(&ret_ty);
- }
-
- // walk the body
- self.nest(ex.id, |v| v.visit_block(&body));
- }
- ast::ExprKind::ForLoop(ref pattern, ref subexpression, ref block, _) |
- ast::ExprKind::WhileLet(ref pattern, ref subexpression, ref block, _) => {
- let value = self.span.snippet(mk_sp(ex.span.lo, subexpression.span.hi));
- self.process_var_decl(pattern, value);
- visit::walk_expr(self, subexpression);
- visit::walk_block(self, block);
- }
- ast::ExprKind::IfLet(ref pattern, ref subexpression, ref block, ref opt_else) => {
- let value = self.span.snippet(mk_sp(ex.span.lo, subexpression.span.hi));
- self.process_var_decl(pattern, value);
- visit::walk_expr(self, subexpression);
- visit::walk_block(self, block);
- opt_else.as_ref().map(|el| visit::walk_expr(self, el));
- }
- _ => {
- visit::walk_expr(self, ex)
- }
- }
- }
-
- fn visit_mac(&mut self, mac: &ast::Mac) {
- // These shouldn't exist in the AST at this point, log a span bug.
- self.sess.span_bug(mac.span, "macro invocation should have been expanded out of AST");
- }
-
- fn visit_pat(&mut self, p: &ast::Pat) {
- self.process_macro_use(p.span, p.id);
- self.process_pat(p);
- }
-
- fn visit_arm(&mut self, arm: &ast::Arm) {
- let mut collector = PathCollector::new();
- for pattern in &arm.pats {
- // collect paths from the arm's patterns
- collector.visit_pat(&pattern);
- self.visit_pat(&pattern);
- }
-
- // This is to get around borrow checking, because we need mut self to call process_path.
- let mut paths_to_process = vec![];
-
- // process collected paths
- for &(id, ref p, immut, ref_kind) in &collector.collected_paths {
- let def_map = self.tcx.def_map.borrow();
- if !def_map.contains_key(&id) {
- self.sess.span_bug(p.span,
- &format!("def_map has no key for {} in visit_arm", id));
- }
- let def = def_map.get(&id).unwrap().full_def();
- match def {
- Def::Local(_, id) => {
- let value = if immut == ast::Mutability::Immutable {
- self.span.snippet(p.span).to_string()
- } else {
- "<mutable>".to_string()
- };
-
- assert!(p.segments.len() == 1,
- "qualified path for local variable def in arm");
- self.fmt.variable_str(p.span, Some(p.span), id, &path_to_string(p), &value, "")
- }
- Def::Variant(..) | Def::Enum(..) |
- Def::TyAlias(..) | Def::Struct(..) => {
- paths_to_process.push((id, p.clone(), Some(ref_kind)))
- }
- // FIXME(nrc) what are these doing here?
- Def::Static(_, _) |
- Def::Const(..) |
- Def::AssociatedConst(..) => {}
- _ => error!("unexpected definition kind when processing collected paths: {:?}",
- def),
- }
- }
-
- for &(id, ref path, ref_kind) in &paths_to_process {
- self.process_path(id, path, ref_kind);
- }
- walk_list!(self, visit_expr, &arm.guard);
- self.visit_expr(&arm.body);
- }
-
- fn visit_stmt(&mut self, s: &ast::Stmt) {
- let id = s.node.id();
- self.process_macro_use(s.span, id.unwrap());
- visit::walk_stmt(self, s)
- }
-
- fn visit_local(&mut self, l: &ast::Local) {
- self.process_macro_use(l.span, l.id);
- let value = self.span.snippet(l.span);
- self.process_var_decl(&l.pat, value);
-
- // Just walk the initialiser and type (don't want to walk the pattern again).
- walk_list!(self, visit_ty, &l.ty);
- walk_list!(self, visit_expr, &l.init);
- }
-}
--- /dev/null
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// 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.
+
+//! Write the output of rustc's analysis to an implementor of Dump. The data is
+//! primarily designed to be used as input to the DXR tool, specifically its
+//! Rust plugin. It could also be used by IDEs or other code browsing, search, or
+//! cross-referencing tools.
+//!
+//! Dumping the analysis is implemented by walking the AST and getting a bunch of
+//! info out from all over the place. We use Def IDs to identify objects. The
+//! tricky part is getting syntactic (span, source text) and semantic (reference
+//! Def IDs) information for parts of expressions which the compiler has discarded.
+//! E.g., in a path `foo::bar::baz`, the compiler only keeps a span for the whole
+//! path and a reference to `baz`, but we want spans and references for all three
+//! idents.
+//!
+//! SpanUtils is used to manipulate spans. In particular, to extract sub-spans
+//! from spans (e.g., the span for `bar` from the above example path).
+//! DumpVisitor walks the AST and processes it, and an implementor of Dump
+//! is used for recording the output in a format-agnostic way (see CsvDumper
+//! for an example).
+
+use session::Session;
+
+use middle::def::Def;
+use middle::def_id::DefId;
+use middle::ty::{self, TyCtxt};
+
+use std::collections::HashSet;
+use std::hash::*;
+
+use syntax::ast::{self, NodeId, PatKind};
+use syntax::codemap::*;
+use syntax::parse::token::{self, keywords};
+use syntax::visit::{self, Visitor};
+use syntax::print::pprust::{path_to_string, ty_to_string};
+use syntax::ptr::P;
+
+use rustc_front::lowering::{lower_expr, LoweringContext};
+
+use super::{escape, generated_code, SaveContext, PathCollector};
+use super::data::*;
+use super::dump::Dump;
+use super::span_utils::SpanUtils;
+use super::recorder;
+
+macro_rules! down_cast_data {
+ ($id:ident, $kind:ident, $this:ident, $sp:expr) => {
+ let $id = if let super::Data::$kind(data) = $id {
+ data
+ } else {
+ $this.sess.span_bug($sp, &format!("unexpected data kind: {:?}", $id));
+ }
+ };
+}
+
+pub struct DumpVisitor<'l, 'tcx: 'l, D: 'l> {
+ save_ctxt: SaveContext<'l, 'tcx>,
+ sess: &'l Session,
+ tcx: &'l TyCtxt<'tcx>,
+ analysis: &'l ty::CrateAnalysis<'l>,
+ dumper: &'l mut D,
+
+ span: SpanUtils<'l>,
+
+ cur_scope: NodeId,
+
+ // Set of macro definition (callee) spans, and the set
+ // of macro use (callsite) spans. We store these to ensure
+ // we only write one macro def per unique macro definition, and
+ // one macro use per unique callsite span.
+ mac_defs: HashSet<Span>,
+ mac_uses: HashSet<Span>,
+
+}
+
+impl <'l, 'tcx, D> DumpVisitor<'l, 'tcx, D>
+where D: Dump
+{
+ pub fn new(tcx: &'l TyCtxt<'tcx>,
+ lcx: &'l LoweringContext<'l>,
+ analysis: &'l ty::CrateAnalysis<'l>,
+ dumper: &'l mut D)
+ -> DumpVisitor<'l, 'tcx, D> {
+ let span_utils = SpanUtils::new(&tcx.sess);
+ DumpVisitor {
+ sess: &tcx.sess,
+ tcx: tcx,
+ save_ctxt: SaveContext::from_span_utils(tcx, lcx, span_utils.clone()),
+ analysis: analysis,
+ dumper: dumper,
+ span: span_utils.clone(),
+ cur_scope: 0,
+ mac_defs: HashSet::new(),
+ mac_uses: HashSet::new(),
+ }
+ }
+
+ fn nest<F>(&mut self, scope_id: NodeId, f: F)
+ where F: FnOnce(&mut DumpVisitor<'l, 'tcx, D>)
+ {
+ let parent_scope = self.cur_scope;
+ self.cur_scope = scope_id;
+ f(self);
+ self.cur_scope = parent_scope;
+ }
+
+ pub fn dump_crate_info(&mut self, name: &str, krate: &ast::Crate) {
+ let source_file = self.tcx.sess.local_crate_source_file.as_ref();
+ let crate_root = source_file.map(|source_file| {
+ match source_file.file_name() {
+ Some(_) => source_file.parent().unwrap().display().to_string(),
+ None => source_file.display().to_string(),
+ }
+ });
+
+ // Info about all the external crates referenced from this crate.
+ let external_crates = self.save_ctxt.get_external_crates().into_iter().map(|c| {
+ ExternalCrateData {
+ name: c.name,
+ num: c.number
+ }
+ }).collect();
+
+ // The current crate.
+ let data = CratePreludeData {
+ crate_name: name.into(),
+ crate_root: crate_root,
+ external_crates: external_crates
+ };
+
+ self.dumper.crate_prelude(krate.span, data);
+ }
+
+ // Return all non-empty prefixes of a path.
+ // For each prefix, we return the span for the last segment in the prefix and
+ // a str representation of the entire prefix.
+ fn process_path_prefixes(&self, path: &ast::Path) -> Vec<(Span, String)> {
+ let spans = self.span.spans_for_path_segments(path);
+
+ // Paths to enums seem to not match their spans - the span includes all the
+ // variants too. But they seem to always be at the end, so I hope we can cope with
+ // always using the first ones. So, only error out if we don't have enough spans.
+ // What could go wrong...?
+ if spans.len() < path.segments.len() {
+ if generated_code(path.span) {
+ return vec!();
+ }
+ error!("Mis-calculated spans for path '{}'. Found {} spans, expected {}. Found spans:",
+ path_to_string(path),
+ spans.len(),
+ path.segments.len());
+ for s in &spans {
+ let loc = self.sess.codemap().lookup_char_pos(s.lo);
+ error!(" '{}' in {}, line {}",
+ self.span.snippet(*s),
+ loc.file.name,
+ loc.line);
+ }
+ return vec!();
+ }
+
+ let mut result: Vec<(Span, String)> = vec!();
+
+ let mut segs = vec!();
+ for (i, (seg, span)) in path.segments.iter().zip(&spans).enumerate() {
+ segs.push(seg.clone());
+ let sub_path = ast::Path {
+ span: *span, // span for the last segment
+ global: path.global,
+ segments: segs,
+ };
+ let qualname = if i == 0 && path.global {
+ format!("::{}", path_to_string(&sub_path))
+ } else {
+ path_to_string(&sub_path)
+ };
+ result.push((*span, qualname));
+ segs = sub_path.segments;
+ }
+
+ result
+ }
+
+ // The global arg allows us to override the global-ness of the path (which
+ // actually means 'does the path start with `::`', rather than 'is the path
+ // semantically global). We use the override for `use` imports (etc.) where
+ // the syntax is non-global, but the semantics are global.
+ fn write_sub_paths(&mut self, path: &ast::Path, global: bool) {
+ let sub_paths = self.process_path_prefixes(path);
+ for (i, &(ref span, ref qualname)) in sub_paths.iter().enumerate() {
+ let qualname = if i == 0 && global && !path.global {
+ format!("::{}", qualname)
+ } else {
+ qualname.clone()
+ };
+ self.dumper.mod_ref(path.span, ModRefData {
+ span: *span,
+ qualname: qualname,
+ scope: self.cur_scope,
+ ref_id: None
+ }.normalize(&self.tcx));
+ }
+ }
+
+ // As write_sub_paths, but does not process the last ident in the path (assuming it
+ // will be processed elsewhere). See note on write_sub_paths about global.
+ fn write_sub_paths_truncated(&mut self, path: &ast::Path, global: bool) {
+ let sub_paths = self.process_path_prefixes(path);
+ let len = sub_paths.len();
+ if len <= 1 {
+ return;
+ }
+
+ let sub_paths = &sub_paths[..len-1];
+ for (i, &(ref span, ref qualname)) in sub_paths.iter().enumerate() {
+ let qualname = if i == 0 && global && !path.global {
+ format!("::{}", qualname)
+ } else {
+ qualname.clone()
+ };
+ self.dumper.mod_ref(path.span, ModRefData {
+ span: *span,
+ qualname: qualname,
+ scope: self.cur_scope,
+ ref_id: None
+ }.normalize(&self.tcx));
+ }
+ }
+
+ // As write_sub_paths, but expects a path of the form module_path::trait::method
+ // Where trait could actually be a struct too.
+ fn write_sub_path_trait_truncated(&mut self, path: &ast::Path) {
+ let sub_paths = self.process_path_prefixes(path);
+ let len = sub_paths.len();
+ if len <= 1 {
+ return;
+ }
+ let sub_paths = &sub_paths[.. (len-1)];
+
+ // write the trait part of the sub-path
+ let (ref span, ref qualname) = sub_paths[len-2];
+ self.dumper.type_ref(path.span, TypeRefData {
+ ref_id: None,
+ span: *span,
+ qualname: qualname.to_owned(),
+ scope: 0
+ });
+
+ // write the other sub-paths
+ if len <= 2 {
+ return;
+ }
+ let sub_paths = &sub_paths[..len-2];
+ for &(ref span, ref qualname) in sub_paths {
+ self.dumper.mod_ref(path.span, ModRefData {
+ span: *span,
+ qualname: qualname.to_owned(),
+ scope: self.cur_scope,
+ ref_id: None
+ }.normalize(&self.tcx));
+ }
+ }
+
+ // looks up anything, not just a type
+ fn lookup_type_ref(&self, ref_id: NodeId) -> Option<DefId> {
+ if !self.tcx.def_map.borrow().contains_key(&ref_id) {
+ self.sess.bug(&format!("def_map has no key for {} in lookup_type_ref",
+ ref_id));
+ }
+ let def = self.tcx.def_map.borrow().get(&ref_id).unwrap().full_def();
+ match def {
+ Def::PrimTy(..) => None,
+ Def::SelfTy(..) => None,
+ _ => Some(def.def_id()),
+ }
+ }
+
+ fn process_def_kind(&mut self,
+ ref_id: NodeId,
+ span: Span,
+ sub_span: Option<Span>,
+ def_id: DefId,
+ scope: NodeId) {
+ if self.span.filter_generated(sub_span, span) {
+ return;
+ }
+
+ let def_map = self.tcx.def_map.borrow();
+ if !def_map.contains_key(&ref_id) {
+ self.sess.span_bug(span,
+ &format!("def_map has no key for {} in lookup_def_kind",
+ ref_id));
+ }
+ let def = def_map.get(&ref_id).unwrap().full_def();
+ match def {
+ Def::Mod(_) |
+ Def::ForeignMod(_) => {
+ self.dumper.mod_ref(span, ModRefData {
+ span: sub_span.expect("No span found for mod ref"),
+ ref_id: Some(def_id),
+ scope: scope,
+ qualname: String::new()
+ }.normalize(&self.tcx));
+ }
+ Def::Struct(..) |
+ Def::Enum(..) |
+ Def::TyAlias(..) |
+ Def::AssociatedTy(..) |
+ Def::Trait(_) => {
+ self.dumper.type_ref(span, TypeRefData {
+ span: sub_span.expect("No span found for type ref"),
+ ref_id: Some(def_id),
+ scope: scope,
+ qualname: String::new()
+ }.normalize(&self.tcx));
+ }
+ Def::Static(_, _) |
+ Def::Const(_) |
+ Def::AssociatedConst(..) |
+ Def::Local(..) |
+ Def::Variant(..) |
+ Def::Upvar(..) => {
+ self.dumper.variable_ref(span, VariableRefData {
+ span: sub_span.expect("No span found for var ref"),
+ ref_id: def_id,
+ scope: scope,
+ name: String::new()
+ }.normalize(&self.tcx));
+ }
+ Def::Fn(..) => {
+ self.dumper.function_ref(span, FunctionRefData {
+ span: sub_span.expect("No span found for fn ref"),
+ ref_id: def_id,
+ scope: scope
+ }.normalize(&self.tcx));
+ }
+ Def::SelfTy(..) |
+ Def::Label(_) |
+ Def::TyParam(..) |
+ Def::Method(..) |
+ Def::PrimTy(_) |
+ Def::Err => {
+ self.sess.span_bug(span,
+ &format!("process_def_kind for unexpected item: {:?}", def));
+ }
+ }
+ }
+
+ fn process_formals(&mut self, formals: &Vec<ast::Arg>, qualname: &str) {
+ for arg in formals {
+ self.visit_pat(&arg.pat);
+ let mut collector = PathCollector::new();
+ collector.visit_pat(&arg.pat);
+ let span_utils = self.span.clone();
+ for &(id, ref p, _, _) in &collector.collected_paths {
+ let typ = self.tcx.node_types().get(&id).unwrap().to_string();
+ // get the span only for the name of the variable (I hope the path is only ever a
+ // variable name, but who knows?)
+ let sub_span = span_utils.span_for_last_ident(p.span);
+ if !self.span.filter_generated(sub_span, p.span) {
+ self.dumper.variable(p.span, VariableData {
+ id: id,
+ span: sub_span.expect("No span found for variable"),
+ name: path_to_string(p),
+ qualname: format!("{}::{}", qualname, path_to_string(p)),
+ type_value: typ,
+ value: String::new(),
+ scope: 0
+ }.normalize(&self.tcx));
+ }
+ }
+ }
+ }
+
+ fn process_method(&mut self,
+ sig: &ast::MethodSig,
+ body: Option<&ast::Block>,
+ id: ast::NodeId,
+ name: ast::Name,
+ span: Span) {
+ debug!("process_method: {}:{}", id, name);
+
+ if let Some(method_data) = self.save_ctxt.get_method_data(id, name, span) {
+
+ if body.is_some() {
+ if !self.span.filter_generated(Some(method_data.span), span) {
+ self.dumper.function(span, method_data.clone().normalize(&self.tcx));
+ }
+ self.process_formals(&sig.decl.inputs, &method_data.qualname);
+ } else {
+ if !self.span.filter_generated(Some(method_data.span), span) {
+ self.dumper.method(span, MethodData {
+ id: method_data.id,
+ span: method_data.span,
+ scope: method_data.scope,
+ qualname: method_data.qualname.clone(),
+ }.normalize(&self.tcx));
+ }
+ }
+ self.process_generic_params(&sig.generics, span, &method_data.qualname, id);
+ }
+
+ // walk arg and return types
+ for arg in &sig.decl.inputs {
+ self.visit_ty(&arg.ty);
+ }
+
+ if let ast::FunctionRetTy::Ty(ref ret_ty) = sig.decl.output {
+ self.visit_ty(ret_ty);
+ }
+
+ // walk the fn body
+ if let Some(body) = body {
+ self.nest(id, |v| v.visit_block(body));
+ }
+ }
+
+ fn process_trait_ref(&mut self, trait_ref: &ast::TraitRef) {
+ let trait_ref_data = self.save_ctxt.get_trait_ref_data(trait_ref, self.cur_scope);
+ if let Some(trait_ref_data) = trait_ref_data {
+ if !self.span.filter_generated(Some(trait_ref_data.span), trait_ref.path.span) {
+ self.dumper.type_ref(trait_ref.path.span, trait_ref_data.normalize(&self.tcx));
+ }
+
+ visit::walk_path(self, &trait_ref.path);
+ }
+ }
+
+ fn process_struct_field_def(&mut self, field: &ast::StructField, parent_id: NodeId) {
+ let field_data = self.save_ctxt.get_field_data(field, parent_id);
+ if let Some(mut field_data) = field_data {
+ if !self.span.filter_generated(Some(field_data.span), field.span) {
+ field_data.scope = normalize_node_id(&self.tcx, field_data.scope) as u32;
+ field_data.value = String::new();
+ self.dumper.variable(field.span, field_data.normalize(&self.tcx));
+ }
+ }
+ }
+
+ // Dump generic params bindings, then visit_generics
+ fn process_generic_params(&mut self,
+ generics: &ast::Generics,
+ full_span: Span,
+ prefix: &str,
+ id: NodeId) {
+ // We can't only use visit_generics since we don't have spans for param
+ // bindings, so we reparse the full_span to get those sub spans.
+ // However full span is the entire enum/fn/struct block, so we only want
+ // the first few to match the number of generics we're looking for.
+ let param_sub_spans = self.span.spans_for_ty_params(full_span,
+ (generics.ty_params.len() as isize));
+ for (param, param_ss) in generics.ty_params.iter().zip(param_sub_spans) {
+ // Append $id to name to make sure each one is unique
+ let name = format!("{}::{}${}",
+ prefix,
+ escape(self.span.snippet(param_ss)),
+ id);
+ if !self.span.filter_generated(Some(param_ss), full_span) {
+ self.dumper.typedef(full_span, TypedefData {
+ span: param_ss,
+ id: param.id,
+ qualname: name,
+ value: String::new()
+ }.normalize(&self.tcx));
+ }
+ }
+ self.visit_generics(generics);
+ }
+
+ fn process_fn(&mut self,
+ item: &ast::Item,
+ decl: &ast::FnDecl,
+ ty_params: &ast::Generics,
+ body: &ast::Block) {
+ if let Some(fn_data) = self.save_ctxt.get_item_data(item) {
+ down_cast_data!(fn_data, FunctionData, self, item.span);
+ if !self.span.filter_generated(Some(fn_data.span), item.span) {
+ self.dumper.function(item.span, fn_data.clone().normalize(&self.tcx));
+ }
+
+ self.process_formals(&decl.inputs, &fn_data.qualname);
+ self.process_generic_params(ty_params, item.span, &fn_data.qualname, item.id);
+ }
+
+ for arg in &decl.inputs {
+ self.visit_ty(&arg.ty);
+ }
+
+ if let ast::FunctionRetTy::Ty(ref ret_ty) = decl.output {
+ self.visit_ty(&ret_ty);
+ }
+
+ self.nest(item.id, |v| v.visit_block(&body));
+ }
+
+ fn process_static_or_const_item(&mut self, item: &ast::Item, typ: &ast::Ty, expr: &ast::Expr) {
+ if let Some(var_data) = self.save_ctxt.get_item_data(item) {
+ down_cast_data!(var_data, VariableData, self, item.span);
+ if !self.span.filter_generated(Some(var_data.span), item.span) {
+ let mut var_data = var_data;
+ var_data.scope = normalize_node_id(&self.tcx, var_data.scope) as u32;
+ self.dumper.variable(item.span, var_data.normalize(&self.tcx));
+ }
+ }
+ self.visit_ty(&typ);
+ self.visit_expr(expr);
+ }
+
+ fn process_const(&mut self,
+ id: ast::NodeId,
+ name: ast::Name,
+ span: Span,
+ typ: &ast::Ty,
+ expr: &ast::Expr) {
+ let qualname = format!("::{}", self.tcx.map.path_to_string(id));
+
+ let sub_span = self.span.sub_span_after_keyword(span, keywords::Const);
+
+ if !self.span.filter_generated(sub_span, span) {
+ self.dumper.variable(span, VariableData {
+ span: sub_span.expect("No span found for variable"),
+ id: id,
+ name: name.to_string(),
+ qualname: qualname,
+ value: self.span.snippet(expr.span),
+ type_value: ty_to_string(&typ),
+ scope: normalize_node_id(&self.tcx, self.cur_scope) as u32
+ }.normalize(&self.tcx));
+ }
+
+ // walk type and init value
+ self.visit_ty(typ);
+ self.visit_expr(expr);
+ }
+
+ fn process_struct(&mut self,
+ item: &ast::Item,
+ def: &ast::VariantData,
+ ty_params: &ast::Generics) {
+ let qualname = format!("::{}", self.tcx.map.path_to_string(item.id));
+
+ let val = self.span.snippet(item.span);
+ let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Struct);
+ if !self.span.filter_generated(sub_span, item.span) {
+ self.dumper.struct_data(item.span, StructData {
+ span: sub_span.expect("No span found for struct"),
+ id: item.id,
+ ctor_id: def.id(),
+ qualname: qualname.clone(),
+ scope: self.cur_scope,
+ value: val
+ }.normalize(&self.tcx));
+ }
+
+
+ // fields
+ for field in def.fields() {
+ self.process_struct_field_def(field, item.id);
+ self.visit_ty(&field.node.ty);
+ }
+
+ self.process_generic_params(ty_params, item.span, &qualname, item.id);
+ }
+
+ fn process_enum(&mut self,
+ item: &ast::Item,
+ enum_definition: &ast::EnumDef,
+ ty_params: &ast::Generics) {
+ let enum_data = self.save_ctxt.get_item_data(item);
+ let enum_data = match enum_data {
+ None => return,
+ Some(data) => data,
+ };
+ down_cast_data!(enum_data, EnumData, self, item.span);
+ let normalized = enum_data.clone().normalize(&self.tcx);
+ if !self.span.filter_generated(Some(normalized.span), item.span) {
+ self.dumper.enum_data(item.span, normalized);
+ }
+
+ for variant in &enum_definition.variants {
+ let name = &variant.node.name.name.as_str();
+ let mut qualname = enum_data.qualname.clone();
+ qualname.push_str("::");
+ qualname.push_str(name);
+ let val = self.span.snippet(variant.span);
+
+ match variant.node.data {
+ ast::VariantData::Struct(..) => {
+ let sub_span = self.span.span_for_first_ident(variant.span);
+ if !self.span.filter_generated(sub_span, variant.span) {
+ self.dumper.struct_variant(variant.span, StructVariantData {
+ span: sub_span.expect("No span found for struct variant"),
+ id: variant.node.data.id(),
+ qualname: qualname,
+ type_value: enum_data.qualname.clone(),
+ value: val,
+ scope: enum_data.scope
+ }.normalize(&self.tcx));
+ }
+ }
+ _ => {
+ let sub_span = self.span.span_for_first_ident(variant.span);
+ if !self.span.filter_generated(sub_span, variant.span) {
+ self.dumper.tuple_variant(variant.span, TupleVariantData {
+ span: sub_span.expect("No span found for tuple variant"),
+ id: variant.node.data.id(),
+ name: name.to_string(),
+ qualname: qualname,
+ type_value: enum_data.qualname.clone(),
+ value: val,
+ scope: enum_data.scope
+ }.normalize(&self.tcx));
+ }
+ }
+ }
+
+
+ for field in variant.node.data.fields() {
+ self.process_struct_field_def(field, variant.node.data.id());
+ self.visit_ty(&field.node.ty);
+ }
+ }
+ self.process_generic_params(ty_params, item.span, &enum_data.qualname, enum_data.id);
+ }
+
+ fn process_impl(&mut self,
+ item: &ast::Item,
+ type_parameters: &ast::Generics,
+ trait_ref: &Option<ast::TraitRef>,
+ typ: &ast::Ty,
+ impl_items: &[ast::ImplItem]) {
+ let mut has_self_ref = false;
+ if let Some(impl_data) = self.save_ctxt.get_item_data(item) {
+ down_cast_data!(impl_data, ImplData, self, item.span);
+ if let Some(ref self_ref) = impl_data.self_ref {
+ has_self_ref = true;
+ if !self.span.filter_generated(Some(self_ref.span), item.span) {
+ self.dumper.type_ref(item.span, self_ref.clone().normalize(&self.tcx));
+ }
+ }
+ if let Some(ref trait_ref_data) = impl_data.trait_ref {
+ if !self.span.filter_generated(Some(trait_ref_data.span), item.span) {
+ self.dumper.type_ref(item.span, trait_ref_data.clone().normalize(&self.tcx));
+ }
+
+ visit::walk_path(self, &trait_ref.as_ref().unwrap().path);
+ }
+
+ if !self.span.filter_generated(Some(impl_data.span), item.span) {
+ self.dumper.impl_data(item.span, ImplData {
+ id: impl_data.id,
+ span: impl_data.span,
+ scope: impl_data.scope,
+ trait_ref: impl_data.trait_ref.map(|d| d.ref_id.unwrap()),
+ self_ref: impl_data.self_ref.map(|d| d.ref_id.unwrap())
+ }.normalize(&self.tcx));
+ }
+ }
+ if !has_self_ref {
+ self.visit_ty(&typ);
+ }
+ self.process_generic_params(type_parameters, item.span, "", item.id);
+ for impl_item in impl_items {
+ self.visit_impl_item(impl_item);
+ }
+ }
+
+ fn process_trait(&mut self,
+ item: &ast::Item,
+ generics: &ast::Generics,
+ trait_refs: &ast::TyParamBounds,
+ methods: &[ast::TraitItem]) {
+ let qualname = format!("::{}", self.tcx.map.path_to_string(item.id));
+ let val = self.span.snippet(item.span);
+ let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Trait);
+ if !self.span.filter_generated(sub_span, item.span) {
+ self.dumper.trait_data(item.span, TraitData {
+ span: sub_span.expect("No span found for trait"),
+ id: item.id,
+ qualname: qualname.clone(),
+ scope: self.cur_scope,
+ value: val
+ }.normalize(&self.tcx));
+ }
+
+ // super-traits
+ for super_bound in trait_refs.iter() {
+ let trait_ref = match *super_bound {
+ ast::TraitTyParamBound(ref trait_ref, _) => {
+ trait_ref
+ }
+ ast::RegionTyParamBound(..) => {
+ continue;
+ }
+ };
+
+ let trait_ref = &trait_ref.trait_ref;
+ if let Some(id) = self.lookup_type_ref(trait_ref.ref_id) {
+ let sub_span = self.span.sub_span_for_type_name(trait_ref.path.span);
+ if !self.span.filter_generated(sub_span, trait_ref.path.span) {
+ self.dumper.type_ref(trait_ref.path.span, TypeRefData {
+ span: sub_span.expect("No span found for trait ref"),
+ ref_id: Some(id),
+ scope: self.cur_scope,
+ qualname: String::new()
+ }.normalize(&self.tcx));
+ }
+
+ if !self.span.filter_generated(sub_span, trait_ref.path.span) {
+ let sub_span = sub_span.expect("No span for inheritance");
+ self.dumper.inheritance(InheritanceData {
+ span: sub_span,
+ base_id: id,
+ deriv_id: item.id
+ }.normalize(&self.tcx));
+ }
+ }
+ }
+
+ // walk generics and methods
+ self.process_generic_params(generics, item.span, &qualname, item.id);
+ for method in methods {
+ self.visit_trait_item(method)
+ }
+ }
+
+ // `item` is the module in question, represented as an item.
+ fn process_mod(&mut self, item: &ast::Item) {
+ if let Some(mod_data) = self.save_ctxt.get_item_data(item) {
+ down_cast_data!(mod_data, ModData, self, item.span);
+ if !self.span.filter_generated(Some(mod_data.span), item.span) {
+ self.dumper.mod_data(mod_data.normalize(&self.tcx));
+ }
+ }
+ }
+
+ fn process_path(&mut self, id: NodeId, path: &ast::Path, ref_kind: Option<recorder::Row>) {
+ let path_data = self.save_ctxt.get_path_data(id, path);
+ if generated_code(path.span) && path_data.is_none() {
+ return;
+ }
+
+ let path_data = match path_data {
+ Some(pd) => pd,
+ None => {
+ self.tcx.sess.span_bug(path.span,
+ &format!("Unexpected def kind while looking up path in \
+ `{}`",
+ self.span.snippet(path.span)))
+ }
+ };
+
+ match path_data {
+ Data::VariableRefData(vrd) => {
+ // FIXME: this whole block duplicates the code in process_def_kind
+ if !self.span.filter_generated(Some(vrd.span), path.span) {
+ match ref_kind {
+ Some(recorder::TypeRef) => {
+ self.dumper.type_ref(path.span, TypeRefData {
+ span: vrd.span,
+ ref_id: Some(vrd.ref_id),
+ scope: vrd.scope,
+ qualname: String::new()
+ }.normalize(&self.tcx));
+ }
+ Some(recorder::FnRef) => {
+ self.dumper.function_ref(path.span, FunctionRefData {
+ span: vrd.span,
+ ref_id: vrd.ref_id,
+ scope: vrd.scope
+ }.normalize(&self.tcx));
+ }
+ Some(recorder::ModRef) => {
+ self.dumper.mod_ref(path.span, ModRefData {
+ span: vrd.span,
+ ref_id: Some(vrd.ref_id),
+ scope: vrd.scope,
+ qualname: String::new()
+ }.normalize(&self.tcx));
+ }
+ Some(recorder::VarRef) | None
+ => self.dumper.variable_ref(path.span, vrd.normalize(&self.tcx))
+ }
+ }
+
+ }
+ Data::TypeRefData(trd) => {
+ if !self.span.filter_generated(Some(trd.span), path.span) {
+ self.dumper.type_ref(path.span, trd.normalize(&self.tcx));
+ }
+ }
+ Data::MethodCallData(mcd) => {
+ if !self.span.filter_generated(Some(mcd.span), path.span) {
+ self.dumper.method_call(path.span, mcd.normalize(&self.tcx));
+ }
+ }
+ Data::FunctionCallData(fcd) => {
+ if !self.span.filter_generated(Some(fcd.span), path.span) {
+ self.dumper.function_call(path.span, fcd.normalize(&self.tcx));
+ }
+ }
+ _ => {
+ self.sess.span_bug(path.span,
+ &format!("Unexpected data: {:?}", path_data));
+ }
+ }
+
+ // Modules or types in the path prefix.
+ let def_map = self.tcx.def_map.borrow();
+ let def = def_map.get(&id).unwrap().full_def();
+ match def {
+ Def::Method(did) => {
+ let ti = self.tcx.impl_or_trait_item(did);
+ if let ty::MethodTraitItem(m) = ti {
+ if m.explicit_self == ty::ExplicitSelfCategory::Static {
+ self.write_sub_path_trait_truncated(path);
+ }
+ }
+ }
+ Def::Local(..) |
+ Def::Static(_,_) |
+ Def::Const(..) |
+ Def::AssociatedConst(..) |
+ Def::Struct(..) |
+ Def::Variant(..) |
+ Def::Fn(..) => self.write_sub_paths_truncated(path, false),
+ _ => {}
+ }
+ }
+
+ fn process_struct_lit(&mut self,
+ ex: &ast::Expr,
+ path: &ast::Path,
+ fields: &Vec<ast::Field>,
+ variant: ty::VariantDef,
+ base: &Option<P<ast::Expr>>) {
+ self.write_sub_paths_truncated(path, false);
+
+ if let Some(struct_lit_data) = self.save_ctxt.get_expr_data(ex) {
+ down_cast_data!(struct_lit_data, TypeRefData, self, ex.span);
+ if !self.span.filter_generated(Some(struct_lit_data.span), ex.span) {
+ self.dumper.type_ref(ex.span, struct_lit_data.normalize(&self.tcx));
+ }
+
+ let scope = self.save_ctxt.enclosing_scope(ex.id);
+
+ for field in fields {
+ if let Some(field_data) = self.save_ctxt
+ .get_field_ref_data(field, variant, scope) {
+
+ if !self.span.filter_generated(Some(field_data.span), field.ident.span) {
+ self.dumper.variable_ref(field.ident.span, field_data.normalize(&self.tcx));
+ }
+ }
+
+ self.visit_expr(&field.expr)
+ }
+ }
+
+ walk_list!(self, visit_expr, base);
+ }
+
+ fn process_method_call(&mut self, ex: &ast::Expr, args: &Vec<P<ast::Expr>>) {
+ if let Some(mcd) = self.save_ctxt.get_expr_data(ex) {
+ down_cast_data!(mcd, MethodCallData, self, ex.span);
+ if !self.span.filter_generated(Some(mcd.span), ex.span) {
+ self.dumper.method_call(ex.span, mcd.normalize(&self.tcx));
+ }
+ }
+
+ // walk receiver and args
+ walk_list!(self, visit_expr, args);
+ }
+
+ fn process_pat(&mut self, p: &ast::Pat) {
+ match p.node {
+ PatKind::Struct(ref path, ref fields, _) => {
+ visit::walk_path(self, path);
+ let adt = self.tcx.node_id_to_type(p.id).ty_adt_def().unwrap();
+ let def = self.tcx.def_map.borrow()[&p.id].full_def();
+ let variant = adt.variant_of_def(def);
+
+ for &Spanned { node: ref field, span } in fields {
+ let sub_span = self.span.span_for_first_ident(span);
+ if let Some(f) = variant.find_field_named(field.ident.name) {
+ if !self.span.filter_generated(sub_span, span) {
+ self.dumper.variable_ref(span, VariableRefData {
+ span: sub_span.expect("No span fund for var ref"),
+ ref_id: f.did,
+ scope: self.cur_scope,
+ name: String::new()
+ }.normalize(&self.tcx));
+ }
+ }
+ self.visit_pat(&field.pat);
+ }
+ }
+ _ => visit::walk_pat(self, p),
+ }
+ }
+
+
+ fn process_var_decl(&mut self, p: &ast::Pat, value: String) {
+ // The local could declare multiple new vars, we must walk the
+ // pattern and collect them all.
+ let mut collector = PathCollector::new();
+ collector.visit_pat(&p);
+ self.visit_pat(&p);
+
+ for &(id, ref p, immut, _) in &collector.collected_paths {
+ let value = if immut == ast::Mutability::Immutable {
+ value.to_string()
+ } else {
+ "<mutable>".to_string()
+ };
+ let types = self.tcx.node_types();
+ let typ = types.get(&id).map(|t| t.to_string()).unwrap_or(String::new());
+ // Get the span only for the name of the variable (I hope the path
+ // is only ever a variable name, but who knows?).
+ let sub_span = self.span.span_for_last_ident(p.span);
+ // Rust uses the id of the pattern for var lookups, so we'll use it too.
+ if !self.span.filter_generated(sub_span, p.span) {
+ self.dumper.variable(p.span, VariableData {
+ span: sub_span.expect("No span found for variable"),
+ id: id,
+ name: path_to_string(p),
+ qualname: format!("{}${}", path_to_string(p), id),
+ value: value,
+ type_value: typ,
+ scope: 0
+ }.normalize(&self.tcx));
+ }
+ }
+ }
+
+ /// Extract macro use and definition information from the AST node defined
+ /// by the given NodeId, using the expansion information from the node's
+ /// span.
+ ///
+ /// If the span is not macro-generated, do nothing, else use callee and
+ /// callsite spans to record macro definition and use data, using the
+ /// mac_uses and mac_defs sets to prevent multiples.
+ fn process_macro_use(&mut self, span: Span, id: NodeId) {
+ let data = match self.save_ctxt.get_macro_use_data(span, id) {
+ None => return,
+ Some(data) => data,
+ };
+ let mut hasher = SipHasher::new();
+ data.callee_span.hash(&mut hasher);
+ let hash = hasher.finish();
+ let qualname = format!("{}::{}", data.name, hash);
+ // Don't write macro definition for imported macros
+ if !self.mac_defs.contains(&data.callee_span)
+ && !data.imported {
+ self.mac_defs.insert(data.callee_span);
+ if let Some(sub_span) = self.span.span_for_macro_def_name(data.callee_span) {
+ self.dumper.macro_data(data.callee_span, MacroData {
+ span: sub_span,
+ name: data.name.clone(),
+ qualname: qualname.clone()
+ });
+ }
+ }
+ if !self.mac_uses.contains(&data.span) {
+ self.mac_uses.insert(data.span);
+ if let Some(sub_span) = self.span.span_for_macro_use_name(data.span) {
+ self.dumper.macro_use(data.span, MacroUseData {
+ span: sub_span,
+ name: data.name,
+ qualname: qualname,
+ scope: data.scope,
+ callee_span: data.callee_span,
+ imported: data.imported
+ }.normalize(&self.tcx));
+ }
+ }
+ }
+}
+
+impl<'l, 'tcx, 'v, D: Dump + 'l> Visitor<'v> for DumpVisitor<'l, 'tcx, D> {
+ fn visit_item(&mut self, item: &ast::Item) {
+ use syntax::ast::ItemKind::*;
+ self.process_macro_use(item.span, item.id);
+ match item.node {
+ Use(ref use_item) => {
+ match use_item.node {
+ ast::ViewPathSimple(ident, ref path) => {
+ let sub_span = self.span.span_for_last_ident(path.span);
+ let mod_id = match self.lookup_type_ref(item.id) {
+ Some(def_id) => {
+ let scope = self.cur_scope;
+ self.process_def_kind(item.id, path.span, sub_span, def_id, scope);
+
+ Some(def_id)
+ }
+ None => None,
+ };
+
+ // 'use' always introduces an alias, if there is not an explicit
+ // one, there is an implicit one.
+ let sub_span = match self.span.sub_span_after_keyword(use_item.span,
+ keywords::As) {
+ Some(sub_span) => Some(sub_span),
+ None => sub_span,
+ };
+
+ if !self.span.filter_generated(sub_span, path.span) {
+ self.dumper.use_data(path.span, UseData {
+ span: sub_span.expect("No span found for use"),
+ id: item.id,
+ mod_id: mod_id,
+ name: ident.name.to_string(),
+ scope: self.cur_scope
+ }.normalize(&self.tcx));
+ }
+ self.write_sub_paths_truncated(path, true);
+ }
+ ast::ViewPathGlob(ref path) => {
+ // Make a comma-separated list of names of imported modules.
+ let mut names = vec![];
+ let glob_map = &self.analysis.glob_map;
+ let glob_map = glob_map.as_ref().unwrap();
+ if glob_map.contains_key(&item.id) {
+ for n in glob_map.get(&item.id).unwrap() {
+ names.push(n.to_string());
+ }
+ }
+
+ let sub_span = self.span
+ .sub_span_of_token(path.span, token::BinOp(token::Star));
+ if !self.span.filter_generated(sub_span, path.span) {
+ self.dumper.use_glob(path.span, UseGlobData {
+ span: sub_span.expect("No span found for use glob"),
+ id: item.id,
+ names: names,
+ scope: self.cur_scope
+ }.normalize(&self.tcx));
+ }
+ self.write_sub_paths(path, true);
+ }
+ ast::ViewPathList(ref path, ref list) => {
+ for plid in list {
+ match plid.node {
+ ast::PathListItemKind::Ident { id, .. } => {
+ let scope = self.cur_scope;
+ if let Some(def_id) = self.lookup_type_ref(id) {
+ self.process_def_kind(id,
+ plid.span,
+ Some(plid.span),
+ def_id,
+ scope);
+ }
+ }
+ ast::PathListItemKind::Mod { .. } => (),
+ }
+ }
+
+ self.write_sub_paths(path, true);
+ }
+ }
+ }
+ ExternCrate(ref s) => {
+ let location = match *s {
+ Some(s) => s.to_string(),
+ None => item.ident.to_string(),
+ };
+ let alias_span = self.span.span_for_last_ident(item.span);
+ let cnum = match self.sess.cstore.extern_mod_stmt_cnum(item.id) {
+ Some(cnum) => cnum,
+ None => 0,
+ };
+
+ if !self.span.filter_generated(alias_span, item.span) {
+ self.dumper.extern_crate(item.span, ExternCrateData {
+ id: item.id,
+ name: item.ident.name.to_string(),
+ crate_num: cnum,
+ location: location,
+ span: alias_span.expect("No span found for extern crate"),
+ scope: self.cur_scope,
+ }.normalize(&self.tcx));
+ }
+ }
+ Fn(ref decl, _, _, _, ref ty_params, ref body) =>
+ self.process_fn(item, &decl, ty_params, &body),
+ Static(ref typ, _, ref expr) =>
+ self.process_static_or_const_item(item, typ, expr),
+ Const(ref typ, ref expr) =>
+ self.process_static_or_const_item(item, &typ, &expr),
+ Struct(ref def, ref ty_params) => self.process_struct(item, def, ty_params),
+ Enum(ref def, ref ty_params) => self.process_enum(item, def, ty_params),
+ Impl(_, _,
+ ref ty_params,
+ ref trait_ref,
+ ref typ,
+ ref impl_items) => {
+ self.process_impl(item, ty_params, trait_ref, &typ, impl_items)
+ }
+ Trait(_, ref generics, ref trait_refs, ref methods) =>
+ self.process_trait(item, generics, trait_refs, methods),
+ Mod(ref m) => {
+ self.process_mod(item);
+ self.nest(item.id, |v| visit::walk_mod(v, m));
+ }
+ Ty(ref ty, ref ty_params) => {
+ let qualname = format!("::{}", self.tcx.map.path_to_string(item.id));
+ let value = ty_to_string(&ty);
+ let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Type);
+ if !self.span.filter_generated(sub_span, item.span) {
+ self.dumper.typedef(item.span, TypedefData {
+ span: sub_span.expect("No span found for typedef"),
+ id: item.id,
+ qualname: qualname.clone(),
+ value: value
+ }.normalize(&self.tcx));
+ }
+
+ self.visit_ty(&ty);
+ self.process_generic_params(ty_params, item.span, &qualname, item.id);
+ }
+ Mac(_) => (),
+ _ => visit::walk_item(self, item),
+ }
+ }
+
+ fn visit_generics(&mut self, generics: &ast::Generics) {
+ for param in generics.ty_params.iter() {
+ for bound in param.bounds.iter() {
+ if let ast::TraitTyParamBound(ref trait_ref, _) = *bound {
+ self.process_trait_ref(&trait_ref.trait_ref);
+ }
+ }
+ if let Some(ref ty) = param.default {
+ self.visit_ty(&ty);
+ }
+ }
+ }
+
+ fn visit_trait_item(&mut self, trait_item: &ast::TraitItem) {
+ self.process_macro_use(trait_item.span, trait_item.id);
+ match trait_item.node {
+ ast::TraitItemKind::Const(ref ty, Some(ref expr)) => {
+ self.process_const(trait_item.id,
+ trait_item.ident.name,
+ trait_item.span,
+ &ty,
+ &expr);
+ }
+ ast::TraitItemKind::Method(ref sig, ref body) => {
+ self.process_method(sig,
+ body.as_ref().map(|x| &**x),
+ trait_item.id,
+ trait_item.ident.name,
+ trait_item.span);
+ }
+ ast::TraitItemKind::Const(_, None) |
+ ast::TraitItemKind::Type(..) => {}
+ }
+ }
+
+ fn visit_impl_item(&mut self, impl_item: &ast::ImplItem) {
+ self.process_macro_use(impl_item.span, impl_item.id);
+ match impl_item.node {
+ ast::ImplItemKind::Const(ref ty, ref expr) => {
+ self.process_const(impl_item.id,
+ impl_item.ident.name,
+ impl_item.span,
+ &ty,
+ &expr);
+ }
+ ast::ImplItemKind::Method(ref sig, ref body) => {
+ self.process_method(sig,
+ Some(body),
+ impl_item.id,
+ impl_item.ident.name,
+ impl_item.span);
+ }
+ ast::ImplItemKind::Type(_) |
+ ast::ImplItemKind::Macro(_) => {}
+ }
+ }
+
+ fn visit_ty(&mut self, t: &ast::Ty) {
+ self.process_macro_use(t.span, t.id);
+ match t.node {
+ ast::TyKind::Path(_, ref path) => {
+ if let Some(id) = self.lookup_type_ref(t.id) {
+ let sub_span = self.span.sub_span_for_type_name(t.span);
+ if !self.span.filter_generated(sub_span, t.span) {
+ self.dumper.type_ref(t.span, TypeRefData {
+ span: sub_span.expect("No span found for type ref"),
+ ref_id: Some(id),
+ scope: self.cur_scope,
+ qualname: String::new()
+ }.normalize(&self.tcx));
+ }
+ }
+
+ self.write_sub_paths_truncated(path, false);
+
+ visit::walk_path(self, path);
+ }
+ _ => visit::walk_ty(self, t),
+ }
+ }
+
+ fn visit_expr(&mut self, ex: &ast::Expr) {
+ self.process_macro_use(ex.span, ex.id);
+ match ex.node {
+ ast::ExprKind::Call(ref _f, ref _args) => {
+ // Don't need to do anything for function calls,
+ // because just walking the callee path does what we want.
+ visit::walk_expr(self, ex);
+ }
+ ast::ExprKind::Path(_, ref path) => {
+ self.process_path(ex.id, path, None);
+ visit::walk_expr(self, ex);
+ }
+ ast::ExprKind::Struct(ref path, ref fields, ref base) => {
+ let hir_expr = lower_expr(self.save_ctxt.lcx, ex);
+ let adt = self.tcx.expr_ty(&hir_expr).ty_adt_def().unwrap();
+ let def = self.tcx.resolve_expr(&hir_expr);
+ self.process_struct_lit(ex, path, fields, adt.variant_of_def(def), base)
+ }
+ ast::ExprKind::MethodCall(_, _, ref args) => self.process_method_call(ex, args),
+ ast::ExprKind::Field(ref sub_ex, _) => {
+ self.visit_expr(&sub_ex);
+
+ if let Some(field_data) = self.save_ctxt.get_expr_data(ex) {
+ down_cast_data!(field_data, VariableRefData, self, ex.span);
+ if !self.span.filter_generated(Some(field_data.span), ex.span) {
+ self.dumper.variable_ref(ex.span, field_data.normalize(&self.tcx));
+ }
+ }
+ }
+ ast::ExprKind::TupField(ref sub_ex, idx) => {
+ self.visit_expr(&sub_ex);
+
+ let hir_node = lower_expr(self.save_ctxt.lcx, sub_ex);
+ let ty = &self.tcx.expr_ty_adjusted(&hir_node).sty;
+ match *ty {
+ ty::TyStruct(def, _) => {
+ let sub_span = self.span.sub_span_after_token(ex.span, token::Dot);
+ if !self.span.filter_generated(sub_span, ex.span) {
+ self.dumper.variable_ref(ex.span, VariableRefData {
+ span: sub_span.expect("No span found for var ref"),
+ ref_id: def.struct_variant().fields[idx.node].did,
+ scope: self.cur_scope,
+ name: String::new()
+ }.normalize(&self.tcx));
+ }
+ }
+ ty::TyTuple(_) => {}
+ _ => self.sess.span_bug(ex.span,
+ &format!("Expected struct or tuple type, found {:?}",
+ ty)),
+ }
+ }
+ ast::ExprKind::Closure(_, ref decl, ref body) => {
+ let mut id = String::from("$");
+ id.push_str(&ex.id.to_string());
+ self.process_formals(&decl.inputs, &id);
+
+ // walk arg and return types
+ for arg in &decl.inputs {
+ self.visit_ty(&arg.ty);
+ }
+
+ if let ast::FunctionRetTy::Ty(ref ret_ty) = decl.output {
+ self.visit_ty(&ret_ty);
+ }
+
+ // walk the body
+ self.nest(ex.id, |v| v.visit_block(&body));
+ }
+ ast::ExprKind::ForLoop(ref pattern, ref subexpression, ref block, _) |
+ ast::ExprKind::WhileLet(ref pattern, ref subexpression, ref block, _) => {
+ let value = self.span.snippet(mk_sp(ex.span.lo, subexpression.span.hi));
+ self.process_var_decl(pattern, value);
+ visit::walk_expr(self, subexpression);
+ visit::walk_block(self, block);
+ }
+ ast::ExprKind::IfLet(ref pattern, ref subexpression, ref block, ref opt_else) => {
+ let value = self.span.snippet(mk_sp(ex.span.lo, subexpression.span.hi));
+ self.process_var_decl(pattern, value);
+ visit::walk_expr(self, subexpression);
+ visit::walk_block(self, block);
+ opt_else.as_ref().map(|el| visit::walk_expr(self, el));
+ }
+ _ => {
+ visit::walk_expr(self, ex)
+ }
+ }
+ }
+
+ fn visit_mac(&mut self, mac: &ast::Mac) {
+ // These shouldn't exist in the AST at this point, log a span bug.
+ self.sess.span_bug(mac.span, "macro invocation should have been expanded out of AST");
+ }
+
+ fn visit_pat(&mut self, p: &ast::Pat) {
+ self.process_macro_use(p.span, p.id);
+ self.process_pat(p);
+ }
+
+ fn visit_arm(&mut self, arm: &ast::Arm) {
+ let mut collector = PathCollector::new();
+ for pattern in &arm.pats {
+ // collect paths from the arm's patterns
+ collector.visit_pat(&pattern);
+ self.visit_pat(&pattern);
+ }
+
+ // This is to get around borrow checking, because we need mut self to call process_path.
+ let mut paths_to_process = vec![];
+
+ // process collected paths
+ for &(id, ref p, immut, ref_kind) in &collector.collected_paths {
+ let def_map = self.tcx.def_map.borrow();
+ if !def_map.contains_key(&id) {
+ self.sess.span_bug(p.span,
+ &format!("def_map has no key for {} in visit_arm", id));
+ }
+ let def = def_map.get(&id).unwrap().full_def();
+ match def {
+ Def::Local(_, id) => {
+ let value = if immut == ast::Mutability::Immutable {
+ self.span.snippet(p.span).to_string()
+ } else {
+ "<mutable>".to_string()
+ };
+
+ assert!(p.segments.len() == 1,
+ "qualified path for local variable def in arm");
+ if !self.span.filter_generated(Some(p.span), p.span) {
+ self.dumper.variable(p.span, VariableData {
+ span: p.span,
+ id: id,
+ name: path_to_string(p),
+ qualname: format!("{}${}", path_to_string(p), id),
+ value: value,
+ type_value: String::new(),
+ scope: 0
+ }.normalize(&self.tcx));
+ }
+ }
+ Def::Variant(..) | Def::Enum(..) |
+ Def::TyAlias(..) | Def::Struct(..) => {
+ paths_to_process.push((id, p.clone(), Some(ref_kind)))
+ }
+ // FIXME(nrc) what are these doing here?
+ Def::Static(_, _) |
+ Def::Const(..) |
+ Def::AssociatedConst(..) => {}
+ _ => error!("unexpected definition kind when processing collected paths: {:?}",
+ def),
+ }
+ }
+
+ for &(id, ref path, ref_kind) in &paths_to_process {
+ self.process_path(id, path, ref_kind);
+ }
+ walk_list!(self, visit_expr, &arm.guard);
+ self.visit_expr(&arm.body);
+ }
+
+ fn visit_stmt(&mut self, s: &ast::Stmt) {
+ let id = s.node.id();
+ self.process_macro_use(s.span, id.unwrap());
+ visit::walk_stmt(self, s)
+ }
+
+ fn visit_local(&mut self, l: &ast::Local) {
+ self.process_macro_use(l.span, l.id);
+ let value = self.span.snippet(l.span);
+ self.process_var_decl(&l.pat, value);
+
+ // Just walk the initialiser and type (don't want to walk the pattern again).
+ walk_list!(self, visit_ty, &l.ty);
+ walk_list!(self, visit_expr, &l.init);
+ }
+}
use syntax::visit::{self, Visitor};
use syntax::print::pprust::ty_to_string;
-use self::span_utils::SpanUtils;
-
+mod csv_dumper;
+#[macro_use]
+mod data;
+mod dump;
+mod dump_visitor;
#[macro_use]
pub mod span_utils;
-pub mod recorder;
-mod dump_csv;
+pub use self::csv_dumper::CsvDumper;
+pub use self::data::*;
+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,
+ }
+}
pub struct SaveContext<'l, 'tcx: 'l> {
tcx: &'l TyCtxt<'tcx>,
span_utils: SpanUtils<'l>,
}
-pub struct CrateData {
- pub name: String,
- pub number: u32,
-}
-
-/// Data for any entity in the Rust language. The actual data contained varied
-/// with the kind of entity being queried. See the nested structs for details.
-#[derive(Debug)]
-pub enum Data {
- /// Data for all kinds of functions and methods.
- FunctionData(FunctionData),
- /// Data for local and global variables (consts and statics), and fields.
- VariableData(VariableData),
- /// Data for modules.
- ModData(ModData),
- /// Data for Enums.
- EnumData(EnumData),
- /// Data for impls.
- ImplData(ImplData),
-
- /// Data for the use of some variable (e.g., the use of a local variable, which
- /// will refere to that variables declaration).
- VariableRefData(VariableRefData),
- /// Data for a reference to a type or trait.
- TypeRefData(TypeRefData),
- /// Data for a reference to a module.
- ModRefData(ModRefData),
- /// Data about a function call.
- FunctionCallData(FunctionCallData),
- /// Data about a method call.
- MethodCallData(MethodCallData),
- /// Data about a macro use.
- MacroUseData(MacroUseData),
-}
-
-/// Data for all kinds of functions and methods.
-#[derive(Debug)]
-pub struct FunctionData {
- pub id: NodeId,
- pub name: String,
- pub qualname: String,
- pub declaration: Option<DefId>,
- pub span: Span,
- pub scope: NodeId,
-}
-
-/// Data for local and global variables (consts and statics).
-#[derive(Debug)]
-pub struct VariableData {
- pub id: NodeId,
- pub name: String,
- pub qualname: String,
- pub span: Span,
- pub scope: NodeId,
- pub value: String,
- pub type_value: String,
-}
-
-/// Data for modules.
-#[derive(Debug)]
-pub struct ModData {
- pub id: NodeId,
- pub name: String,
- pub qualname: String,
- pub span: Span,
- pub scope: NodeId,
- pub filename: String,
-}
-
-/// Data for enum declarations.
-#[derive(Debug)]
-pub struct EnumData {
- pub id: NodeId,
- pub value: String,
- pub qualname: String,
- pub span: Span,
- pub scope: NodeId,
-}
-
-#[derive(Debug)]
-pub struct ImplData {
- pub id: NodeId,
- pub span: Span,
- pub scope: NodeId,
- // FIXME: I'm not really sure inline data is the best way to do this. Seems
- // OK in this case, but generalising leads to returning chunks of AST, which
- // feels wrong.
- pub trait_ref: Option<TypeRefData>,
- pub self_ref: Option<TypeRefData>,
-}
-
-/// Data for the use of some item (e.g., the use of a local variable, which
-/// will refer to that variables declaration (by ref_id)).
-#[derive(Debug)]
-pub struct VariableRefData {
- pub name: String,
- pub span: Span,
- pub scope: NodeId,
- pub ref_id: DefId,
-}
-
-/// Data for a reference to a type or trait.
-#[derive(Debug)]
-pub struct TypeRefData {
- pub span: Span,
- pub scope: NodeId,
- pub ref_id: DefId,
-}
-
-/// Data for a reference to a module.
-#[derive(Debug)]
-pub struct ModRefData {
- pub span: Span,
- pub scope: NodeId,
- pub ref_id: DefId,
-}
-
-/// Data about a function call.
-#[derive(Debug)]
-pub struct FunctionCallData {
- pub span: Span,
- pub scope: NodeId,
- pub ref_id: DefId,
-}
-
-/// Data about a method call.
-#[derive(Debug)]
-pub struct MethodCallData {
- pub span: Span,
- pub scope: NodeId,
- pub ref_id: Option<DefId>,
- pub decl_id: Option<DefId>,
-}
-
-/// Data about a macro use.
-#[derive(Debug)]
-pub struct MacroUseData {
- pub span: Span,
- pub name: String,
- // Because macro expansion happens before ref-ids are determined,
- // we use the callee span to reference the associated macro definition.
- pub callee_span: Span,
- pub scope: NodeId,
- pub imported: bool,
-}
-
macro_rules! option_try(
($e:expr) => (match $e { Some(e) => e, None => return None })
);
-
-
impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
pub fn new(tcx: &'l TyCtxt<'tcx>,
lcx: &'l lowering::LoweringContext<'l>)
TypeRefData {
span: sub_span.unwrap(),
scope: parent,
- ref_id: id,
+ ref_id: Some(id),
+ qualname: String::new() // FIXME: generate the real qualname
}
});
}
.and_then(|tr| self.get_trait_ref_data(tr, parent));
filter!(self.span_utils, sub_span, typ.span, None);
- Some(Data::ImplData(ImplData {
+ Some(Data::ImplData(ImplData2 {
id: item.id,
span: sub_span.unwrap(),
scope: parent,
Some(TypeRefData {
span: sub_span.unwrap(),
scope: parent,
- ref_id: def_id,
+ ref_id: Some(def_id),
+ qualname: String::new() // FIXME: generate the real qualname
})
})
}
Some(Data::TypeRefData(TypeRefData {
span: sub_span.unwrap(),
scope: self.enclosing_scope(expr.id),
- ref_id: def.did,
+ ref_id: Some(def.did),
+ qualname: String::new() // FIXME: generate the real qualname
}))
}
_ => {
Def::TyParam(_, _, def_id, _) => {
Some(Data::TypeRefData(TypeRefData {
span: sub_span.unwrap(),
- ref_id: def_id,
+ ref_id: Some(def_id),
scope: self.enclosing_scope(id),
+ qualname: String::new() // FIXME: generate the real qualname
}))
}
Def::Method(decl_id) => {
}
Def::Mod(def_id) => {
Some(Data::ModRefData(ModRefData {
- ref_id: def_id,
+ ref_id: Some(def_id),
span: sub_span.unwrap(),
scope: self.enclosing_scope(id),
+ qualname: String::new() // FIXME: generate the real qualname
}))
}
_ => None,
callee_span: mac_span,
scope: self.enclosing_scope(id),
imported: true,
+ qualname: String::new()// FIXME: generate the real qualname
});
}
callee_span: callee_span,
scope: self.enclosing_scope(id),
imported: false,
+ qualname: String::new() // FIXME: generate the real qualname
})
}
out_name.push_str(&tcx.sess.opts.cg.extra_filename);
out_name.push_str(".csv");
root_path.push(&out_name);
- let output_file = match File::create(&root_path) {
- Ok(f) => box f,
- Err(e) => {
- let disp = root_path.display();
- tcx.sess.fatal(&format!("Could not open {}: {}", disp, e));
- }
- };
+ let mut output_file = File::create(&root_path).unwrap_or_else(|e| {
+ let disp = root_path.display();
+ tcx.sess.fatal(&format!("Could not open {}: {}", disp, e));
+ });
root_path.pop();
- let mut visitor = dump_csv::DumpCsvVisitor::new(tcx, lcx, analysis, output_file);
+ let utils = SpanUtils::new(&tcx.sess);
+ let mut dumper = CsvDumper::new(&mut output_file, utils);
+ let mut visitor = DumpVisitor::new(tcx, lcx, analysis, &mut dumper);
+ // FIXME: we don't write anything!
visitor.dump_crate_info(cratename, krate);
visit::walk_crate(&mut visitor, krate);
+++ /dev/null
-// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
-// 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.
-
-pub use self::Row::*;
-
-use super::escape;
-use super::span_utils::SpanUtils;
-
-use middle::cstore::LOCAL_CRATE;
-use middle::def_id::{CRATE_DEF_INDEX, DefId};
-use middle::ty::TyCtxt;
-
-use std::io::Write;
-
-use syntax::ast;
-use syntax::ast::NodeId;
-use syntax::codemap::*;
-
-const CRATE_ROOT_DEF_ID: DefId = DefId {
- krate: LOCAL_CRATE,
- index: CRATE_DEF_INDEX,
-};
-
-pub struct Recorder {
- // output file
- pub out: Box<Write + 'static>,
- pub dump_spans: bool,
-}
-
-impl Recorder {
- pub fn record(&mut self, info: &str) {
- match write!(self.out, "{}", info) {
- Err(_) => error!("Error writing output '{}'", info),
- _ => (),
- }
- }
-
- pub fn dump_span(&mut self, su: SpanUtils, kind: &str, span: Span, _sub_span: Option<Span>) {
- assert!(self.dump_spans);
- let result = format!("span,kind,{},{},text,\"{}\"\n",
- kind,
- su.extent_str(span),
- escape(su.snippet(span)));
- self.record(&result[..]);
- }
-}
-
-pub struct FmtStrs<'a, 'tcx: 'a> {
- pub recorder: Box<Recorder>,
- span: SpanUtils<'a>,
- tcx: &'a TyCtxt<'tcx>,
-}
-
-macro_rules! s { ($e:expr) => { format!("{}", $e) }}
-macro_rules! svec {
- ($($e:expr),*) => ({
- // leading _ to allow empty construction without a warning.
- let mut _temp = ::std::vec::Vec::new();
- $(_temp.push(s!($e));)*
- _temp
- })
-}
-
-// FIXME recorder should operate on super::Data, rather than lots of ad hoc
-// data.
-
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-pub enum Row {
- Variable,
- Enum,
- Variant,
- VariantStruct,
- Function,
- MethodDecl,
- Struct,
- Trait,
- Impl,
- Module,
- UseAlias,
- UseGlob,
- ExternCrate,
- Inheritance,
- MethodCall,
- Typedef,
- ExternalCrate,
- Crate,
- FnCall,
- ModRef,
- VarRef,
- TypeRef,
- FnRef,
- Macro,
- MacroUse,
-}
-
-impl<'a, 'tcx: 'a> FmtStrs<'a, 'tcx> {
- pub fn new(rec: Box<Recorder>,
- span: SpanUtils<'a>,
- tcx: &'a TyCtxt<'tcx>)
- -> FmtStrs<'a, 'tcx> {
- FmtStrs {
- recorder: rec,
- span: span,
- tcx: tcx,
- }
- }
-
- // Emitted ids are used to cross-reference items across crates. DefIds and
- // NodeIds do not usually correspond in any way. The strategy is to use the
- // index from the DefId as a crate-local id. However, within a crate, DefId
- // indices and NodeIds can overlap. So, we must adjust the NodeIds. If an
- // item can be identified by a DefId as well as a NodeId, then we use the
- // DefId index as the id. If it can't, then we have to use the NodeId, but
- // need to adjust it so it will not clash with any possible DefId index.
- fn normalize_node_id(&self, id: NodeId) -> usize {
- match self.tcx.map.opt_local_def_id(id) {
- Some(id) => id.index.as_usize(),
- None => id as usize + self.tcx.map.num_local_def_ids()
- }
- }
-
- // A map from kind of item to a tuple of
- // a string representation of the name
- // a vector of field names
- // whether this kind requires a span
- // whether dump_spans should dump for this kind
- fn lookup_row(r: Row) -> (&'static str, Vec<&'static str>, bool, bool) {
- match r {
- Variable => ("variable",
- vec!("id", "name", "qualname", "value", "type", "scopeid"),
- true,
- true),
- Enum => ("enum",
- vec!("id", "qualname", "scopeid", "value"),
- true,
- true),
- Variant => ("variant",
- vec!("id", "name", "qualname", "type", "value", "scopeid"),
- true,
- true),
- VariantStruct => ("variant_struct",
- vec!("id", "ctor_id", "qualname", "type", "value", "scopeid"),
- true,
- true),
- Function => ("function",
- vec!("id", "qualname", "declid", "declidcrate", "scopeid"),
- true,
- true),
- MethodDecl => ("method_decl",
- vec!("id", "qualname", "scopeid"),
- true,
- true),
- Struct => ("struct",
- vec!("id", "ctor_id", "qualname", "scopeid", "value"),
- true,
- true),
- Trait => ("trait",
- vec!("id", "qualname", "scopeid", "value"),
- true,
- true),
- Impl => ("impl",
- vec!("id",
- "refid",
- "refidcrate",
- "traitid",
- "traitidcrate",
- "scopeid"),
- true,
- true),
- Module => ("module",
- vec!("id", "qualname", "scopeid", "def_file"),
- true,
- false),
- UseAlias => ("use_alias",
- vec!("id", "refid", "refidcrate", "name", "scopeid"),
- true,
- true),
- UseGlob => ("use_glob", vec!("id", "value", "scopeid"), true, true),
- ExternCrate => ("extern_crate",
- vec!("id", "name", "location", "crate", "scopeid"),
- true,
- true),
- Inheritance => ("inheritance",
- vec!("base", "basecrate", "derived", "derivedcrate"),
- true,
- false),
- MethodCall => ("method_call",
- vec!("refid", "refidcrate", "declid", "declidcrate", "scopeid"),
- true,
- true),
- Typedef => ("typedef", vec!("id", "qualname", "value"), true, true),
- ExternalCrate => ("external_crate",
- vec!("name", "crate", "file_name"),
- false,
- false),
- Crate => ("crate", vec!("name", "crate_root"), true, false),
- FnCall => ("fn_call",
- vec!("refid", "refidcrate", "qualname", "scopeid"),
- true,
- true),
- ModRef => ("mod_ref",
- vec!("refid", "refidcrate", "qualname", "scopeid"),
- true,
- true),
- VarRef => ("var_ref",
- vec!("refid", "refidcrate", "qualname", "scopeid"),
- true,
- true),
- TypeRef => ("type_ref",
- vec!("refid", "refidcrate", "qualname", "scopeid"),
- true,
- true),
- FnRef => ("fn_ref",
- vec!("refid", "refidcrate", "qualname", "scopeid"),
- true,
- true),
- Macro => ("macro",
- vec!("name", "qualname"),
- true,
- true),
- MacroUse => ("macro_use",
- vec!("callee_name", "qualname", "scopeid"),
- true,
- true),
- }
- }
-
- pub fn make_values_str(&self,
- kind: &'static str,
- fields: &Vec<&'static str>,
- values: Vec<String>,
- span: Span)
- -> Option<String> {
- if values.len() != fields.len() {
- self.span.sess.span_bug(span,
- &format!("Mismatch between length of fields for '{}', \
- expected '{}', found '{}'",
- kind,
- fields.len(),
- values.len()));
- }
-
- let values = values.iter().map(|s| {
- // Never take more than 1020 chars
- if s.len() > 1020 {
- &s[..1020]
- } else {
- &s[..]
- }
- });
-
- let pairs = fields.iter().zip(values);
- let strs = pairs.map(|(f, v)| format!(",{},\"{}\"", f, escape(String::from(v))));
- Some(strs.fold(String::new(),
- |mut s, ss| {
- s.push_str(&ss[..]);
- s
- }))
- }
-
- pub fn record_without_span(&mut self, kind: Row, values: Vec<String>, span: Span) {
- let (label, ref fields, needs_span, dump_spans) = FmtStrs::lookup_row(kind);
-
- if needs_span {
- self.span.sess.span_bug(span,
- &format!("Called record_without_span for '{}' which does \
- requires a span",
- label));
- }
- assert!(!dump_spans);
-
- if self.recorder.dump_spans {
- return;
- }
-
- let values_str = match self.make_values_str(label, fields, values, span) {
- Some(vs) => vs,
- None => return,
- };
-
- let mut result = String::from(label);
- result.push_str(&values_str[..]);
- result.push_str("\n");
- self.recorder.record(&result[..]);
- }
-
- pub fn record_with_span(&mut self,
- kind: Row,
- span: Span,
- sub_span: Span,
- values: Vec<String>) {
- let (label, ref fields, needs_span, dump_spans) = FmtStrs::lookup_row(kind);
-
- if self.recorder.dump_spans {
- if dump_spans {
- self.recorder.dump_span(self.span.clone(), label, span, Some(sub_span));
- }
- return;
- }
-
- if !needs_span {
- self.span.sess.span_bug(span,
- &format!("Called record_with_span for '{}' which does not \
- require a span",
- label));
- }
-
- let values_str = match self.make_values_str(label, fields, values, span) {
- Some(vs) => vs,
- None => return,
- };
- let result = format!("{},{}{}\n",
- label,
- self.span.extent_str(sub_span),
- values_str);
- self.recorder.record(&result[..]);
- }
-
- pub fn check_and_record(&mut self,
- kind: Row,
- span: Span,
- sub_span: Option<Span>,
- values: Vec<String>) {
- filter!(self.span, sub_span, span);
- match sub_span {
- Some(sub_span) => self.record_with_span(kind, span, sub_span, values),
- None => {
- let (label, _, _, _) = FmtStrs::lookup_row(kind);
- self.span.report_span_err(label, span);
- }
- }
- }
-
- pub fn variable_str(&mut self,
- span: Span,
- sub_span: Option<Span>,
- id: NodeId,
- name: &str,
- value: &str,
- typ: &str) {
- // Getting a fully qualified name for a variable is hard because in
- // the local case they can be overridden in one block and there is no nice way
- // to refer to such a scope in english, so we just hack it by appending the
- // variable def's node id
- let mut qualname = String::from(name);
- qualname.push_str("$");
- qualname.push_str(&id.to_string());
- let id = self.normalize_node_id(id);
- self.check_and_record(Variable,
- span,
- sub_span,
- svec!(id, name, qualname, value, typ, 0));
- }
-
- // formal parameters
- pub fn formal_str(&mut self,
- span: Span,
- sub_span: Option<Span>,
- id: NodeId,
- fn_name: &str,
- name: &str,
- typ: &str) {
- let mut qualname = String::from(fn_name);
- qualname.push_str("::");
- qualname.push_str(name);
- let id = self.normalize_node_id(id);
- self.check_and_record(Variable,
- span,
- sub_span,
- svec!(id, name, qualname, "", typ, 0));
- }
-
- // value is the initialising expression of the static if it is not mut, otherwise "".
- pub fn static_str(&mut self,
- span: Span,
- sub_span: Option<Span>,
- id: NodeId,
- name: &str,
- qualname: &str,
- value: &str,
- typ: &str,
- scope_id: NodeId) {
- let id = self.normalize_node_id(id);
- let scope_id = self.normalize_node_id(scope_id);
- self.check_and_record(Variable,
- span,
- sub_span,
- svec!(id, name, qualname, value, typ, scope_id));
- }
-
- pub fn field_str(&mut self,
- span: Span,
- sub_span: Option<Span>,
- id: NodeId,
- name: &str,
- qualname: &str,
- typ: &str,
- scope_id: NodeId) {
- let id = self.normalize_node_id(id);
- let scope_id = self.normalize_node_id(scope_id);
- self.check_and_record(Variable,
- span,
- sub_span,
- svec!(id, name, qualname, "", typ, scope_id));
- }
-
- pub fn enum_str(&mut self,
- span: Span,
- sub_span: Option<Span>,
- id: NodeId,
- name: &str,
- scope_id: NodeId,
- value: &str) {
- let id = self.normalize_node_id(id);
- let scope_id = self.normalize_node_id(scope_id);
- self.check_and_record(Enum, span, sub_span, svec!(id, name, scope_id, value));
- }
-
- pub fn tuple_variant_str(&mut self,
- span: Span,
- sub_span: Option<Span>,
- id: NodeId,
- name: &str,
- qualname: &str,
- typ: &str,
- val: &str,
- scope_id: NodeId) {
- let id = self.normalize_node_id(id);
- let scope_id = self.normalize_node_id(scope_id);
- self.check_and_record(Variant,
- span,
- sub_span,
- svec!(id, name, qualname, typ, val, scope_id));
- }
-
- pub fn struct_variant_str(&mut self,
- span: Span,
- sub_span: Option<Span>,
- id: NodeId,
- name: &str,
- typ: &str,
- val: &str,
- scope_id: NodeId) {
- let id = self.normalize_node_id(id);
- let ctor_id = id;
- let scope_id = self.normalize_node_id(scope_id);
- self.check_and_record(VariantStruct,
- span,
- sub_span,
- svec!(id, ctor_id, name, typ, val, scope_id));
- }
-
- pub fn fn_str(&mut self,
- span: Span,
- sub_span: Option<Span>,
- id: NodeId,
- name: &str,
- scope_id: NodeId) {
- let id = self.normalize_node_id(id);
- let scope_id = self.normalize_node_id(scope_id);
- self.check_and_record(Function,
- span,
- sub_span,
- svec!(id, name, "", "", scope_id));
- }
-
- pub fn method_str(&mut self,
- span: Span,
- sub_span: Option<Span>,
- id: NodeId,
- name: &str,
- decl_id: Option<DefId>,
- scope_id: NodeId) {
- let id = self.normalize_node_id(id);
- let scope_id = self.normalize_node_id(scope_id);
- let values = match decl_id {
- Some(decl_id) => svec!(id,
- name,
- decl_id.index.as_usize(),
- decl_id.krate,
- scope_id),
- None => svec!(id, name, "", "", scope_id),
- };
- self.check_and_record(Function, span, sub_span, values);
- }
-
- pub fn method_decl_str(&mut self,
- span: Span,
- sub_span: Option<Span>,
- id: NodeId,
- name: &str,
- scope_id: NodeId) {
- let id = self.normalize_node_id(id);
- let scope_id = self.normalize_node_id(scope_id);
- self.check_and_record(MethodDecl, span, sub_span, svec!(id, name, scope_id));
- }
-
- pub fn struct_str(&mut self,
- span: Span,
- sub_span: Option<Span>,
- id: NodeId,
- ctor_id: NodeId,
- name: &str,
- scope_id: NodeId,
- value: &str) {
- let id = self.normalize_node_id(id);
- let scope_id = self.normalize_node_id(scope_id);
- let ctor_id = self.normalize_node_id(ctor_id);
- self.check_and_record(Struct,
- span,
- sub_span,
- svec!(id, ctor_id, name, scope_id, value));
- }
-
- pub fn trait_str(&mut self,
- span: Span,
- sub_span: Option<Span>,
- id: NodeId,
- name: &str,
- scope_id: NodeId,
- value: &str) {
- let id = self.normalize_node_id(id);
- let scope_id = self.normalize_node_id(scope_id);
- self.check_and_record(Trait, span, sub_span, svec!(id, name, scope_id, value));
- }
-
- pub fn impl_str(&mut self,
- span: Span,
- sub_span: Option<Span>,
- id: NodeId,
- ref_id: Option<DefId>,
- trait_id: Option<DefId>,
- scope_id: NodeId) {
- let id = self.normalize_node_id(id);
- let scope_id = self.normalize_node_id(scope_id);
- let ref_id = ref_id.unwrap_or(CRATE_ROOT_DEF_ID);
- let trait_id = trait_id.unwrap_or(CRATE_ROOT_DEF_ID);
- self.check_and_record(Impl,
- span,
- sub_span,
- svec!(id,
- ref_id.index.as_usize(),
- ref_id.krate,
- trait_id.index.as_usize(),
- trait_id.krate,
- scope_id));
- }
-
- pub fn mod_str(&mut self,
- span: Span,
- sub_span: Option<Span>,
- id: NodeId,
- name: &str,
- parent: NodeId,
- filename: &str) {
- let id = self.normalize_node_id(id);
- let parent = self.normalize_node_id(parent);
- self.check_and_record(Module,
- span,
- sub_span,
- svec!(id, name, parent, filename));
- }
-
- pub fn use_alias_str(&mut self,
- span: Span,
- sub_span: Option<Span>,
- id: NodeId,
- mod_id: Option<DefId>,
- name: &str,
- parent: NodeId) {
- let id = self.normalize_node_id(id);
- let parent = self.normalize_node_id(parent);
- let mod_id = mod_id.unwrap_or(CRATE_ROOT_DEF_ID);
- self.check_and_record(UseAlias,
- span,
- sub_span,
- svec!(id, mod_id.index.as_usize(), mod_id.krate, name, parent));
- }
-
- pub fn use_glob_str(&mut self,
- span: Span,
- sub_span: Option<Span>,
- id: NodeId,
- values: &str,
- parent: NodeId) {
- let id = self.normalize_node_id(id);
- let parent = self.normalize_node_id(parent);
- self.check_and_record(UseGlob, span, sub_span, svec!(id, values, parent));
- }
-
- pub fn extern_crate_str(&mut self,
- span: Span,
- sub_span: Option<Span>,
- id: NodeId,
- cnum: ast::CrateNum,
- name: &str,
- loc: &str,
- parent: NodeId) {
- let id = self.normalize_node_id(id);
- let parent = self.normalize_node_id(parent);
- self.check_and_record(ExternCrate,
- span,
- sub_span,
- svec!(id, name, loc, cnum, parent));
- }
-
- pub fn inherit_str(&mut self,
- span: Span,
- sub_span: Option<Span>,
- base_id: DefId,
- deriv_id: NodeId) {
- let deriv_id = self.normalize_node_id(deriv_id);
- self.check_and_record(Inheritance,
- span,
- sub_span,
- svec!(base_id.index.as_usize(), base_id.krate, deriv_id, 0));
- }
-
- pub fn fn_call_str(&mut self,
- span: Span,
- sub_span: Option<Span>,
- id: DefId,
- scope_id: NodeId) {
- let scope_id = self.normalize_node_id(scope_id);
- self.check_and_record(FnCall,
- span,
- sub_span,
- svec!(id.index.as_usize(), id.krate, "", scope_id));
- }
-
- pub fn meth_call_str(&mut self,
- span: Span,
- sub_span: Option<Span>,
- defid: Option<DefId>,
- declid: Option<DefId>,
- scope_id: NodeId) {
- let scope_id = self.normalize_node_id(scope_id);
- let defid = defid.unwrap_or(CRATE_ROOT_DEF_ID);
- let (dcn, dck) = match declid {
- Some(declid) => (s!(declid.index.as_usize()), s!(declid.krate)),
- None => ("".to_string(), "".to_string()),
- };
- self.check_and_record(MethodCall,
- span,
- sub_span,
- svec!(defid.index.as_usize(), defid.krate, dcn, dck, scope_id));
- }
-
- pub fn sub_mod_ref_str(&mut self, span: Span, sub_span: Span, qualname: &str, parent: NodeId) {
- let parent = self.normalize_node_id(parent);
- self.record_with_span(ModRef, span, sub_span, svec!(0, 0, qualname, parent));
- }
-
- pub fn typedef_str(&mut self,
- span: Span,
- sub_span: Option<Span>,
- id: NodeId,
- qualname: &str,
- value: &str) {
- let id = self.normalize_node_id(id);
- self.check_and_record(Typedef, span, sub_span, svec!(id, qualname, value));
- }
-
- pub fn crate_str(&mut self, span: Span, name: &str, crate_root: &str) {
- self.record_with_span(Crate, span, span, svec!(name, crate_root));
- }
-
- pub fn external_crate_str(&mut self, span: Span, name: &str, num: ast::CrateNum) {
- let lo_loc = self.span.sess.codemap().lookup_char_pos(span.lo);
- self.record_without_span(ExternalCrate,
- svec!(name, num, SpanUtils::make_path_string(&lo_loc.file.name)),
- span);
- }
-
- pub fn sub_type_ref_str(&mut self, span: Span, sub_span: Span, qualname: &str) {
- self.record_with_span(TypeRef, span, sub_span, svec!(0, 0, qualname, 0));
- }
-
- // A slightly generic function for a reference to an item of any kind.
- pub fn ref_str(&mut self,
- kind: Row,
- span: Span,
- sub_span: Option<Span>,
- id: DefId,
- scope_id: NodeId) {
- let scope_id = self.normalize_node_id(scope_id);
- self.check_and_record(kind,
- span,
- sub_span,
- svec!(id.index.as_usize(), id.krate, "", scope_id));
- }
-
- pub fn macro_str(&mut self, span: Span, sub_span: Span, name: String, qualname: String) {
- self.record_with_span(Macro, span, sub_span, svec!(name, qualname));
- }
-
- pub fn macro_use_str(&mut self,
- span: Span,
- sub_span: Span,
- name: String,
- qualname: String,
- scope_id: NodeId) {
- let scope_id = self.normalize_node_id(scope_id);
- self.record_with_span(MacroUse, span, sub_span,
- svec!(name, qualname, scope_id));
- }
-}