about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorTim Chevalier <chevalier@alum.wellesley.edu>2012-01-31 21:08:53 -0800
committerTim Chevalier <chevalier@alum.wellesley.edu>2012-01-31 21:08:53 -0800
commite204aa281d093bd7de4214c45375949d197eb2ab (patch)
treef3eafafb7ca6ea30aa36fba34b750a4501f07c8a /src
parent11ecec1b716aa430f80cc18cf850336a990c2850 (diff)
parente8a5d81b24967abe53509e187cef60ce8d2fe5cd (diff)
Merge branch 'master' into register-snapshots
Diffstat (limited to 'src')
-rw-r--r--src/libcore/core.rc4
-rw-r--r--src/libcore/iter.rs171
-rw-r--r--src/rustdoc/attr_parser.rs13
-rw-r--r--src/rustdoc/attr_pass.rs193
-rw-r--r--src/rustdoc/demo.rs11
-rw-r--r--src/rustdoc/desc_pass.rs194
-rw-r--r--src/rustdoc/desc_to_brief_pass.rs92
-rw-r--r--src/rustdoc/doc.rs23
-rw-r--r--src/rustdoc/extract.rs123
-rw-r--r--src/rustdoc/fold.rs24
-rw-r--r--src/rustdoc/markdown_pass.rs96
-rw-r--r--src/rustdoc/prune_undoc_pass.rs238
-rw-r--r--src/rustdoc/prune_unexported_pass.rs88
-rw-r--r--src/rustdoc/trim_pass.rs58
-rw-r--r--src/rustdoc/tystr_pass.rs234
15 files changed, 1018 insertions, 544 deletions
diff --git a/src/libcore/core.rc b/src/libcore/core.rc
index b90c1c16d1a..3bc5a031949 100644
--- a/src/libcore/core.rc
+++ b/src/libcore/core.rc
@@ -30,7 +30,7 @@ This behavior can be disabled with the `no_core` crate attribute."
 
 export box, char, float, bessel, f32, f64, int, str, ptr;
 export uint, u8, u32, u64, vec, bool;
-export either, option, result;
+export either, option, result, iter;
 export ctypes, sys, unsafe, comm, task, logging;
 export extfmt;
 export math;
@@ -64,7 +64,7 @@ mod either;
 mod option;
 mod result;
 mod tuple;
-
+mod iter;
 
 // Runtime and language-primitive support
 
diff --git a/src/libcore/iter.rs b/src/libcore/iter.rs
new file mode 100644
index 00000000000..7aa08a6980c
--- /dev/null
+++ b/src/libcore/iter.rs
@@ -0,0 +1,171 @@
+iface iterable<A> {
+    fn iter(blk: fn(A));
+}
+
+impl<A> of iterable<A> for fn@(fn(A)) {
+    fn iter(blk: fn(A)) {
+        self(blk);
+    }
+}
+
+// accomodate the fact that int/uint are passed by value by default:
+impl of iterable<int> for fn@(fn(int)) {
+    fn iter(blk: fn(&&int)) {
+        self {|i| blk(i)}
+    }
+}
+
+impl of iterable<uint> for fn@(fn(uint)) {
+    fn iter(blk: fn(&&uint)) {
+        self {|i| blk(i)}
+    }
+}
+
+impl<A> of iterable<A> for [A] {
+    fn iter(blk: fn(A)) {
+        vec::iter(self, blk)
+    }
+}
+
+impl<A> of iterable<A> for option<A> {
+    fn iter(blk: fn(A)) {
+        option::may(self, blk)
+    }
+}
+
+fn enumerate<A,IA:iterable<A>>(self: IA, blk: fn(uint, A)) {
+    let i = 0u;
+    self.iter {|a|
+        blk(i, a);
+        i += 1u;
+    }
+}
+
+// Here: we have to use fn@ for predicates and map functions, because
+// we will be binding them up into a closure.  Disappointing.  A true
+// region type system might be able to do better than this.
+
+fn filter<A,IA:iterable<A>>(self: IA, prd: fn@(A) -> bool, blk: fn(A)) {
+    self.iter {|a|
+        if prd(a) { blk(a) }
+    }
+}
+
+fn map<A,B,IA:iterable<A>>(self: IA, cnv: fn@(A) -> B, blk: fn(B)) {
+    self.iter {|a|
+        let b = cnv(a);
+        blk(b);
+    }
+}
+
+fn flat_map<A,B,IA:iterable<A>,IB:iterable<B>>(
+    self: IA, cnv: fn@(A) -> IB, blk: fn(B)) {
+    self.iter {|a|
+        cnv(a).iter(blk)
+    }
+}
+
+fn foldl<A,B:copy,IA:iterable<A>>(self: IA, b0: B, blk: fn(B, A) -> B) -> B {
+    let b = b0;
+    self.iter {|a|
+        b = blk(b, a);
+    }
+    ret b;
+}
+
+fn to_list<A:copy,IA:iterable<A>>(self: IA) -> [A] {
+    foldl::<A,[A],IA>(self, [], {|r, a| r + [a]})
+}
+
+fn repeat(times: uint, blk: fn()) {
+    let i = 0u;
+    while i < times {
+        blk();
+        i += 1u;
+    }
+}
+
+
+#[test]
+fn test_enumerate() {
+    enumerate(["0", "1", "2"]) {|i,j|
+        assert #fmt["%u",i] == j;
+    }
+}
+
+#[test]
+fn test_map_and_to_list() {
+    let a = bind vec::iter([0, 1, 2], _);
+    let b = bind map(a, {|i| i*2}, _);
+    let c = to_list(b);
+    assert c == [0, 2, 4];
+}
+
+#[test]
+fn test_map_directly_on_vec() {
+    let b = bind map([0, 1, 2], {|i| i*2}, _);
+    let c = to_list(b);
+    assert c == [0, 2, 4];
+}
+
+#[test]
+fn test_filter_on_int_range() {
+    fn is_even(&&i: int) -> bool {
+        ret (i % 2) == 0;
+    }
+
+    let l = to_list(bind filter(bind int::range(0, 10, _), is_even, _));
+    assert l == [0, 2, 4, 6, 8];
+}
+
+#[test]
+fn test_filter_on_uint_range() {
+    fn is_even(&&i: uint) -> bool {
+        ret (i % 2u) == 0u;
+    }
+
+    let l = to_list(bind filter(bind uint::range(0u, 10u, _), is_even, _));
+    assert l == [0u, 2u, 4u, 6u, 8u];
+}
+
+#[test]
+fn test_flat_map_with_option() {
+    fn if_even(&&i: int) -> option<int> {
+        if (i % 2) == 0 { some(i) }
+        else { none }
+    }
+
+    let a = bind vec::iter([0, 1, 2], _);
+    let b = bind flat_map(a, if_even, _);
+    let c = to_list(b);
+    assert c == [0, 2];
+}
+
+#[test]
+fn test_flat_map_with_list() {
+    fn repeat(&&i: int) -> [int] {
+        let r = [];
+        int::range(0, i) {|_j| r += [i]; }
+        r
+    }
+
+    let a = bind vec::iter([0, 1, 2, 3], _);
+    let b = bind flat_map(a, repeat, _);
+    let c = to_list(b);
+    #debug["c = %?", c];
+    assert c == [1, 2, 2, 3, 3, 3];
+}
+
+#[test]
+fn test_repeat() {
+    let c = [],
+        i = 0u;
+    repeat(5u) {||
+        c += [(i * i)];
+        i += 1u;
+    };
+    #debug["c = %?", c];
+    assert c == [0u, 1u, 4u, 9u, 16u];
+}
+
+
diff --git a/src/rustdoc/attr_parser.rs b/src/rustdoc/attr_parser.rs
index 9d886b6d4c9..818efe018fe 100644
--- a/src/rustdoc/attr_parser.rs
+++ b/src/rustdoc/attr_parser.rs
@@ -11,10 +11,10 @@ import core::tuple;
 
 export crate_attrs, mod_attrs, fn_attrs, arg_attrs,
        const_attrs, enum_attrs, variant_attrs, res_attrs,
-       iface_attrs, method_attrs;
+       iface_attrs, method_attrs, impl_attrs;
 export parse_crate, parse_mod, parse_fn, parse_const,
        parse_enum, parse_variant, parse_res,
-       parse_iface, parse_method;
+       parse_iface, parse_method, parse_impl;
 
 type crate_attrs = {
     name: option<str>
@@ -63,6 +63,11 @@ type iface_attrs = {
     desc: option<str>
 };
 
+type impl_attrs = {
+    brief: option<str>,
+    desc: option<str>
+};
+
 type method_attrs = fn_attrs;
 
 #[cfg(test)]
@@ -499,3 +504,7 @@ fn parse_iface(attrs: [ast::attribute]) -> iface_attrs {
 fn parse_method(attrs: [ast::attribute]) -> method_attrs {
     parse_fn(attrs)
 }
+
+fn parse_impl(attrs: [ast::attribute]) -> impl_attrs {
+    parse_basic(attrs)
+}
\ No newline at end of file
diff --git a/src/rustdoc/attr_pass.rs b/src/rustdoc/attr_pass.rs
index 91e065f310b..4e0c85c8ac3 100644
--- a/src/rustdoc/attr_pass.rs
+++ b/src/rustdoc/attr_pass.rs
@@ -26,7 +26,8 @@ fn run(
         fold_const: fold_const,
         fold_enum: fold_enum,
         fold_res: fold_res,
-        fold_iface: fold_iface
+        fold_iface: fold_iface,
+        fold_impl: fold_impl
         with *fold::default_seq_fold(srv)
     });
     fold.fold_crate(fold, doc)
@@ -55,11 +56,7 @@ fn fold_crate(
 
 #[test]
 fn should_replace_top_module_name_with_crate_name() {
-    let source = "#[link(name = \"bond\")];";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let fold = fold::default_seq_fold(srv);
-    let doc = fold_crate(fold, doc);
+    let doc = test::mk_doc("#[link(name = \"bond\")];");
     assert doc.topmod.name == "bond";
 }
 
@@ -105,22 +102,14 @@ fn fold_mod(fold: fold::fold<astsrv::srv>, doc: doc::moddoc) -> doc::moddoc {
 
 #[test]
 fn fold_mod_should_extract_mod_attributes() {
-    let source = "#[doc = \"test\"] mod a { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let fold = fold::default_seq_fold(srv);
-    let doc = fold_mod(fold, doc.topmod.mods()[0]);
-    assert doc.desc == some("test");
+    let doc = test::mk_doc("#[doc = \"test\"] mod a { }");
+    assert doc.topmod.mods()[0].desc == some("test");
 }
 
 #[test]
 fn fold_mod_should_extract_top_mod_attributes() {
-    let source = "#[doc = \"test\"];";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let fold = fold::default_seq_fold(srv);
-    let doc = fold_mod(fold, doc.topmod);
-    assert doc.desc == some("test");
+    let doc = test::mk_doc("#[doc = \"test\"];");
+    assert doc.topmod.desc == some("test");
 }
 
 fn fold_fn(
@@ -181,22 +170,14 @@ fn merge_ret_attrs(
 
 #[test]
 fn fold_fn_should_extract_fn_attributes() {
-    let source = "#[doc = \"test\"] fn a() -> int { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let fold = fold::default_seq_fold(srv);
-    let doc = fold_fn(fold, doc.topmod.fns()[0]);
-    assert doc.desc == some("test");
+    let doc = test::mk_doc("#[doc = \"test\"] fn a() -> int { }");
+    assert doc.topmod.fns()[0].desc == some("test");
 }
 
 #[test]
 fn fold_fn_should_extract_arg_attributes() {
-    let source = "#[doc(args(a = \"b\"))] fn c(a: bool) { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let fold = fold::default_seq_fold(srv);
-    let doc = fold_fn(fold, doc.topmod.fns()[0]);
-    assert doc.args[0].desc == some("b");
+    let doc = test::mk_doc("#[doc(args(a = \"b\"))] fn c(a: bool) { }");
+    assert doc.topmod.fns()[0].args[0].desc == some("b");
 }
 
 #[test]
@@ -223,12 +204,8 @@ fn fold_fn_should_preserve_sig() {
 
 #[test]
 fn fold_fn_should_extract_failure_conditions() {
-    let source = "#[doc(failure = \"what\")] fn a() { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let fold = fold::default_seq_fold(srv);
-    let doc = fold_fn(fold, doc.topmod.fns()[0]);
-    assert doc.failure == some("what");
+    let doc = test::mk_doc("#[doc(failure = \"what\")] fn a() { }");
+    assert doc.topmod.fns()[0].failure == some("what");
 }
 
 fn fold_const(
@@ -247,14 +224,10 @@ fn fold_const(
 
 #[test]
 fn fold_const_should_extract_docs() {
-    let source = "#[doc(brief = \"foo\", desc = \"bar\")]\
-                  const a: bool = true;";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let fold = fold::default_seq_fold(srv);
-    let doc = fold_const(fold, doc.topmod.consts()[0]);
-    assert doc.brief == some("foo");
-    assert doc.desc == some("bar");
+    let doc = test::mk_doc("#[doc(brief = \"foo\", desc = \"bar\")]\
+                            const a: bool = true;");
+    assert doc.topmod.consts()[0].brief == some("foo");
+    assert doc.topmod.consts()[0].desc == some("bar");
 }
 
 fn fold_enum(
@@ -295,24 +268,16 @@ fn fold_enum(
 
 #[test]
 fn fold_enum_should_extract_docs() {
-    let source = "#[doc(brief = \"a\", desc = \"b\")]\
-                  enum a { v }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let fold = fold::default_seq_fold(srv);
-    let doc = fold_enum(fold, doc.topmod.enums()[0]);
-    assert doc.brief == some("a");
-    assert doc.desc == some("b");
+    let doc = test::mk_doc("#[doc(brief = \"a\", desc = \"b\")]\
+                            enum a { v }");
+    assert doc.topmod.enums()[0].brief == some("a");
+    assert doc.topmod.enums()[0].desc == some("b");
 }
 
 #[test]
 fn fold_enum_should_extract_variant_docs() {
-    let source = "enum a { #[doc = \"c\"] v }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let fold = fold::default_seq_fold(srv);
-    let doc = fold_enum(fold, doc.topmod.enums()[0]);
-    assert doc.variants[0].desc == some("c");
+    let doc = test::mk_doc("enum a { #[doc = \"c\"] v }");
+    assert doc.topmod.enums()[0].variants[0].desc == some("c");
 }
 
 fn fold_res(
@@ -345,26 +310,18 @@ fn fold_res(
 
 #[test]
 fn fold_res_should_extract_docs() {
-    let source = "#[doc(brief = \"a\", desc = \"b\")]\
-                  resource r(b: bool) { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let fold = fold::default_seq_fold(srv);
-    let doc = fold_res(fold, doc.topmod.resources()[0]);
-    assert doc.brief == some("a");
-    assert doc.desc == some("b");
+    let doc = test::mk_doc("#[doc(brief = \"a\", desc = \"b\")]\
+                            resource r(b: bool) { }");
+    assert doc.topmod.resources()[0].brief == some("a");
+    assert doc.topmod.resources()[0].desc == some("b");
 }
 
 #[test]
 fn fold_res_should_extract_arg_docs() {
-    let source = "#[doc(args(a = \"b\"))]\
-                  resource r(a: bool) { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let fold = fold::default_seq_fold(srv);
-    let doc = fold_res(fold, doc.topmod.resources()[0]);
-    assert doc.args[0].name == "a";
-    assert doc.args[0].desc == some("b");
+    let doc = test::mk_doc("#[doc(args(a = \"b\"))]\
+                            resource r(a: bool) { }");
+    assert doc.topmod.resources()[0].args[0].name == "a";
+    assert doc.topmod.resources()[0].args[0].desc == some("b");
 }
 
 fn fold_iface(
@@ -398,9 +355,14 @@ fn merge_method_attrs(
                 (method.ident, attr_parser::parse_method(method.attrs))
             }
           }
-          _ {
-            fail "Undocumented invariant in merge_method_attrs";
+          ast_map::node_item(@{
+            node: ast::item_impl(_, _, _, methods), _
+          }) {
+            vec::map(methods) {|method|
+                (method.ident, attr_parser::parse_method(method.attrs))
+            }
           }
+          _ { fail "unexpected item" }
         }
     };
 
@@ -421,30 +383,77 @@ fn merge_method_attrs(
 
 #[test]
 fn should_extract_iface_docs() {
-    let source = "#[doc = \"whatever\"] iface i { fn a(); }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("#[doc = \"whatever\"] iface i { fn a(); }");
     assert doc.topmod.ifaces()[0].desc == some("whatever");
 }
 
 #[test]
 fn should_extract_iface_method_docs() {
-    let source = "iface i {\
-                  #[doc(\
-                  brief = \"brief\",\
-                  desc = \"desc\",\
-                  args(a = \"a\"),\
-                  return = \"return\",\
-                  failure = \"failure\")]\
-                  fn f(a: bool) -> bool;\
-                  }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc(
+        "iface i {\
+         #[doc(\
+         brief = \"brief\",\
+         desc = \"desc\",\
+         args(a = \"a\"),\
+         return = \"return\",\
+         failure = \"failure\")]\
+         fn f(a: bool) -> bool;\
+         }");
     assert doc.topmod.ifaces()[0].methods[0].brief == some("brief");
     assert doc.topmod.ifaces()[0].methods[0].desc == some("desc");
     assert doc.topmod.ifaces()[0].methods[0].args[0].desc == some("a");
     assert doc.topmod.ifaces()[0].methods[0].return.desc == some("return");
     assert doc.topmod.ifaces()[0].methods[0].failure == some("failure");
 }
+
+
+fn fold_impl(
+    fold: fold::fold<astsrv::srv>,
+    doc: doc::impldoc
+) -> doc::impldoc {
+    let srv = fold.ctxt;
+    let doc = fold::default_seq_fold_impl(fold, doc);
+    let attrs = parse_item_attrs(srv, doc.id, attr_parser::parse_impl);
+
+    {
+        brief: attrs.brief,
+        desc: attrs.desc,
+        methods: merge_method_attrs(srv, doc.id, doc.methods)
+        with doc
+    }
+}
+
+#[test]
+fn should_extract_impl_docs() {
+    let doc = test::mk_doc(
+        "#[doc = \"whatever\"] impl i for int { fn a() { } }");
+    assert doc.topmod.impls()[0].desc == some("whatever");
+}
+
+#[test]
+fn should_extract_impl_method_docs() {
+    let doc = test::mk_doc(
+        "impl i for int {\
+         #[doc(\
+         brief = \"brief\",\
+         desc = \"desc\",\
+         args(a = \"a\"),\
+         return = \"return\",\
+         failure = \"failure\")]\
+         fn f(a: bool) -> bool { }\
+         }");
+    assert doc.topmod.impls()[0].methods[0].brief == some("brief");
+    assert doc.topmod.impls()[0].methods[0].desc == some("desc");
+    assert doc.topmod.impls()[0].methods[0].args[0].desc == some("a");
+    assert doc.topmod.impls()[0].methods[0].return.desc == some("return");
+    assert doc.topmod.impls()[0].methods[0].failure == some("failure");
+}
+
+#[cfg(test)]
+mod test {
+    fn mk_doc(source: str) -> doc::cratedoc {
+        let srv = astsrv::mk_srv_from_str(source);
+        let doc = extract::from_srv(srv, "");
+        run(srv, doc)
+    }
+}
\ No newline at end of file
diff --git a/src/rustdoc/demo.rs b/src/rustdoc/demo.rs
index 924c7d73ee6..4feae4106a5 100644
--- a/src/rustdoc/demo.rs
+++ b/src/rustdoc/demo.rs
@@ -167,4 +167,15 @@ iface the_shunned_house {
         failure = "Will fail if bodies are removed from premises"
     )]
     fn construct() -> bool;
+}
+
+#[doc = "Whatever"]
+impl of the_shunned_house for omnomnomy {
+    #[doc(args(_unkempt_yard = "Whatever"))]
+    fn dingy_house(_unkempt_yard: int) {
+    }
+
+    fn construct() -> bool {
+        fail;
+    }
 }
\ No newline at end of file
diff --git a/src/rustdoc/desc_pass.rs b/src/rustdoc/desc_pass.rs
index c2ebde8b1a0..317c1f70a43 100644
--- a/src/rustdoc/desc_pass.rs
+++ b/src/rustdoc/desc_pass.rs
@@ -21,7 +21,8 @@ fn run(
         fold_fn: fold_fn,
         fold_enum: fold_enum,
         fold_res: fold_res,
-        fold_iface: fold_iface
+        fold_iface: fold_iface,
+        fold_impl: fold_impl
         with *fold::default_seq_fold(op)
     });
     fold.fold_crate(fold, doc)
@@ -104,154 +105,181 @@ fn fold_iface(fold: fold::fold<op>, doc: doc::ifacedoc) -> doc::ifacedoc {
     {
         brief: maybe_apply_op(fold.ctxt, doc.brief),
         desc: maybe_apply_op(fold.ctxt, doc.desc),
-        methods: vec::map(doc.methods) {|doc|
-            {
-                brief: maybe_apply_op(fold.ctxt, doc.brief),
-                desc: maybe_apply_op(fold.ctxt, doc.desc),
-                args: vec::map(doc.args) {|doc|
-                    {
-                        desc: maybe_apply_op(fold.ctxt, doc.desc)
-                        with doc
-                    }
-                },
-                return: {
-                    desc: maybe_apply_op(fold.ctxt, doc.return.desc)
-                    with doc.return
-                },
-                failure: maybe_apply_op(fold.ctxt, doc.failure)
-                with doc
-            }
+        methods: apply_to_methods(fold.ctxt, doc.methods)
+        with doc
+    }
+}
+
+fn apply_to_methods(op: op, docs: [doc::methoddoc]) -> [doc::methoddoc] {
+    vec::map(docs) {|doc|
+        {
+            brief: maybe_apply_op(op, doc.brief),
+            desc: maybe_apply_op(op, doc.desc),
+            args: vec::map(doc.args) {|doc|
+                {
+                    desc: maybe_apply_op(op, doc.desc)
+                    with doc
+                }
+            },
+            return: {
+                desc: maybe_apply_op(op, doc.return.desc)
+                with doc.return
+            },
+            failure: maybe_apply_op(op, doc.failure)
+            with doc
         }
+    }
+}
+
+fn fold_impl(fold: fold::fold<op>, doc: doc::impldoc) -> doc::impldoc {
+    {
+        brief: maybe_apply_op(fold.ctxt, doc.brief),
+        desc: maybe_apply_op(fold.ctxt, doc.desc),
+        methods: apply_to_methods(fold.ctxt, doc.methods)
         with doc
     }
 }
 
 #[test]
 fn should_execute_op_on_enum_brief() {
-    let source = "#[doc(brief = \" a \")] enum a { b }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = mk_pass(str::trim)(srv, doc);
+    let doc = test::mk_doc("#[doc(brief = \" a \")] enum a { b }");
     assert doc.topmod.enums()[0].brief == some("a");
 }
 
 #[test]
 fn should_execute_op_on_enum_desc() {
-    let source = "#[doc(desc = \" a \")] enum a { b }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = mk_pass(str::trim)(srv, doc);
+    let doc = test::mk_doc("#[doc(desc = \" a \")] enum a { b }");
     assert doc.topmod.enums()[0].desc == some("a");
 }
 
 #[test]
 fn should_execute_op_on_variant_desc() {
-    let source = "enum a { #[doc = \" a \"] b }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = mk_pass(str::trim)(srv, doc);
+    let doc = test::mk_doc("enum a { #[doc = \" a \"] b }");
     assert doc.topmod.enums()[0].variants[0].desc == some("a");
 }
 
 #[test]
 fn should_execute_op_on_resource_brief() {
-    let source = "#[doc(brief = \" a \")] resource r(a: bool) { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = mk_pass(str::trim)(srv, doc);
+    let doc = test::mk_doc("#[doc(brief = \" a \")] resource r(a: bool) { }");
     assert doc.topmod.resources()[0].brief == some("a");
 }
 
 #[test]
 fn should_execute_op_on_resource_desc() {
-    let source = "#[doc(desc = \" a \")] resource r(a: bool) { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = mk_pass(str::trim)(srv, doc);
+    let doc = test::mk_doc("#[doc(desc = \" a \")] resource r(a: bool) { }");
     assert doc.topmod.resources()[0].desc == some("a");
 }
 
 #[test]
 fn should_execute_op_on_resource_args() {
-    let source = "#[doc(args(a = \" a \"))] resource r(a: bool) { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = mk_pass(str::trim)(srv, doc);
+    let doc = test::mk_doc(
+        "#[doc(args(a = \" a \"))] resource r(a: bool) { }");
     assert doc.topmod.resources()[0].args[0].desc == some("a");
 }
 
 #[test]
 fn should_execute_op_on_iface_brief() {
-    let source = "#[doc(brief = \" a \")] iface i { fn a(); }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = mk_pass(str::trim)(srv, doc);
+    let doc = test::mk_doc(
+        "#[doc(brief = \" a \")] iface i { fn a(); }");
     assert doc.topmod.ifaces()[0].brief == some("a");
 }
 
 #[test]
 fn should_execute_op_on_iface_desc() {
-    let source = "#[doc(desc = \" a \")] iface i { fn a(); }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = mk_pass(str::trim)(srv, doc);
+    let doc = test::mk_doc(
+        "#[doc(desc = \" a \")] iface i { fn a(); }");
     assert doc.topmod.ifaces()[0].desc == some("a");
 }
 
 #[test]
 fn should_execute_op_on_iface_method_brief() {
-    let source = "iface i { #[doc(brief = \" a \")] fn a(); }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = mk_pass(str::trim)(srv, doc);
+    let doc = test::mk_doc(
+        "iface i { #[doc(brief = \" a \")] fn a(); }");
     assert doc.topmod.ifaces()[0].methods[0].brief == some("a");
 }
 
 #[test]
 fn should_execute_op_on_iface_method_desc() {
-    let source = "iface i { #[doc(desc = \" a \")] fn a(); }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = mk_pass(str::trim)(srv, doc);
+    let doc = test::mk_doc(
+        "iface i { #[doc(desc = \" a \")] fn a(); }");
     assert doc.topmod.ifaces()[0].methods[0].desc == some("a");
 }
 
 #[test]
 fn should_execute_op_on_iface_method_args() {
-    let source = "iface i { #[doc(args(a = \" a \"))] fn a(a: bool); }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = mk_pass(str::trim)(srv, doc);
+    let doc = test::mk_doc(
+        "iface i { #[doc(args(a = \" a \"))] fn a(a: bool); }");
     assert doc.topmod.ifaces()[0].methods[0].args[0].desc == some("a");
 }
 
 #[test]
 fn should_execute_op_on_iface_method_return() {
-    let source = "iface i { #[doc(return = \" a \")] fn a() -> int; }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = mk_pass(str::trim)(srv, doc);
+    let doc = test::mk_doc(
+        "iface i { #[doc(return = \" a \")] fn a() -> int; }");
     assert doc.topmod.ifaces()[0].methods[0].return.desc == some("a");
 }
 
 #[test]
 fn should_execute_op_on_iface_method_failure_condition() {
-    let source = "iface i { #[doc(failure = \" a \")] fn a(); }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = mk_pass(str::trim)(srv, doc);
+    let doc = test::mk_doc("iface i { #[doc(failure = \" a \")] fn a(); }");
     assert doc.topmod.ifaces()[0].methods[0].failure == some("a");
 }
+
+#[test]
+fn should_execute_op_on_impl_brief() {
+    let doc = test::mk_doc(
+        "#[doc(brief = \" a \")] impl i for int { fn a() { } }");
+    assert doc.topmod.impls()[0].brief == some("a");
+}
+
+#[test]
+fn should_execute_op_on_impl_desc() {
+    let doc = test::mk_doc(
+        "#[doc(desc = \" a \")] impl i for int { fn a() { } }");
+    assert doc.topmod.impls()[0].desc == some("a");
+}
+
+#[test]
+fn should_execute_op_on_impl_method_brief() {
+    let doc = test::mk_doc(
+        "impl i for int { #[doc(brief = \" a \")] fn a() { } }");
+    assert doc.topmod.impls()[0].methods[0].brief == some("a");
+}
+
+#[test]
+fn should_execute_op_on_impl_method_desc() {
+    let doc = test::mk_doc(
+        "impl i for int { #[doc(desc = \" a \")] fn a() { } }");
+    assert doc.topmod.impls()[0].methods[0].desc == some("a");
+}
+
+#[test]
+fn should_execute_op_on_impl_method_args() {
+    let doc = test::mk_doc(
+        "impl i for int { #[doc(args(a = \" a \"))] fn a(a: bool) { } }");
+    assert doc.topmod.impls()[0].methods[0].args[0].desc == some("a");
+}
+
+#[test]
+fn should_execute_op_on_impl_method_return() {
+    let doc = test::mk_doc(
+        "impl i for int { #[doc(return = \" a \")] fn a() -> int { fail } }");
+    assert doc.topmod.impls()[0].methods[0].return.desc == some("a");
+}
+
+#[test]
+fn should_execute_op_on_impl_method_failure_condition() {
+    let doc = test::mk_doc(
+        "impl i for int { #[doc(failure = \" a \")] fn a() { } }");
+    assert doc.topmod.impls()[0].methods[0].failure == some("a");
+}
+
+#[cfg(test)]
+mod test {
+    fn mk_doc(source: str) -> doc::cratedoc {
+        let srv = astsrv::mk_srv_from_str(source);
+        let doc = extract::from_srv(srv, "");
+        let doc = attr_pass::mk_pass()(srv, doc);
+        mk_pass(str::trim)(srv, doc)
+    }
+}
\ No newline at end of file
diff --git a/src/rustdoc/desc_to_brief_pass.rs b/src/rustdoc/desc_to_brief_pass.rs
index 2ab9a86db4a..315138db3d3 100644
--- a/src/rustdoc/desc_to_brief_pass.rs
+++ b/src/rustdoc/desc_to_brief_pass.rs
@@ -23,7 +23,8 @@ fn run(
         fold_fn: fold_fn,
         fold_enum: fold_enum,
         fold_res: fold_res,
-        fold_iface: fold_iface
+        fold_iface: fold_iface,
+        fold_impl: fold_impl
         with *fold::default_seq_fold(())
     });
     fold.fold_crate(fold, doc)
@@ -104,83 +105,102 @@ fn fold_iface(fold: fold::fold<()>, doc: doc::ifacedoc) -> doc::ifacedoc {
     }
 }
 
+fn fold_impl(fold: fold::fold<()>, doc: doc::impldoc) -> doc::impldoc {
+    let doc =fold::default_seq_fold_impl(fold, doc);
+    let (brief, desc) = modify(doc.brief, doc.desc);
+
+    {
+        brief: brief,
+        desc: desc,
+        methods: vec::map(doc.methods) {|doc|
+            let (brief, desc) = modify(doc.brief, doc.desc);
+
+            {
+                brief: brief,
+                desc: desc
+                with doc
+            }
+        }
+        with doc
+    }
+}
+
 #[test]
 fn should_promote_mod_desc() {
-    let source = "#[doc(desc = \"desc\")] mod m { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("#[doc(desc = \"desc\")] mod m { }");
     assert doc.topmod.mods()[0].brief == some("desc");
     assert doc.topmod.mods()[0].desc == none;
 }
 
 #[test]
 fn should_promote_const_desc() {
-    let source = "#[doc(desc = \"desc\")] const a: bool = true;";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("#[doc(desc = \"desc\")] const a: bool = true;");
     assert doc.topmod.consts()[0].brief == some("desc");
     assert doc.topmod.consts()[0].desc == none;
 }
 
 #[test]
 fn should_promote_fn_desc() {
-    let source = "#[doc(desc = \"desc\")] fn a() { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("#[doc(desc = \"desc\")] fn a() { }");
     assert doc.topmod.fns()[0].brief == some("desc");
     assert doc.topmod.fns()[0].desc == none;
 }
 
 #[test]
 fn should_promote_enum_desc() {
-    let source = "#[doc(desc = \"desc\")] enum a { b }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("#[doc(desc = \"desc\")] enum a { b }");
     assert doc.topmod.enums()[0].brief == some("desc");
     assert doc.topmod.enums()[0].desc == none;
 }
 
 #[test]
 fn should_promote_resource_desc() {
-    let source = "#[doc(desc = \"desc\")] resource r(a: bool) { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = run(srv, doc);
+    let doc = test::mk_doc(
+        "#[doc(desc = \"desc\")] resource r(a: bool) { }");
     assert doc.topmod.resources()[0].brief == some("desc");
     assert doc.topmod.resources()[0].desc == none;
 }
 
 #[test]
 fn should_promote_iface_desc() {
-    let source = "#[doc(desc = \"desc\")] iface i { fn a(); }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("#[doc(desc = \"desc\")] iface i { fn a(); }");
     assert doc.topmod.ifaces()[0].brief == some("desc");
     assert doc.topmod.ifaces()[0].desc == none;
 }
 
 #[test]
 fn should_promote_iface_method_desc() {
-    let source = "iface i { #[doc(desc = \"desc\")] fn a(); }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("iface i { #[doc(desc = \"desc\")] fn a(); }");
     assert doc.topmod.ifaces()[0].methods[0].brief == some("desc");
     assert doc.topmod.ifaces()[0].methods[0].desc == none;
 }
 
+#[test]
+fn should_promote_impl_desc() {
+    let doc = test::mk_doc(
+        "#[doc(desc = \"desc\")] impl i for int { fn a() { } }");
+    assert doc.topmod.impls()[0].brief == some("desc");
+    assert doc.topmod.impls()[0].desc == none;
+}
+
+#[test]
+fn should_promote_impl_method_desc() {
+    let doc = test::mk_doc(
+        "impl i for int { #[doc(desc = \"desc\")] fn a() { } }");
+    assert doc.topmod.impls()[0].methods[0].brief == some("desc");
+    assert doc.topmod.impls()[0].methods[0].desc == none;
+}
+
+#[cfg(test)]
+mod test {
+    fn mk_doc(source: str) -> doc::cratedoc {
+        let srv = astsrv::mk_srv_from_str(source);
+        let doc = extract::from_srv(srv, "");
+        let doc = attr_pass::mk_pass()(srv, doc);
+        run(srv, doc)
+    }
+}
+
 fn modify(
     brief: option<str>,
     desc: option<str>
diff --git a/src/rustdoc/doc.rs b/src/rustdoc/doc.rs
index 13c618c685b..9370ffb6c51 100644
--- a/src/rustdoc/doc.rs
+++ b/src/rustdoc/doc.rs
@@ -12,7 +12,8 @@ enum itemtag {
     fntag(fndoc),
     enumtag(enumdoc),
     restag(resdoc),
-    ifacetag(ifacedoc)
+    ifacetag(ifacedoc),
+    impltag(impldoc)
 }
 
 type moddoc = {
@@ -96,6 +97,16 @@ type methoddoc = {
     sig: option<str>
 };
 
+type impldoc = {
+    id: ast_id,
+    name: str,
+    brief: option<str>,
+    desc: option<str>,
+    iface_ty: option<str>,
+    self_ty: option<str>,
+    methods: [methoddoc]
+};
+
 #[doc = "Some helper methods on moddoc, mostly for testing"]
 impl util for moddoc {
 
@@ -152,6 +163,15 @@ impl util for moddoc {
             }
         }
     }
+
+    fn impls() -> [impldoc] {
+        vec::filter_map(*self.items) {|itemtag|
+            alt itemtag {
+              impltag(impldoc) { some(impldoc) }
+              _ { none }
+            }
+        }
+    }
 }
 
 #[doc = "Helper methods on itemtag"]
@@ -164,6 +184,7 @@ impl util for itemtag {
           doc::enumtag({name, _}) { name }
           doc::restag({name, _}) { name }
           doc::ifacetag({name, _}) { name }
+          doc::impltag({name, _}) { name }
         }
     }
 }
diff --git a/src/rustdoc/extract.rs b/src/rustdoc/extract.rs
index 64e179c78c0..a36536fd3e8 100644
--- a/src/rustdoc/extract.rs
+++ b/src/rustdoc/extract.rs
@@ -75,6 +75,11 @@ fn moddoc_from_mod(
                     ifacedoc_from_iface(methods, item.ident, item.id)
                 ))
               }
+              ast::item_impl(_, _, _, methods) {
+                some(doc::impltag(
+                    impldoc_from_impl(methods, item.ident, item.id)
+                ))
+              }
               _ {
                 none
               }
@@ -140,9 +145,7 @@ fn constdoc_from_const(
 
 #[test]
 fn should_extract_const_name_and_id() {
-    let source = "const a: int = 0;";
-    let ast = parse::from_str(source);
-    let doc = extract(ast, "");
+    let doc = test::mk_doc("const a: int = 0;");
     assert doc.topmod.consts()[0].id != 0;
     assert doc.topmod.consts()[0].name == "a";
 }
@@ -177,18 +180,14 @@ fn variantdoc_from_variant(variant: ast::variant) -> doc::variantdoc {
 
 #[test]
 fn should_extract_enums() {
-    let source = "enum e { v }";
-    let ast = parse::from_str(source);
-    let doc = extract(ast, "");
+    let doc = test::mk_doc("enum e { v }");
     assert doc.topmod.enums()[0].id != 0;
     assert doc.topmod.enums()[0].name == "e";
 }
 
 #[test]
 fn should_extract_enum_variants() {
-    let source = "enum e { v }";
-    let ast = parse::from_str(source);
-    let doc = extract(ast, "");
+    let doc = test::mk_doc("enum e { v }");
     assert doc.topmod.enums()[0].variants[0].name == "v";
 }
 
@@ -209,18 +208,14 @@ fn resdoc_from_resource(
 
 #[test]
 fn should_extract_resources() {
-    let source = "resource r(b: bool) { }";
-    let ast = parse::from_str(source);
-    let doc = extract(ast, "");
+    let doc = test::mk_doc("resource r(b: bool) { }");
     assert doc.topmod.resources()[0].id != 0;
     assert doc.topmod.resources()[0].name == "r";
 }
 
 #[test]
 fn should_extract_resource_args() {
-    let source = "resource r(b: bool) { }";
-    let ast = parse::from_str(source);
-    let doc = extract(ast, "");
+    let doc = test::mk_doc("resource r(b: bool) { }");
     assert doc.topmod.resources()[0].args[0].name == "b";
 }
 
@@ -253,45 +248,93 @@ fn ifacedoc_from_iface(
 
 #[test]
 fn should_extract_ifaces() {
-    let source = "iface i { fn f(); }";
-    let ast = parse::from_str(source);
-    let doc = extract(ast, "");
+    let doc = test::mk_doc("iface i { fn f(); }");
     assert doc.topmod.ifaces()[0].name == "i";
 }
 
 #[test]
 fn should_extract_iface_methods() {
-    let source = "iface i { fn f(); }";
-    let ast = parse::from_str(source);
-    let doc = extract(ast, "");
+    let doc = test::mk_doc("iface i { fn f(); }");
     assert doc.topmod.ifaces()[0].methods[0].name == "f";
 }
 
 #[test]
 fn should_extract_iface_method_args() {
-    let source = "iface i { fn f(a: bool); }";
-    let ast = parse::from_str(source);
-    let doc = extract(ast, "");
+    let doc = test::mk_doc("iface i { fn f(a: bool); }");
     assert doc.topmod.ifaces()[0].methods[0].args[0].name == "a";
 }
 
+fn impldoc_from_impl(
+    methods: [@ast::method],
+    name: str,
+    id: ast::node_id
+) -> doc::impldoc {
+    {
+        id: id,
+        name: name,
+        brief: none,
+        desc: none,
+        iface_ty: none,
+        self_ty: none,
+        methods: vec::map(methods) {|method|
+            {
+                name: method.ident,
+                brief: none,
+                desc: none,
+                args: argdocs_from_args(method.decl.inputs),
+                return: {
+                    desc: none,
+                    ty: none
+                },
+                failure: none,
+                sig: none
+            }
+        }
+    }
+}
+
+#[test]
+fn should_extract_impls_with_names() {
+    let doc = test::mk_doc("impl i for int { fn a() { } }");
+    assert doc.topmod.impls()[0].name == "i";
+}
+
+#[test]
+fn should_extract_impls_without_names() {
+    let doc = test::mk_doc("impl of i for int { fn a() { } }");
+    assert doc.topmod.impls()[0].name == "i";
+}
+
+#[test]
+fn should_extract_impl_methods() {
+    let doc = test::mk_doc("impl i for int { fn f() { } }");
+    assert doc.topmod.impls()[0].methods[0].name == "f";
+}
+
+#[test]
+fn should_extract_impl_method_args() {
+    let doc = test::mk_doc("impl i for int { fn f(a: bool) { } }");
+    assert doc.topmod.impls()[0].methods[0].args[0].name == "a";
+}
+
 #[cfg(test)]
-mod tests {
+mod test {
+
+    fn mk_doc(source: str) -> doc::cratedoc {
+        let ast = parse::from_str(source);
+        extract(ast, "")
+    }
 
     #[test]
     fn extract_empty_crate() {
-        let source = ""; // empty crate
-        let ast = parse::from_str(source);
-        let doc = extract(ast, "");
+        let doc = mk_doc("");
         assert vec::is_empty(doc.topmod.mods());
         assert vec::is_empty(doc.topmod.fns());
     }
 
     #[test]
     fn extract_mods() {
-        let source = "mod a { mod b { } mod c { } }";
-        let ast = parse::from_str(source);
-        let doc = extract(ast, "");
+        let doc = mk_doc("mod a { mod b { } mod c { } }");
         assert doc.topmod.mods()[0].name == "a";
         assert doc.topmod.mods()[0].mods()[0].name == "b";
         assert doc.topmod.mods()[0].mods()[1].name == "c";
@@ -299,36 +342,28 @@ mod tests {
 
     #[test]
     fn extract_mods_deep() {
-        let source = "mod a { mod b { mod c { } } }";
-        let ast = parse::from_str(source);
-        let doc = extract(ast, "");
+        let doc = mk_doc("mod a { mod b { mod c { } } }");
         assert doc.topmod.mods()[0].mods()[0].mods()[0].name == "c";
     }
 
     #[test]
     fn extract_should_set_mod_ast_id() {
-        let source = "mod a { }";
-        let ast = parse::from_str(source);
-        let doc = extract(ast, "");
+        let doc = mk_doc("mod a { }");
         assert doc.topmod.mods()[0].id != 0;
     }
 
     #[test]
     fn extract_fns() {
-        let source =
+        let doc = mk_doc(
             "fn a() { } \
-             mod b { fn c() { } }";
-        let ast = parse::from_str(source);
-        let doc = extract(ast, "");
+             mod b { fn c() { } }");
         assert doc.topmod.fns()[0].name == "a";
         assert doc.topmod.mods()[0].fns()[0].name == "c";
     }
 
     #[test]
     fn extract_should_set_fn_ast_id() {
-        let source = "fn a() { }";
-        let ast = parse::from_str(source);
-        let doc = extract(ast, "");
+        let doc = mk_doc("fn a() { }");
         assert doc.topmod.fns()[0].id != 0;
     }
 
diff --git a/src/rustdoc/fold.rs b/src/rustdoc/fold.rs
index 6851299db87..ba5283bd6ff 100644
--- a/src/rustdoc/fold.rs
+++ b/src/rustdoc/fold.rs
@@ -11,6 +11,7 @@ export default_seq_fold_const;
 export default_seq_fold_enum;
 export default_seq_fold_res;
 export default_seq_fold_iface;
+export default_seq_fold_impl;
 
 enum fold<T> = t<T>;
 
@@ -21,6 +22,7 @@ type fold_const<T> = fn~(fold: fold<T>, doc: doc::constdoc) -> doc::constdoc;
 type fold_enum<T> = fn~(fold: fold<T>, doc: doc::enumdoc) -> doc::enumdoc;
 type fold_res<T> = fn~(fold: fold<T>, doc: doc::resdoc) -> doc::resdoc;
 type fold_iface<T> = fn~(fold: fold<T>, doc: doc::ifacedoc) -> doc::ifacedoc;
+type fold_impl<T> = fn~(fold: fold<T>, doc: doc::impldoc) -> doc::impldoc;
 
 type t<T> = {
     ctxt: T,
@@ -30,7 +32,8 @@ type t<T> = {
     fold_const: fold_const<T>,
     fold_enum: fold_enum<T>,
     fold_res: fold_res<T>,
-    fold_iface: fold_iface<T>
+    fold_iface: fold_iface<T>,
+    fold_impl: fold_impl<T>
 };
 
 
@@ -44,7 +47,8 @@ fn mk_fold<T:copy>(
     fold_const: fold_const<T>,
     fold_enum: fold_enum<T>,
     fold_res: fold_res<T>,
-    fold_iface: fold_iface<T>
+    fold_iface: fold_iface<T>,
+    fold_impl: fold_impl<T>
 ) -> fold<T> {
     fold({
         ctxt: ctxt,
@@ -54,7 +58,8 @@ fn mk_fold<T:copy>(
         fold_const: fold_const,
         fold_enum: fold_enum,
         fold_res: fold_res,
-        fold_iface: fold_iface
+        fold_iface: fold_iface,
+        fold_impl: fold_impl
     })
 }
 
@@ -67,7 +72,8 @@ fn default_seq_fold<T:copy>(ctxt: T) -> fold<T> {
         {|f, d| default_seq_fold_const(f, d)},
         {|f, d| default_seq_fold_enum(f, d)},
         {|f, d| default_seq_fold_res(f, d)},
-        {|f, d| default_seq_fold_iface(f, d)}
+        {|f, d| default_seq_fold_iface(f, d)},
+        {|f, d| default_seq_fold_impl(f, d)}
     )
 }
 
@@ -105,6 +111,9 @@ fn default_seq_fold_mod<T>(
               doc::ifacetag(ifacedoc) {
                 doc::ifacetag(fold.fold_iface(fold, ifacedoc))
               }
+              doc::impltag(impldoc) {
+                doc::impltag(fold.fold_impl(fold, impldoc))
+              }
             }
         }
         with doc
@@ -146,6 +155,13 @@ fn default_seq_fold_iface<T>(
     doc
 }
 
+fn default_seq_fold_impl<T>(
+    _fold: fold<T>,
+    doc: doc::impldoc
+) -> doc::impldoc {
+    doc
+}
+
 #[test]
 fn default_fold_should_produce_same_doc() {
     let source = "mod a { fn b() { } mod c { fn d() { } } }";
diff --git a/src/rustdoc/markdown_pass.rs b/src/rustdoc/markdown_pass.rs
index 0833b5c7e74..4aab41e38cc 100644
--- a/src/rustdoc/markdown_pass.rs
+++ b/src/rustdoc/markdown_pass.rs
@@ -138,6 +138,7 @@ fn write_mod_contents(
           doc::enumtag(enumdoc) { write_enum(ctxt, enumdoc) }
           doc::restag(resdoc) { write_res(ctxt, resdoc) }
           doc::ifacetag(ifacedoc) { write_iface(ctxt, ifacedoc) }
+          doc::impltag(impldoc) { write_impl(ctxt, impldoc) }
         }
     }
 }
@@ -647,6 +648,101 @@ fn should_write_iface_method_failure_conditions() {
     assert str::contains(markdown, "Failure conditions: nuked");
 }
 
+fn write_impl(ctxt: ctxt, doc: doc::impldoc) {
+    assert option::is_some(doc.self_ty);
+    let self_ty = option::get(doc.self_ty);
+    alt doc.iface_ty {
+      some(iface_ty) {
+        write_header(ctxt, h2,
+                     #fmt("Implementation `%s` of `%s` for `%s`",
+                          doc.name, iface_ty, self_ty));
+      }
+      none {
+        write_header(ctxt, h2,
+                     #fmt("Implementation `%s` for `%s`",
+                          doc.name, self_ty));
+      }
+    }
+    write_brief(ctxt, doc.brief);
+    write_desc(ctxt, doc.desc);
+    write_methods(ctxt, doc.methods);
+}
+
+#[test]
+fn should_write_impl_header() {
+    let markdown = test::render("impl i for int { fn a() { } }");
+    assert str::contains(markdown, "## Implementation `i` for `int`");
+}
+
+#[test]
+fn should_write_impl_header_with_iface() {
+    let markdown = test::render("impl i of j for int { fn a() { } }");
+    assert str::contains(markdown, "## Implementation `i` of `j` for `int`");
+}
+
+#[test]
+fn should_write_impl_brief() {
+    let markdown = test::render(
+        "#[doc(brief = \"brief\")] impl i for int { fn a() { } }");
+    assert str::contains(markdown, "brief");
+}
+
+#[test]
+fn should_write_impl_desc() {
+    let markdown = test::render(
+        "#[doc(desc = \"desc\")] impl i for int { fn a() { } }");
+    assert str::contains(markdown, "desc");
+}
+
+#[test]
+fn should_write_impl_method_header() {
+    let markdown = test::render(
+        "impl i for int { fn a() { } }");
+    assert str::contains(markdown, "### Method `a`");
+}
+
+#[test]
+fn should_write_impl_method_signature() {
+    let markdown = test::render(
+        "impl i for int { fn a() { } }");
+    assert str::contains(markdown, "\n    fn a()");
+}
+
+#[test]
+fn should_write_impl_method_argument_header() {
+    let markdown = test::render(
+        "impl a for int { fn a(b: int) { } }");
+    assert str::contains(markdown, "\n\nArguments:\n\n");
+}
+
+#[test]
+fn should_write_impl_method_arguments() {
+    let markdown = test::render(
+        "impl a for int { fn a(b: int) { } }");
+    assert str::contains(markdown, "* `b`: `int`\n");
+}
+
+#[test]
+fn should_not_write_impl_method_arguments_if_none() {
+    let markdown = test::render(
+        "impl a for int { fn a() { } }");
+    assert !str::contains(markdown, "Arguments");
+}
+
+#[test]
+fn should_write_impl_method_return_info() {
+    let markdown = test::render(
+        "impl a for int { fn a() -> int { } }");
+    assert str::contains(markdown, "Returns `int`");
+}
+
+#[test]
+fn should_write_impl_method_failure_conditions() {
+    let markdown = test::render(
+        "impl a for int { #[doc(failure = \"nuked\")] fn a() { } }");
+    assert str::contains(markdown, "Failure conditions: nuked");
+}
+
 #[cfg(test)]
 mod test {
     fn render(source: str) -> str {
diff --git a/src/rustdoc/prune_undoc_pass.rs b/src/rustdoc/prune_undoc_pass.rs
index a68477058c1..5b197b9859a 100644
--- a/src/rustdoc/prune_undoc_pass.rs
+++ b/src/rustdoc/prune_undoc_pass.rs
@@ -23,7 +23,8 @@ fn run(
         fold_const: fold_const,
         fold_enum: fold_enum,
         fold_res: fold_res,
-        fold_iface: fold_iface
+        fold_iface: fold_iface,
+        fold_impl: fold_impl
         with *fold::default_seq_fold(ctxt)
     });
     fold.fold_crate(fold, doc)
@@ -84,6 +85,14 @@ fn fold_mod(
                     none
                 }
               }
+              doc::impltag(impldoc) {
+                let doc = fold.fold_impl(fold, impldoc);
+                if fold.ctxt.have_docs {
+                    some(doc::impltag(doc))
+                } else {
+                    none
+                }
+              }
               _ { some(itemtag) }
             }
         }
@@ -138,21 +147,13 @@ fn prune_return(doc: doc::retdoc) -> doc::retdoc {
 
 #[test]
 fn should_elide_undocumented_arguments() {
-    let source = "#[doc = \"hey\"] fn a(b: int) { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("#[doc = \"hey\"] fn a(b: int) { }");
     assert vec::is_empty(doc.topmod.fns()[0].args);
 }
 
 #[test]
 fn should_not_elide_fns_with_documented_arguments() {
-    let source = "#[doc(args(a = \"b\"))] fn a(a: int) { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("#[doc(args(a = \"b\"))] fn a(a: int) { }");
     assert vec::is_not_empty(doc.topmod.fns());
 }
 
@@ -169,49 +170,31 @@ fn should_elide_undocumented_return_values() {
 
 #[test]
 fn should_not_elide_fns_with_documented_failure_conditions() {
-    let source = "#[doc(failure = \"yup\")] fn a() { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("#[doc(failure = \"yup\")] fn a() { }");
     assert vec::is_not_empty(doc.topmod.fns());
 }
 
 #[test]
 fn should_elide_undocumented_mods() {
-    let source = "mod a { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("mod a { }");
     assert vec::is_empty(doc.topmod.mods());
 }
 
 #[test]
 fn should_not_elide_undocument_mods_with_documented_mods() {
-    let source = "mod a { #[doc = \"b\"] mod b { } }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("mod a { #[doc = \"b\"] mod b { } }");
     assert vec::is_not_empty(doc.topmod.mods());
 }
 
 #[test]
 fn should_not_elide_undocument_mods_with_documented_fns() {
-    let source = "mod a { #[doc = \"b\"] fn b() { } }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("mod a { #[doc = \"b\"] fn b() { } }");
     assert vec::is_not_empty(doc.topmod.mods());
 }
 
 #[test]
 fn should_elide_undocumented_fns() {
-    let source = "fn a() { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("fn a() { }");
     assert vec::is_empty(doc.topmod.fns());
 }
 
@@ -228,10 +211,7 @@ fn fold_const(
 
 #[test]
 fn should_elide_undocumented_consts() {
-    let source = "const a: bool = true;";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("const a: bool = true;");
     assert vec::is_empty(doc.topmod.consts());
 }
 
@@ -255,31 +235,19 @@ fn fold_enum(fold: fold::fold<ctxt>, doc: doc::enumdoc) -> doc::enumdoc {
 
 #[test]
 fn should_elide_undocumented_enums() {
-    let source = "enum a { b }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("enum a { b }");
     assert vec::is_empty(doc.topmod.enums());
 }
 
 #[test]
 fn should_elide_undocumented_variants() {
-    let source = "#[doc = \"a\"] enum a { b }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("#[doc = \"a\"] enum a { b }");
     assert vec::is_empty(doc.topmod.enums()[0].variants);
 }
 
 #[test]
 fn should_not_elide_enums_with_documented_variants() {
-    let source = "enum a { #[doc = \"a\"] b }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("enum a { #[doc = \"a\"] b }");
     assert vec::is_not_empty(doc.topmod.enums());
 }
 
@@ -303,32 +271,21 @@ fn fold_res(fold: fold::fold<ctxt>, doc: doc::resdoc) -> doc::resdoc {
 
 #[test]
 fn should_elide_undocumented_resources() {
-    let source = "resource r(a: bool) { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("resource r(a: bool) { }");
     assert vec::is_empty(doc.topmod.resources());
 }
 
 #[test]
 fn should_elide_undocumented_resource_args() {
-    let source = "#[doc = \"drunk\"]\
-                  resource r(a: bool) { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("#[doc = \"drunk\"]\
+                            resource r(a: bool) { }");
     assert vec::is_empty(doc.topmod.resources()[0].args);
 }
 
 #[test]
 fn should_not_elide_resources_with_documented_args() {
-    let source = "#[doc(args(a = \"drunk\"))]\
-                  resource r(a: bool) { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("#[doc(args(a = \"drunk\"))]\
+                            resource r(a: bool) { }");
     assert vec::is_not_empty(doc.topmod.resources());
 }
 
@@ -338,86 +295,133 @@ fn fold_iface(
 ) -> doc::ifacedoc {
     let doc = fold::default_seq_fold_iface(fold, doc);
     let doc = {
-        methods: vec::map(doc.methods) {|doc|
-            {
-                args: prune_args(doc.args),
-                return: prune_return(doc.return)
-                with doc
-            }
-        }
+        methods: prune_methods(doc.methods)
         with doc
     };
-    let methods_have_docs = vec::foldl(false, doc.methods) {|accum, doc|
+    fold.ctxt.have_docs =
+        doc.brief != none
+        || doc.desc != none
+        || methods_have_docs(doc.methods);
+    ret doc;
+}
+
+fn prune_methods(docs: [doc::methoddoc]) -> [doc::methoddoc] {
+    vec::map(docs) {|doc|
+        {
+            args: prune_args(doc.args),
+            return: prune_return(doc.return)
+            with doc
+        }
+    }
+}
+
+fn methods_have_docs(docs: [doc::methoddoc]) -> bool {
+    vec::foldl(false, docs) {|accum, doc|
         accum
             || doc.brief != none
             || doc.desc != none
             || vec::is_not_empty(doc.args)
             || doc.return.desc != none
             || doc.failure != none
-    };
-    fold.ctxt.have_docs =
-        doc.brief != none
-        || doc.desc != none
-        || methods_have_docs;
-    ret doc;
+    }
 }
 
 #[test]
 fn should_elide_undocumented_ifaces() {
-    let source = "iface i { fn a(); }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("iface i { fn a(); }");
     assert vec::is_empty(doc.topmod.ifaces());
 }
 
 #[test]
 fn should_not_elide_documented_ifaces() {
-    let source = "#[doc = \"hey\"] iface i { fn a(); }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("#[doc = \"hey\"] iface i { fn a(); }");
     assert vec::is_not_empty(doc.topmod.ifaces());
 }
 
 #[test]
 fn should_not_elide_ifaces_with_documented_methods() {
-    let source = "iface i { #[doc = \"hey\"] fn a(); }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("iface i { #[doc = \"hey\"] fn a(); }");
     assert vec::is_not_empty(doc.topmod.ifaces());
 }
 
 #[test]
-fn should_not_elide_undocumented_methods() {
-    let source = "#[doc = \"hey\"] iface i { fn a(); }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = run(srv, doc);
+fn should_not_elide_undocumented_iface_methods() {
+    let doc = test::mk_doc("#[doc = \"hey\"] iface i { fn a(); }");
     assert vec::is_not_empty(doc.topmod.ifaces()[0].methods);
 }
 
 #[test]
-fn should_elide_undocumented_method_args() {
-    let source = "#[doc = \"hey\"] iface i { fn a(); }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = run(srv, doc);
+fn should_elide_undocumented_iface_method_args() {
+    let doc = test::mk_doc("#[doc = \"hey\"] iface i { fn a(); }");
     assert vec::is_empty(doc.topmod.ifaces()[0].methods[0].args);
 }
 
 #[test]
-fn should_elide_undocumented_method_return_values() {
-    let source = "#[doc = \"hey\"] iface i { fn a() -> int; }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = run(srv, doc);
+fn should_elide_undocumented_iface_method_return_values() {
+    let doc = test::mk_doc("#[doc = \"hey\"] iface i { fn a() -> int; }");
     assert doc.topmod.ifaces()[0].methods[0].return.ty == none;
-}
\ No newline at end of file
+}
+
+fn fold_impl(
+    fold: fold::fold<ctxt>,
+    doc: doc::impldoc
+) -> doc::impldoc {
+    let doc = fold::default_seq_fold_impl(fold, doc);
+    let doc = {
+        methods: prune_methods(doc.methods)
+        with doc
+    };
+    fold.ctxt.have_docs =
+        doc.brief != none
+        || doc.desc != none
+        || methods_have_docs(doc.methods);
+    ret doc;
+}
+
+#[test]
+fn should_elide_undocumented_impls() {
+    let doc = test::mk_doc("impl i for int { fn a() { } }");
+    assert vec::is_empty(doc.topmod.impls());
+}
+
+#[test]
+fn should_not_elide_documented_impls() {
+    let doc = test::mk_doc("#[doc = \"hey\"] impl i for int { fn a() { } }");
+    assert vec::is_not_empty(doc.topmod.impls());
+}
+
+#[test]
+fn should_not_elide_impls_with_documented_methods() {
+    let doc = test::mk_doc("impl i for int { #[doc = \"hey\"] fn a() { } }");
+    assert vec::is_not_empty(doc.topmod.impls());
+}
+
+#[test]
+fn should_not_elide_undocumented_impl_methods() {
+    let doc = test::mk_doc("#[doc = \"hey\"] impl i for int { fn a() { } }");
+    assert vec::is_not_empty(doc.topmod.impls()[0].methods);
+}
+
+#[test]
+fn should_elide_undocumented_impl_method_args() {
+    let doc = test::mk_doc(
+        "#[doc = \"hey\"] impl i for int { fn a(b: bool) { } }");
+    assert vec::is_empty(doc.topmod.impls()[0].methods[0].args);
+}
+
+#[test]
+fn should_elide_undocumented_impl_method_return_values() {
+    let doc = test::mk_doc(
+        "#[doc = \"hey\"] impl i for int { fn a() -> int { } }");
+    assert doc.topmod.impls()[0].methods[0].return.ty == none;
+}
+
+#[cfg(test)]
+mod test {
+    fn mk_doc(source: str) -> doc::cratedoc {
+        let srv = astsrv::mk_srv_from_str(source);
+        let doc = extract::from_srv(srv, "");
+        let doc = attr_pass::mk_pass()(srv, doc);
+        run(srv, doc)
+    }
+}
diff --git a/src/rustdoc/prune_unexported_pass.rs b/src/rustdoc/prune_unexported_pass.rs
index 554124f6348..94e7332e2cc 100644
--- a/src/rustdoc/prune_unexported_pass.rs
+++ b/src/rustdoc/prune_unexported_pass.rs
@@ -132,119 +132,99 @@ fn is_exported_from_crate(
 
 #[test]
 fn should_prune_unexported_fns() {
-    let source = "mod b { export a; fn a() { } fn b() { } }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("mod b { export a; fn a() { } fn b() { } }");
     assert vec::len(doc.topmod.mods()[0].fns()) == 1u;
 }
 
 #[test]
 fn should_prune_unexported_fns_from_top_mod() {
-    let source = "export a; fn a() { } fn b() { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("export a; fn a() { } fn b() { }");
     assert vec::len(doc.topmod.fns()) == 1u;
 }
 
 #[test]
 fn should_prune_unexported_modules() {
-    let source = "mod a { export a; mod a { } mod b { } }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("mod a { export a; mod a { } mod b { } }");
     assert vec::len(doc.topmod.mods()[0].mods()) == 1u;
 }
 
 #[test]
 fn should_prune_unexported_modules_from_top_mod() {
-    let source = "export a; mod a { } mod b { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("export a; mod a { } mod b { }");
     assert vec::len(doc.topmod.mods()) == 1u;
 }
 
 #[test]
 fn should_prune_unexported_consts() {
-    let source = "mod a { export a; \
-                  const a: bool = true; \
-                  const b: bool = true; }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc(
+        "mod a { export a; \
+         const a: bool = true; \
+         const b: bool = true; }");
     assert vec::len(doc.topmod.mods()[0].consts()) == 1u;
 }
 
 #[test]
 fn should_prune_unexported_consts_from_top_mod() {
-    let source = "export a; const a: bool = true; const b: bool = true;";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc(
+        "export a; const a: bool = true; const b: bool = true;");
     assert vec::len(doc.topmod.consts()) == 1u;
 }
 
 #[test]
 fn should_prune_unexported_enums_from_top_mod() {
-    let source = "export a; mod a { } enum b { c }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("export a; mod a { } enum b { c }");
     assert vec::len(doc.topmod.enums()) == 0u;
 }
 
 #[test]
 fn should_prune_unexported_enums() {
-    let source = "mod a { export a; mod a { } enum b { c } }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("mod a { export a; mod a { } enum b { c } }");
     assert vec::len(doc.topmod.mods()[0].enums()) == 0u;
 }
 
 #[test]
 fn should_prune_unexported_variants_from_top_mod() {
-    let source = "export b::{}; enum b { c }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("export b::{}; enum b { c }");
     assert vec::len(doc.topmod.enums()[0].variants) == 0u;
 }
 
 #[test]
 fn should_prune_unexported_variants() {
-    let source = "mod a { export b::{}; enum b { c } }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("mod a { export b::{}; enum b { c } }");
     assert vec::len(doc.topmod.mods()[0].enums()[0].variants) == 0u;
 }
 
 #[test]
 fn should_prune_unexported_resources_from_top_mod() {
-    let source = "export a; mod a { } resource r(a: bool) { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("export a; mod a { } resource r(a: bool) { }");
     assert vec::is_empty(doc.topmod.resources());
 }
 
 #[test]
 fn should_prune_unexported_resources() {
-    let source = "mod a { export a; mod a { } resource r(a: bool) { } }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc(
+        "mod a { export a; mod a { } resource r(a: bool) { } }");
     assert vec::is_empty(doc.topmod.mods()[0].resources());
 }
 
 #[test]
 fn should_prune_unexported_ifaces_from_top_mod() {
-    let source = "export a; mod a { } iface b { fn c(); }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("export a; mod a { } iface b { fn c(); }");
     assert vec::is_empty(doc.topmod.ifaces());
 }
+
+#[test]
+fn should_prune_unexported_impls_from_top_mod() {
+    let doc = test::mk_doc(
+        "export a; mod a { } impl b for int { fn c() { } }");
+    assert vec::is_empty(doc.topmod.impls())
+}
+
+#[cfg(test)]
+mod test {
+    fn mk_doc(source: str) -> doc::cratedoc {
+        let srv = astsrv::mk_srv_from_str(source);
+        let doc = extract::from_srv(srv, "");
+        run(srv, doc)
+    }
+}
\ No newline at end of file
diff --git a/src/rustdoc/trim_pass.rs b/src/rustdoc/trim_pass.rs
index f315be73c59..bec08afdbd2 100644
--- a/src/rustdoc/trim_pass.rs
+++ b/src/rustdoc/trim_pass.rs
@@ -15,69 +15,55 @@ fn mk_pass() -> pass {
 
 #[test]
 fn should_trim_mod() {
-    let source = "#[doc(brief = \"\nbrief\n\", \
-                  desc = \"\ndesc\n\")] \
-                  mod m { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = mk_pass()(srv, doc);
+    let doc = test::mk_doc("#[doc(brief = \"\nbrief\n\", \
+                            desc = \"\ndesc\n\")] \
+                            mod m { }");
     assert doc.topmod.mods()[0].brief == some("brief");
     assert doc.topmod.mods()[0].desc == some("desc");
 }
 
 #[test]
 fn should_trim_const() {
-    let source = "#[doc(brief = \"\nbrief\n\", \
-                  desc = \"\ndesc\n\")] \
-                  const a: bool = true;";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = mk_pass()(srv, doc);
+    let doc = test::mk_doc("#[doc(brief = \"\nbrief\n\", \
+                            desc = \"\ndesc\n\")] \
+                            const a: bool = true;");
     assert doc.topmod.consts()[0].brief == some("brief");
     assert doc.topmod.consts()[0].desc == some("desc");
 }
 
 #[test]
 fn should_trim_fn() {
-    let source = "#[doc(brief = \"\nbrief\n\", \
-                  desc = \"\ndesc\n\")] \
-                  fn a() { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = mk_pass()(srv, doc);
+    let doc = test::mk_doc("#[doc(brief = \"\nbrief\n\", \
+                            desc = \"\ndesc\n\")] \
+                            fn a() { }");
     assert doc.topmod.fns()[0].brief == some("brief");
     assert doc.topmod.fns()[0].desc == some("desc");
 }
 
 #[test]
 fn should_trim_args() {
-    let source = "#[doc(args(a = \"\na\n\"))] fn a(a: int) { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = mk_pass()(srv, doc);
+    let doc = test::mk_doc("#[doc(args(a = \"\na\n\"))] fn a(a: int) { }");
     assert doc.topmod.fns()[0].args[0].desc == some("a");
 }
 
 #[test]
 fn should_trim_ret() {
-    let source = "#[doc(return = \"\na\n\")] fn a() -> int { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = mk_pass()(srv, doc);
+    let doc = test::mk_doc("#[doc(return = \"\na\n\")] fn a() -> int { }");
     assert doc.topmod.fns()[0].return.desc == some("a");
 }
 
 #[test]
 fn should_trim_failure_conditions() {
-    let source = "#[doc(failure = \"\na\n\")] fn a() -> int { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = attr_pass::mk_pass()(srv, doc);
-    let doc = mk_pass()(srv, doc);
+    let doc = test::mk_doc("#[doc(failure = \"\na\n\")] fn a() -> int { }");
     assert doc.topmod.fns()[0].failure == some("a");
 }
+
+#[cfg(test)]
+mod test {
+    fn mk_doc(source: str) -> doc::cratedoc {
+        let srv = astsrv::mk_srv_from_str(source);
+        let doc = extract::from_srv(srv, "");
+        let doc = attr_pass::mk_pass()(srv, doc);
+        mk_pass()(srv, doc)
+    }
+}
\ No newline at end of file
diff --git a/src/rustdoc/tystr_pass.rs b/src/rustdoc/tystr_pass.rs
index ecf3049b156..604e411dde0 100644
--- a/src/rustdoc/tystr_pass.rs
+++ b/src/rustdoc/tystr_pass.rs
@@ -20,7 +20,8 @@ fn run(
         fold_const: fold_const,
         fold_enum: fold_enum,
         fold_res: fold_res,
-        fold_iface: fold_iface
+        fold_iface: fold_iface,
+        fold_impl: fold_impl
         with *fold::default_seq_fold(srv)
     });
     fold.fold_crate(fold, doc)
@@ -59,10 +60,7 @@ fn get_fn_sig(srv: astsrv::srv, fn_id: doc::ast_id) -> option<str> {
 
 #[test]
 fn should_add_fn_sig() {
-    let source = "fn a() -> int { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("fn a() -> int { }");
     assert doc.topmod.fns()[0].sig == some("fn a() -> int");
 }
 
@@ -106,19 +104,13 @@ fn ret_ty_to_str(decl: ast::fn_decl) -> option<str> {
 
 #[test]
 fn should_add_fn_ret_types() {
-    let source = "fn a() -> int { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("fn a() -> int { }");
     assert doc.topmod.fns()[0].return.ty == some("int");
 }
 
 #[test]
 fn should_not_add_nil_ret_type() {
-    let source = "fn a() { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("fn a() { }");
     assert doc.topmod.fns()[0].return.ty == none;
 }
 
@@ -164,10 +156,7 @@ fn decl_arg_tys(decl: ast::fn_decl) -> [(str, str)] {
 
 #[test]
 fn should_add_arg_types() {
-    let source = "fn a(b: int, c: bool) { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("fn a(b: int, c: bool) { }");
     let fn_ = doc.topmod.fns()[0];
     assert fn_.args[0].ty == some("int");
     assert fn_.args[1].ty == some("bool");
@@ -198,10 +187,7 @@ fn fold_const(
 
 #[test]
 fn should_add_const_types() {
-    let source = "const a: bool = true;";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("const a: bool = true;");
     assert doc.topmod.consts()[0].ty == some("bool");
 }
 
@@ -240,10 +226,7 @@ fn fold_enum(
 
 #[test]
 fn should_add_variant_sigs() {
-    let source = "enum a { b(int) }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("enum a { b(int) }");
     assert doc.topmod.enums()[0].variants[0].sig == some("b(int)");
 }
 
@@ -271,19 +254,13 @@ fn fold_res(
 
 #[test]
 fn should_add_resource_sigs() {
-    let source = "resource r(b: bool) { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("resource r(b: bool) { }");
     assert doc.topmod.resources()[0].sig == some("resource r(b: bool)");
 }
 
 #[test]
 fn should_add_resource_arg_tys() {
-    let source = "resource r(a: bool) { }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("resource r(a: bool) { }");
     assert doc.topmod.resources()[0].args[0].ty == some("bool");
 }
 
@@ -291,30 +268,35 @@ fn fold_iface(
     fold: fold::fold<astsrv::srv>,
     doc: doc::ifacedoc
 ) -> doc::ifacedoc {
-
-    let srv = fold.ctxt;
-
     {
-        methods: vec::map(doc.methods) {|methoddoc|
-            {
-                args: merge_method_arg_tys(
-                    srv,
-                    doc.id,
-                    methoddoc.args,
-                    methoddoc.name),
-                return: merge_method_ret_ty(
-                    srv,
-                    doc.id,
-                    methoddoc.return,
-                    methoddoc.name),
-                sig: get_method_sig(srv, doc.id, methoddoc.name)
-                with methoddoc
-            }
-        }
+        methods: merge_methods(fold.ctxt, doc.id, doc.methods)
         with doc
     }
 }
 
+fn merge_methods(
+    srv: astsrv::srv,
+    item_id: doc::ast_id,
+    docs: [doc::methoddoc]
+) -> [doc::methoddoc] {
+    vec::map(docs) {|doc|
+        {
+            args: merge_method_arg_tys(
+                srv,
+                item_id,
+                doc.args,
+                doc.name),
+            return: merge_method_ret_ty(
+                srv,
+                item_id,
+                doc.return,
+                doc.name),
+            sig: get_method_sig(srv, item_id, doc.name)
+            with doc
+        }
+    }
+}
+
 fn merge_method_ret_ty(
     srv: astsrv::srv,
     item_id: doc::ast_id,
@@ -351,7 +333,19 @@ fn get_method_ret_ty(
                 _ { fail "get_method_ret_ty: undocumented invariant"; }
             }
           }
-          _ { fail "get_method_ret_ty: undocumented invariant"; }
+          ast_map::node_item(@{
+            node: ast::item_impl(_, _, _, methods), _
+          }) {
+            alt vec::find(methods) {|method|
+                method.ident == method_name
+            } {
+                some(method) {
+                    ret_ty_to_str(method.decl)
+                }
+                _ { fail "get_method_ret_ty: undocumented invariant"; }
+            }
+          }
+          _ { fail }
         }
     }
 }
@@ -372,10 +366,22 @@ fn get_method_sig(
                 some(method) {
                     some(pprust::fun_to_str(method.decl, method.ident, []))
                 }
-                _ { fail "get_method_ret_sig: undocumented invariant"; }
+                _ { fail "get_method_sig: undocumented invariant"; }
             }
           }
-          _ { fail "get_method_ret_sig: undocumented invariant"; }
+          ast_map::node_item(@{
+            node: ast::item_impl(_, _, _, methods), _
+          }) {
+            alt vec::find(methods) {|method|
+                method.ident == method_name
+            } {
+                some(method) {
+                    some(pprust::fun_to_str(method.decl, method.ident, []))
+                }
+                _ { fail "get_method_sig: undocumented invariant"; }
+            }
+          }
+          _ { fail "get_method_sig: undocumented invariant"; }
         }
     }
 }
@@ -412,48 +418,130 @@ fn get_method_arg_tys(
                 some(method) {
                     decl_arg_tys(method.decl)
                 }
-                _ { fail "get_method_arg_tys: undocumented invariant"; }
+                _ { fail "get_method_arg_tys: expected method"; }
             }
           }
-          _ { fail "get_method_arg_tys: undocumented invariant"; }
+          ast_map::node_item(@{
+            node: ast::item_impl(_, _, _, methods), _
+          }) {
+            alt vec::find(methods) {|method|
+                method.ident == method_name
+            } {
+                some(method) {
+                    decl_arg_tys(method.decl)
+                }
+                _ { fail "get_method_arg_tys: expected method"; }
+            }
+          }
+          _ { fail }
         }
     }
 }
 
 #[test]
 fn should_add_iface_method_sigs() {
-    let source = "iface i { fn a() -> int; }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("iface i { fn a() -> int; }");
     assert doc.topmod.ifaces()[0].methods[0].sig == some("fn a() -> int");
 }
 
 #[test]
 fn should_add_iface_method_ret_types() {
-    let source = "iface i { fn a() -> int; }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("iface i { fn a() -> int; }");
     assert doc.topmod.ifaces()[0].methods[0].return.ty == some("int");
 }
 
 #[test]
 fn should_not_add_iface_method_nil_ret_type() {
-    let source = "iface i { fn a(); }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("iface i { fn a(); }");
     assert doc.topmod.ifaces()[0].methods[0].return.ty == none;
 }
 
 #[test]
 fn should_add_iface_method_arg_types() {
-    let source = "iface i { fn a(b: int, c: bool); }";
-    let srv = astsrv::mk_srv_from_str(source);
-    let doc = extract::from_srv(srv, "");
-    let doc = run(srv, doc);
+    let doc = test::mk_doc("iface i { fn a(b: int, c: bool); }");
     let fn_ = doc.topmod.ifaces()[0].methods[0];
     assert fn_.args[0].ty == some("int");
     assert fn_.args[1].ty == some("bool");
 }
+
+fn fold_impl(
+    fold: fold::fold<astsrv::srv>,
+    doc: doc::impldoc
+) -> doc::impldoc {
+
+    let srv = fold.ctxt;
+
+    let (iface_ty, self_ty) = astsrv::exec(srv) {|ctxt|
+        alt ctxt.ast_map.get(doc.id) {
+          ast_map::node_item(@{
+            node: ast::item_impl(_, iface_ty, self_ty, _), _
+          }) {
+            let iface_ty = option::map(iface_ty) {|iface_ty|
+                pprust::ty_to_str(iface_ty)
+            };
+            (iface_ty, some(pprust::ty_to_str(self_ty)))
+          }
+          _ { fail "expected impl" }
+        }
+    };
+
+    {
+        iface_ty: iface_ty,
+        self_ty: self_ty,
+        methods: merge_methods(fold.ctxt, doc.id, doc.methods)
+        with doc
+    }
+}
+
+#[test]
+fn should_add_impl_iface_ty() {
+    let doc = test::mk_doc("impl i of j for int { fn a() { } }");
+    assert doc.topmod.impls()[0].iface_ty == some("j");
+}
+
+#[test]
+fn should_not_add_impl_iface_ty_if_none() {
+    let doc = test::mk_doc("impl i for int { fn a() { } }");
+    assert doc.topmod.impls()[0].iface_ty == none;
+}
+
+#[test]
+fn should_add_impl_self_ty() {
+    let doc = test::mk_doc("impl i for int { fn a() { } }");
+    assert doc.topmod.impls()[0].self_ty == some("int");
+}
+
+#[test]
+fn should_add_impl_method_sigs() {
+    let doc = test::mk_doc("impl i for int { fn a() -> int { fail } }");
+    assert doc.topmod.impls()[0].methods[0].sig == some("fn a() -> int");
+}
+
+#[test]
+fn should_add_impl_method_ret_types() {
+    let doc = test::mk_doc("impl i for int { fn a() -> int { fail } }");
+    assert doc.topmod.impls()[0].methods[0].return.ty == some("int");
+}
+
+#[test]
+fn should_not_add_impl_method_nil_ret_type() {
+    let doc = test::mk_doc("impl i for int { fn a() { } }");
+    assert doc.topmod.impls()[0].methods[0].return.ty == none;
+}
+
+#[test]
+fn should_add_impl_method_arg_types() {
+    let doc = test::mk_doc("impl i for int { fn a(b: int, c: bool) { } }");
+    let fn_ = doc.topmod.impls()[0].methods[0];
+    assert fn_.args[0].ty == some("int");
+    assert fn_.args[1].ty == some("bool");
+}
+
+#[cfg(test)]
+mod test {
+    fn mk_doc(source: str) -> doc::cratedoc {
+        let srv = astsrv::mk_srv_from_str(source);
+        let doc = extract::from_srv(srv, "");
+        run(srv, doc)
+    }
+}
\ No newline at end of file