1
1
use crate :: clean;
2
2
use crate :: core:: DocContext ;
3
+ use crate :: html:: item_type:: ItemType ;
3
4
use crate :: fold:: { self , DocFolder } ;
4
5
use crate :: passes:: Pass ;
5
6
6
7
use syntax:: attr;
7
8
8
- use std:: ops :: Sub ;
9
+ use std:: collections :: BTreeMap ;
9
10
use std:: fmt;
11
+ use std:: ops;
10
12
11
13
pub const CALCULATE_DOC_COVERAGE : Pass = Pass {
12
14
name : "calculate-doc-coverage" ,
@@ -18,17 +20,7 @@ fn calculate_doc_coverage(krate: clean::Crate, _: &DocContext<'_, '_, '_>) -> cl
18
20
let mut calc = CoverageCalculator :: default ( ) ;
19
21
let krate = calc. fold_crate ( krate) ;
20
22
21
- let non_traits = calc. items - calc. trait_impl_items ;
22
-
23
- print ! ( "Rustdoc found {} items with documentation" , calc. items) ;
24
- println ! ( " ({} not counting trait impls)" , non_traits) ;
25
-
26
- if let ( Some ( percentage) , Some ( percentage_non_traits) ) =
27
- ( calc. items . percentage ( ) , non_traits. percentage ( ) )
28
- {
29
- println ! ( " Score: {:.1}% ({:.1}% not counting trait impls)" ,
30
- percentage, percentage_non_traits) ;
31
- }
23
+ calc. print_results ( ) ;
32
24
33
25
krate
34
26
}
@@ -57,7 +49,7 @@ impl ItemCount {
57
49
}
58
50
}
59
51
60
- impl Sub for ItemCount {
52
+ impl ops :: Sub for ItemCount {
61
53
type Output = Self ;
62
54
63
55
fn sub ( self , rhs : Self ) -> Self {
@@ -68,6 +60,13 @@ impl Sub for ItemCount {
68
60
}
69
61
}
70
62
63
+ impl ops:: AddAssign for ItemCount {
64
+ fn add_assign ( & mut self , rhs : Self ) {
65
+ self . total += rhs. total ;
66
+ self . with_docs += rhs. with_docs ;
67
+ }
68
+ }
69
+
71
70
impl fmt:: Display for ItemCount {
72
71
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
73
72
write ! ( f, "{}/{}" , self . with_docs, self . total)
@@ -76,58 +75,183 @@ impl fmt::Display for ItemCount {
76
75
77
76
#[ derive( Default ) ]
78
77
struct CoverageCalculator {
79
- items : ItemCount ,
80
- trait_impl_items : ItemCount ,
78
+ items : BTreeMap < ItemType , ItemCount > ,
79
+ }
80
+
81
+ impl CoverageCalculator {
82
+ fn print_results ( & self ) {
83
+ use crate :: html:: item_type:: ItemType :: * ;
84
+
85
+ let mut total = ItemCount :: default ( ) ;
86
+
87
+ let main_types = [
88
+ Module , Function ,
89
+ Struct , StructField ,
90
+ Enum , Variant ,
91
+ Union ,
92
+ Method ,
93
+ Trait , TyMethod ,
94
+ AssociatedType , AssociatedConst ,
95
+ Macro ,
96
+ Static , Constant ,
97
+ ForeignType , Existential ,
98
+ Typedef , TraitAlias ,
99
+ Primitive , Keyword ,
100
+ ] ;
101
+
102
+ println ! ( "+-{0:->25}-+-{0:->10}-+-{0:->10}-+-{0:->10}-+" , "" ) ;
103
+ println ! ( "| {:<25} | {:>10} | {:>10} | {:>10} |" ,
104
+ "Item Type" , "Documented" , "Total" , "Percentage" ) ;
105
+ println ! ( "+-{0:->25}-+-{0:->10}-+-{0:->10}-+-{0:->10}-+" , "" ) ;
106
+
107
+ for item_type in & main_types {
108
+ let count = self . items . get ( item_type) . cloned ( ) . unwrap_or_default ( ) ;
109
+
110
+ if let Some ( percentage) = count. percentage ( ) {
111
+ println ! ( "| {:<25} | {:>10} | {:>10} | {:>9.1}% |" ,
112
+ table_name( item_type) , count. with_docs, count. total, percentage) ;
113
+
114
+ total += count;
115
+ }
116
+ }
117
+
118
+ println ! ( "+-{0:->25}-+-{0:->10}-+-{0:->10}-+-{0:->10}-+" , "" ) ;
119
+
120
+ if let Some ( count) = self . items . get ( & Impl ) {
121
+ if let Some ( percentage) = count. percentage ( ) {
122
+ if let Some ( percentage) = total. percentage ( ) {
123
+ println ! ( "| {:<25} | {:>10} | {:>10} | {:>9.1}% |" ,
124
+ "Total (non trait impls)" , total. with_docs, total. total, percentage) ;
125
+ }
126
+
127
+ println ! ( "+-{0:->25}-+-{0:->10}-+-{0:->10}-+-{0:->10}-+" , "" ) ;
128
+
129
+ println ! ( "| {:<25} | {:>10} | {:>10} | {:>9.1}% |" ,
130
+ table_name( & Impl ) , count. with_docs, count. total, percentage) ;
131
+
132
+ println ! ( "+-{0:->25}-+-{0:->10}-+-{0:->10}-+-{0:->10}-+" , "" ) ;
133
+
134
+ total += * count;
135
+ }
136
+ }
137
+
138
+ println ! ( "| {:<25} | {:>10} | {:>10} | {:>9.1}% |" ,
139
+ "Total" , total. with_docs, total. total, total. percentage( ) . unwrap_or( 0.0 ) ) ;
140
+ println ! ( "+-{0:->25}-+-{0:->10}-+-{0:->10}-+-{0:->10}-+" , "" ) ;
141
+ }
81
142
}
82
143
83
144
impl fold:: DocFolder for CoverageCalculator {
84
- fn fold_item ( & mut self , i : clean:: Item ) -> Option < clean:: Item > {
145
+ fn fold_item ( & mut self , mut i : clean:: Item ) -> Option < clean:: Item > {
146
+ let has_docs = !i. attrs . doc_strings . is_empty ( ) ;
147
+
85
148
match i. inner {
149
+ _ if !i. def_id . is_local ( ) => {
150
+ // non-local items are skipped because they can be out of the users control,
151
+ // especially in the case of trait impls, which rustdoc eagerly inlines
152
+ return Some ( i) ;
153
+ }
86
154
clean:: StrippedItem ( ..) => {
87
155
// don't count items in stripped modules
88
156
return Some ( i) ;
89
157
}
90
- clean:: ImportItem ( ..) | clean:: ExternCrateItem ( ..) => { }
158
+ clean:: ImportItem ( ..) | clean:: ExternCrateItem ( ..) => {
159
+ // docs on `use` and `extern crate` statements are not displayed, so they're not
160
+ // worth counting
161
+ return Some ( i) ;
162
+ }
91
163
clean:: ImplItem ( ref impl_)
92
164
if attr:: contains_name ( & i. attrs . other_attrs , "automatically_derived" )
93
165
|| impl_. synthetic || impl_. blanket_impl . is_some ( ) =>
94
166
{
95
- // skip counting anything inside these impl blocks
167
+ // built-in derives get the `#[automatically_derived]` attribute, and
168
+ // synthetic/blanket impls are made up by rustdoc and can't be documented
96
169
// FIXME(misdreavus): need to also find items that came out of a derive macro
97
170
return Some ( i) ;
98
171
}
99
- // non-local items are skipped because they can be out of the users control, especially
100
- // in the case of trait impls, which rustdoc eagerly inlines
101
- _ => if i. def_id . is_local ( ) {
102
- let has_docs = !i. attrs . doc_strings . is_empty ( ) ;
103
-
104
- if let clean:: ImplItem ( ref i) = i. inner {
105
- if let Some ( ref tr) = i. trait_ {
106
- debug ! ( "counting impl {:#} for {:#}" , tr, i. for_) ;
172
+ clean:: ImplItem ( ref impl_) => {
173
+ if let Some ( ref tr) = impl_. trait_ {
174
+ debug ! ( "counting impl {:#} for {:#}" , tr, impl_. for_) ;
107
175
108
- self . items . count_item ( has_docs) ;
176
+ // trait impls inherit their docs from the trait definition, so documenting
177
+ // them can be considered optional
178
+ self . items . entry ( ItemType :: Impl ) . or_default ( ) . count_item ( has_docs) ;
109
179
110
- // trait impls inherit their docs from the trait definition, so documenting
111
- // them can be considered optional
180
+ for it in & impl_. items {
181
+ let has_docs = !it. attrs . doc_strings . is_empty ( ) ;
182
+ self . items . entry ( ItemType :: Impl ) . or_default ( ) . count_item ( has_docs) ;
183
+ }
112
184
113
- self . trait_impl_items . count_item ( has_docs) ;
185
+ // now skip recursing, so that we don't double-count this impl's items
186
+ return Some ( i) ;
187
+ } else {
188
+ // inherent impls *can* be documented, and those docs show up, but in most
189
+ // cases it doesn't make sense, as all methods on a type are in one single
190
+ // impl block
191
+ debug ! ( "not counting impl {:#}" , impl_. for_) ;
192
+ }
193
+ }
194
+ clean:: MacroItem ( ..) | clean:: ProcMacroItem ( ..) => {
195
+ // combine `macro_rules!` macros and proc-macros in the same count
196
+ debug ! ( "counting macro {:?}" , i. name) ;
197
+ self . items . entry ( ItemType :: Macro ) . or_default ( ) . count_item ( has_docs) ;
198
+ }
199
+ clean:: TraitItem ( ref mut trait_) => {
200
+ // because both trait methods with a default impl and struct methods are
201
+ // ItemType::Method, we need to properly tag trait methods as TyMethod instead
202
+ debug ! ( "counting trait {:?}" , i. name) ;
203
+ self . items . entry ( ItemType :: Trait ) . or_default ( ) . count_item ( has_docs) ;
114
204
115
- for it in & i. items {
116
- self . trait_impl_items . count_item ( !it. attrs . doc_strings . is_empty ( ) ) ;
117
- }
205
+ // since we're not going on to document the crate, it doesn't matter if we discard
206
+ // the item after counting it
207
+ trait_. items . retain ( |it| {
208
+ if it. type_ ( ) == ItemType :: Method {
209
+ let has_docs = !it. attrs . doc_strings . is_empty ( ) ;
210
+ self . items . entry ( ItemType :: TyMethod ) . or_default ( ) . count_item ( has_docs) ;
211
+ false
118
212
} else {
119
- // inherent impls *can* be documented, and those docs show up, but in most
120
- // cases it doesn't make sense, as all methods on a type are in one single
121
- // impl block
122
- debug ! ( "not counting impl {:#}" , i. for_) ;
213
+ true
123
214
}
124
- } else {
125
- debug ! ( "counting {} {:?}" , i. type_( ) , i. name) ;
126
- self . items . count_item ( has_docs) ;
127
- }
215
+ } ) ;
216
+ }
217
+ _ => {
218
+ debug ! ( "counting {} {:?}" , i. type_( ) , i. name) ;
219
+ self . items . entry ( i. type_ ( ) ) . or_default ( ) . count_item ( has_docs) ;
128
220
}
129
221
}
130
222
131
223
self . fold_item_recur ( i)
132
224
}
133
225
}
226
+
227
+ fn table_name ( type_ : & ItemType ) -> & ' static str {
228
+ match * type_ {
229
+ ItemType :: Module => "Modules" ,
230
+ ItemType :: Struct => "Structs" ,
231
+ ItemType :: Union => "Unions" ,
232
+ ItemType :: Enum => "Enums" ,
233
+ ItemType :: Function => "Functions" ,
234
+ ItemType :: Typedef => "Type Aliases" ,
235
+ ItemType :: Static => "Statics" ,
236
+ ItemType :: Trait => "Traits" ,
237
+ // inherent impls aren't counted, and trait impls get all their items thrown into this
238
+ // counter
239
+ ItemType :: Impl => "Trait Impl Items" ,
240
+ // even though trait methods with a default impl get cleaned as Method, we convert them
241
+ // to TyMethod when counting
242
+ ItemType :: TyMethod => "Trait Methods" ,
243
+ ItemType :: Method => "Methods" ,
244
+ ItemType :: StructField => "Struct Fields" ,
245
+ ItemType :: Variant => "Enum Variants" ,
246
+ ItemType :: Macro => "Macros" ,
247
+ ItemType :: Primitive => "Primitives" ,
248
+ ItemType :: AssociatedType => "Associated Types" ,
249
+ ItemType :: Constant => "Constants" ,
250
+ ItemType :: AssociatedConst => "Associated Constants" ,
251
+ ItemType :: ForeignType => "Foreign Types" ,
252
+ ItemType :: Keyword => "Keywords" ,
253
+ ItemType :: Existential => "Existential Types" ,
254
+ ItemType :: TraitAlias => "Trait Aliases" ,
255
+ _ => panic ! ( "unanticipated ItemType: {}" , type_) ,
256
+ }
257
+ }
0 commit comments