2 use crate::config::OutputFormat;
3 use crate::core::DocContext;
4 use crate::fold::{self, DocFolder};
5 use crate::passes::Pass;
7 use rustc_span::symbol::sym;
8 use rustc_span::FileName;
11 use std::collections::BTreeMap;
14 pub const CALCULATE_DOC_COVERAGE: Pass = Pass {
15 name: "calculate-doc-coverage",
16 run: calculate_doc_coverage,
17 description: "counts the number of items with and without documentation",
20 fn calculate_doc_coverage(krate: clean::Crate, ctx: &DocContext<'_>) -> clean::Crate {
21 let mut calc = CoverageCalculator::new();
22 let krate = calc.fold_crate(krate);
24 calc.print_results(ctx.renderinfo.borrow().output_format);
29 #[derive(Default, Copy, Clone, Serialize)]
36 fn count_item(&mut self, has_docs: bool) {
44 fn percentage(&self) -> Option<f64> {
46 Some((self.with_docs as f64 * 100.0) / self.total as f64)
53 impl ops::Sub for ItemCount {
56 fn sub(self, rhs: Self) -> Self {
57 ItemCount { total: self.total - rhs.total, with_docs: self.with_docs - rhs.with_docs }
61 impl ops::AddAssign for ItemCount {
62 fn add_assign(&mut self, rhs: Self) {
63 self.total += rhs.total;
64 self.with_docs += rhs.with_docs;
68 struct CoverageCalculator {
69 items: BTreeMap<FileName, ItemCount>,
72 fn limit_filename_len(filename: String) -> String {
73 let nb_chars = filename.chars().count();
76 + &filename[filename.char_indices().nth(nb_chars - 32).map(|x| x.0).unwrap_or(0)..]
82 impl CoverageCalculator {
83 fn new() -> CoverageCalculator {
84 CoverageCalculator { items: Default::default() }
87 fn to_json(&self) -> String {
88 serde_json::to_string(
92 .map(|(k, v)| (k.to_string(), v))
93 .collect::<BTreeMap<String, &ItemCount>>(),
95 .expect("failed to convert JSON data to string")
98 fn print_results(&self, output_format: Option<OutputFormat>) {
99 if output_format.map(|o| o.is_json()).unwrap_or_else(|| false) {
100 println!("{}", self.to_json());
103 let mut total = ItemCount::default();
105 fn print_table_line() {
106 println!("+-{0:->35}-+-{0:->10}-+-{0:->10}-+-{0:->10}-+", "");
109 fn print_table_record(name: &str, count: ItemCount, percentage: f64) {
111 "| {:<35} | {:>10} | {:>10} | {:>9.1}% |",
112 name, count.with_docs, count.total, percentage
118 "| {:<35} | {:>10} | {:>10} | {:>10} |",
119 "File", "Documented", "Total", "Percentage"
123 for (file, &count) in &self.items {
124 if let Some(percentage) = count.percentage() {
125 print_table_record(&limit_filename_len(file.to_string()), count, percentage);
132 print_table_record("Total", total, total.percentage().unwrap_or(0.0));
137 impl fold::DocFolder for CoverageCalculator {
138 fn fold_item(&mut self, i: clean::Item) -> Option<clean::Item> {
139 let has_docs = !i.attrs.doc_strings.is_empty();
142 _ if !i.def_id.is_local() => {
143 // non-local items are skipped because they can be out of the users control,
144 // especially in the case of trait impls, which rustdoc eagerly inlines
147 clean::StrippedItem(..) => {
148 // don't count items in stripped modules
151 clean::ImportItem(..) | clean::ExternCrateItem(..) => {
152 // docs on `use` and `extern crate` statements are not displayed, so they're not
156 clean::ImplItem(ref impl_)
160 .any(|item| item.has_name(sym::automatically_derived))
162 || impl_.blanket_impl.is_some() =>
164 // built-in derives get the `#[automatically_derived]` attribute, and
165 // synthetic/blanket impls are made up by rustdoc and can't be documented
166 // FIXME(misdreavus): need to also find items that came out of a derive macro
169 clean::ImplItem(ref impl_) => {
170 if let Some(ref tr) = impl_.trait_ {
172 "impl {:#} for {:#} in {}",
178 // don't count trait impls, the missing-docs lint doesn't so we shouldn't
182 // inherent impls *can* be documented, and those docs show up, but in most
183 // cases it doesn't make sense, as all methods on a type are in one single
185 debug!("impl {:#} in {}", impl_.for_.print(), i.source.filename);
189 debug!("counting {:?} {:?} in {}", i.type_(), i.name, i.source.filename);
190 self.items.entry(i.source.filename.clone()).or_default().count_item(has_docs);
194 self.fold_item_recur(i)