about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/session/config.rs10
-rw-r--r--src/librustc_driver/driver.rs3
-rw-r--r--src/librustc_driver/lib.rs5
-rw-r--r--src/librustc_save_analysis/data.rs56
-rw-r--r--src/librustc_save_analysis/dump_visitor.rs154
-rw-r--r--src/librustc_save_analysis/external_data.rs42
-rw-r--r--src/librustc_save_analysis/json_api_dumper.rs393
-rw-r--r--src/librustc_save_analysis/lib.rs27
-rw-r--r--src/test/run-make/save-analysis/Makefile1
9 files changed, 608 insertions, 83 deletions
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index a2f926aa92c..e80f004f150 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -849,9 +849,13 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
     ls: bool = (false, parse_bool, [UNTRACKED],
         "list the symbols defined by a library crate"),
     save_analysis: bool = (false, parse_bool, [UNTRACKED],
-        "write syntax and type analysis (in JSON format) information in addition to normal output"),
+        "write syntax and type analysis (in JSON format) information, in \
+         addition to normal output"),
     save_analysis_csv: bool = (false, parse_bool, [UNTRACKED],
-        "write syntax and type analysis (in CSV format) information in addition to normal output"),
+        "write syntax and type analysis (in CSV format) information, in addition to normal output"),
+    save_analysis_api: bool = (false, parse_bool, [UNTRACKED],
+        "write syntax and type analysis information for opaque libraries (in JSON format), \
+         in addition to normal output"),
     print_move_fragments: bool = (false, parse_bool, [UNTRACKED],
         "print out move-fragment data for every fn"),
     flowgraph_print_loans: bool = (false, parse_bool, [UNTRACKED],
@@ -2365,6 +2369,8 @@ mod tests {
         assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
         opts.debugging_opts.save_analysis_csv = true;
         assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.save_analysis_api = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
         opts.debugging_opts.print_move_fragments = true;
         assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
         opts.debugging_opts.flowgraph_print_loans = true;
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index 47a0399b632..e059b6445c1 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -250,7 +250,8 @@ fn keep_hygiene_data(sess: &Session) -> bool {
 fn keep_ast(sess: &Session) -> bool {
     sess.opts.debugging_opts.keep_ast ||
     sess.opts.debugging_opts.save_analysis ||
-    sess.opts.debugging_opts.save_analysis_csv
+    sess.opts.debugging_opts.save_analysis_csv ||
+    sess.opts.debugging_opts.save_analysis_api
 }
 
 /// The name used for source code that doesn't originate in a file
diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs
index 6616e9579e8..ef2234a5f61 100644
--- a/src/librustc_driver/lib.rs
+++ b/src/librustc_driver/lib.rs
@@ -555,7 +555,8 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
 
 fn save_analysis(sess: &Session) -> bool {
     sess.opts.debugging_opts.save_analysis ||
-    sess.opts.debugging_opts.save_analysis_csv
+    sess.opts.debugging_opts.save_analysis_csv ||
+    sess.opts.debugging_opts.save_analysis_api
 }
 
 fn save_analysis_format(sess: &Session) -> save::Format {
@@ -563,6 +564,8 @@ fn save_analysis_format(sess: &Session) -> save::Format {
         save::Format::Json
     } else if sess.opts.debugging_opts.save_analysis_csv {
         save::Format::Csv
+    } else if sess.opts.debugging_opts.save_analysis_api {
+        save::Format::JsonApi
     } else {
         unreachable!();
     }
diff --git a/src/librustc_save_analysis/data.rs b/src/librustc_save_analysis/data.rs
index 493f7669337..a58cce0745f 100644
--- a/src/librustc_save_analysis/data.rs
+++ b/src/librustc_save_analysis/data.rs
@@ -13,8 +13,9 @@
 //! The `Dump` trait can be used together with `DumpVisitor` in order to
 //! retrieve the data from a crate.
 
+use rustc::hir;
 use rustc::hir::def_id::DefId;
-use syntax::ast::{CrateNum, NodeId};
+use syntax::ast::{self, CrateNum, NodeId};
 use syntax_pos::Span;
 
 pub struct CrateData {
@@ -76,6 +77,35 @@ pub enum Data {
     VariableRefData(VariableRefData),
 }
 
+#[derive(Eq, PartialEq, Clone, Copy, Debug, RustcEncodable)]
+pub enum Visibility {
+    Public,
+    Restricted,
+    Inherited,
+}
+
+impl<'a> From<&'a ast::Visibility> for Visibility {
+    fn from(v: &'a ast::Visibility) -> Visibility {
+        match *v {
+            ast::Visibility::Public => Visibility::Public,
+            ast::Visibility::Crate(_) => Visibility::Restricted,
+            ast::Visibility::Restricted { .. } => Visibility::Restricted,
+            ast::Visibility::Inherited => Visibility::Inherited,
+        }
+    }
+}
+
+impl<'a> From<&'a hir::Visibility> for Visibility {
+    fn from(v: &'a hir::Visibility) -> Visibility {
+        match *v {
+            hir::Visibility::Public => Visibility::Public,
+            hir::Visibility::Crate => Visibility::Restricted,
+            hir::Visibility::Restricted { .. } => Visibility::Restricted,
+            hir::Visibility::Inherited => Visibility::Inherited,
+        }
+    }
+}
+
 /// Data for the prelude of a crate.
 #[derive(Debug, RustcEncodable)]
 pub struct CratePreludeData {
@@ -103,7 +133,7 @@ pub struct EnumData {
     pub span: Span,
     pub scope: NodeId,
     pub variants: Vec<NodeId>,
-
+    pub visibility: Visibility,
 }
 
 /// Data for extern crates.
@@ -135,6 +165,8 @@ pub struct FunctionData {
     pub span: Span,
     pub scope: NodeId,
     pub value: String,
+    pub visibility: Visibility,
+    pub parent: Option<NodeId>,
 }
 
 /// Data about a function call.
@@ -215,6 +247,7 @@ pub struct MethodData {
     pub scope: NodeId,
     pub value: String,
     pub decl_id: Option<DefId>,
+    pub visibility: Visibility,
 }
 
 /// Data for modules.
@@ -227,6 +260,7 @@ pub struct ModData {
     pub scope: NodeId,
     pub filename: String,
     pub items: Vec<NodeId>,
+    pub visibility: Visibility,
 }
 
 /// Data for a reference to a module.
@@ -248,6 +282,7 @@ pub struct StructData {
     pub scope: NodeId,
     pub value: String,
     pub fields: Vec<NodeId>,
+    pub visibility: Visibility,
 }
 
 #[derive(Debug, RustcEncodable)]
@@ -258,7 +293,8 @@ pub struct StructVariantData {
     pub qualname: String,
     pub type_value: String,
     pub value: String,
-    pub scope: NodeId
+    pub scope: NodeId,
+    pub parent: Option<NodeId>,
 }
 
 #[derive(Debug, RustcEncodable)]
@@ -270,6 +306,7 @@ pub struct TraitData {
     pub scope: NodeId,
     pub value: String,
     pub items: Vec<NodeId>,
+    pub visibility: Visibility,
 }
 
 #[derive(Debug, RustcEncodable)]
@@ -280,7 +317,8 @@ pub struct TupleVariantData {
     pub qualname: String,
     pub type_value: String,
     pub value: String,
-    pub scope: NodeId
+    pub scope: NodeId,
+    pub parent: Option<NodeId>,
 }
 
 /// Data for a typedef.
@@ -291,6 +329,8 @@ pub struct TypeDefData {
     pub span: Span,
     pub qualname: String,
     pub value: String,
+    pub visibility: Visibility,
+    pub parent: Option<NodeId>,
 }
 
 /// Data for a reference to a type or trait.
@@ -308,7 +348,8 @@ pub struct UseData {
     pub span: Span,
     pub name: String,
     pub mod_id: Option<DefId>,
-    pub scope: NodeId
+    pub scope: NodeId,
+    pub visibility: Visibility,
 }
 
 #[derive(Debug, RustcEncodable)]
@@ -316,7 +357,8 @@ pub struct UseGlobData {
     pub id: NodeId,
     pub span: Span,
     pub names: Vec<String>,
-    pub scope: NodeId
+    pub scope: NodeId,
+    pub visibility: Visibility,
 }
 
 /// Data for local and global variables (consts and statics).
@@ -328,8 +370,10 @@ pub struct VariableData {
     pub qualname: String,
     pub span: Span,
     pub scope: NodeId,
+    pub parent: Option<NodeId>,
     pub value: String,
     pub type_value: String,
+    pub visibility: Visibility,
 }
 
 #[derive(Debug, RustcEncodable)]
diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs
index f9a20cec42d..a695a070662 100644
--- a/src/librustc_save_analysis/dump_visitor.rs
+++ b/src/librustc_save_analysis/dump_visitor.rs
@@ -365,7 +365,9 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                         qualname: format!("{}::{}", qualname, path_to_string(p)),
                         type_value: typ,
                         value: String::new(),
-                        scope: 0
+                        scope: 0,
+                        parent: None,
+                        visibility: Visibility::Inherited,
                     }.lower(self.tcx));
                 }
             }
@@ -377,6 +379,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                       body: Option<&ast::Block>,
                       id: ast::NodeId,
                       name: ast::Name,
+                      vis: Visibility,
                       span: Span) {
         debug!("process_method: {}:{}", id, name);
 
@@ -417,6 +420,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                     qualname: method_data.qualname.clone(),
                     value: sig_str,
                     decl_id: decl_id,
+                    visibility: vis,
                 }.lower(self.tcx));
             }
 
@@ -484,7 +488,9 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                     name: name,
                     id: param.id,
                     qualname: qualname,
-                    value: String::new()
+                    value: String::new(),
+                    visibility: Visibility::Inherited,
+                    parent: None,
                 }.lower(self.tcx));
             }
         }
@@ -528,12 +534,14 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
         self.visit_expr(expr);
     }
 
-    fn process_const(&mut self,
-                     id: ast::NodeId,
-                     name: ast::Name,
-                     span: Span,
-                     typ: &ast::Ty,
-                     expr: &ast::Expr) {
+    fn process_assoc_const(&mut self,
+                           id: ast::NodeId,
+                           name: ast::Name,
+                           span: Span,
+                           typ: &ast::Ty,
+                           expr: &ast::Expr,
+                           parent_id: NodeId,
+                           vis: Visibility) {
         let qualname = format!("::{}", self.tcx.node_path_str(id));
 
         let sub_span = self.span.sub_span_after_keyword(span, keywords::Const);
@@ -547,7 +555,9 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                 qualname: qualname,
                 value: self.span.snippet(expr.span),
                 type_value: ty_to_string(&typ),
-                scope: self.cur_scope
+                scope: self.cur_scope,
+                parent: Some(parent_id),
+                visibility: vis,
             }.lower(self.tcx));
         }
 
@@ -589,6 +599,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                 scope: self.cur_scope,
                 value: val,
                 fields: fields,
+                visibility: From::from(&item.vis),
             }.lower(self.tcx));
         }
 
@@ -640,7 +651,8 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                             qualname: qualname,
                             type_value: enum_data.qualname.clone(),
                             value: val,
-                            scope: enum_data.scope
+                            scope: enum_data.scope,
+                            parent: Some(item.id),
                         }.lower(self.tcx));
                     }
                 }
@@ -663,7 +675,8 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                             qualname: qualname,
                             type_value: enum_data.qualname.clone(),
                             value: val,
-                            scope: enum_data.scope
+                            scope: enum_data.scope,
+                            parent: Some(item.id),
                         }.lower(self.tcx));
                     }
                 }
@@ -716,7 +729,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
         }
         self.process_generic_params(type_parameters, item.span, "", item.id);
         for impl_item in impl_items {
-            self.visit_impl_item(impl_item);
+            self.process_impl_item(impl_item, item.id);
         }
     }
 
@@ -745,6 +758,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                 scope: self.cur_scope,
                 value: val,
                 items: methods.iter().map(|i| i.id).collect(),
+                visibility: From::from(&item.vis),
             }.lower(self.tcx));
         }
 
@@ -785,7 +799,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
         // walk generics and methods
         self.process_generic_params(generics, item.span, &qualname, item.id);
         for method in methods {
-            self.visit_trait_item(method)
+            self.process_trait_item(method, item.id)
         }
     }
 
@@ -990,7 +1004,9 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                     qualname: format!("{}${}", path_to_string(p), id),
                     value: value,
                     type_value: typ,
-                    scope: 0
+                    scope: 0,
+                    parent: None,
+                    visibility: Visibility::Inherited,
                 }.lower(self.tcx));
             }
         }
@@ -1038,6 +1054,57 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
             }
         }
     }
+
+    fn process_trait_item(&mut self, trait_item: &ast::TraitItem, trait_id: NodeId) {
+        self.process_macro_use(trait_item.span, trait_item.id);
+        match trait_item.node {
+            ast::TraitItemKind::Const(ref ty, Some(ref expr)) => {
+                self.process_assoc_const(trait_item.id,
+                                         trait_item.ident.name,
+                                         trait_item.span,
+                                         &ty,
+                                         &expr,
+                                         trait_id,
+                                         Visibility::Public);
+            }
+            ast::TraitItemKind::Method(ref sig, ref body) => {
+                self.process_method(sig,
+                                    body.as_ref().map(|x| &**x),
+                                    trait_item.id,
+                                    trait_item.ident.name,
+                                    Visibility::Public,
+                                    trait_item.span);
+            }
+            ast::TraitItemKind::Const(_, None) |
+            ast::TraitItemKind::Type(..) |
+            ast::TraitItemKind::Macro(_) => {}
+        }
+    }
+
+    fn process_impl_item(&mut self, impl_item: &ast::ImplItem, impl_id: NodeId) {
+        self.process_macro_use(impl_item.span, impl_item.id);
+        match impl_item.node {
+            ast::ImplItemKind::Const(ref ty, ref expr) => {
+                self.process_assoc_const(impl_item.id,
+                                         impl_item.ident.name,
+                                         impl_item.span,
+                                         &ty,
+                                         &expr,
+                                         impl_id,
+                                         From::from(&impl_item.vis));
+            }
+            ast::ImplItemKind::Method(ref sig, ref body) => {
+                self.process_method(sig,
+                                    Some(body),
+                                    impl_item.id,
+                                    impl_item.ident.name,
+                                    From::from(&impl_item.vis),
+                                    impl_item.span);
+            }
+            ast::ImplItemKind::Type(_) |
+            ast::ImplItemKind::Macro(_) => {}
+        }
+    }
 }
 
 impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor for DumpVisitor<'l, 'tcx, 'll, D> {
@@ -1073,7 +1140,8 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor for DumpVisitor<'l, 'tcx, 'll, D>
                                 id: item.id,
                                 mod_id: mod_id,
                                 name: ident.to_string(),
-                                scope: self.cur_scope
+                                scope: self.cur_scope,
+                                visibility: From::from(&item.vis),
                             }.lower(self.tcx));
                         }
                         self.write_sub_paths_truncated(path, true);
@@ -1096,7 +1164,8 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor for DumpVisitor<'l, 'tcx, 'll, D>
                                 span: sub_span.expect("No span found for use glob"),
                                 id: item.id,
                                 names: names,
-                                scope: self.cur_scope
+                                scope: self.cur_scope,
+                                visibility: From::from(&item.vis),
                             }.lower(self.tcx));
                         }
                         self.write_sub_paths(path, true);
@@ -1168,7 +1237,9 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor for DumpVisitor<'l, 'tcx, 'll, D>
                         name: item.ident.to_string(),
                         id: item.id,
                         qualname: qualname.clone(),
-                        value: value
+                        value: value,
+                        visibility: From::from(&item.vis),
+                        parent: None,
                     }.lower(self.tcx));
                 }
 
@@ -1193,51 +1264,6 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor for DumpVisitor<'l, 'tcx, 'll, D>
         }
     }
 
-    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(..) |
-            ast::TraitItemKind::Macro(_) => {}
-        }
-    }
-
-    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 {
@@ -1400,7 +1426,9 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor for DumpVisitor<'l, 'tcx, 'll, D>
                             qualname: format!("{}${}", path_to_string(p), id),
                             value: value,
                             type_value: String::new(),
-                            scope: 0
+                            scope: 0,
+                            parent: None,
+                            visibility: Visibility::Inherited,
                         }.lower(self.tcx));
                     }
                 }
diff --git a/src/librustc_save_analysis/external_data.rs b/src/librustc_save_analysis/external_data.rs
index 65e4f7e869b..4333c6dd18e 100644
--- a/src/librustc_save_analysis/external_data.rs
+++ b/src/librustc_save_analysis/external_data.rs
@@ -15,7 +15,7 @@ use syntax::ast::{CrateNum, NodeId};
 use syntax::codemap::CodeMap;
 use syntax_pos::Span;
 
-use data;
+use data::{self, Visibility};
 
 // FIXME: this should be pub(crate), but the current snapshot doesn't allow it yet
 pub trait Lower {
@@ -91,7 +91,8 @@ pub struct EnumData {
     pub qualname: String,
     pub span: SpanData,
     pub scope: DefId,
-    pub variants: Vec<DefId>
+    pub variants: Vec<DefId>,
+    pub visibility: Visibility,
 }
 
 impl Lower for data::EnumData {
@@ -106,6 +107,7 @@ impl Lower for data::EnumData {
             span: SpanData::from_span(self.span, tcx.sess.codemap()),
             scope: make_def_id(self.scope, &tcx.map),
             variants: self.variants.into_iter().map(|id| make_def_id(id, &tcx.map)).collect(),
+            visibility: self.visibility,
         }
     }
 }
@@ -166,6 +168,8 @@ pub struct FunctionData {
     pub span: SpanData,
     pub scope: DefId,
     pub value: String,
+    pub visibility: Visibility,
+    pub parent: Option<DefId>,
 }
 
 impl Lower for data::FunctionData {
@@ -180,6 +184,8 @@ impl Lower for data::FunctionData {
             span: SpanData::from_span(self.span, tcx.sess.codemap()),
             scope: make_def_id(self.scope, &tcx.map),
             value: self.value,
+            visibility: self.visibility,
+            parent: self.parent.map(|id| make_def_id(id, &tcx.map)),
         }
     }
 }
@@ -323,6 +329,8 @@ pub struct MethodData {
     pub scope: DefId,
     pub value: String,
     pub decl_id: Option<DefId>,
+    pub visibility: Visibility,
+    pub parent: Option<DefId>
 }
 
 impl Lower for data::MethodData {
@@ -337,6 +345,8 @@ impl Lower for data::MethodData {
             qualname: self.qualname,
             value: self.value,
             decl_id: self.decl_id,
+            visibility: self.visibility,
+            parent: Some(make_def_id(self.scope, &tcx.map)),
         }
     }
 }
@@ -351,6 +361,7 @@ pub struct ModData {
     pub scope: DefId,
     pub filename: String,
     pub items: Vec<DefId>,
+    pub visibility: Visibility,
 }
 
 impl Lower for data::ModData {
@@ -365,6 +376,7 @@ impl Lower for data::ModData {
             scope: make_def_id(self.scope, &tcx.map),
             filename: self.filename,
             items: self.items.into_iter().map(|id| make_def_id(id, &tcx.map)).collect(),
+            visibility: self.visibility,
         }
     }
 }
@@ -401,6 +413,7 @@ pub struct StructData {
     pub scope: DefId,
     pub value: String,
     pub fields: Vec<DefId>,
+    pub visibility: Visibility,
 }
 
 impl Lower for data::StructData {
@@ -416,6 +429,7 @@ impl Lower for data::StructData {
             scope: make_def_id(self.scope, &tcx.map),
             value: self.value,
             fields: self.fields.into_iter().map(|id| make_def_id(id, &tcx.map)).collect(),
+            visibility: self.visibility,
         }
     }
 }
@@ -428,7 +442,8 @@ pub struct StructVariantData {
     pub qualname: String,
     pub type_value: String,
     pub value: String,
-    pub scope: DefId
+    pub scope: DefId,
+    pub parent: Option<DefId>,
 }
 
 impl Lower for data::StructVariantData {
@@ -443,6 +458,7 @@ impl Lower for data::StructVariantData {
             type_value: self.type_value,
             value: self.value,
             scope: make_def_id(self.scope, &tcx.map),
+            parent: self.parent.map(|id| make_def_id(id, &tcx.map)),
         }
     }
 }
@@ -456,6 +472,7 @@ pub struct TraitData {
     pub scope: DefId,
     pub value: String,
     pub items: Vec<DefId>,
+    pub visibility: Visibility,
 }
 
 impl Lower for data::TraitData {
@@ -470,6 +487,7 @@ impl Lower for data::TraitData {
             scope: make_def_id(self.scope, &tcx.map),
             value: self.value,
             items: self.items.into_iter().map(|id| make_def_id(id, &tcx.map)).collect(),
+            visibility: self.visibility,
         }
     }
 }
@@ -483,6 +501,7 @@ pub struct TupleVariantData {
     pub type_value: String,
     pub value: String,
     pub scope: DefId,
+    pub parent: Option<DefId>,
 }
 
 impl Lower for data::TupleVariantData {
@@ -497,6 +516,7 @@ impl Lower for data::TupleVariantData {
             type_value: self.type_value,
             value: self.value,
             scope: make_def_id(self.scope, &tcx.map),
+            parent: self.parent.map(|id| make_def_id(id, &tcx.map)),
         }
     }
 }
@@ -509,6 +529,8 @@ pub struct TypeDefData {
     pub span: SpanData,
     pub qualname: String,
     pub value: String,
+    pub visibility: Visibility,
+    pub parent: Option<DefId>,
 }
 
 impl Lower for data::TypeDefData {
@@ -521,6 +543,8 @@ impl Lower for data::TypeDefData {
             span: SpanData::from_span(self.span, tcx.sess.codemap()),
             qualname: self.qualname,
             value: self.value,
+            visibility: self.visibility,
+            parent: self.parent.map(|id| make_def_id(id, &tcx.map)),
         }
     }
 }
@@ -553,7 +577,8 @@ pub struct UseData {
     pub span: SpanData,
     pub name: String,
     pub mod_id: Option<DefId>,
-    pub scope: DefId
+    pub scope: DefId,
+    pub visibility: Visibility,
 }
 
 impl Lower for data::UseData {
@@ -566,6 +591,7 @@ impl Lower for data::UseData {
             name: self.name,
             mod_id: self.mod_id,
             scope: make_def_id(self.scope, &tcx.map),
+            visibility: self.visibility,
         }
     }
 }
@@ -575,7 +601,8 @@ pub struct UseGlobData {
     pub id: DefId,
     pub span: SpanData,
     pub names: Vec<String>,
-    pub scope: DefId
+    pub scope: DefId,
+    pub visibility: Visibility,
 }
 
 impl Lower for data::UseGlobData {
@@ -587,6 +614,7 @@ impl Lower for data::UseGlobData {
             span: SpanData::from_span(self.span, tcx.sess.codemap()),
             names: self.names,
             scope: make_def_id(self.scope, &tcx.map),
+            visibility: self.visibility,
         }
     }
 }
@@ -602,6 +630,8 @@ pub struct VariableData {
     pub scope: DefId,
     pub value: String,
     pub type_value: String,
+    pub parent: Option<DefId>,
+    pub visibility: Visibility,
 }
 
 impl Lower for data::VariableData {
@@ -617,6 +647,8 @@ impl Lower for data::VariableData {
             scope: make_def_id(self.scope, &tcx.map),
             value: self.value,
             type_value: self.type_value,
+            parent: self.parent.map(|id| make_def_id(id, &tcx.map)),
+            visibility: self.visibility,
         }
     }
 }
diff --git a/src/librustc_save_analysis/json_api_dumper.rs b/src/librustc_save_analysis/json_api_dumper.rs
new file mode 100644
index 00000000000..874babb907e
--- /dev/null
+++ b/src/librustc_save_analysis/json_api_dumper.rs
@@ -0,0 +1,393 @@
+// Copyright 2016 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 rustc::hir::def_id::DefId;
+use rustc_serialize::json::as_json;
+
+use external_data::*;
+use data::{VariableKind, Visibility};
+use dump::Dump;
+
+// A dumper to dump a restricted set of JSON information, designed for use with
+// libraries distributed without their source. Clients are likely to use type
+// information here, and (for example) generate Rustdoc URLs, but don't need
+// information for navigating the source of the crate.
+// Relative to the regular JSON save-analysis info, this form is filtered to
+// remove non-visible items, but includes some extra info for items (e.g., the
+// parent field for finding the struct to which a field belongs).
+pub struct JsonApiDumper<'b, W: Write + 'b> {
+    output: &'b mut W,
+    result: Analysis,
+}
+
+impl<'b, W: Write> JsonApiDumper<'b, W> {
+    pub fn new(writer: &'b mut W) -> JsonApiDumper<'b, W> {
+        JsonApiDumper { output: writer, result: Analysis::new() }
+    }
+}
+
+impl<'b, W: Write> Drop for JsonApiDumper<'b, W> {
+    fn drop(&mut self) {
+        if let Err(_) = write!(self.output, "{}", as_json(&self.result)) {
+            error!("Error writing output");
+        }
+    }
+}
+
+macro_rules! impl_fn {
+    ($fn_name: ident, $data_type: ident, $bucket: ident) => {
+        fn $fn_name(&mut self, data: $data_type) {
+            if let Some(datum) = From::from(data) {
+                self.result.$bucket.push(datum);
+            }
+        }
+    }
+}
+
+impl<'b, W: Write + 'b> Dump for JsonApiDumper<'b, W> {
+    fn crate_prelude(&mut self, data: CratePreludeData) {
+        self.result.prelude = Some(data)
+    }
+
+    impl_fn!(use_data, UseData, imports);
+    impl_fn!(use_glob, UseGlobData, imports);
+
+    impl_fn!(enum_data, EnumData, defs);
+    impl_fn!(tuple_variant, TupleVariantData, defs);
+    impl_fn!(struct_variant, StructVariantData, defs);
+    impl_fn!(struct_data, StructData, defs);
+    impl_fn!(trait_data, TraitData, defs);
+    impl_fn!(function, FunctionData, defs);
+    impl_fn!(method, MethodData, defs);
+    impl_fn!(macro_data, MacroData, defs);
+    impl_fn!(mod_data, ModData, defs);
+    impl_fn!(typedef, TypeDefData, defs);
+    impl_fn!(variable, VariableData, defs);
+}
+
+// FIXME methods. The defs have information about possible overriding and the
+// refs have decl information (e.g., a trait method where we know the required
+// method, but not the supplied method). In both cases, we are currently
+// ignoring it.
+
+#[derive(Debug, RustcEncodable)]
+struct Analysis {
+    prelude: Option<CratePreludeData>,
+    imports: Vec<Import>,
+    defs: Vec<Def>,
+}
+
+impl Analysis {
+    fn new() -> Analysis {
+        Analysis {
+            prelude: None,
+            imports: vec![],
+            defs: vec![],
+        }
+    }
+}
+
+// DefId::index is a newtype and so the JSON serialisation is ugly. Therefore
+// we use our own Id which is the same, but without the newtype.
+#[derive(Debug, RustcEncodable)]
+struct Id {
+    krate: u32,
+    index: u32,
+}
+
+impl From<DefId> for Id {
+    fn from(id: DefId) -> Id {
+        Id {
+            krate: id.krate,
+            index: id.index.as_u32(),
+        }
+    }
+}
+
+#[derive(Debug, RustcEncodable)]
+struct Import {
+    kind: ImportKind,
+    id: Id,
+    span: SpanData,
+    name: String,
+    value: String,
+}
+
+#[derive(Debug, RustcEncodable)]
+enum ImportKind {
+    Use,
+    GlobUse,
+}
+
+impl From<UseData> for Option<Import> {
+    fn from(data: UseData) -> Option<Import> {
+        match data.visibility {
+            Visibility::Public => Some(Import {
+                kind: ImportKind::Use,
+                id: From::from(data.id),
+                span: data.span,
+                name: data.name,
+                value: String::new(),
+            }),
+            _ => None,
+        }
+    }
+}
+impl From<UseGlobData> for Option<Import> {
+    fn from(data: UseGlobData) -> Option<Import> {
+        match data.visibility {
+            Visibility::Public => Some(Import {
+                kind: ImportKind::GlobUse,
+                id: From::from(data.id),
+                span: data.span,
+                name: "*".to_owned(),
+                value: data.names.join(", "),
+            }),
+            _ => None,
+        }
+    }
+}
+
+#[derive(Debug, RustcEncodable)]
+struct Def {
+    kind: DefKind,
+    id: Id,
+    span: SpanData,
+    name: String,
+    qualname: String,
+    value: String,
+    parent: Option<Id>,
+    children: Vec<Id>,
+    decl_id: Option<Id>,
+}
+
+#[derive(Debug, RustcEncodable)]
+enum DefKind {
+    // value = variant names
+    Enum,
+    // value = enum name + variant name + types
+    Tuple,
+    // value = [enum name +] name + fields
+    Struct,
+    // value = signature
+    Trait,
+    // value = type + generics
+    Function,
+    // value = type + generics
+    Method,
+    // No id, no value.
+    Macro,
+    // value = file_name
+    Mod,
+    // value = aliased type
+    Type,
+    // value = type and init expression (for all variable kinds).
+    Static,
+    Const,
+    Field,
+}
+
+impl From<EnumData> for Option<Def> {
+    fn from(data: EnumData) -> Option<Def> {
+        match data.visibility {
+            Visibility::Public => Some(Def {
+                kind: DefKind::Enum,
+                id: From::from(data.id),
+                span: data.span,
+                name: data.name,
+                qualname: data.qualname,
+                value: data.value,
+                parent: None,
+                children: data.variants.into_iter().map(|id| From::from(id)).collect(),
+                decl_id: None,
+            }),
+            _ => None,
+        }
+    }
+}
+
+impl From<TupleVariantData> for Option<Def> {
+    fn from(data: TupleVariantData) -> Option<Def> {
+        Some(Def {
+            kind: DefKind::Tuple,
+            id: From::from(data.id),
+            span: data.span,
+            name: data.name,
+            qualname: data.qualname,
+            value: data.value,
+            parent: data.parent.map(|id| From::from(id)),
+            children: vec![],
+            decl_id: None,
+        })
+    }
+}
+impl From<StructVariantData> for Option<Def> {
+    fn from(data: StructVariantData) -> Option<Def> {
+        Some(Def {
+            kind: DefKind::Struct,
+            id: From::from(data.id),
+            span: data.span,
+            name: data.name,
+            qualname: data.qualname,
+            value: data.value,
+            parent: data.parent.map(|id| From::from(id)),
+            children: vec![],
+            decl_id: None,
+        })
+    }
+}
+impl From<StructData> for Option<Def> {
+    fn from(data: StructData) -> Option<Def> {
+        match data.visibility {
+            Visibility::Public => Some(Def {
+            kind: DefKind::Struct,
+            id: From::from(data.id),
+            span: data.span,
+            name: data.name,
+            qualname: data.qualname,
+            value: data.value,
+            parent: None,
+            children: data.fields.into_iter().map(|id| From::from(id)).collect(),
+            decl_id: None,
+        }),
+            _ => None,
+        }
+    }
+}
+impl From<TraitData> for Option<Def> {
+    fn from(data: TraitData) -> Option<Def> {
+        match data.visibility {
+            Visibility::Public => Some(Def {
+                kind: DefKind::Trait,
+                id: From::from(data.id),
+                span: data.span,
+                name: data.name,
+                qualname: data.qualname,
+                value: data.value,
+                children: data.items.into_iter().map(|id| From::from(id)).collect(),
+                parent: None,
+                decl_id: None,
+            }),
+            _ => None,
+        }
+    }
+}
+impl From<FunctionData> for Option<Def> {
+    fn from(data: FunctionData) -> Option<Def> {
+        match data.visibility {
+            Visibility::Public => Some(Def {
+                kind: DefKind::Function,
+                id: From::from(data.id),
+                span: data.span,
+                name: data.name,
+                qualname: data.qualname,
+                value: data.value,
+                children: vec![],
+                parent: data.parent.map(|id| From::from(id)),
+                decl_id: None,
+            }),
+            _ => None,
+        }
+    }
+}
+impl From<MethodData> for Option<Def> {
+    fn from(data: MethodData) -> Option<Def> {
+        match data.visibility {
+            Visibility::Public => Some(Def {
+                kind: DefKind::Method,
+                id: From::from(data.id),
+                span: data.span,
+                name: data.name,
+                qualname: data.qualname,
+                value: data.value,
+                children: vec![],
+                parent: data.parent.map(|id| From::from(id)),
+                decl_id: data.decl_id.map(|id| From::from(id)),
+            }),
+            _ => None,
+        }
+    }
+}
+impl From<MacroData> for Option<Def> {
+    fn from(data: MacroData) -> Option<Def> {
+        Some(Def {
+            kind: DefKind::Macro,
+            id: From::from(null_def_id()),
+            span: data.span,
+            name: data.name,
+            qualname: data.qualname,
+            value: String::new(),
+            children: vec![],
+            parent: None,
+            decl_id: None,
+        })
+    }
+}
+impl From<ModData> for Option<Def> {
+    fn from(data:ModData) -> Option<Def> {
+        match data.visibility {
+            Visibility::Public => Some(Def {
+                kind: DefKind::Mod,
+                id: From::from(data.id),
+                span: data.span,
+                name: data.name,
+                qualname: data.qualname,
+                value: data.filename,
+                children: data.items.into_iter().map(|id| From::from(id)).collect(),
+                parent: None,
+                decl_id: None,
+            }),
+            _ => None,
+        }
+    }
+}
+impl From<TypeDefData> for Option<Def> {
+    fn from(data: TypeDefData) -> Option<Def> {
+        match data.visibility {
+            Visibility::Public => Some(Def {
+                kind: DefKind::Type,
+                id: From::from(data.id),
+                span: data.span,
+                name: data.name,
+                qualname: data.qualname,
+                value: data.value,
+                children: vec![],
+                parent: data.parent.map(|id| From::from(id)),
+                decl_id: None,
+            }),
+            _ => None,
+        }
+    }
+}
+impl From<VariableData> for Option<Def> {
+    fn from(data: VariableData) -> Option<Def> {
+        match data.visibility {
+            Visibility::Public => Some(Def {
+                kind: match data.kind {
+                    VariableKind::Static => DefKind::Static,
+                    VariableKind::Const => DefKind::Const,
+                    VariableKind::Local => { return None }
+                    VariableKind::Field => DefKind::Field,
+                },
+                id: From::from(data.id),
+                span: data.span,
+                name: data.name,
+                qualname: data.qualname,
+                value: data.value,
+                children: vec![],
+                parent: data.parent.map(|id| From::from(id)),
+                decl_id: None,
+            }),
+            _ => None,
+        }
+    }
+}
diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs
index 47f3a06de1b..f32baf30ff4 100644
--- a/src/librustc_save_analysis/lib.rs
+++ b/src/librustc_save_analysis/lib.rs
@@ -30,6 +30,7 @@ extern crate serialize as rustc_serialize;
 extern crate syntax_pos;
 
 mod csv_dumper;
+mod json_api_dumper;
 mod json_dumper;
 mod data;
 mod dump;
@@ -57,6 +58,7 @@ use syntax::codemap::MacroAttribute;
 use syntax_pos::*;
 
 pub use self::csv_dumper::CsvDumper;
+pub use self::json_api_dumper::JsonApiDumper;
 pub use self::json_dumper::JsonDumper;
 pub use self::data::*;
 pub use self::dump::Dump;
@@ -138,6 +140,8 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                     span: sub_span.unwrap(),
                     scope: self.enclosing_scope(item.id),
                     value: make_signature(decl, generics),
+                    visibility: From::from(&item.vis),
+                    parent: None,
                 }))
             }
             ast::ItemKind::Static(ref typ, mt, ref expr) => {
@@ -160,8 +164,10 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                     qualname: qualname,
                     span: sub_span.unwrap(),
                     scope: self.enclosing_scope(item.id),
+                    parent: None,
                     value: value,
                     type_value: ty_to_string(&typ),
+                    visibility: From::from(&item.vis),
                 }))
             }
             ast::ItemKind::Const(ref typ, ref expr) => {
@@ -175,8 +181,10 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                     qualname: qualname,
                     span: sub_span.unwrap(),
                     scope: self.enclosing_scope(item.id),
+                    parent: None,
                     value: self.span_utils.snippet(expr.span),
                     type_value: ty_to_string(&typ),
+                    visibility: From::from(&item.vis),
                 }))
             }
             ast::ItemKind::Mod(ref m) => {
@@ -195,6 +203,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                     scope: self.enclosing_scope(item.id),
                     filename: filename,
                     items: m.items.iter().map(|i| i.id).collect(),
+                    visibility: From::from(&item.vis),
                 }))
             }
             ast::ItemKind::Enum(ref def, _) => {
@@ -215,6 +224,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                     qualname: qualname,
                     scope: self.enclosing_scope(item.id),
                     variants: def.variants.iter().map(|v| v.node.data.id()).collect(),
+                    visibility: From::from(&item.vis),
                 }))
             }
             ast::ItemKind::Impl(_, _, _, ref trait_ref, ref typ, _) => {
@@ -277,8 +287,10 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                 qualname: qualname,
                 span: sub_span.unwrap(),
                 scope: scope,
+                parent: Some(scope),
                 value: "".to_owned(),
                 type_value: typ,
+                visibility: From::from(&field.vis),
             })
         } else {
             None
@@ -291,7 +303,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                            name: ast::Name, span: Span) -> Option<FunctionData> {
         // The qualname for a method is the trait name or name of the struct in an impl in
         // which the method is declared in, followed by the method's name.
-        let qualname = match self.tcx.impl_of_method(self.tcx.map.local_def_id(id)) {
+        let (qualname, vis) = match self.tcx.impl_of_method(self.tcx.map.local_def_id(id)) {
             Some(impl_id) => match self.tcx.map.get_if_local(impl_id) {
                 Some(NodeItem(item)) => {
                     match item.node {
@@ -304,7 +316,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                                 result.push_str(&self.tcx.item_path_str(def_id));
                             }
                             result.push_str(">");
-                            result
+                            (result, From::from(&item.vis))
                         }
                         _ => {
                             span_bug!(span,
@@ -325,8 +337,8 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
             None => match self.tcx.trait_of_item(self.tcx.map.local_def_id(id)) {
                 Some(def_id) => {
                     match self.tcx.map.get_if_local(def_id) {
-                        Some(NodeItem(_)) => {
-                            format!("::{}", self.tcx.item_path_str(def_id))
+                        Some(NodeItem(item)) => {
+                            (format!("::{}", self.tcx.item_path_str(def_id)), From::from(&item.vis))
                         }
                         r => {
                             span_bug!(span,
@@ -358,6 +370,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
 
         let sub_span = self.span_utils.sub_span_after_keyword(span, keywords::Fn);
         filter!(self.span_utils, sub_span, span, None);
+        let parent_scope = self.enclosing_scope(id);
         Some(FunctionData {
             id: id,
             name: name.to_string(),
@@ -367,6 +380,8 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
             scope: self.enclosing_scope(id),
             // FIXME you get better data here by using the visitor.
             value: String::new(),
+            visibility: vis,
+            parent: Some(parent_scope),
         })
     }
 
@@ -728,13 +743,14 @@ impl Visitor for PathCollector {
 pub enum Format {
     Csv,
     Json,
+    JsonApi,
 }
 
 impl Format {
     fn extension(&self) -> &'static str {
         match *self {
             Format::Csv => ".csv",
-            Format::Json => ".json",
+            Format::Json | Format::JsonApi => ".json",
         }
     }
 }
@@ -804,6 +820,7 @@ pub fn process_crate<'l, 'tcx>(tcx: TyCtxt<'l, 'tcx, 'tcx>,
     match format {
         Format::Csv => dump!(CsvDumper::new(output)),
         Format::Json => dump!(JsonDumper::new(output)),
+        Format::JsonApi => dump!(JsonApiDumper::new(output)),
     }
 }
 
diff --git a/src/test/run-make/save-analysis/Makefile b/src/test/run-make/save-analysis/Makefile
index 3c636baaedc..3711b6ea895 100644
--- a/src/test/run-make/save-analysis/Makefile
+++ b/src/test/run-make/save-analysis/Makefile
@@ -5,3 +5,4 @@ krate2: krate2.rs
 code: foo.rs krate2
 	$(RUSTC) foo.rs -Zsave-analysis-csv
 	$(RUSTC) foo.rs -Zsave-analysis
+	$(RUSTC) foo.rs -Zsave-analysis-api