about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorEduard-Mihai Burtescu <edy.burt@gmail.com>2016-11-12 10:38:36 +0200
committerGitHub <noreply@github.com>2016-11-12 10:38:36 +0200
commit2db360eec1685a7bf33cf48ff9b9ab1757dcb358 (patch)
tree9a1a082687486baa9bd72c8f35ebfa651a148dda /src
parentb33873f5411d4ad6a3bd5a4a18dffce8a960a316 (diff)
parent61cc8700dfcecde9e7de132356f3c32eb01b147e (diff)
downloadrust-2db360eec1685a7bf33cf48ff9b9ab1757dcb358.tar.gz
rust-2db360eec1685a7bf33cf48ff9b9ab1757dcb358.zip
Rollup merge of #37190 - QuietMisdreavus:rustdoc-where-newline, r=GuillaumeGomez
rustdoc: add line breaks to where clauses a la rustfmt

Much like my last PR for rustdoc (#36679), this adds line breaks to certain statements based on their line length. Here the focus was on where clauses.

Some examples:
- [Where clause in a trait function](https://shiva.icesoldier.me/custom-std/std/iter/trait.Iterator.html?search=#method.unzip) (also in the trait header block at the top of the page)
- [Where clause on a bare function](https://shiva.icesoldier.me/doc-custom2/petgraph/visit/fn.depth_first_search.html)
- [Where clauses in trait impls on a struct](https://shiva.icesoldier.me/custom-std/std/collections/struct.HashMap.html) (scroll to the bottom) These are regularly not on their own line, but will be given their own line now if their "prefix text" doesn't give them enough room to sensibly print their constraints. HashMap's trait impls provide some examples of both behaviors.

The libstd links above are the whole docs rendered with this, and the "bare function" link above is in another set that pulls some notable crates together. `petgraph` was the one that brought this request up, and that collection also includes [itertools](https://shiva.icesoldier.me/doc-custom2/itertools/trait.Itertools.html) which provided an easy sample to test with.

r? @GuillaumeGomez
Diffstat (limited to 'src')
-rw-r--r--src/librustdoc/html/format.rs84
-rw-r--r--src/librustdoc/html/render.rs93
-rw-r--r--src/librustdoc/html/static/rustdoc.css12
-rw-r--r--src/test/rustdoc/line-breaks.rs19
4 files changed, 156 insertions, 52 deletions
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 625acce27bd..eef530081ab 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -42,7 +42,7 @@ pub struct UnsafetySpace(pub hir::Unsafety);
 #[derive(Copy, Clone)]
 pub struct ConstnessSpace(pub hir::Constness);
 /// Wrapper struct for properly emitting a method declaration.
-pub struct Method<'a>(pub &'a clean::FnDecl, pub &'a str);
+pub struct Method<'a>(pub &'a clean::FnDecl, pub usize);
 /// Similar to VisSpace, but used for mutability
 #[derive(Copy, Clone)]
 pub struct MutableSpace(pub clean::Mutability);
@@ -50,7 +50,7 @@ pub struct MutableSpace(pub clean::Mutability);
 #[derive(Copy, Clone)]
 pub struct RawMutableSpace(pub clean::Mutability);
 /// Wrapper struct for emitting a where clause from Generics.
-pub struct WhereClause<'a>(pub &'a clean::Generics);
+pub struct WhereClause<'a>(pub &'a clean::Generics, pub usize);
 /// Wrapper struct for emitting type parameter bounds.
 pub struct TyParamBounds<'a>(pub &'a [clean::TyParamBound]);
 /// Wrapper struct for emitting a comma-separated list of items
@@ -157,52 +157,71 @@ impl fmt::Display for clean::Generics {
 
 impl<'a> fmt::Display for WhereClause<'a> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let &WhereClause(gens) = self;
+        let &WhereClause(gens, pad) = self;
         if gens.where_predicates.is_empty() {
             return Ok(());
         }
+        let mut clause = String::new();
         if f.alternate() {
-            f.write_str(" ")?;
+            clause.push_str(" where ");
         } else {
-            f.write_str(" <span class='where'>where ")?;
+            clause.push_str(" <span class='where'>where ");
         }
         for (i, pred) in gens.where_predicates.iter().enumerate() {
             if i > 0 {
-                f.write_str(", ")?;
+                if f.alternate() {
+                    clause.push_str(", ");
+                } else {
+                    clause.push_str(",<br>");
+                }
             }
             match pred {
                 &clean::WherePredicate::BoundPredicate { ref ty, ref bounds } => {
                     let bounds = bounds;
                     if f.alternate() {
-                        write!(f, "{:#}: {:#}", ty, TyParamBounds(bounds))?;
+                        clause.push_str(&format!("{:#}: {:#}", ty, TyParamBounds(bounds)));
                     } else {
-                        write!(f, "{}: {}", ty, TyParamBounds(bounds))?;
+                        clause.push_str(&format!("{}: {}", ty, TyParamBounds(bounds)));
                     }
                 }
                 &clean::WherePredicate::RegionPredicate { ref lifetime,
                                                           ref bounds } => {
-                    write!(f, "{}: ", lifetime)?;
+                    clause.push_str(&format!("{}: ", lifetime));
                     for (i, lifetime) in bounds.iter().enumerate() {
                         if i > 0 {
-                            f.write_str(" + ")?;
+                            clause.push_str(" + ");
                         }
 
-                        write!(f, "{}", lifetime)?;
+                        clause.push_str(&format!("{}", lifetime));
                     }
                 }
                 &clean::WherePredicate::EqPredicate { ref lhs, ref rhs } => {
                     if f.alternate() {
-                        write!(f, "{:#} == {:#}", lhs, rhs)?;
+                        clause.push_str(&format!("{:#} == {:#}", lhs, rhs));
                     } else {
-                        write!(f, "{} == {}", lhs, rhs)?;
+                        clause.push_str(&format!("{} == {}", lhs, rhs));
                     }
                 }
             }
         }
         if !f.alternate() {
-            f.write_str("</span>")?;
+            clause.push_str("</span>");
+            let plain = format!("{:#}", self);
+            if plain.len() > 80 {
+                //break it onto its own line regardless, but make sure method impls and trait
+                //blocks keep their fixed padding (2 and 9, respectively)
+                let padding = if pad > 10 {
+                    clause = clause.replace("class='where'", "class='where fmt-newline'");
+                    repeat("&nbsp;").take(8).collect::<String>()
+                } else {
+                    repeat("&nbsp;").take(pad + 6).collect::<String>()
+                };
+                clause = clause.replace("<br>", &format!("<br>{}", padding));
+            } else {
+                clause = clause.replace("<br>", " ");
+            }
         }
-        Ok(())
+        write!(f, "{}", clause)
     }
 }
 
@@ -718,30 +737,43 @@ impl fmt::Display for clean::Type {
 }
 
 fn fmt_impl(i: &clean::Impl, f: &mut fmt::Formatter, link_trait: bool) -> fmt::Result {
+    let mut plain = String::new();
+
     if f.alternate() {
         write!(f, "impl{:#} ", i.generics)?;
     } else {
         write!(f, "impl{} ", i.generics)?;
     }
+    plain.push_str(&format!("impl{:#} ", i.generics));
+
     if let Some(ref ty) = i.trait_ {
-        write!(f, "{}",
-               if i.polarity == Some(clean::ImplPolarity::Negative) { "!" } else { "" })?;
+        if i.polarity == Some(clean::ImplPolarity::Negative) {
+            write!(f, "!")?;
+            plain.push_str("!");
+        }
+
         if link_trait {
             fmt::Display::fmt(ty, f)?;
+            plain.push_str(&format!("{:#}", ty));
         } else {
             match *ty {
                 clean::ResolvedPath{ typarams: None, ref path, is_generic: false, .. } => {
                     let last = path.segments.last().unwrap();
                     fmt::Display::fmt(&last.name, f)?;
                     fmt::Display::fmt(&last.params, f)?;
+                    plain.push_str(&format!("{:#}{:#}", last.name, last.params));
                 }
                 _ => unreachable!(),
             }
         }
         write!(f, " for ")?;
+        plain.push_str(" for ");
     }
+
     fmt::Display::fmt(&i.for_, f)?;
-    fmt::Display::fmt(&WhereClause(&i.generics), f)?;
+    plain.push_str(&format!("{:#}", i.for_));
+
+    fmt::Display::fmt(&WhereClause(&i.generics, plain.len() + 1), f)?;
     Ok(())
 }
 
@@ -870,24 +902,30 @@ impl<'a> fmt::Display for Method<'a> {
 
         let mut output: String;
         let plain: String;
+        let pad = repeat(" ").take(indent).collect::<String>();
         if arrow.is_empty() {
             output = format!("({})", args);
-            plain = format!("{}({})", indent.replace("&nbsp;", " "), args_plain);
+            plain = format!("{}({})", pad, args_plain);
         } else {
             output = format!("({args})<br>{arrow}", args = args, arrow = arrow);
-            plain = format!("{indent}({args}){arrow}",
-                            indent = indent.replace("&nbsp;", " "),
+            plain = format!("{pad}({args}){arrow}",
+                            pad = pad,
                             args = args_plain,
                             arrow = arrow_plain);
         }
 
         if plain.len() > 80 {
-            let pad = format!("<br>{}", indent);
+            let pad = repeat("&nbsp;").take(indent).collect::<String>();
+            let pad = format!("<br>{}", pad);
             output = output.replace("<br>", &pad);
         } else {
             output = output.replace("<br>", "");
         }
-        write!(f, "{}", output)
+        if f.alternate() {
+            write!(f, "{}", output.replace("<br>", "\n"))
+        } else {
+            write!(f, "{}", output)
+        }
     }
 }
 
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 2db771d7711..93827a01038 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -1972,14 +1972,13 @@ fn item_function(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
         UnstableFeatures::Allow => f.constness,
         _ => hir::Constness::NotConst
     };
-    let prefix = format!("{}{}{}{:#}fn {}{:#}",
+    let indent = format!("{}{}{}{:#}fn {}{:#}",
                          VisSpace(&it.visibility),
                          ConstnessSpace(vis_constness),
                          UnsafetySpace(f.unsafety),
                          AbiSpace(f.abi),
                          it.name.as_ref().unwrap(),
-                         f.generics);
-    let indent = repeat("&nbsp;").take(prefix.len()).collect::<String>();
+                         f.generics).len();
     write!(w, "<pre class='rust fn'>{vis}{constness}{unsafety}{abi}fn \
                {name}{generics}{decl}{where_clause}</pre>",
            vis = VisSpace(&it.visibility),
@@ -1988,22 +1987,29 @@ fn item_function(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
            abi = AbiSpace(f.abi),
            name = it.name.as_ref().unwrap(),
            generics = f.generics,
-           where_clause = WhereClause(&f.generics),
-           decl = Method(&f.decl, &indent))?;
+           where_clause = WhereClause(&f.generics, 2),
+           decl = Method(&f.decl, indent))?;
     document(w, cx, it)
 }
 
 fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
               t: &clean::Trait) -> fmt::Result {
     let mut bounds = String::new();
+    let mut bounds_plain = String::new();
     if !t.bounds.is_empty() {
         if !bounds.is_empty() {
             bounds.push(' ');
+            bounds_plain.push(' ');
         }
         bounds.push_str(": ");
+        bounds_plain.push_str(": ");
         for (i, p) in t.bounds.iter().enumerate() {
-            if i > 0 { bounds.push_str(" + "); }
+            if i > 0 {
+                bounds.push_str(" + ");
+                bounds_plain.push_str(" + ");
+            }
             bounds.push_str(&format!("{}", *p));
+            bounds_plain.push_str(&format!("{:#}", *p));
         }
     }
 
@@ -2014,7 +2020,8 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
            it.name.as_ref().unwrap(),
            t.generics,
            bounds,
-           WhereClause(&t.generics))?;
+           // Where clauses in traits are indented nine spaces, per rustdoc.css
+           WhereClause(&t.generics, 9))?;
 
     let types = t.items.iter().filter(|m| m.is_associated_type()).collect::<Vec<_>>();
     let consts = t.items.iter().filter(|m| m.is_associated_const()).collect::<Vec<_>>();
@@ -2028,7 +2035,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
         write!(w, "{{\n")?;
         for t in &types {
             write!(w, "    ")?;
-            render_assoc_item(w, t, AssocItemLink::Anchor(None))?;
+            render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait)?;
             write!(w, ";\n")?;
         }
         if !types.is_empty() && !consts.is_empty() {
@@ -2036,7 +2043,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
         }
         for t in &consts {
             write!(w, "    ")?;
-            render_assoc_item(w, t, AssocItemLink::Anchor(None))?;
+            render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait)?;
             write!(w, ";\n")?;
         }
         if !consts.is_empty() && !required.is_empty() {
@@ -2044,7 +2051,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
         }
         for m in &required {
             write!(w, "    ")?;
-            render_assoc_item(w, m, AssocItemLink::Anchor(None))?;
+            render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait)?;
             write!(w, ";\n")?;
         }
         if !required.is_empty() && !provided.is_empty() {
@@ -2052,7 +2059,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
         }
         for m in &provided {
             write!(w, "    ")?;
-            render_assoc_item(w, m, AssocItemLink::Anchor(None))?;
+            render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait)?;
             write!(w, " {{ ... }}\n")?;
         }
         write!(w, "}}")?;
@@ -2073,7 +2080,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
                id = id,
                stab = m.stability_class(),
                ns_id = ns_id)?;
-        render_assoc_item(w, m, AssocItemLink::Anchor(Some(&id)))?;
+        render_assoc_item(w, m, AssocItemLink::Anchor(Some(&id)), ItemType::Impl)?;
         write!(w, "</code>")?;
         render_stability_since(w, m, t)?;
         write!(w, "</span></h3>")?;
@@ -2227,7 +2234,8 @@ fn render_stability_since(w: &mut fmt::Formatter,
 
 fn render_assoc_item(w: &mut fmt::Formatter,
                      item: &clean::Item,
-                     link: AssocItemLink) -> fmt::Result {
+                     link: AssocItemLink,
+                     parent: ItemType) -> fmt::Result {
     fn method(w: &mut fmt::Formatter,
               meth: &clean::Item,
               unsafety: hir::Unsafety,
@@ -2235,7 +2243,8 @@ fn render_assoc_item(w: &mut fmt::Formatter,
               abi: abi::Abi,
               g: &clean::Generics,
               d: &clean::FnDecl,
-              link: AssocItemLink)
+              link: AssocItemLink,
+              parent: ItemType)
               -> fmt::Result {
         let name = meth.name.as_ref().unwrap();
         let anchor = format!("#{}.{}", meth.type_(), name);
@@ -2265,7 +2274,16 @@ fn render_assoc_item(w: &mut fmt::Formatter,
                              AbiSpace(abi),
                              name,
                              *g);
-        let indent = repeat("&nbsp;").take(prefix.len()).collect::<String>();
+        let mut indent = prefix.len();
+        let where_indent = if parent == ItemType::Trait {
+            indent += 4;
+            8
+        } else if parent == ItemType::Impl {
+            2
+        } else {
+            let prefix = prefix + &format!("{:#}", Method(d, indent));
+            prefix.lines().last().unwrap().len() + 1
+        };
         write!(w, "{}{}{}fn <a href='{href}' class='fnname'>{name}</a>\
                    {generics}{decl}{where_clause}",
                ConstnessSpace(vis_constness),
@@ -2274,19 +2292,18 @@ fn render_assoc_item(w: &mut fmt::Formatter,
                href = href,
                name = name,
                generics = *g,
-               decl = Method(d, &indent),
-               where_clause = WhereClause(g))
+               decl = Method(d, indent),
+               where_clause = WhereClause(g, where_indent))
     }
     match item.inner {
         clean::StrippedItem(..) => Ok(()),
         clean::TyMethodItem(ref m) => {
             method(w, item, m.unsafety, hir::Constness::NotConst,
-                   m.abi, &m.generics, &m.decl, link)
+                   m.abi, &m.generics, &m.decl, link, parent)
         }
         clean::MethodItem(ref m) => {
             method(w, item, m.unsafety, m.constness,
-                   m.abi, &m.generics, &m.decl,
-                   link)
+                   m.abi, &m.generics, &m.decl, link, parent)
         }
         clean::AssociatedConstItem(ref ty, ref default) => {
             assoc_const(w, item, ty, default.as_ref(), link)
@@ -2383,11 +2400,15 @@ fn item_enum(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
              e: &clean::Enum) -> fmt::Result {
     write!(w, "<pre class='rust enum'>")?;
     render_attributes(w, it)?;
+    let padding = format!("{}enum {}{:#} ",
+                          VisSpace(&it.visibility),
+                          it.name.as_ref().unwrap(),
+                          e.generics).len();
     write!(w, "{}enum {}{}{}",
            VisSpace(&it.visibility),
            it.name.as_ref().unwrap(),
            e.generics,
-           WhereClause(&e.generics))?;
+           WhereClause(&e.generics, padding))?;
     if e.variants.is_empty() && !e.variants_stripped {
         write!(w, " {{}}")?;
     } else {
@@ -2559,17 +2580,23 @@ fn render_struct(w: &mut fmt::Formatter, it: &clean::Item,
                  fields: &[clean::Item],
                  tab: &str,
                  structhead: bool) -> fmt::Result {
+    let mut plain = String::new();
     write!(w, "{}{}{}",
            VisSpace(&it.visibility),
            if structhead {"struct "} else {""},
            it.name.as_ref().unwrap())?;
+    plain.push_str(&format!("{}{}{}",
+                            VisSpace(&it.visibility),
+                            if structhead {"struct "} else {""},
+                            it.name.as_ref().unwrap()));
     if let Some(g) = g {
+        plain.push_str(&format!("{:#}", g));
         write!(w, "{}", g)?
     }
     match ty {
         doctree::Plain => {
             if let Some(g) = g {
-                write!(w, "{}", WhereClause(g))?
+                write!(w, "{}", WhereClause(g, plain.len() + 1))?
             }
             let mut has_visible_fields = false;
             write!(w, " {{")?;
@@ -2598,30 +2625,35 @@ fn render_struct(w: &mut fmt::Formatter, it: &clean::Item,
         }
         doctree::Tuple => {
             write!(w, "(")?;
+            plain.push_str("(");
             for (i, field) in fields.iter().enumerate() {
                 if i > 0 {
                     write!(w, ", ")?;
+                    plain.push_str(", ");
                 }
                 match field.inner {
                     clean::StrippedItem(box clean::StructFieldItem(..)) => {
+                        plain.push_str("_");
                         write!(w, "_")?
                     }
                     clean::StructFieldItem(ref ty) => {
+                        plain.push_str(&format!("{}{:#}", VisSpace(&field.visibility), *ty));
                         write!(w, "{}{}", VisSpace(&field.visibility), *ty)?
                     }
                     _ => unreachable!()
                 }
             }
             write!(w, ")")?;
+            plain.push_str(")");
             if let Some(g) = g {
-                write!(w, "{}", WhereClause(g))?
+                write!(w, "{}", WhereClause(g, plain.len() + 1))?
             }
             write!(w, ";")?;
         }
         doctree::Unit => {
             // Needed for PhantomData.
             if let Some(g) = g {
-                write!(w, "{}", WhereClause(g))?
+                write!(w, "{}", WhereClause(g, plain.len() + 1))?
             }
             write!(w, ";")?;
         }
@@ -2634,13 +2666,19 @@ fn render_union(w: &mut fmt::Formatter, it: &clean::Item,
                 fields: &[clean::Item],
                 tab: &str,
                 structhead: bool) -> fmt::Result {
+    let mut plain = String::new();
     write!(w, "{}{}{}",
            VisSpace(&it.visibility),
            if structhead {"union "} else {""},
            it.name.as_ref().unwrap())?;
+    plain.push_str(&format!("{}{}{}",
+                            VisSpace(&it.visibility),
+                            if structhead {"union "} else {""},
+                            it.name.as_ref().unwrap()));
     if let Some(g) = g {
         write!(w, "{}", g)?;
-        write!(w, "{}", WhereClause(g))?;
+        plain.push_str(&format!("{:#}", g));
+        write!(w, "{}", WhereClause(g, plain.len() + 1))?;
     }
 
     write!(w, " {{\n{}", tab)?;
@@ -2831,7 +2869,7 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi
                     write!(w, "<h4 id='{}' class='{}'>", id, item_type)?;
                     write!(w, "<span id='{}' class='invisible'>", ns_id)?;
                     write!(w, "<code>")?;
-                    render_assoc_item(w, item, link.anchor(&id))?;
+                    render_assoc_item(w, item, link.anchor(&id), ItemType::Impl)?;
                     write!(w, "</code>")?;
                     render_stability_since_raw(w, item.stable_since(), outer_version)?;
                     write!(w, "</span></h4>\n")?;
@@ -2941,10 +2979,11 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi
 
 fn item_typedef(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
                 t: &clean::Typedef) -> fmt::Result {
+    let indent = format!("type {}{:#} ", it.name.as_ref().unwrap(), t.generics).len();
     write!(w, "<pre class='rust typedef'>type {}{}{where_clause} = {type_};</pre>",
            it.name.as_ref().unwrap(),
            t.generics,
-           where_clause = WhereClause(&t.generics),
+           where_clause = WhereClause(&t.generics, indent),
            type_ = t.type_)?;
 
     document(w, cx, it)
diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css
index 46b34b5a638..917b5f4fadc 100644
--- a/src/librustdoc/html/static/rustdoc.css
+++ b/src/librustdoc/html/static/rustdoc.css
@@ -361,9 +361,17 @@ h4 > code, h3 > code, .invisible > code {
 	position: relative;
 }
 /* Shift "where ..." part of method or fn definition down a line */
-.content .method .where, .content .fn .where { display: block; }
+.content .method .where,
+.content .fn .where,
+.content .where.fmt-newline {
+    display: block;
+}
 /* Bit of whitespace to indent it */
-.content .method .where::before, .content .fn .where::before { content: '  '; }
+.content .method .where::before,
+.content .fn .where::before,
+.content .where.fmt-newline::before {
+    content: '  ';
+}
 
 .content .methods > div { margin-left: 40px; }
 
diff --git a/src/test/rustdoc/line-breaks.rs b/src/test/rustdoc/line-breaks.rs
index cc608a24475..a1eabb515a5 100644
--- a/src/test/rustdoc/line-breaks.rs
+++ b/src/test/rustdoc/line-breaks.rs
@@ -10,6 +10,9 @@
 
 #![crate_name = "foo"]
 
+use std::ops::Add;
+use std::fmt::Display;
+
 //@count foo/fn.function_with_a_really_long_name.html //pre/br 2
 pub fn function_with_a_really_long_name(parameter_one: i32,
                                         parameter_two: i32)
@@ -19,3 +22,19 @@ pub fn function_with_a_really_long_name(parameter_one: i32,
 
 //@count foo/fn.short_name.html //pre/br 0
 pub fn short_name(param: i32) -> i32 { param + 1 }
+
+//@count foo/fn.where_clause.html //pre/br 4
+pub fn where_clause<T, U>(param_one: T,
+                          param_two: U)
+    where T: Add<U> + Display + Copy,
+          U: Add<T> + Display + Copy,
+          T::Output: Display + Add<U::Output> + Copy,
+          <T::Output as Add<U::Output>>::Output: Display,
+          U::Output: Display + Copy
+{
+    let x = param_one + param_two;
+    println!("{} + {} = {}", param_one, param_two, x);
+    let y = param_two + param_one;
+    println!("{} + {} = {}", param_two, param_one, y);
+    println!("{} + {} = {}", x, y, x + y);
+}