@@ -95,8 +95,7 @@ pub enum lint {
95
95
unused_mut,
96
96
unnecessary_allocation,
97
97
98
- missing_struct_doc,
99
- missing_trait_doc,
98
+ missing_doc,
100
99
}
101
100
102
101
pub fn level_to_str ( lv : level ) -> & ' static str {
@@ -268,17 +267,10 @@ static lint_table: &'static [(&'static str, LintSpec)] = &[
268
267
default : warn
269
268
} ) ,
270
269
271
- ( "missing_struct_doc " ,
270
+ ( "missing_doc " ,
272
271
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" ,
282
274
default : allow
283
275
} ) ,
284
276
] ;
@@ -302,6 +294,9 @@ struct Context {
302
294
curr : SmallIntMap < ( level , LintSource ) > ,
303
295
// context we're checking in (used to access fields like sess)
304
296
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 ,
305
300
// When recursing into an attributed node of the ast which modifies lint
306
301
// levels, this stack keeps track of the previous lint levels of whatever
307
302
// was modified.
@@ -311,7 +306,15 @@ struct Context {
311
306
// Others operate directly on @ast::item structures (or similar). Finally,
312
307
// others still are added to the Session object via `add_lint`, and these
313
308
// 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 > ) ] ,
315
318
}
316
319
317
320
impl Context {
@@ -429,29 +432,31 @@ impl Context {
429
432
}
430
433
431
434
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) ) ) ;
433
436
}
434
437
435
438
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.
436
441
match n {
437
442
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 ) ;
440
445
}
441
446
}
442
447
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 ) ;
445
450
}
446
451
}
447
452
// Can't use visit::visit_method_helper because the
448
453
// item_stopping_visitor has overridden visit_fn(&fk_method(... ))
449
454
// to be a no-op, so manually invoke visit_fn.
450
455
Method ( m) => {
451
456
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 ) ;
455
460
}
456
461
}
457
462
}
@@ -495,16 +500,16 @@ pub fn each_lint(sess: session::Session,
495
500
// This is used to make the simple visitors used for the lint passes
496
501
// not traverse into subitems, since that is handled by the outer
497
502
// 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 > {
499
504
visit:: mk_vt ( @visit:: Visitor {
500
505
visit_item : |_i, _e, _v| { } ,
501
506
visit_fn : |fk, fd, b, s, id, e, v| {
502
507
match * fk {
503
508
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)
505
510
}
506
511
} ,
507
- .. * * ( ty_stopping_visitor ( v ) ) } )
512
+ .. * * ( ty_stopping_visitor ( outer ) ) } )
508
513
}
509
514
510
515
fn ty_stopping_visitor < E > ( v : visit:: vt < E > ) -> visit:: vt < E > {
@@ -972,68 +977,84 @@ fn lint_unnecessary_allocations() -> visit::vt<@mut Context> {
972
977
} )
973
978
}
974
979
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
+
976
988
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
+ } ,
982
1004
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" ) ;
989
1015
}
990
1016
}
991
- if !has_doc {
992
- cx. span_lint ( missing_struct_doc, field. span , "missing documentation \
993
- for a field.") ;
994
- }
995
- }
996
1017
997
- visit:: visit_struct_field ( field, cx, vt) ;
1018
+ _ => { }
1019
+ }
1020
+ visit:: visit_fn ( fk, d, b, sp, id, cx, vt) ;
998
1021
} ,
999
- .. * visit:: default_visitor ( )
1000
- } )
1001
- }
1002
1022
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" ) ;
1025
1036
}
1037
+ ast:: unnamed_field | ast:: named_field( * ) => { }
1026
1038
}
1027
1039
}
1028
- m. span
1029
1040
}
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
+ _ => { }
1030
1053
} ;
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) ;
1036
1056
} ,
1057
+
1037
1058
.. * visit:: default_visitor ( )
1038
1059
} )
1039
1060
}
@@ -1045,6 +1066,7 @@ pub fn check_crate(tcx: ty::ctxt, crate: @ast::crate) {
1045
1066
tcx : tcx,
1046
1067
lint_stack : ~[ ] ,
1047
1068
visitors : ~[ ] ,
1069
+ in_trait_impl : false ,
1048
1070
} ;
1049
1071
1050
1072
// Install defaults.
@@ -1066,8 +1088,7 @@ pub fn check_crate(tcx: ty::ctxt, crate: @ast::crate) {
1066
1088
cx. add_lint ( lint_unused_mut ( ) ) ;
1067
1089
cx. add_lint ( lint_session ( ) ) ;
1068
1090
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 ( ) ) ;
1071
1092
1072
1093
// Actually perform the lint checks (iterating the ast)
1073
1094
do cx. with_lint_attrs ( crate . node. attrs ) {
@@ -1076,13 +1097,20 @@ pub fn check_crate(tcx: ty::ctxt, crate: @ast::crate) {
1076
1097
visit:: visit_crate ( crate , cx, visit:: mk_vt ( @visit:: Visitor {
1077
1098
visit_item : |it, cx : @mut Context , vt| {
1078
1099
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
+ }
1079
1106
check_item_ctypes ( cx, it) ;
1080
1107
check_item_non_camel_case_types ( cx, it) ;
1081
1108
check_item_default_methods ( cx, it) ;
1082
1109
check_item_heap ( cx, it) ;
1083
1110
1084
1111
cx. process ( Item ( it) ) ;
1085
1112
visit:: visit_item ( it, cx, vt) ;
1113
+ cx. in_trait_impl = false ;
1086
1114
}
1087
1115
} ,
1088
1116
visit_fn : |fk, decl, body, span, id, cx, vt| {
0 commit comments