Skip to content

Commit 314d6f6

Browse files
committed
auto merge of #10277 : dcrewi/rust/missing-doc-and-visibility-rules, r=alexcrichton
Now the privacy pass returns enough information that other passes do not need to duplicate the visibility rules, and the missing_doc implementation is more consistent with other lint checks.
2 parents 825b127 + 1f7eb4f commit 314d6f6

File tree

8 files changed

+253
-244
lines changed

8 files changed

+253
-244
lines changed

src/librustc/driver/driver.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -302,11 +302,10 @@ pub fn phase_3_run_analysis_passes(sess: Session,
302302

303303
let reachable_map =
304304
time(time_passes, "reachability checking", (), |_|
305-
reachable::find_reachable(ty_cx, method_map, exp_map2,
306-
&exported_items));
305+
reachable::find_reachable(ty_cx, method_map, &exported_items));
307306

308307
time(time_passes, "lint checking", (), |_|
309-
lint::check_crate(ty_cx, crate));
308+
lint::check_crate(ty_cx, &exported_items, crate));
310309

311310
CrateAnalysis {
312311
exp_map2: exp_map2,

src/librustc/middle/lint.rs

Lines changed: 128 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
//! Context itself, span_lint should be used instead of add_lint.
3535
3636
use driver::session;
37+
use middle::privacy;
3738
use middle::trans::adt; // for `adt::is_ffi_safe`
3839
use middle::ty;
3940
use middle::pat_util;
@@ -309,21 +310,28 @@ pub fn get_lint_dict() -> LintDict {
309310
return map;
310311
}
311312

312-
struct Context {
313+
struct Context<'self> {
313314
// All known lint modes (string versions)
314315
dict: @LintDict,
315316
// Current levels of each lint warning
316317
cur: SmallIntMap<(level, LintSource)>,
317318
// context we're checking in (used to access fields like sess)
318319
tcx: ty::ctxt,
320+
// Items exported by the crate; used by the missing_doc lint.
321+
exported_items: &'self privacy::ExportedItems,
322+
// The id of the current `ast::struct_def` being walked.
323+
cur_struct_def_id: ast::NodeId,
324+
// Whether some ancestor of the current node was marked
325+
// #[doc(hidden)].
326+
is_doc_hidden: bool,
319327

320328
// When recursing into an attributed node of the ast which modifies lint
321329
// levels, this stack keeps track of the previous lint levels of whatever
322330
// was modified.
323331
lint_stack: ~[(lint, level, LintSource)],
324332
}
325333

326-
impl Context {
334+
impl<'self> Context<'self> {
327335
fn get_level(&self, lint: lint) -> level {
328336
match self.cur.find(&(lint as uint)) {
329337
Some(&(lvl, _)) => lvl,
@@ -432,9 +440,16 @@ impl Context {
432440
true
433441
};
434442

443+
let old_is_doc_hidden = self.is_doc_hidden;
444+
self.is_doc_hidden = self.is_doc_hidden ||
445+
attrs.iter().any(|attr| ("doc" == attr.name() && match attr.meta_item_list()
446+
{ None => false,
447+
Some(l) => attr::contains_name(l, "hidden") }));
448+
435449
f(self);
436450

437451
// rollback
452+
self.is_doc_hidden = old_is_doc_hidden;
438453
do pushed.times {
439454
let (lint, lvl, src) = self.lint_stack.pop();
440455
self.set_level(lint, lvl, src);
@@ -862,125 +877,83 @@ fn check_unnecessary_allocation(cx: &Context, e: &ast::Expr) {
862877
}
863878
}
864879

865-
struct MissingDocLintVisitor(ty::ctxt);
866-
867-
impl MissingDocLintVisitor {
868-
fn check_attrs(&self, attrs: &[ast::Attribute], id: ast::NodeId,
869-
sp: Span, msg: ~str) {
870-
if !attrs.iter().any(|a| a.node.is_sugared_doc) {
871-
self.sess.add_lint(missing_doc, id, sp, msg);
872-
}
873-
}
874-
875-
fn check_struct(&self, sdef: &ast::struct_def) {
876-
for field in sdef.fields.iter() {
877-
match field.node.kind {
878-
ast::named_field(_, vis) if vis != ast::private => {
879-
self.check_attrs(field.node.attrs, field.node.id, field.span,
880-
~"missing documentation for a field");
881-
}
882-
ast::unnamed_field | ast::named_field(*) => {}
883-
}
884-
}
885-
}
886-
887-
fn doc_hidden(&self, attrs: &[ast::Attribute]) -> bool {
888-
do attrs.iter().any |attr| {
889-
"doc" == attr.name() &&
890-
match attr.meta_item_list() {
891-
Some(l) => attr::contains_name(l, "hidden"),
892-
None => false // not of the form #[doc(...)]
893-
}
894-
}
880+
fn check_missing_doc_attrs(cx: &Context,
881+
id: ast::NodeId,
882+
attrs: &[ast::Attribute],
883+
sp: Span,
884+
desc: &'static str) {
885+
// If we're building a test harness, then warning about
886+
// documentation is probably not really relevant right now.
887+
if cx.tcx.sess.opts.test { return }
888+
889+
// `#[doc(hidden)]` disables missing_doc check.
890+
if cx.is_doc_hidden { return }
891+
892+
// Only check publicly-visible items, using the result from the
893+
// privacy pass.
894+
if !cx.exported_items.contains(&id) { return }
895+
896+
if !attrs.iter().any(|a| a.node.is_sugared_doc) {
897+
cx.span_lint(missing_doc, sp,
898+
format!("missing documentation for {}", desc));
895899
}
896900
}
897901

898-
impl Visitor<()> for MissingDocLintVisitor {
899-
fn visit_ty_method(&mut self, m:&ast::TypeMethod, _: ()) {
900-
if self.doc_hidden(m.attrs) { return }
901-
902-
// All ty_method objects are linted about because they're part of a
903-
// trait (no visibility)
904-
self.check_attrs(m.attrs, m.id, m.span,
905-
~"missing documentation for a method");
906-
visit::walk_ty_method(self, m, ());
907-
}
902+
fn check_missing_doc_item(cx: &mut Context, it: &ast::item) { // XXX doesn't need to be mut
903+
let desc = match it.node {
904+
ast::item_fn(*) => "a function",
905+
ast::item_mod(*) => "a module",
906+
ast::item_enum(*) => "an enum",
907+
ast::item_struct(*) => "a struct",
908+
ast::item_trait(*) => "a trait",
909+
_ => return
910+
};
911+
check_missing_doc_attrs(cx, it.id, it.attrs, it.span, desc);
912+
}
908913

909-
fn visit_fn(&mut self, fk: &visit::fn_kind, d: &ast::fn_decl,
910-
b: &ast::Block, sp: Span, id: ast::NodeId, _: ()) {
911-
// Only warn about explicitly public methods.
912-
match *fk {
913-
visit::fk_method(_, _, m) => {
914-
if self.doc_hidden(m.attrs) {
915-
return;
916-
}
917-
// If we're in a trait implementation, no need to duplicate
918-
// documentation
919-
if m.vis == ast::public {
920-
self.check_attrs(m.attrs, id, sp,
921-
~"missing documentation for a method");
914+
fn check_missing_doc_method(cx: &Context, m: &ast::method) {
915+
let did = ast::DefId {
916+
crate: ast::LOCAL_CRATE,
917+
node: m.id
918+
};
919+
match cx.tcx.methods.find(&did) {
920+
None => cx.tcx.sess.span_bug(m.span, "missing method descriptor?!"),
921+
Some(md) => {
922+
match md.container {
923+
// Always check default methods defined on traits.
924+
ty::TraitContainer(*) => {}
925+
// For methods defined on impls, it depends on whether
926+
// it is an implementation for a trait or is a plain
927+
// impl.
928+
ty::ImplContainer(cid) => {
929+
match ty::impl_trait_ref(cx.tcx, cid) {
930+
Some(*) => return, // impl for trait: don't doc
931+
None => {} // plain impl: doc according to privacy
932+
}
922933
}
923934
}
924-
_ => {}
925935
}
926-
visit::walk_fn(self, fk, d, b, sp, id, ());
927936
}
937+
check_missing_doc_attrs(cx, m.id, m.attrs, m.span, "a method");
938+
}
928939

929-
fn visit_item(&mut self, it: @ast::item, _: ()) {
930-
// If we're building a test harness, then warning about documentation is
931-
// probably not really relevant right now
932-
if self.sess.opts.test { return }
933-
if self.doc_hidden(it.attrs) { return }
934-
935-
match it.node {
936-
ast::item_struct(sdef, _) if it.vis == ast::public => {
937-
self.check_attrs(it.attrs, it.id, it.span,
938-
~"missing documentation for a struct");
939-
self.check_struct(sdef);
940-
}
941-
942-
// Skip implementations because they inherit documentation from the
943-
// trait (which was already linted)
944-
ast::item_impl(_, Some(*), _, _) => return,
945-
946-
ast::item_trait(*) if it.vis != ast::public => return,
947-
ast::item_trait(*) => self.check_attrs(it.attrs, it.id, it.span,
948-
~"missing documentation for a trait"),
949-
950-
ast::item_fn(*) if it.vis == ast::public => {
951-
self.check_attrs(it.attrs, it.id, it.span,
952-
~"missing documentation for a function");
953-
}
954-
955-
ast::item_mod(*) if it.vis == ast::public => {
956-
self.check_attrs(it.attrs, it.id, it.span,
957-
~"missing documentation for a module");
958-
}
959-
960-
ast::item_enum(ref edef, _) if it.vis == ast::public => {
961-
self.check_attrs(it.attrs, it.id, it.span,
962-
~"missing documentation for an enum");
963-
for variant in edef.variants.iter() {
964-
if variant.node.vis == ast::private { continue; }
965-
966-
self.check_attrs(variant.node.attrs, variant.node.id,
967-
variant.span,
968-
~"missing documentation for a variant");
969-
match variant.node.kind {
970-
ast::struct_variant_kind(sdef) => {
971-
self.check_struct(sdef);
972-
}
973-
_ => ()
974-
}
975-
}
976-
}
940+
fn check_missing_doc_ty_method(cx: &Context, tm: &ast::TypeMethod) {
941+
check_missing_doc_attrs(cx, tm.id, tm.attrs, tm.span, "a type method");
942+
}
977943

978-
_ => {}
979-
}
980-
visit::walk_item(self, it, ());
944+
fn check_missing_doc_struct_field(cx: &Context, sf: &ast::struct_field) {
945+
match sf.node.kind {
946+
ast::named_field(_, vis) if vis != ast::private =>
947+
check_missing_doc_attrs(cx, cx.cur_struct_def_id, sf.node.attrs,
948+
sf.span, "a struct field"),
949+
_ => {}
981950
}
982951
}
983952

953+
fn check_missing_doc_variant(cx: &Context, v: &ast::variant) {
954+
check_missing_doc_attrs(cx, v.node.id, v.node.attrs, v.span, "a variant");
955+
}
956+
984957
/// Checks for use of items with #[deprecated], #[experimental] and
985958
/// #[unstable] (or none of them) attributes.
986959
fn check_stability(cx: &Context, e: &ast::Expr) {
@@ -1054,13 +1027,14 @@ fn check_stability(cx: &Context, e: &ast::Expr) {
10541027
cx.span_lint(lint, e.span, msg);
10551028
}
10561029

1057-
impl Visitor<()> for Context {
1030+
impl<'self> Visitor<()> for Context<'self> {
10581031
fn visit_item(&mut self, it: @ast::item, _: ()) {
10591032
do self.with_lint_attrs(it.attrs) |cx| {
10601033
check_item_ctypes(cx, it);
10611034
check_item_non_camel_case_types(cx, it);
10621035
check_item_non_uppercase_statics(cx, it);
10631036
check_heap_item(cx, it);
1037+
check_missing_doc_item(cx, it);
10641038

10651039
do cx.visit_ids |v| {
10661040
v.visit_item(it, ());
@@ -1103,6 +1077,8 @@ impl Visitor<()> for Context {
11031077
match *fk {
11041078
visit::fk_method(_, _, m) => {
11051079
do self.with_lint_attrs(m.attrs) |cx| {
1080+
check_missing_doc_method(cx, m);
1081+
11061082
do cx.visit_ids |v| {
11071083
v.visit_fn(fk, decl, body, span, id, ());
11081084
}
@@ -1112,9 +1088,45 @@ impl Visitor<()> for Context {
11121088
_ => recurse(self),
11131089
}
11141090
}
1091+
1092+
fn visit_ty_method(&mut self, t: &ast::TypeMethod, _: ()) {
1093+
do self.with_lint_attrs(t.attrs) |cx| {
1094+
check_missing_doc_ty_method(cx, t);
1095+
1096+
visit::walk_ty_method(cx, t, ());
1097+
}
1098+
}
1099+
1100+
fn visit_struct_def(&mut self,
1101+
s: @ast::struct_def,
1102+
i: ast::Ident,
1103+
g: &ast::Generics,
1104+
id: ast::NodeId,
1105+
_: ()) {
1106+
let old_id = self.cur_struct_def_id;
1107+
self.cur_struct_def_id = id;
1108+
visit::walk_struct_def(self, s, i, g, id, ());
1109+
self.cur_struct_def_id = old_id;
1110+
}
1111+
1112+
fn visit_struct_field(&mut self, s: @ast::struct_field, _: ()) {
1113+
do self.with_lint_attrs(s.node.attrs) |cx| {
1114+
check_missing_doc_struct_field(cx, s);
1115+
1116+
visit::walk_struct_field(cx, s, ());
1117+
}
1118+
}
1119+
1120+
fn visit_variant(&mut self, v: &ast::variant, g: &ast::Generics, _: ()) {
1121+
do self.with_lint_attrs(v.node.attrs) |cx| {
1122+
check_missing_doc_variant(cx, v);
1123+
1124+
visit::walk_variant(cx, v, g, ());
1125+
}
1126+
}
11151127
}
11161128

1117-
impl ast_util::IdVisitingOperation for Context {
1129+
impl<'self> ast_util::IdVisitingOperation for Context<'self> {
11181130
fn visit_id(&self, id: ast::NodeId) {
11191131
match self.tcx.sess.lints.pop(&id) {
11201132
None => {}
@@ -1127,17 +1139,16 @@ impl ast_util::IdVisitingOperation for Context {
11271139
}
11281140
}
11291141

1130-
pub fn check_crate(tcx: ty::ctxt, crate: &ast::Crate) {
1131-
// This visitor contains more state than is currently maintained in Context,
1132-
// and there's no reason for the Context to keep track of this information
1133-
// really
1134-
let mut dox = MissingDocLintVisitor(tcx);
1135-
visit::walk_crate(&mut dox, crate, ());
1136-
1142+
pub fn check_crate(tcx: ty::ctxt,
1143+
exported_items: &privacy::ExportedItems,
1144+
crate: &ast::Crate) {
11371145
let mut cx = Context {
11381146
dict: @get_lint_dict(),
11391147
cur: SmallIntMap::new(),
11401148
tcx: tcx,
1149+
exported_items: exported_items,
1150+
cur_struct_def_id: -1,
1151+
is_doc_hidden: false,
11411152
lint_stack: ~[],
11421153
};
11431154

0 commit comments

Comments
 (0)