@@ -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
102101pub 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
317320impl 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
510515fn 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| {
0 commit comments