]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #45620 - ollie27:rustdoc_impl_generic_dupe, r=QuietMisdreavus
authorbors <bors@rust-lang.org>
Tue, 7 Nov 2017 07:24:13 +0000 (07:24 +0000)
committerbors <bors@rust-lang.org>
Tue, 7 Nov 2017 07:24:13 +0000 (07:24 +0000)
rustdoc: Fix duplicated impls with generics

The same type can appear multiple times in impls so we need to use a set
to avoid adding it multiple times.

Fixes: #45584
1  2 
src/librustdoc/html/render.rs

index f3a897d2121cb4c2f3345341a2fed5536e6290ac,867ac0d0459208a13eb3917725ab17fe6be72c37..88e1f780d0342520f9b4315758a8649392cf80b1
  //! both occur before the crate is rendered.
  pub use self::ExternalLocation::*;
  
 +#[cfg(stage0)]
  use std::ascii::AsciiExt;
  use std::cell::RefCell;
  use std::cmp::Ordering;
 -use std::collections::BTreeMap;
 +use std::collections::{BTreeMap, HashSet};
  use std::default::Default;
  use std::error;
  use std::fmt::{self, Display, Formatter, Write as FmtWrite};
@@@ -1326,7 -1325,7 +1326,7 @@@ impl DocFolder for Cache 
                  // Figure out the id of this impl. This may map to a
                  // primitive rather than always to a struct/enum.
                  // Note: matching twice to restrict the lifetime of the `i` borrow.
-                 let mut dids = vec![];
+                 let mut dids = FxHashSet();
                  if let clean::Item { inner: clean::ImplItem(ref i), .. } = item {
                      let masked_trait = i.trait_.def_id().map_or(false,
                          |d| self.masked_crates.contains(&d.krate));
                              clean::BorrowedRef {
                                  type_: box clean::ResolvedPath { did, .. }, ..
                              } => {
-                                 dids.push(did);
+                                 dids.insert(did);
                              }
                              ref t => {
                                  let did = t.primitive_type().and_then(|t| {
                                  });
  
                                  if let Some(did) = did {
-                                     dids.push(did);
+                                     dids.insert(did);
                                  }
                              }
                          }
                      if let Some(generics) = i.trait_.as_ref().and_then(|t| t.generics()) {
                          for bound in generics {
                              if let Some(did) = bound.def_id() {
-                                 dids.push(did);
+                                 dids.insert(did);
                              }
                          }
                      }
@@@ -1930,7 -1929,7 +1930,7 @@@ fn item_module(w: &mut fmt::Formatter, 
      document(w, cx, item)?;
  
      let mut indices = (0..items.len()).filter(|i| {
 -        if let clean::DefaultImplItem(..) = items[*i].inner {
 +        if let clean::AutoImplItem(..) = items[*i].inner {
              return false;
          }
          !items[*i].is_stripped()
                  ItemType::Primitive       => ("primitives", "Primitive Types"),
                  ItemType::AssociatedType  => ("associated-types", "Associated Types"),
                  ItemType::AssociatedConst => ("associated-consts", "Associated Constants"),
 +                ItemType::ForeignType     => ("foreign-types", "Foreign Types"),
              };
              write!(w, "<h2 id='{id}' class='section-header'>\
                         <a href=\"#{id}\">{name}</a></h2>\n<table>",
@@@ -3208,37 -3206,12 +3208,37 @@@ fn render_deref_methods(w: &mut fmt::Fo
      }
  }
  
 +fn should_render_item(item: &clean::Item, deref_mut_: bool) -> bool {
 +    let self_type_opt = match item.inner {
 +        clean::MethodItem(ref method) => method.decl.self_type(),
 +        clean::TyMethodItem(ref method) => method.decl.self_type(),
 +        _ => None
 +    };
 +
 +    if let Some(self_ty) = self_type_opt {
 +        let (by_mut_ref, by_box) = match self_ty {
 +            SelfTy::SelfBorrowed(_, mutability) |
 +            SelfTy::SelfExplicit(clean::BorrowedRef { mutability, .. }) => {
 +                (mutability == Mutability::Mutable, false)
 +            },
 +            SelfTy::SelfExplicit(clean::ResolvedPath { did, .. }) => {
 +                (false, Some(did) == cache().owned_box_did)
 +            },
 +            _ => (false, false),
 +        };
 +
 +        (deref_mut_ || !by_mut_ref) && !by_box
 +    } else {
 +        false
 +    }
 +}
 +
  fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLink,
                 render_mode: RenderMode, outer_version: Option<&str>,
                 show_def_docs: bool) -> fmt::Result {
      if render_mode == RenderMode::Normal {
          let id = derive_id(match i.inner_impl().trait_ {
 -            Some(ref t) => format!("impl-{}", Escape(&format!("{:#}", t))),
 +            Some(ref t) => format!("impl-{}", small_url_encode(&format!("{:#}", t))),
              None => "impl".to_string(),
          });
          write!(w, "<h3 id='{}' class='impl'><span class='in-band'><code>{}</code>",
  
          let render_method_item: bool = match render_mode {
              RenderMode::Normal => true,
 -            RenderMode::ForDeref { mut_: deref_mut_ } => {
 -                let self_type_opt = match item.inner {
 -                    clean::MethodItem(ref method) => method.decl.self_type(),
 -                    clean::TyMethodItem(ref method) => method.decl.self_type(),
 -                    _ => None
 -                };
 -
 -                if let Some(self_ty) = self_type_opt {
 -                    let (by_mut_ref, by_box) = match self_ty {
 -                        SelfTy::SelfBorrowed(_, mutability) |
 -                        SelfTy::SelfExplicit(clean::BorrowedRef { mutability, .. }) => {
 -                            (mutability == Mutability::Mutable, false)
 -                        },
 -                        SelfTy::SelfExplicit(clean::ResolvedPath { did, .. }) => {
 -                            (false, Some(did) == cache().owned_box_did)
 -                        },
 -                        _ => (false, false),
 -                    };
 -
 -                    (deref_mut_ || !by_mut_ref) && !by_box
 -                } else {
 -                    false
 -                }
 -            },
 +            RenderMode::ForDeref { mut_: deref_mut_ } => should_render_item(&item, deref_mut_),
          };
  
          match item.inner {
@@@ -3517,48 -3513,12 +3517,48 @@@ impl<'a> fmt::Display for Sidebar<'a> 
      }
  }
  
 +fn get_methods(i: &clean::Impl, for_deref: bool) -> Vec<String> {
 +    i.items.iter().filter_map(|item| {
 +        match item.name {
 +            // Maybe check with clean::Visibility::Public as well?
 +            Some(ref name) if !name.is_empty() && item.visibility.is_some() && item.is_method() => {
 +                if !for_deref || should_render_item(item, false) {
 +                    Some(format!("<a href=\"#method.{name}\">{name}</a>", name = name))
 +                } else {
 +                    None
 +                }
 +            }
 +            _ => None,
 +        }
 +    }).collect::<Vec<_>>()
 +}
 +
 +// The point is to url encode any potential character from a type with genericity.
 +fn small_url_encode(s: &str) -> String {
 +    s.replace("<", "%3C")
 +     .replace(">", "%3E")
 +     .replace(" ", "%20")
 +     .replace("?", "%3F")
 +     .replace("'", "%27")
 +     .replace("&", "%26")
 +     .replace(",", "%2C")
 +     .replace(":", "%3A")
 +     .replace(";", "%3B")
 +     .replace("[", "%5B")
 +     .replace("]", "%5D")
 +}
 +
  fn sidebar_assoc_items(it: &clean::Item) -> String {
      let mut out = String::new();
      let c = cache();
      if let Some(v) = c.impls.get(&it.def_id) {
 -        if v.iter().any(|i| i.inner_impl().trait_.is_none()) {
 -            out.push_str("<li><a href=\"#methods\">Methods</a></li>");
 +        let ret = v.iter()
 +                   .filter(|i| i.inner_impl().trait_.is_none())
 +                   .flat_map(|i| get_methods(i.inner_impl(), false))
 +                   .collect::<String>();
 +        if !ret.is_empty() {
 +            out.push_str(&format!("<a class=\"sidebar-title\" href=\"#methods\">Methods\
 +                                   </a><div class=\"sidebar-links\">{}</div>", ret));
          }
  
          if v.iter().any(|i| i.inner_impl().trait_.is_some()) {
                      let inner_impl = target.def_id().or(target.primitive_type().and_then(|prim| {
                          c.primitive_locations.get(&prim).cloned()
                      })).and_then(|did| c.impls.get(&did));
 -                    if inner_impl.is_some() {
 -                        out.push_str("<li><a href=\"#deref-methods\">");
 +                    if let Some(impls) = inner_impl {
 +                        out.push_str("<a class=\"sidebar-title\" href=\"#deref-methods\">");
                          out.push_str(&format!("Methods from {:#}&lt;Target={:#}&gt;",
 -                                                  impl_.inner_impl().trait_.as_ref().unwrap(),
 -                                                  target));
 -                        out.push_str("</a></li>");
 +                                              impl_.inner_impl().trait_.as_ref().unwrap(),
 +                                              target));
 +                        out.push_str("</a>");
 +                        let ret = impls.iter()
 +                                       .filter(|i| i.inner_impl().trait_.is_none())
 +                                       .flat_map(|i| get_methods(i.inner_impl(), true))
 +                                       .collect::<String>();
 +                        out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", ret));
                      }
                  }
              }
 -            out.push_str("<li><a href=\"#implementations\">Trait Implementations</a></li>");
 +            let mut links = HashSet::new();
 +            let ret = v.iter()
 +                       .filter_map(|i| if let Some(ref i) = i.inner_impl().trait_ {
 +                           let out = format!("{:#}", i).replace("<", "&lt;").replace(">", "&gt;");
 +                           let encoded = small_url_encode(&format!("{:#}", i));
 +                           let generated = format!("<a href=\"#impl-{}\">{}</a>", encoded, out);
 +                           if !links.contains(&generated) && links.insert(generated.clone()) {
 +                               Some(generated)
 +                           } else {
 +                               None
 +                           }
 +                       } else {
 +                           None
 +                       })
 +                       .collect::<String>();
 +            if !ret.is_empty() {
 +                out.push_str("<a class=\"sidebar-title\" href=\"#implementations\">\
 +                              Trait Implementations</a>");
 +                out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", ret));
 +            }
          }
      }
  
@@@ -3628,7 -3564,7 +3628,7 @@@ fn sidebar_struct(fmt: &mut fmt::Format
      sidebar.push_str(&sidebar_assoc_items(it));
  
      if !sidebar.is_empty() {
 -        write!(fmt, "<div class=\"block items\"><ul>{}</ul></div>", sidebar)?;
 +        write!(fmt, "<div class=\"block items\">{}</div>", sidebar)?;
      }
      Ok(())
  }
@@@ -3655,6 -3591,8 +3655,6 @@@ fn sidebar_trait(fmt: &mut fmt::Formatt
          sidebar.push_str("<li><a href=\"#provided-methods\">Provided Methods</a></li>");
      }
  
 -    sidebar.push_str(&sidebar_assoc_items(it));
 -
      let c = cache();
  
      if let Some(implementors) = c.implementors.get(&it.def_id) {
  
      sidebar.push_str("<li><a href=\"#implementors\">Implementors</a></li>");
  
 -    write!(fmt, "<div class=\"block items\"><ul>{}</ul></div>", sidebar)
 +    sidebar.push_str(&sidebar_assoc_items(it));
 +
 +    write!(fmt, "<div class=\"block items\">{}</div>", sidebar)
  }
  
  fn sidebar_primitive(fmt: &mut fmt::Formatter, it: &clean::Item,
      let sidebar = sidebar_assoc_items(it);
  
      if !sidebar.is_empty() {
 -        write!(fmt, "<div class=\"block items\"><ul>{}</ul></div>", sidebar)?;
 +        write!(fmt, "<div class=\"block items\">{}</div>", sidebar)?;
      }
      Ok(())
  }
@@@ -3688,7 -3624,7 +3688,7 @@@ fn sidebar_typedef(fmt: &mut fmt::Forma
      let sidebar = sidebar_assoc_items(it);
  
      if !sidebar.is_empty() {
 -        write!(fmt, "<div class=\"block items\"><ul>{}</ul></div>", sidebar)?;
 +        write!(fmt, "<div class=\"block items\">{}</div>", sidebar)?;
      }
      Ok(())
  }
@@@ -3705,7 -3641,7 +3705,7 @@@ fn sidebar_union(fmt: &mut fmt::Formatt
      sidebar.push_str(&sidebar_assoc_items(it));
  
      if !sidebar.is_empty() {
 -        write!(fmt, "<div class=\"block items\"><ul>{}</ul></div>", sidebar)?;
 +        write!(fmt, "<div class=\"block items\">{}</div>", sidebar)?;
      }
      Ok(())
  }
@@@ -3721,7 -3657,7 +3721,7 @@@ fn sidebar_enum(fmt: &mut fmt::Formatte
      sidebar.push_str(&sidebar_assoc_items(it));
  
      if !sidebar.is_empty() {
 -        write!(fmt, "<div class=\"block items\"><ul>{}</ul></div>", sidebar)?;
 +        write!(fmt, "<div class=\"block items\">{}</div>", sidebar)?;
      }
      Ok(())
  }
@@@ -3743,9 -3679,9 +3743,9 @@@ fn sidebar_module(fmt: &mut fmt::Format
                     ItemType::Enum, ItemType::Constant, ItemType::Static, ItemType::Trait,
                     ItemType::Function, ItemType::Typedef, ItemType::Union, ItemType::Impl,
                     ItemType::TyMethod, ItemType::Method, ItemType::StructField, ItemType::Variant,
 -                   ItemType::AssociatedType, ItemType::AssociatedConst] {
 +                   ItemType::AssociatedType, ItemType::AssociatedConst, ItemType::ForeignType] {
          if items.iter().any(|it| {
 -            if let clean::DefaultImplItem(..) = it.inner {
 +            if let clean::AutoImplItem(..) = it.inner {
                  false
              } else {
                  !it.is_stripped() && it.type_() == myty
                  ItemType::Primitive       => ("primitives", "Primitive Types"),
                  ItemType::AssociatedType  => ("associated-types", "Associated Types"),
                  ItemType::AssociatedConst => ("associated-consts", "Associated Constants"),
 +                ItemType::ForeignType     => ("foreign-types", "Foreign Types"),
              };
              sidebar.push_str(&format!("<li><a href=\"#{id}\">{name}</a></li>",
                                        id = short,