Skip to content

Commit af995ce

Browse files
committed
Make missing documentation linting more robust
Add some more cases for warning about missing documentation, and also add a test to make sure it doesn't die in the future.
1 parent 3a3bf8b commit af995ce

File tree

3 files changed

+177
-75
lines changed

3 files changed

+177
-75
lines changed

src/librustc/front/intrinsic.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
// and injected into each crate the compiler builds. Keep it small.
1313

1414
pub mod intrinsic {
15+
#[allow(missing_doc)];
16+
1517
pub use intrinsic::rusti::visit_tydesc;
1618

1719
// FIXME (#3727): remove this when the interface has settled and the

src/librustc/middle/lint.rs

Lines changed: 103 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,7 @@ pub enum lint {
9595
unused_mut,
9696
unnecessary_allocation,
9797

98-
missing_struct_doc,
99-
missing_trait_doc,
98+
missing_doc,
10099
}
101100

102101
pub fn level_to_str(lv: level) -> &'static str {
@@ -268,17 +267,10 @@ static lint_table: &'static [(&'static str, LintSpec)] = &[
268267
default: warn
269268
}),
270269

271-
("missing_struct_doc",
270+
("missing_doc",
272271
LintSpec {
273-
lint: missing_struct_doc,
274-
desc: "detects missing documentation for structs",
275-
default: allow
276-
}),
277-
278-
("missing_trait_doc",
279-
LintSpec {
280-
lint: missing_trait_doc,
281-
desc: "detects missing documentation for traits",
272+
lint: missing_doc,
273+
desc: "detects missing documentation for public members",
282274
default: allow
283275
}),
284276
];
@@ -302,6 +294,9 @@ struct Context {
302294
curr: SmallIntMap<(level, LintSource)>,
303295
// context we're checking in (used to access fields like sess)
304296
tcx: ty::ctxt,
297+
// Just a simple flag if we're currently recursing into a trait
298+
// implementation. This is only used by the lint_missing_doc() pass
299+
in_trait_impl: bool,
305300
// When recursing into an attributed node of the ast which modifies lint
306301
// levels, this stack keeps track of the previous lint levels of whatever
307302
// was modified.
@@ -311,7 +306,15 @@ struct Context {
311306
// Others operate directly on @ast::item structures (or similar). Finally,
312307
// others still are added to the Session object via `add_lint`, and these
313308
// are all passed with the lint_session visitor.
314-
visitors: ~[visit::vt<@mut Context>],
309+
//
310+
// This is a pair so every visitor can visit every node. When a lint pass is
311+
// registered, another visitor is created which stops at all items which can
312+
// alter the attributes of the ast. This "item stopping visitor" is the
313+
// second element of the pair, while the original visitor is the first
314+
// element. This means that when visiting a node, the original recursive
315+
// call can used the original visitor's method, although the recursing
316+
// visitor supplied to the method is the item stopping visitor.
317+
visitors: ~[(visit::vt<@mut Context>, visit::vt<@mut Context>)],
315318
}
316319

317320
impl Context {
@@ -429,29 +432,31 @@ impl Context {
429432
}
430433

431434
fn add_lint(&mut self, v: visit::vt<@mut Context>) {
432-
self.visitors.push(item_stopping_visitor(v));
435+
self.visitors.push((v, item_stopping_visitor(v)));
433436
}
434437

435438
fn process(@mut self, n: AttributedNode) {
439+
// see comment of the `visitors` field in the struct for why there's a
440+
// pair instead of just one visitor.
436441
match n {
437442
Item(it) => {
438-
for self.visitors.each |v| {
439-
visit::visit_item(it, self, *v);
443+
for self.visitors.each |&(orig, stopping)| {
444+
(orig.visit_item)(it, self, stopping);
440445
}
441446
}
442447
Crate(c) => {
443-
for self.visitors.each |v| {
444-
visit::visit_crate(c, self, *v);
448+
for self.visitors.each |&(_, stopping)| {
449+
visit::visit_crate(c, self, stopping);
445450
}
446451
}
447452
// Can't use visit::visit_method_helper because the
448453
// item_stopping_visitor has overridden visit_fn(&fk_method(... ))
449454
// to be a no-op, so manually invoke visit_fn.
450455
Method(m) => {
451456
let fk = visit::fk_method(copy m.ident, &m.generics, m);
452-
for self.visitors.each |v| {
453-
visit::visit_fn(&fk, &m.decl, &m.body, m.span, m.id,
454-
self, *v);
457+
for self.visitors.each |&(orig, stopping)| {
458+
(orig.visit_fn)(&fk, &m.decl, &m.body, m.span, m.id,
459+
self, stopping);
455460
}
456461
}
457462
}
@@ -495,16 +500,16 @@ pub fn each_lint(sess: session::Session,
495500
// This is used to make the simple visitors used for the lint passes
496501
// not traverse into subitems, since that is handled by the outer
497502
// lint visitor.
498-
fn item_stopping_visitor<E: Copy>(v: visit::vt<E>) -> visit::vt<E> {
503+
fn item_stopping_visitor<E: Copy>(outer: visit::vt<E>) -> visit::vt<E> {
499504
visit::mk_vt(@visit::Visitor {
500505
visit_item: |_i, _e, _v| { },
501506
visit_fn: |fk, fd, b, s, id, e, v| {
502507
match *fk {
503508
visit::fk_method(*) => {}
504-
_ => visit::visit_fn(fk, fd, b, s, id, e, v)
509+
_ => (outer.visit_fn)(fk, fd, b, s, id, e, v)
505510
}
506511
},
507-
.. **(ty_stopping_visitor(v))})
512+
.. **(ty_stopping_visitor(outer))})
508513
}
509514

510515
fn ty_stopping_visitor<E>(v: visit::vt<E>) -> visit::vt<E> {
@@ -972,68 +977,84 @@ fn lint_unnecessary_allocations() -> visit::vt<@mut Context> {
972977
})
973978
}
974979

975-
fn lint_missing_struct_doc() -> visit::vt<@mut Context> {
980+
fn lint_missing_doc() -> visit::vt<@mut Context> {
981+
fn check_attrs(cx: @mut Context, attrs: &[ast::attribute],
982+
sp: span, msg: &str) {
983+
if !attrs.any(|a| a.node.is_sugared_doc) {
984+
cx.span_lint(missing_doc, sp, msg);
985+
}
986+
}
987+
976988
visit::mk_vt(@visit::Visitor {
977-
visit_struct_field: |field, cx: @mut Context, vt| {
978-
let relevant = match field.node.kind {
979-
ast::named_field(_, vis) => vis != ast::private,
980-
ast::unnamed_field => false,
981-
};
989+
visit_struct_method: |m, cx, vt| {
990+
if m.vis == ast::public {
991+
check_attrs(cx, m.attrs, m.span,
992+
"missing documentation for a method");
993+
}
994+
visit::visit_struct_method(m, cx, vt);
995+
},
996+
997+
visit_ty_method: |m, cx, vt| {
998+
// All ty_method objects are linted about because they're part of a
999+
// trait (no visibility)
1000+
check_attrs(cx, m.attrs, m.span,
1001+
"missing documentation for a method");
1002+
visit::visit_ty_method(m, cx, vt);
1003+
},
9821004

983-
if relevant {
984-
let mut has_doc = false;
985-
for field.node.attrs.each |attr| {
986-
if attr.node.is_sugared_doc {
987-
has_doc = true;
988-
break;
1005+
visit_fn: |fk, d, b, sp, id, cx, vt| {
1006+
// Only warn about explicitly public methods. Soon implicit
1007+
// public-ness will hopefully be going away.
1008+
match *fk {
1009+
visit::fk_method(_, _, m) if m.vis == ast::public => {
1010+
// If we're in a trait implementation, no need to duplicate
1011+
// documentation
1012+
if !cx.in_trait_impl {
1013+
check_attrs(cx, m.attrs, sp,
1014+
"missing documentation for a method");
9891015
}
9901016
}
991-
if !has_doc {
992-
cx.span_lint(missing_struct_doc, field.span, "missing documentation \
993-
for a field.");
994-
}
995-
}
9961017

997-
visit::visit_struct_field(field, cx, vt);
1018+
_ => {}
1019+
}
1020+
visit::visit_fn(fk, d, b, sp, id, cx, vt);
9981021
},
999-
.. *visit::default_visitor()
1000-
})
1001-
}
10021022

1003-
fn lint_missing_trait_doc() -> visit::vt<@mut Context> {
1004-
visit::mk_vt(@visit::Visitor {
1005-
visit_trait_method: |method, cx: @mut Context, vt| {
1006-
let mut has_doc = false;
1007-
let span = match copy *method {
1008-
ast::required(m) => {
1009-
for m.attrs.each |attr| {
1010-
if attr.node.is_sugared_doc {
1011-
has_doc = true;
1012-
break;
1013-
}
1014-
}
1015-
m.span
1016-
},
1017-
ast::provided(m) => {
1018-
if m.vis == ast::private {
1019-
has_doc = true;
1020-
} else {
1021-
for m.attrs.each |attr| {
1022-
if attr.node.is_sugared_doc {
1023-
has_doc = true;
1024-
break;
1023+
visit_item: |it, cx, vt| {
1024+
match it.node {
1025+
// Go ahead and match the fields here instead of using
1026+
// visit_struct_field while we have access to the enclosing
1027+
// struct's visibility
1028+
ast::item_struct(sdef, _) if it.vis == ast::public => {
1029+
check_attrs(cx, it.attrs, it.span,
1030+
"missing documentation for a struct");
1031+
for sdef.fields.each |field| {
1032+
match field.node.kind {
1033+
ast::named_field(_, vis) if vis != ast::private => {
1034+
check_attrs(cx, field.node.attrs, field.span,
1035+
"missing documentation for a field");
10251036
}
1037+
ast::unnamed_field | ast::named_field(*) => {}
10261038
}
10271039
}
1028-
m.span
10291040
}
1041+
1042+
ast::item_trait(*) if it.vis == ast::public => {
1043+
check_attrs(cx, it.attrs, it.span,
1044+
"missing documentation for a trait");
1045+
}
1046+
1047+
ast::item_fn(*) if it.vis == ast::public => {
1048+
check_attrs(cx, it.attrs, it.span,
1049+
"missing documentation for a function");
1050+
}
1051+
1052+
_ => {}
10301053
};
1031-
if !has_doc {
1032-
cx.span_lint(missing_trait_doc, span, "missing documentation \
1033-
for a method.");
1034-
}
1035-
visit::visit_trait_method(method, cx, vt);
1054+
1055+
visit::visit_item(it, cx, vt);
10361056
},
1057+
10371058
.. *visit::default_visitor()
10381059
})
10391060
}
@@ -1045,6 +1066,7 @@ pub fn check_crate(tcx: ty::ctxt, crate: @ast::crate) {
10451066
tcx: tcx,
10461067
lint_stack: ~[],
10471068
visitors: ~[],
1069+
in_trait_impl: false,
10481070
};
10491071

10501072
// Install defaults.
@@ -1066,8 +1088,7 @@ pub fn check_crate(tcx: ty::ctxt, crate: @ast::crate) {
10661088
cx.add_lint(lint_unused_mut());
10671089
cx.add_lint(lint_session());
10681090
cx.add_lint(lint_unnecessary_allocations());
1069-
cx.add_lint(lint_missing_struct_doc());
1070-
cx.add_lint(lint_missing_trait_doc());
1091+
cx.add_lint(lint_missing_doc());
10711092

10721093
// Actually perform the lint checks (iterating the ast)
10731094
do cx.with_lint_attrs(crate.node.attrs) {
@@ -1076,13 +1097,20 @@ pub fn check_crate(tcx: ty::ctxt, crate: @ast::crate) {
10761097
visit::visit_crate(crate, cx, visit::mk_vt(@visit::Visitor {
10771098
visit_item: |it, cx: @mut Context, vt| {
10781099
do cx.with_lint_attrs(it.attrs) {
1100+
match it.node {
1101+
ast::item_impl(_, Some(*), _, _) => {
1102+
cx.in_trait_impl = true;
1103+
}
1104+
_ => {}
1105+
}
10791106
check_item_ctypes(cx, it);
10801107
check_item_non_camel_case_types(cx, it);
10811108
check_item_default_methods(cx, it);
10821109
check_item_heap(cx, it);
10831110

10841111
cx.process(Item(it));
10851112
visit::visit_item(it, cx, vt);
1113+
cx.in_trait_impl = false;
10861114
}
10871115
},
10881116
visit_fn: |fk, decl, body, span, id, cx, vt| {
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright 2013 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+
// When denying at the crate level, be sure to not get random warnings from the
12+
// injected intrinsics by the compiler.
13+
#[deny(missing_doc)];
14+
15+
struct Foo {
16+
a: int,
17+
priv b: int,
18+
pub c: int, // doesn't matter, Foo is private
19+
}
20+
21+
pub struct PubFoo { //~ ERROR: missing documentation
22+
a: int, //~ ERROR: missing documentation
23+
priv b: int,
24+
pub c: int, //~ ERROR: missing documentation
25+
}
26+
27+
#[allow(missing_doc)]
28+
pub struct PubFoo2 {
29+
a: int,
30+
pub c: int,
31+
}
32+
33+
/// dox
34+
pub fn foo() {}
35+
pub fn foo2() {} //~ ERROR: missing documentation
36+
fn foo3() {}
37+
#[allow(missing_doc)] pub fn foo4() {}
38+
39+
/// dox
40+
pub trait A {}
41+
trait B {}
42+
pub trait C {} //~ ERROR: missing documentation
43+
#[allow(missing_doc)] pub trait D {}
44+
45+
trait Bar {
46+
/// dox
47+
pub fn foo();
48+
fn foo2(); //~ ERROR: missing documentation
49+
pub fn foo3(); //~ ERROR: missing documentation
50+
}
51+
52+
impl Foo {
53+
pub fn foo() {} //~ ERROR: missing documentation
54+
/// dox
55+
pub fn foo1() {}
56+
fn foo2() {}
57+
#[allow(missing_doc)] pub fn foo3() {}
58+
}
59+
60+
#[allow(missing_doc)]
61+
trait F {
62+
pub fn a();
63+
fn b(&self);
64+
}
65+
66+
// should need to redefine documentation for implementations of traits
67+
impl F for Foo {
68+
pub fn a() {}
69+
fn b(&self) {}
70+
}
71+
72+
fn main() {}

0 commit comments

Comments
 (0)