Skip to content

Commit 915bbda

Browse files
committed
rustdoc: Filter more incorrect methods inherited through Deref
Old code filtered out only static methods. This code also excludes &mut self methods if there is no DerefMut implementation
1 parent 987b475 commit 915bbda

File tree

6 files changed

+154
-27
lines changed

6 files changed

+154
-27
lines changed

src/librustdoc/clean/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ impl<'a, 'tcx> Clean<Crate> for visit_ast::RustdocVisitor<'a, 'tcx> {
134134
if let Some(t) = cx.tcx_opt() {
135135
cx.deref_trait_did.set(t.lang_items.deref_trait());
136136
cx.renderinfo.borrow_mut().deref_trait_did = cx.deref_trait_did.get();
137+
cx.deref_mut_trait_did.set(t.lang_items.deref_mut_trait());
138+
cx.renderinfo.borrow_mut().deref_mut_trait_did = cx.deref_mut_trait_did.get();
137139
}
138140

139141
let mut externs = Vec::new();
@@ -1117,6 +1119,10 @@ impl FnDecl {
11171119
pub fn has_self(&self) -> bool {
11181120
return self.inputs.values.len() > 0 && self.inputs.values[0].name == "self";
11191121
}
1122+
1123+
pub fn self_type(&self) -> Option<SelfTy> {
1124+
self.inputs.values.get(0).and_then(|v| v.to_self())
1125+
}
11201126
}
11211127

11221128
#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Debug)]

src/librustdoc/core.rs

+2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ pub struct DocContext<'a, 'tcx: 'a> {
5353
pub input: Input,
5454
pub populated_crate_impls: RefCell<FnvHashSet<ast::CrateNum>>,
5555
pub deref_trait_did: Cell<Option<DefId>>,
56+
pub deref_mut_trait_did: Cell<Option<DefId>>,
5657
// Note that external items for which `doc(hidden)` applies to are shown as
5758
// non-reachable while local items aren't. This is because we're reusing
5859
// the access levels from crateanalysis.
@@ -180,6 +181,7 @@ pub fn run_core(search_paths: SearchPaths,
180181
input: input,
181182
populated_crate_impls: RefCell::new(FnvHashSet()),
182183
deref_trait_did: Cell::new(None),
184+
deref_mut_trait_did: Cell::new(None),
183185
access_levels: RefCell::new(access_levels),
184186
external_traits: RefCell::new(FnvHashMap()),
185187
renderinfo: RefCell::new(Default::default()),

src/librustdoc/html/render.rs

+60-27
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ use rustc::hir;
6464
use rustc::util::nodemap::{FnvHashMap, FnvHashSet};
6565
use rustc_data_structures::flock;
6666

67-
use clean::{self, Attributes, GetDefId};
67+
use clean::{self, Attributes, GetDefId, SelfTy, Mutability};
6868
use doctree;
6969
use fold::DocFolder;
7070
use html::escape::Escape;
@@ -266,6 +266,7 @@ pub struct Cache {
266266
seen_mod: bool,
267267
stripped_mod: bool,
268268
deref_trait_did: Option<DefId>,
269+
deref_mut_trait_did: Option<DefId>,
269270

270271
// In rare case where a structure is defined in one module but implemented
271272
// in another, if the implementing module is parsed before defining module,
@@ -283,6 +284,7 @@ pub struct RenderInfo {
283284
pub external_paths: ::core::ExternalPaths,
284285
pub external_typarams: FnvHashMap<DefId, String>,
285286
pub deref_trait_did: Option<DefId>,
287+
pub deref_mut_trait_did: Option<DefId>,
286288
}
287289

288290
/// Helper struct to render all source code to HTML pages
@@ -508,6 +510,7 @@ pub fn run(mut krate: clean::Crate,
508510
external_paths,
509511
external_typarams,
510512
deref_trait_did,
513+
deref_mut_trait_did,
511514
} = renderinfo;
512515

513516
let external_paths = external_paths.into_iter()
@@ -532,6 +535,7 @@ pub fn run(mut krate: clean::Crate,
532535
orphan_methods: Vec::new(),
533536
traits: mem::replace(&mut krate.external_traits, FnvHashMap()),
534537
deref_trait_did: deref_trait_did,
538+
deref_mut_trait_did: deref_mut_trait_did,
535539
typarams: external_typarams,
536540
};
537541

@@ -2604,7 +2608,13 @@ impl<'a> AssocItemLink<'a> {
26042608

26052609
enum AssocItemRender<'a> {
26062610
All,
2607-
DerefFor { trait_: &'a clean::Type, type_: &'a clean::Type },
2611+
DerefFor { trait_: &'a clean::Type, type_: &'a clean::Type, deref_mut_: bool }
2612+
}
2613+
2614+
#[derive(Copy, Clone, PartialEq)]
2615+
enum RenderMode {
2616+
Normal,
2617+
ForDeref { mut_: bool },
26082618
}
26092619

26102620
fn render_assoc_items(w: &mut fmt::Formatter,
@@ -2621,19 +2631,19 @@ fn render_assoc_items(w: &mut fmt::Formatter,
26212631
i.inner_impl().trait_.is_none()
26222632
});
26232633
if !non_trait.is_empty() {
2624-
let render_header = match what {
2634+
let render_mode = match what {
26252635
AssocItemRender::All => {
26262636
write!(w, "<h2 id='methods'>Methods</h2>")?;
2627-
true
2637+
RenderMode::Normal
26282638
}
2629-
AssocItemRender::DerefFor { trait_, type_ } => {
2639+
AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => {
26302640
write!(w, "<h2 id='deref-methods'>Methods from \
26312641
{}&lt;Target={}&gt;</h2>", trait_, type_)?;
2632-
false
2642+
RenderMode::ForDeref { mut_: deref_mut_ }
26332643
}
26342644
};
26352645
for i in &non_trait {
2636-
render_impl(w, cx, i, AssocItemLink::Anchor(None), render_header,
2646+
render_impl(w, cx, i, AssocItemLink::Anchor(None), render_mode,
26372647
containing_item.stable_since())?;
26382648
}
26392649
}
@@ -2645,29 +2655,34 @@ fn render_assoc_items(w: &mut fmt::Formatter,
26452655
t.inner_impl().trait_.def_id() == c.deref_trait_did
26462656
});
26472657
if let Some(impl_) = deref_impl {
2648-
render_deref_methods(w, cx, impl_, containing_item)?;
2658+
let has_deref_mut = traits.iter().find(|t| {
2659+
t.inner_impl().trait_.def_id() == c.deref_mut_trait_did
2660+
}).is_some();
2661+
render_deref_methods(w, cx, impl_, containing_item, has_deref_mut)?;
26492662
}
26502663
write!(w, "<h2 id='implementations'>Trait \
26512664
Implementations</h2>")?;
26522665
for i in &traits {
26532666
let did = i.trait_did().unwrap();
26542667
let assoc_link = AssocItemLink::GotoSource(did, &i.inner_impl().provided_trait_methods);
2655-
render_impl(w, cx, i, assoc_link, true, containing_item.stable_since())?;
2668+
render_impl(w, cx, i, assoc_link,
2669+
RenderMode::Normal, containing_item.stable_since())?;
26562670
}
26572671
}
26582672
Ok(())
26592673
}
26602674

26612675
fn render_deref_methods(w: &mut fmt::Formatter, cx: &Context, impl_: &Impl,
2662-
container_item: &clean::Item) -> fmt::Result {
2676+
container_item: &clean::Item, deref_mut: bool) -> fmt::Result {
26632677
let deref_type = impl_.inner_impl().trait_.as_ref().unwrap();
26642678
let target = impl_.inner_impl().items.iter().filter_map(|item| {
26652679
match item.inner {
26662680
clean::TypedefItem(ref t, true) => Some(&t.type_),
26672681
_ => None,
26682682
}
26692683
}).next().expect("Expected associated type binding");
2670-
let what = AssocItemRender::DerefFor { trait_: deref_type, type_: target };
2684+
let what = AssocItemRender::DerefFor { trait_: deref_type, type_: target,
2685+
deref_mut_: deref_mut };
26712686
if let Some(did) = target.def_id() {
26722687
render_assoc_items(w, cx, container_item, did, what)
26732688
} else {
@@ -2681,12 +2696,9 @@ fn render_deref_methods(w: &mut fmt::Formatter, cx: &Context, impl_: &Impl,
26812696
}
26822697
}
26832698

2684-
// Render_header is false when we are rendering a `Deref` impl and true
2685-
// otherwise. If render_header is false, we will avoid rendering static
2686-
// methods, since they are not accessible for the type implementing `Deref`
26872699
fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLink,
2688-
render_header: bool, outer_version: Option<&str>) -> fmt::Result {
2689-
if render_header {
2700+
render_mode: RenderMode, outer_version: Option<&str>) -> fmt::Result {
2701+
if render_mode == RenderMode::Normal {
26902702
write!(w, "<h3 class='impl'><span class='in-band'><code>{}</code>", i.inner_impl())?;
26912703
write!(w, "</span><span class='out-of-band'>")?;
26922704
let since = i.impl_item.stability.as_ref().map(|s| &s.since[..]);
@@ -2707,22 +2719,43 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi
27072719
}
27082720

27092721
fn doc_impl_item(w: &mut fmt::Formatter, cx: &Context, item: &clean::Item,
2710-
link: AssocItemLink, render_static: bool,
2722+
link: AssocItemLink, render_mode: RenderMode,
27112723
is_default_item: bool, outer_version: Option<&str>,
27122724
trait_: Option<&clean::Trait>) -> fmt::Result {
27132725
let item_type = item_type(item);
27142726
let name = item.name.as_ref().unwrap();
27152727

2716-
let is_static = match item.inner {
2717-
clean::MethodItem(ref method) => !method.decl.has_self(),
2718-
clean::TyMethodItem(ref method) => !method.decl.has_self(),
2719-
_ => false
2728+
let render_method_item: bool = match render_mode {
2729+
RenderMode::Normal => true,
2730+
RenderMode::ForDeref { mut_: deref_mut_ } => {
2731+
let self_type_opt = match item.inner {
2732+
clean::MethodItem(ref method) => method.decl.self_type(),
2733+
clean::TyMethodItem(ref method) => method.decl.self_type(),
2734+
_ => None
2735+
};
2736+
2737+
if let Some(self_ty) = self_type_opt {
2738+
let by_mut_ref = match self_ty {
2739+
SelfTy::SelfBorrowed(_lifetime, mutability) => {
2740+
mutability == Mutability::Mutable
2741+
},
2742+
SelfTy::SelfExplicit(clean::BorrowedRef { mutability, .. }) => {
2743+
mutability == Mutability::Mutable
2744+
},
2745+
_ => false,
2746+
};
2747+
2748+
deref_mut_ || !by_mut_ref
2749+
} else {
2750+
false
2751+
}
2752+
},
27202753
};
27212754

27222755
match item.inner {
27232756
clean::MethodItem(..) | clean::TyMethodItem(..) => {
27242757
// Only render when the method is not static or we allow static methods
2725-
if !is_static || render_static {
2758+
if render_method_item {
27262759
let id = derive_id(format!("{}.{}", item_type, name));
27272760
let ns_id = derive_id(format!("{}.{}", name, item_type.name_space()));
27282761
write!(w, "<h4 id='{}' class='{}'>", id, item_type)?;
@@ -2770,7 +2803,7 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi
27702803
_ => panic!("can't make docs for trait item with name {:?}", item.name)
27712804
}
27722805

2773-
if !is_static || render_static {
2806+
if render_method_item || render_mode == RenderMode::Normal {
27742807
if !is_default_item {
27752808
if let Some(t) = trait_ {
27762809
// The trait item may have been stripped so we might not
@@ -2803,15 +2836,15 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi
28032836

28042837
write!(w, "<div class='impl-items'>")?;
28052838
for trait_item in &i.inner_impl().items {
2806-
doc_impl_item(w, cx, trait_item, link, render_header,
2839+
doc_impl_item(w, cx, trait_item, link, render_mode,
28072840
false, outer_version, trait_)?;
28082841
}
28092842

28102843
fn render_default_items(w: &mut fmt::Formatter,
28112844
cx: &Context,
28122845
t: &clean::Trait,
28132846
i: &clean::Impl,
2814-
render_static: bool,
2847+
render_mode: RenderMode,
28152848
outer_version: Option<&str>) -> fmt::Result {
28162849
for trait_item in &t.items {
28172850
let n = trait_item.name.clone();
@@ -2821,7 +2854,7 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi
28212854
let did = i.trait_.as_ref().unwrap().def_id().unwrap();
28222855
let assoc_link = AssocItemLink::GotoSource(did, &i.provided_trait_methods);
28232856

2824-
doc_impl_item(w, cx, trait_item, assoc_link, render_static, true,
2857+
doc_impl_item(w, cx, trait_item, assoc_link, render_mode, true,
28252858
outer_version, None)?;
28262859
}
28272860
Ok(())
@@ -2830,7 +2863,7 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi
28302863
// If we've implemented a trait, then also emit documentation for all
28312864
// default items which weren't overridden in the implementation block.
28322865
if let Some(t) = trait_ {
2833-
render_default_items(w, cx, t, &i.inner_impl(), render_header, outer_version)?;
2866+
render_default_items(w, cx, t, &i.inner_impl(), render_mode, outer_version)?;
28342867
}
28352868
write!(w, "</div>")?;
28362869
Ok(())

src/librustdoc/test.rs

+1
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ pub fn run(input: &str,
110110
external_traits: RefCell::new(FnvHashMap()),
111111
populated_crate_impls: RefCell::new(FnvHashSet()),
112112
deref_trait_did: Cell::new(None),
113+
deref_mut_trait_did: Cell::new(None),
113114
access_levels: Default::default(),
114115
renderinfo: Default::default(),
115116
};

src/test/rustdoc/issue-35169-2.rs

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use std::ops::Deref;
12+
use std::ops::DerefMut;
13+
14+
pub struct Foo;
15+
pub struct Bar;
16+
17+
impl Foo {
18+
pub fn by_ref(&self) {}
19+
pub fn by_explicit_ref(self: &Foo) {}
20+
pub fn by_mut_ref(&mut self) {}
21+
pub fn by_explicit_mut_ref(self: &mut Foo) {}
22+
pub fn static_foo() {}
23+
}
24+
25+
impl Deref for Bar {
26+
type Target = Foo;
27+
fn deref(&self) -> &Foo { loop {} }
28+
}
29+
30+
impl DerefMut for Bar {
31+
fn deref_mut(&mut self) -> &mut Foo { loop {} }
32+
}
33+
34+
// @has issue_35169_2/Bar.t.html
35+
// @has issue_35169_2/struct.Bar.html
36+
// @has - '//*[@id="by_ref.v"]' 'fn by_ref(&self)'
37+
// @has - '//*[@id="method.by_ref"]' 'fn by_ref(&self)'
38+
// @has - '//*[@id="by_explicit_ref.v"]' 'fn by_explicit_ref(self: &Foo)'
39+
// @has - '//*[@id="method.by_explicit_ref"]' 'fn by_explicit_ref(self: &Foo)'
40+
// @has - '//*[@id="by_mut_ref.v"]' 'fn by_mut_ref(&mut self)'
41+
// @has - '//*[@id="method.by_mut_ref"]' 'fn by_mut_ref(&mut self)'
42+
// @has - '//*[@id="by_explicit_mut_ref.v"]' 'fn by_explicit_mut_ref(self: &mut Foo)'
43+
// @has - '//*[@id="method.by_explicit_mut_ref"]' 'fn by_explicit_mut_ref(self: &mut Foo)'
44+
// @!has - '//*[@id="static_foo.v"]' 'fn static_foo()'
45+
// @!has - '//*[@id="method.static_foo"]' 'fn static_foo()'

src/test/rustdoc/issue-35169.rs

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use std::ops::Deref;
12+
13+
pub struct Foo;
14+
pub struct Bar;
15+
16+
impl Foo {
17+
pub fn by_ref(&self) {}
18+
pub fn by_explicit_ref(self: &Foo) {}
19+
pub fn by_mut_ref(&mut self) {}
20+
pub fn by_explicit_mut_ref(self: &mut Foo) {}
21+
pub fn static_foo() {}
22+
}
23+
24+
impl Deref for Bar {
25+
type Target = Foo;
26+
fn deref(&self) -> &Foo { loop {} }
27+
}
28+
29+
// @has issue_35169/Bar.t.html
30+
// @has issue_35169/struct.Bar.html
31+
// @has - '//*[@id="by_ref.v"]' 'fn by_ref(&self)'
32+
// @has - '//*[@id="method.by_ref"]' 'fn by_ref(&self)'
33+
// @has - '//*[@id="by_explicit_ref.v"]' 'fn by_explicit_ref(self: &Foo)'
34+
// @has - '//*[@id="method.by_explicit_ref"]' 'fn by_explicit_ref(self: &Foo)'
35+
// @!has - '//*[@id="by_mut_ref.v"]' 'fn by_mut_ref(&mut self)'
36+
// @!has - '//*[@id="method.by_mut_ref"]' 'fn by_mut_ref(&mut self)'
37+
// @!has - '//*[@id="by_explicit_mut_ref.v"]' 'fn by_explicit_mut_ref(self: &mut Foo)'
38+
// @!has - '//*[@id="method.by_explicit_mut_ref"]' 'fn by_explicit_mut_ref(self: &mut Foo)'
39+
// @!has - '//*[@id="static_foo.v"]' 'fn static_foo()'
40+
// @!has - '//*[@id="method.static_foo"]' 'fn static_foo()'

0 commit comments

Comments
 (0)