11use crate :: clean;
22use crate :: core:: DocContext ;
3+ use crate :: html:: item_type:: ItemType ;
34use crate :: fold:: { self , DocFolder } ;
45use crate :: passes:: Pass ;
56
67use syntax:: attr;
78
8- use std:: ops :: Sub ;
9+ use std:: collections :: BTreeMap ;
910use std:: fmt;
11+ use std:: ops;
1012
1113pub const CALCULATE_DOC_COVERAGE : Pass = Pass {
1214 name : "calculate-doc-coverage" ,
@@ -18,17 +20,7 @@ fn calculate_doc_coverage(krate: clean::Crate, _: &DocContext<'_, '_, '_>) -> cl
1820 let mut calc = CoverageCalculator :: default ( ) ;
1921 let krate = calc. fold_crate ( krate) ;
2022
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 ( ) ;
3224
3325 krate
3426}
@@ -57,7 +49,7 @@ impl ItemCount {
5749 }
5850}
5951
60- impl Sub for ItemCount {
52+ impl ops :: Sub for ItemCount {
6153 type Output = Self ;
6254
6355 fn sub ( self , rhs : Self ) -> Self {
@@ -68,6 +60,13 @@ impl Sub for ItemCount {
6860 }
6961}
7062
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+
7170impl fmt:: Display for ItemCount {
7271 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
7372 write ! ( f, "{}/{}" , self . with_docs, self . total)
@@ -76,58 +75,183 @@ impl fmt::Display for ItemCount {
7675
7776#[ derive( Default ) ]
7877struct 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+ }
81142}
82143
83144impl 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+
85148 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+ }
86154 clean:: StrippedItem ( ..) => {
87155 // don't count items in stripped modules
88156 return Some ( i) ;
89157 }
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+ }
91163 clean:: ImplItem ( ref impl_)
92164 if attr:: contains_name ( & i. attrs . other_attrs , "automatically_derived" )
93165 || impl_. synthetic || impl_. blanket_impl . is_some ( ) =>
94166 {
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
96169 // FIXME(misdreavus): need to also find items that came out of a derive macro
97170 return Some ( i) ;
98171 }
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_) ;
107175
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) ;
109179
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+ }
112184
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) ;
114204
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
118212 } 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
123214 }
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) ;
128220 }
129221 }
130222
131223 self . fold_item_recur ( i)
132224 }
133225}
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