diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 1ea130cf16ae2f4e8e4cc29679883da520c85e99..287637ae0da9d37e0198e4c3a65e3256cffe2faf 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -105,15 +105,14 @@ pub fn try_inline(cx: &DocContext, def: Def, name: ast::Name, visited: &mut FxHa record_extern_fqn(cx, did, clean::TypeKind::Const); clean::ConstantItem(build_const(cx, did)) } - // FIXME(misdreavus): if attributes/derives come down here we should probably document them - // separately - Def::Macro(did, MacroKind::Bang) => { - record_extern_fqn(cx, did, clean::TypeKind::Macro); - if let Some(mac) = build_macro(cx, did, name) { - clean::MacroItem(mac) - } else { - return None; + Def::Macro(did, mac_kind) => { + match mac_kind { + MacroKind::Bang => record_extern_fqn(cx, did, clean::TypeKind::Macro), + MacroKind::Attr => record_extern_fqn(cx, did, clean::TypeKind::Attr), + MacroKind::Derive => record_extern_fqn(cx, did, clean::TypeKind::Derive), + MacroKind::ProcMacroStub => return None, } + build_macro(cx, did, name) } _ => return None, }; @@ -442,31 +441,35 @@ fn build_static(cx: &DocContext, did: DefId, mutable: bool) -> clean::Static { } } -fn build_macro(cx: &DocContext, did: DefId, name: ast::Name) -> Option { +fn build_macro(cx: &DocContext, did: DefId, name: ast::Name) -> clean::ItemEnum { let imported_from = cx.tcx.original_crate_name(did.krate); - let def = match cx.cstore.load_macro_untracked(did, cx.sess()) { - LoadedMacro::MacroDef(macro_def) => macro_def, - // FIXME(jseyfried): document proc macro re-exports - LoadedMacro::ProcMacro(..) => return None, - }; - - let matchers: hir::HirVec = if let ast::ItemKind::MacroDef(ref def) = def.node { - let tts: Vec<_> = def.stream().into_trees().collect(); - tts.chunks(4).map(|arm| arm[0].span()).collect() - } else { - unreachable!() - }; - - let source = format!("macro_rules! {} {{\n{}}}", - name.clean(cx), - matchers.iter().map(|span| { - format!(" {} => {{ ... }};\n", span.to_src(cx)) - }).collect::()); + match cx.cstore.load_macro_untracked(did, cx.sess()) { + LoadedMacro::MacroDef(def) => { + let matchers: hir::HirVec = if let ast::ItemKind::MacroDef(ref def) = def.node { + let tts: Vec<_> = def.stream().into_trees().collect(); + tts.chunks(4).map(|arm| arm[0].span()).collect() + } else { + unreachable!() + }; + + let source = format!("macro_rules! {} {{\n{}}}", + name.clean(cx), + matchers.iter().map(|span| { + format!(" {} => {{ ... }};\n", span.to_src(cx)) + }).collect::()); + + clean::MacroItem(clean::Macro { + source, + imported_from: Some(imported_from).clean(cx), + }) + } + LoadedMacro::ProcMacro(ext) => { + clean::ProcMacroItem(clean::ProcMacro { + kind: ext.kind(), + }) + } + } - Some(clean::Macro { - source, - imported_from: Some(imported_from).clean(cx), - }) } /// A trait's generics clause actually contains all of the predicates for all of diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index a982933f6c1a26a594bb4fd80846462671d36d03..244911150043c4682dd4a96d2d161504537fe765 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -21,6 +21,7 @@ use rustc_target::spec::abi::Abi; use syntax::ast::{self, AttrStyle, Ident}; use syntax::attr; +use syntax::ext::base::MacroKind; use syntax::source_map::{dummy_spanned, Spanned}; use syntax::ptr::P; use syntax::symbol::keywords::{self, Keyword}; @@ -527,6 +528,7 @@ pub enum ItemEnum { /// `type`s from an extern block ForeignTypeItem, MacroItem(Macro), + ProcMacroItem(ProcMacro), PrimitiveItem(PrimitiveType), AssociatedConstItem(Type, Option), AssociatedTypeItem(Vec, Option), @@ -588,6 +590,7 @@ fn clean(&self, cx: &DocContext) -> Item { items.extend(self.traits.iter().map(|x| x.clean(cx))); items.extend(self.impls.iter().flat_map(|x| x.clean(cx))); items.extend(self.macros.iter().map(|x| x.clean(cx))); + items.extend(self.proc_macros.iter().map(|x| x.clean(cx))); // determine if we should display the inner contents or // the outer `mod` item for the source code. @@ -2189,6 +2192,8 @@ pub enum TypeKind { Typedef, Foreign, Macro, + Attr, + Derive, } pub trait GetDefId { @@ -3725,7 +3730,12 @@ pub fn register_def(cx: &DocContext, def: Def) -> DefId { Def::Static(i, _) => (i, TypeKind::Static), Def::Variant(i) => (cx.tcx.parent_def_id(i).expect("cannot get parent def id"), TypeKind::Enum), - Def::Macro(i, _) => (i, TypeKind::Macro), + Def::Macro(i, mac_kind) => match mac_kind { + MacroKind::Bang => (i, TypeKind::Macro), + MacroKind::Attr => (i, TypeKind::Attr), + MacroKind::Derive => (i, TypeKind::Derive), + MacroKind::ProcMacroStub => unreachable!(), + }, Def::SelfTy(Some(def_id), _) => (def_id, TypeKind::Trait), Def::SelfTy(_, Some(impl_def_id)) => { return impl_def_id @@ -3780,6 +3790,28 @@ fn clean(&self, cx: &DocContext) -> Item { } } +#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +pub struct ProcMacro { + pub kind: MacroKind, +} + +impl Clean for doctree::ProcMacro { + fn clean(&self, cx: &DocContext) -> Item { + Item { + name: Some(self.name.clean(cx)), + attrs: self.attrs.clean(cx), + source: self.whence.clean(cx), + visibility: Some(Public), + stability: self.stab.clean(cx), + deprecation: self.depr.clean(cx), + def_id: cx.tcx.hir.local_def_id(self.id), + inner: ProcMacroItem(ProcMacro { + kind: self.kind, + }), + } + } +} + #[derive(Clone, RustcEncodable, RustcDecodable, Debug)] pub struct Stability { pub level: stability::StabilityLevel, diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs index dd1e1e99957cea9cdca83cc679fee2ecbc83cf2b..538e61fcb4d32a1660cec6c8ad0a40bc4f724eb4 100644 --- a/src/librustdoc/doctree.rs +++ b/src/librustdoc/doctree.rs @@ -15,6 +15,7 @@ use syntax::ast; use syntax::ast::{Name, NodeId}; use syntax::attr; +use syntax::ext::base::MacroKind; use syntax::ptr::P; use syntax::source_map::Spanned; use syntax_pos::{self, Span}; @@ -46,6 +47,7 @@ pub struct Module { pub impls: Vec, pub foreigns: Vec, pub macros: Vec, + pub proc_macros: Vec, pub is_crate: bool, } @@ -75,6 +77,7 @@ pub fn new(name: Option) -> Module { impls : Vec::new(), foreigns : Vec::new(), macros : Vec::new(), + proc_macros: Vec::new(), is_crate : false, } } @@ -264,6 +267,16 @@ pub struct Import { pub whence: Span, } +pub struct ProcMacro { + pub name: Name, + pub id: NodeId, + pub kind: MacroKind, + pub attrs: hir::HirVec, + pub whence: Span, + pub stab: Option, + pub depr: Option, +} + pub fn struct_type_from_def(vdata: &hir::VariantData) -> StructType { match *vdata { hir::VariantData::Struct(..) => Plain, diff --git a/src/librustdoc/html/item_type.rs b/src/librustdoc/html/item_type.rs index a5131e327e08e85df06fce1199ce12d0dbb08c55..acb8f6a66dfcb42ff3d87c4fdb52ca4186dfac0d 100644 --- a/src/librustdoc/html/item_type.rs +++ b/src/librustdoc/html/item_type.rs @@ -11,6 +11,7 @@ //! Item types. use std::fmt; +use syntax::ext::base::MacroKind; use clean; /// Item type. Corresponds to `clean::ItemEnum` variants. @@ -19,6 +20,11 @@ /// discriminants. JavaScript then is used to decode them into the original value. /// Consequently, every change to this type should be synchronized to /// the `itemTypes` mapping table in `static/main.js`. +/// +/// In addition, code in `html::render` uses this enum to generate CSS classes, page prefixes, and +/// module headings. If you are adding to this enum and want to ensure that the sidebar also prints +/// a heading, edit the listing in `html/render.rs`, function `sidebar_module`. This uses an +/// ordering based on a helper function inside `item_module`, in the same file. #[derive(Copy, PartialEq, Clone, Debug)] pub enum ItemType { Module = 0, @@ -44,6 +50,8 @@ pub enum ItemType { ForeignType = 20, Keyword = 21, Existential = 22, + ProcAttribute = 23, + ProcDerive = 24, } @@ -88,6 +96,12 @@ fn from(item: &'a clean::Item) -> ItemType { clean::AssociatedTypeItem(..) => ItemType::AssociatedType, clean::ForeignTypeItem => ItemType::ForeignType, clean::KeywordItem(..) => ItemType::Keyword, + clean::ProcMacroItem(ref mac) => match mac.kind { + MacroKind::Bang => ItemType::Macro, + MacroKind::Attr => ItemType::ProcAttribute, + MacroKind::Derive => ItemType::ProcDerive, + MacroKind::ProcMacroStub => unreachable!(), + } clean::StrippedItem(..) => unreachable!(), } } @@ -107,7 +121,9 @@ fn from(kind: clean::TypeKind) -> ItemType { clean::TypeKind::Variant => ItemType::Variant, clean::TypeKind::Typedef => ItemType::Typedef, clean::TypeKind::Foreign => ItemType::ForeignType, - clean::TypeKind::Macro => ItemType::Macro, + clean::TypeKind::Macro => ItemType::Macro, + clean::TypeKind::Attr => ItemType::ProcAttribute, + clean::TypeKind::Derive => ItemType::ProcDerive, } } } @@ -138,6 +154,8 @@ pub fn css_class(&self) -> &'static str { ItemType::ForeignType => "foreigntype", ItemType::Keyword => "keyword", ItemType::Existential => "existential", + ItemType::ProcAttribute => "attr", + ItemType::ProcDerive => "derive", } } @@ -166,7 +184,9 @@ pub fn name_space(&self) -> NameSpace { ItemType::Constant | ItemType::AssociatedConst => NameSpace::Value, - ItemType::Macro => NameSpace::Macro, + ItemType::Macro | + ItemType::ProcAttribute | + ItemType::ProcDerive => NameSpace::Macro, ItemType::Keyword => NameSpace::Keyword, } diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 3e1720f8b8ab2a639c74fbaf5ee4ed07ed689664..dc3255d2e528a88c45bd0e494d028a9f70f7177f 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -56,6 +56,7 @@ use serialize::json::{ToJson, Json, as_json}; use syntax::ast; +use syntax::ext::base::MacroKind; use syntax::source_map::FileName; use syntax::feature_gate::UnstableFeatures; use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefId}; @@ -2155,6 +2156,12 @@ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { clean::EnumItem(..) => write!(fmt, "Enum ")?, clean::TypedefItem(..) => write!(fmt, "Type Definition ")?, clean::MacroItem(..) => write!(fmt, "Macro ")?, + clean::ProcMacroItem(ref mac) => match mac.kind { + MacroKind::Bang => write!(fmt, "Macro ")?, + MacroKind::Attr => write!(fmt, "Attribute Macro ")?, + MacroKind::Derive => write!(fmt, "Derive Macro ")?, + MacroKind::ProcMacroStub => unreachable!(), + } clean::PrimitiveItem(..) => write!(fmt, "Primitive Type ")?, clean::StaticItem(..) | clean::ForeignStaticItem(..) => write!(fmt, "Static ")?, clean::ConstantItem(..) => write!(fmt, "Constant ")?, @@ -2191,6 +2198,7 @@ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { clean::EnumItem(ref e) => item_enum(fmt, self.cx, self.item, e), clean::TypedefItem(ref t, _) => item_typedef(fmt, self.cx, self.item, t), clean::MacroItem(ref m) => item_macro(fmt, self.cx, self.item, m), + clean::ProcMacroItem(ref m) => item_proc_macro(fmt, self.cx, self.item, m), clean::PrimitiveItem(ref p) => item_primitive(fmt, self.cx, self.item, p), clean::StaticItem(ref i) | clean::ForeignStaticItem(ref i) => item_static(fmt, self.cx, self.item, i), @@ -4523,6 +4531,8 @@ fn item_ty_to_strs(ty: &ItemType) -> (&'static str, &'static str) { ItemType::ForeignType => ("foreign-types", "Foreign Types"), ItemType::Keyword => ("keywords", "Keywords"), ItemType::Existential => ("existentials", "Existentials"), + ItemType::ProcAttribute => ("attributes", "Attribute Macros"), + ItemType::ProcDerive => ("derives", "Derive Macros"), } } @@ -4598,6 +4608,17 @@ fn item_macro(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, document(w, cx, it) } +fn item_proc_macro(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, m: &clean::ProcMacro) + -> fmt::Result +{ + if m.kind == MacroKind::Bang { + write!(w, "
")?;
+        write!(w, "{}!() {{ /* proc-macro */ }}", it.name.as_ref().unwrap())?;
+        write!(w, "
")?; + } + document(w, cx, it) +} + fn item_primitive(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, _p: &clean::PrimitiveType) -> fmt::Result { diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index 3dbefabace18f978f6c52902ed00ef2a5f05149d..f81391ecefe3983be23163924a36d856bcd61b34 100644 --- a/src/librustdoc/html/static/main.js +++ b/src/librustdoc/html/static/main.js @@ -39,7 +39,10 @@ "associatedconstant", "union", "foreigntype", - "keyword"]; + "keyword", + "existential", + "attr", + "derive"]; var search_input = document.getElementsByClassName('search-input')[0]; diff --git a/src/librustdoc/html/static/themes/dark.css b/src/librustdoc/html/static/themes/dark.css index 12d2208489368a52e7466c1f964990eebcdd2229..34a1d71beecfcdf863f5fdc29b89549b9c7fadaf 100644 --- a/src/librustdoc/html/static/themes/dark.css +++ b/src/librustdoc/html/static/themes/dark.css @@ -124,6 +124,8 @@ pre { .content .highlighted.tymethod { background-color: #4950ed; } .content .highlighted.type { background-color: #38902c; } .content .highlighted.foreigntype { background-color: #b200d6; } +.content .highlighted.attr, +.content .highlighted.derive, .content .highlighted.macro { background-color: #217d1c; } .content .highlighted.constant, .content .highlighted.static { background-color: #0063cc; } @@ -134,6 +136,8 @@ pre { .content span.struct, .content a.struct, .block a.current.struct { color: #2dbfb8; } .content span.type, .content a.type, .block a.current.type { color: #ff7f00; } .content span.foreigntype, .content a.foreigntype, .block a.current.foreigntype { color: #dd7de8; } +.content span.attr, .content a.attr, .block a.current.attr, +.content span.derive, .content a.derive, .block a.current.derive, .content span.macro, .content a.macro, .block a.current.macro { color: #09bd00; } .content span.union, .content a.union, .block a.current.union { color: #a6ae37; } .content span.constant, .content a.constant, .block a.current.constant, diff --git a/src/librustdoc/html/static/themes/light.css b/src/librustdoc/html/static/themes/light.css index 043d7ae23c2e4aaa6876541d41ce131b68316844..8218b1b371ea72fcba87a0e3fb2205ef90c34355 100644 --- a/src/librustdoc/html/static/themes/light.css +++ b/src/librustdoc/html/static/themes/light.css @@ -124,6 +124,8 @@ pre { .content .highlighted.tymethod { background-color: #c6afb3; } .content .highlighted.type { background-color: #ffc891; } .content .highlighted.foreigntype { background-color: #f5c4ff; } +.content .highlighted.attr, +.content .highlighted.derive, .content .highlighted.macro { background-color: #8ce488; } .content .highlighted.constant, .content .highlighted.static { background-color: #c3e0ff; } @@ -134,6 +136,8 @@ pre { .content span.struct, .content a.struct, .block a.current.struct { color: #ad448e; } .content span.type, .content a.type, .block a.current.type { color: #ba5d00; } .content span.foreigntype, .content a.foreigntype, .block a.current.foreigntype { color: #cd00e2; } +.content span.attr, .content a.attr, .block a.current.attr, +.content span.derive, .content a.derive, .block a.current.derive, .content span.macro, .content a.macro, .block a.current.macro { color: #068000; } .content span.union, .content a.union, .block a.current.union { color: #767b27; } .content span.constant, .content a.constant, .block a.current.constant, diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs index 24fec62dd573a3876b8e5d153e6dc32128a6cb14..d00eb3257d43c6a5aacb537390c24361fcceb10d 100644 --- a/src/librustdoc/passes/mod.rs +++ b/src/librustdoc/passes/mod.rs @@ -249,6 +249,9 @@ fn fold_item(&mut self, i: Item) -> Option { // tymethods/macros have no control over privacy clean::MacroItem(..) | clean::TyMethodItem(..) => {} + // Proc-macros are always public + clean::ProcMacroItem(..) => {} + // Primitives are never stripped clean::PrimitiveItem(..) => {} diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 0e12fd34eb7d84b99bc36723133f8a46b4684fc4..dbe25c5ff8a49f67426b35a51fc1fcdb9e5bd768 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -15,6 +15,7 @@ use syntax::ast; use syntax::attr; +use syntax::ext::base::MacroKind; use syntax::source_map::Spanned; use syntax_pos::{self, Span}; @@ -168,24 +169,59 @@ pub fn visit_enum_def(&mut self, it: &hir::Item, } } - pub fn visit_fn(&mut self, item: &hir::Item, + pub fn visit_fn(&mut self, om: &mut Module, item: &hir::Item, name: ast::Name, fd: &hir::FnDecl, header: hir::FnHeader, gen: &hir::Generics, - body: hir::BodyId) -> Function { + body: hir::BodyId) { debug!("Visiting fn"); - Function { - id: item.id, - vis: item.vis.clone(), - stab: self.stability(item.id), - depr: self.deprecation(item.id), - attrs: item.attrs.clone(), - decl: fd.clone(), - name, - whence: item.span, - generics: gen.clone(), - header, - body, + let macro_kind = item.attrs.iter().filter_map(|a| { + if a.check_name("proc_macro") { + Some(MacroKind::Bang) + } else if a.check_name("proc_macro_derive") { + Some(MacroKind::Derive) + } else if a.check_name("proc_macro_attribute") { + Some(MacroKind::Attr) + } else { + None + } + }).next(); + match macro_kind { + Some(kind) => { + let name = if kind == MacroKind::Derive { + item.attrs.lists("proc_macro_derive") + .filter_map(|mi| mi.name()) + .next() + .expect("proc-macro derives require a name") + } else { + name + }; + + om.proc_macros.push(ProcMacro { + name, + id: item.id, + kind, + attrs: item.attrs.clone(), + whence: item.span, + stab: self.stability(item.id), + depr: self.deprecation(item.id), + }); + } + None => { + om.fns.push(Function { + id: item.id, + vis: item.vis.clone(), + stab: self.stability(item.id), + depr: self.deprecation(item.id), + attrs: item.attrs.clone(), + decl: fd.clone(), + name, + whence: item.span, + generics: gen.clone(), + header, + body, + }); + } } } @@ -425,7 +461,7 @@ pub fn visit_item(&mut self, item: &hir::Item, hir::ItemKind::Union(ref sd, ref gen) => om.unions.push(self.visit_union_data(item, name, sd, gen)), hir::ItemKind::Fn(ref fd, header, ref gen, body) => - om.fns.push(self.visit_fn(item, name, &**fd, header, gen, body)), + self.visit_fn(om, item, name, &**fd, header, gen, body), hir::ItemKind::Ty(ref ty, ref gen) => { let t = Typedef { ty: ty.clone(), diff --git a/src/test/rustdoc/proc-macro.rs b/src/test/rustdoc/proc-macro.rs new file mode 100644 index 0000000000000000000000000000000000000000..cdf2783aec7492943da05308d3acdce61cc09c63 --- /dev/null +++ b/src/test/rustdoc/proc-macro.rs @@ -0,0 +1,54 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-stage1 + +#![crate_type="proc-macro"] +#![crate_name="some_macros"] + +extern crate proc_macro; + +use proc_macro::TokenStream; + +// @has some_macros/index.html +// @has - '//h2' 'Macros' +// @has - '//h2' 'Attribute Macros' +// @has - '//h2' 'Derive Macros' +// @!has - '//h2' 'Functions' + +// @has some_macros/index.html '//a/@href' 'macro.some_proc_macro.html' +// @!has - '//a/@href' 'fn.some_proc_macro.html' +// @has some_macros/macro.some_proc_macro.html +// @!has some_macros/fn.some_proc_macro.html +/// a proc-macro that swallows its input and does nothing. +#[proc_macro] +pub fn some_proc_macro(_input: TokenStream) -> TokenStream { + TokenStream::new() +} + +// @has some_macros/index.html '//a/@href' 'attr.some_proc_attr.html' +// @!has - '//a/@href' 'fn.some_proc_attr.html' +// @has some_macros/attr.some_proc_attr.html +// @!has some_macros/fn.some_proc_attr.html +/// a proc-macro attribute that passes its item through verbatim. +#[proc_macro_attribute] +pub fn some_proc_attr(_attr: TokenStream, item: TokenStream) -> TokenStream { + item +} + +// @has some_macros/index.html '//a/@href' 'derive.SomeDerive.html' +// @!has - '//a/@href' 'fn.some_derive.html' +// @has some_macros/derive.SomeDerive.html +// @!has some_macros/fn.some_derive.html +/// a derive attribute that adds nothing to its input. +#[proc_macro_derive(SomeDerive)] +pub fn some_derive(_item: TokenStream) -> TokenStream { + TokenStream::new() +}