about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorQuietMisdreavus <grey@quietmisdreavus.net>2018-09-25 14:56:43 -0500
committerQuietMisdreavus <grey@quietmisdreavus.net>2018-09-25 14:56:43 -0500
commitaea1bd0a5908430d41b6f2d38240f2652ef5651f (patch)
tree9671097b4f4dd0acf7f64d06959f2da92e8d87e2 /src
parentf49f6e73a8a2dd6ec9f86df9922ccfa2210d9eda (diff)
downloadrust-aea1bd0a5908430d41b6f2d38240f2652ef5651f.tar.gz
rust-aea1bd0a5908430d41b6f2d38240f2652ef5651f.zip
handle proc-macros as macros instead of functions
Diffstat (limited to 'src')
-rw-r--r--src/librustdoc/clean/inline.rs65
-rw-r--r--src/librustdoc/clean/mod.rs34
-rw-r--r--src/librustdoc/doctree.rs13
-rw-r--r--src/librustdoc/html/item_type.rs24
-rw-r--r--src/librustdoc/html/render.rs21
-rw-r--r--src/librustdoc/html/static/main.js5
-rw-r--r--src/librustdoc/html/static/themes/dark.css4
-rw-r--r--src/librustdoc/html/static/themes/light.css4
-rw-r--r--src/librustdoc/passes/mod.rs3
-rw-r--r--src/librustdoc/visit_ast.rs66
-rw-r--r--src/test/rustdoc/proc-macro.rs54
11 files changed, 243 insertions, 50 deletions
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 1ea130cf16a..287637ae0da 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<clean::Macro> {
+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<Span> = 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::<String>());
+    match cx.cstore.load_macro_untracked(did, cx.sess()) {
+        LoadedMacro::MacroDef(def) => {
+            let matchers: hir::HirVec<Span> = 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::<String>());
+
+            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 a982933f6c1..24491115004 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -21,6 +21,7 @@ pub use self::Visibility::{Public, Inherited};
 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<String>),
     AssociatedTypeItem(Vec<GenericBound>, Option<Type>),
@@ -588,6 +590,7 @@ impl Clean<Item> for doctree::Module {
         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
@@ -3781,6 +3791,28 @@ impl Clean<Item> for doctree::Macro {
 }
 
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
+pub struct ProcMacro {
+    pub kind: MacroKind,
+}
+
+impl Clean<Item> 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,
     pub feature: String,
diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs
index dd1e1e99957..538e61fcb4d 100644
--- a/src/librustdoc/doctree.rs
+++ b/src/librustdoc/doctree.rs
@@ -15,6 +15,7 @@ pub use self::StructType::*;
 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<Impl>,
     pub foreigns: Vec<hir::ForeignMod>,
     pub macros: Vec<Macro>,
+    pub proc_macros: Vec<ProcMacro>,
     pub is_crate: bool,
 }
 
@@ -75,6 +77,7 @@ impl 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<ast::Attribute>,
+    pub whence: Span,
+    pub stab: Option<attr::Stability>,
+    pub depr: Option<attr::Deprecation>,
+}
+
 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 a5131e327e0..acb8f6a66df 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 @@ use clean;
 /// 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 @@ impl<'a> From<&'a clean::Item> for 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 @@ impl From<clean::TypeKind> for 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 @@ impl ItemType {
             ItemType::ForeignType     => "foreigntype",
             ItemType::Keyword         => "keyword",
             ItemType::Existential     => "existential",
+            ItemType::ProcAttribute   => "attr",
+            ItemType::ProcDerive      => "derive",
         }
     }
 
@@ -166,7 +184,9 @@ impl ItemType {
             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 3e1720f8b8a..dc3255d2e52 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -56,6 +56,7 @@ use externalfiles::ExternalHtml;
 
 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 @@ impl<'a> fmt::Display for Item<'a> {
             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 @@ impl<'a> fmt::Display for Item<'a> {
             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, "<pre class='rust macro'>")?;
+        write!(w, "{}!() {{ /* proc-macro */ }}", it.name.as_ref().unwrap())?;
+        write!(w, "</pre>")?;
+    }
+    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 3dbefabace1..f81391ecefe 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 12d22084893..34a1d71beec 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 043d7ae23c2..8218b1b371e 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 24fec62dd57..d00eb3257d4 100644
--- a/src/librustdoc/passes/mod.rs
+++ b/src/librustdoc/passes/mod.rs
@@ -249,6 +249,9 @@ impl<'a> fold::DocFolder for Stripper<'a> {
             // 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 0e12fd34eb7..dbe25c5ff8a 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -15,6 +15,7 @@ use std::mem;
 
 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 @@ impl<'a, 'tcx, 'rcx, 'cstore> RustdocVisitor<'a, 'tcx, 'rcx, 'cstore> {
         }
     }
 
-    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 @@ impl<'a, 'tcx, 'rcx, 'cstore> RustdocVisitor<'a, 'tcx, 'rcx, 'cstore> {
             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 00000000000..cdf2783aec7
--- /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 <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.
+
+// 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()
+}