34
34
//! Context itself, span_lint should be used instead of add_lint.
35
35
36
36
use driver:: session;
37
+ use middle:: privacy;
37
38
use middle:: trans:: adt; // for `adt::is_ffi_safe`
38
39
use middle:: ty;
39
40
use middle:: pat_util;
@@ -317,21 +318,28 @@ pub fn get_lint_dict() -> LintDict {
317
318
return map;
318
319
}
319
320
320
- struct Context {
321
+ struct Context < ' self > {
321
322
// All known lint modes (string versions)
322
323
dict : @LintDict ,
323
324
// Current levels of each lint warning
324
325
cur : SmallIntMap < ( level , LintSource ) > ,
325
326
// context we're checking in (used to access fields like sess)
326
327
tcx : ty:: ctxt ,
328
+ // Items exported by the crate; used by the missing_doc lint.
329
+ exported_items : & ' self privacy:: ExportedItems ,
330
+ // The id of the current `ast::struct_def` being walked.
331
+ cur_struct_def_id : ast:: NodeId ,
332
+ // Whether some ancestor of the current node was marked
333
+ // #[doc(hidden)].
334
+ is_doc_hidden : bool ,
327
335
328
336
// When recursing into an attributed node of the ast which modifies lint
329
337
// levels, this stack keeps track of the previous lint levels of whatever
330
338
// was modified.
331
339
lint_stack : ~[ ( lint , level , LintSource ) ] ,
332
340
}
333
341
334
- impl Context {
342
+ impl < ' self > Context < ' self > {
335
343
fn get_level ( & self , lint : lint ) -> level {
336
344
match self . cur . find ( & ( lint as uint ) ) {
337
345
Some ( & ( lvl, _) ) => lvl,
@@ -440,9 +448,16 @@ impl Context {
440
448
true
441
449
} ;
442
450
451
+ let old_is_doc_hidden = self . is_doc_hidden ;
452
+ self . is_doc_hidden = self . is_doc_hidden ||
453
+ attrs. iter ( ) . any ( |attr| ( "doc" == attr. name ( ) && match attr. meta_item_list ( )
454
+ { None => false ,
455
+ Some ( l) => attr:: contains_name ( l, "hidden" ) } ) ) ;
456
+
443
457
f ( self ) ;
444
458
445
459
// rollback
460
+ self . is_doc_hidden = old_is_doc_hidden;
446
461
do pushed. times {
447
462
let ( lint, lvl, src) = self . lint_stack . pop ( ) ;
448
463
self . set_level ( lint, lvl, src) ;
@@ -870,125 +885,83 @@ fn check_unnecessary_allocation(cx: &Context, e: &ast::Expr) {
870
885
}
871
886
}
872
887
873
- struct MissingDocLintVisitor ( ty:: ctxt ) ;
874
-
875
- impl MissingDocLintVisitor {
876
- fn check_attrs ( & self , attrs : & [ ast:: Attribute ] , id : ast:: NodeId ,
877
- sp : Span , msg : ~str ) {
878
- if !attrs. iter ( ) . any ( |a| a. node . is_sugared_doc ) {
879
- self . sess . add_lint ( missing_doc, id, sp, msg) ;
880
- }
881
- }
882
-
883
- fn check_struct ( & self , sdef : & ast:: struct_def ) {
884
- for field in sdef. fields . iter ( ) {
885
- match field. node . kind {
886
- ast:: named_field( _, vis) if vis != ast:: private => {
887
- self . check_attrs ( field. node . attrs , field. node . id , field. span ,
888
- ~"missing documentation for a field") ;
889
- }
890
- ast:: unnamed_field | ast:: named_field( * ) => { }
891
- }
892
- }
893
- }
894
-
895
- fn doc_hidden ( & self , attrs : & [ ast:: Attribute ] ) -> bool {
896
- do attrs. iter ( ) . any |attr| {
897
- "doc" == attr. name ( ) &&
898
- match attr. meta_item_list ( ) {
899
- Some ( l) => attr:: contains_name ( l, "hidden" ) ,
900
- None => false // not of the form #[doc(...)]
901
- }
902
- }
888
+ fn check_missing_doc_attrs ( cx : & Context ,
889
+ id : ast:: NodeId ,
890
+ attrs : & [ ast:: Attribute ] ,
891
+ sp : Span ,
892
+ desc : & ' static str ) {
893
+ // If we're building a test harness, then warning about
894
+ // documentation is probably not really relevant right now.
895
+ if cx. tcx . sess . opts . test { return }
896
+
897
+ // `#[doc(hidden)]` disables missing_doc check.
898
+ if cx. is_doc_hidden { return }
899
+
900
+ // Only check publicly-visible items, using the result from the
901
+ // privacy pass.
902
+ if !cx. exported_items . contains ( & id) { return }
903
+
904
+ if !attrs. iter ( ) . any ( |a| a. node . is_sugared_doc ) {
905
+ cx. span_lint ( missing_doc, sp,
906
+ format ! ( "missing documentation for {}" , desc) ) ;
903
907
}
904
908
}
905
909
906
- impl Visitor < ( ) > for MissingDocLintVisitor {
907
- fn visit_ty_method( & mut self , m : & ast:: TypeMethod , _: ( ) ) {
908
- if self . doc_hidden ( m. attrs ) { return }
909
-
910
- // All ty_method objects are linted about because they're part of a
911
- // trait (no visibility)
912
- self . check_attrs ( m. attrs , m. id , m. span ,
913
- ~"missing documentation for a method") ;
914
- visit:: walk_ty_method( self , m, ( ) ) ;
915
- }
910
+ fn check_missing_doc_item ( cx : & mut Context , it : & ast:: item ) { // XXX doesn't need to be mut
911
+ let desc = match it. node {
912
+ ast:: item_fn( * ) => "a function" ,
913
+ ast:: item_mod( * ) => "a module" ,
914
+ ast:: item_enum( * ) => "an enum" ,
915
+ ast:: item_struct( * ) => "a struct" ,
916
+ ast:: item_trait( * ) => "a trait" ,
917
+ _ => return
918
+ } ;
919
+ check_missing_doc_attrs ( cx, it. id , it. attrs , it. span , desc) ;
920
+ }
916
921
917
- fn visit_fn( & mut self , fk: & visit:: fn_kind, d: & ast:: fn_decl,
918
- b: & ast:: Block , sp: Span , id: ast:: NodeId , _: ( ) ) {
919
- // Only warn about explicitly public methods.
920
- match * fk {
921
- visit:: fk_method( _, _, m) => {
922
- if self . doc_hidden ( m. attrs ) {
923
- return ;
924
- }
925
- // If we're in a trait implementation, no need to duplicate
926
- // documentation
927
- if m. vis == ast:: public {
928
- self . check_attrs ( m. attrs , id, sp,
929
- ~"missing documentation for a method") ;
922
+ fn check_missing_doc_method ( cx : & Context , m : & ast:: method ) {
923
+ let did = ast:: DefId {
924
+ crate : ast:: LOCAL_CRATE ,
925
+ node : m. id
926
+ } ;
927
+ match cx. tcx . methods . find ( & did) {
928
+ None => cx. tcx . sess . span_bug ( m. span , "missing method descriptor?!" ) ,
929
+ Some ( md) => {
930
+ match md. container {
931
+ // Always check default methods defined on traits.
932
+ ty:: TraitContainer ( * ) => { }
933
+ // For methods defined on impls, it depends on whether
934
+ // it is an implementation for a trait or is a plain
935
+ // impl.
936
+ ty:: ImplContainer ( cid) => {
937
+ match ty:: impl_trait_ref ( cx. tcx , cid) {
938
+ Some ( * ) => return , // impl for trait: don't doc
939
+ None => { } // plain impl: doc according to privacy
940
+ }
930
941
}
931
942
}
932
- _ => { }
933
943
}
934
- visit:: walk_fn ( self , fk, d, b, sp, id, ( ) ) ;
935
944
}
945
+ check_missing_doc_attrs ( cx, m. id , m. attrs , m. span , "a method" ) ;
946
+ }
936
947
937
- fn visit_item( & mut self , it : @ast:: item , _: ( ) ) {
938
- // If we're building a test harness, then warning about documentation is
939
- // probably not really relevant right now
940
- if self . sess . opts . test { return }
941
- if self . doc_hidden ( it. attrs ) { return }
942
-
943
- match it. node {
944
- ast:: item_struct( sdef, _) if it. vis == ast:: public => {
945
- self . check_attrs ( it. attrs , it. id , it. span ,
946
- ~"missing documentation for a struct") ;
947
- self . check_struct( sdef) ;
948
- }
949
-
950
- // Skip implementations because they inherit documentation from the
951
- // trait (which was already linted)
952
- ast:: item_impl( _, Some ( * ) , _, _) => return ,
953
-
954
- ast:: item_trait( * ) if it. vis != ast:: public => return ,
955
- ast:: item_trait( * ) => self . check_attrs ( it. attrs , it. id , it. span ,
956
- ~"missing documentation for a trait") ,
957
-
958
- ast:: item_fn( * ) if it. vis == ast:: public => {
959
- self . check_attrs ( it. attrs , it. id , it. span ,
960
- ~"missing documentation for a function") ;
961
- }
962
-
963
- ast:: item_mod( * ) if it. vis == ast:: public => {
964
- self . check_attrs ( it. attrs , it. id , it. span ,
965
- ~"missing documentation for a module") ;
966
- }
967
-
968
- ast:: item_enum( ref edef, _) if it. vis == ast:: public => {
969
- self . check_attrs ( it. attrs , it. id , it. span ,
970
- ~"missing documentation for an enum") ;
971
- for variant in edef. variants . iter ( ) {
972
- if variant. node . vis == ast:: private { continue ; }
973
-
974
- self . check_attrs ( variant. node . attrs , variant. node . id ,
975
- variant. span ,
976
- ~"missing documentation for a variant") ;
977
- match variant. node . kind {
978
- ast:: struct_variant_kind( sdef) => {
979
- self . check_struct ( sdef) ;
980
- }
981
- _ => ( )
982
- }
983
- }
984
- }
948
+ fn check_missing_doc_ty_method ( cx : & Context , tm : & ast:: TypeMethod ) {
949
+ check_missing_doc_attrs ( cx, tm. id , tm. attrs , tm. span , "a type method" ) ;
950
+ }
985
951
986
- _ => { }
987
- }
988
- visit:: walk_item ( self , it, ( ) ) ;
952
+ fn check_missing_doc_struct_field ( cx : & Context , sf : & ast:: struct_field ) {
953
+ match sf. node . kind {
954
+ ast:: named_field( _, vis) if vis != ast:: private =>
955
+ check_missing_doc_attrs ( cx, cx. cur_struct_def_id , sf. node . attrs ,
956
+ sf. span , "a struct field" ) ,
957
+ _ => { }
989
958
}
990
959
}
991
960
961
+ fn check_missing_doc_variant ( cx : & Context , v : & ast:: variant ) {
962
+ check_missing_doc_attrs ( cx, v. node . id , v. node . attrs , v. span , "a variant" ) ;
963
+ }
964
+
992
965
/// Checks for use of items with #[deprecated], #[experimental] and
993
966
/// #[unstable] (or none of them) attributes.
994
967
fn check_stability ( cx : & Context , e : & ast:: Expr ) {
@@ -1062,13 +1035,14 @@ fn check_stability(cx: &Context, e: &ast::Expr) {
1062
1035
cx. span_lint ( lint, e. span , msg) ;
1063
1036
}
1064
1037
1065
- impl Visitor < ( ) > for Context {
1038
+ impl < ' self > Visitor < ( ) > for Context < ' self > {
1066
1039
fn visit_item ( & mut self , it : @ast:: item , _: ( ) ) {
1067
1040
do self . with_lint_attrs ( it. attrs ) |cx| {
1068
1041
check_item_ctypes ( cx, it) ;
1069
1042
check_item_non_camel_case_types ( cx, it) ;
1070
1043
check_item_non_uppercase_statics ( cx, it) ;
1071
1044
check_heap_item ( cx, it) ;
1045
+ check_missing_doc_item ( cx, it) ;
1072
1046
1073
1047
do cx. visit_ids |v| {
1074
1048
v. visit_item ( it, ( ) ) ;
@@ -1111,6 +1085,8 @@ impl Visitor<()> for Context {
1111
1085
match * fk {
1112
1086
visit:: fk_method( _, _, m) => {
1113
1087
do self . with_lint_attrs ( m. attrs ) |cx| {
1088
+ check_missing_doc_method ( cx, m) ;
1089
+
1114
1090
do cx. visit_ids |v| {
1115
1091
v. visit_fn ( fk, decl, body, span, id, ( ) ) ;
1116
1092
}
@@ -1120,9 +1096,45 @@ impl Visitor<()> for Context {
1120
1096
_ => recurse ( self ) ,
1121
1097
}
1122
1098
}
1099
+
1100
+ fn visit_ty_method ( & mut self , t : & ast:: TypeMethod , _: ( ) ) {
1101
+ do self . with_lint_attrs ( t. attrs ) |cx| {
1102
+ check_missing_doc_ty_method ( cx, t) ;
1103
+
1104
+ visit:: walk_ty_method ( cx, t, ( ) ) ;
1105
+ }
1106
+ }
1107
+
1108
+ fn visit_struct_def ( & mut self ,
1109
+ s : @ast:: struct_def ,
1110
+ i : ast:: Ident ,
1111
+ g : & ast:: Generics ,
1112
+ id : ast:: NodeId ,
1113
+ _: ( ) ) {
1114
+ let old_id = self . cur_struct_def_id ;
1115
+ self . cur_struct_def_id = id;
1116
+ visit:: walk_struct_def ( self , s, i, g, id, ( ) ) ;
1117
+ self . cur_struct_def_id = old_id;
1118
+ }
1119
+
1120
+ fn visit_struct_field ( & mut self , s : @ast:: struct_field , _: ( ) ) {
1121
+ do self . with_lint_attrs ( s. node . attrs ) |cx| {
1122
+ check_missing_doc_struct_field ( cx, s) ;
1123
+
1124
+ visit:: walk_struct_field ( cx, s, ( ) ) ;
1125
+ }
1126
+ }
1127
+
1128
+ fn visit_variant ( & mut self , v : & ast:: variant , g : & ast:: Generics , _: ( ) ) {
1129
+ do self . with_lint_attrs ( v. node . attrs ) |cx| {
1130
+ check_missing_doc_variant ( cx, v) ;
1131
+
1132
+ visit:: walk_variant ( cx, v, g, ( ) ) ;
1133
+ }
1134
+ }
1123
1135
}
1124
1136
1125
- impl ast_util:: IdVisitingOperation for Context {
1137
+ impl < ' self > ast_util:: IdVisitingOperation for Context < ' self > {
1126
1138
fn visit_id ( & self , id : ast:: NodeId ) {
1127
1139
match self . tcx . sess . lints . pop ( & id) {
1128
1140
None => { }
@@ -1135,17 +1147,16 @@ impl ast_util::IdVisitingOperation for Context {
1135
1147
}
1136
1148
}
1137
1149
1138
- pub fn check_crate( tcx : ty:: ctxt , crate : & ast:: Crate ) {
1139
- // This visitor contains more state than is currently maintained in Context,
1140
- // and there's no reason for the Context to keep track of this information
1141
- // really
1142
- let mut dox = MissingDocLintVisitor ( tcx) ;
1143
- visit:: walk_crate ( & mut dox, crate , ( ) ) ;
1144
-
1150
+ pub fn check_crate( tcx : ty:: ctxt ,
1151
+ exported_items : & privacy:: ExportedItems ,
1152
+ crate : & ast:: Crate ) {
1145
1153
let mut cx = Context {
1146
1154
dict : @get_lint_dict ( ) ,
1147
1155
cur : SmallIntMap :: new ( ) ,
1148
1156
tcx : tcx,
1157
+ exported_items : exported_items,
1158
+ cur_struct_def_id : -1 ,
1159
+ is_doc_hidden : false ,
1149
1160
lint_stack : ~[ ] ,
1150
1161
} ;
1151
1162
0 commit comments