Skip to content

Commit d27f598

Browse files
committed
Auto merge of rust-lang#8110 - xFrednet:0000-lintcheck-markdown-output, r=matthiaskrgr
Update lintcheck output to use tables and add markdown support This PR updates changes lintcheck's output to use text tables and adds a markdown option to use Markdown links in the reports table. At first, I tried to keep the original output format, but the loading of old stats broke with the markdown option. The old format is therefore sadly incompatible with the new one. I can if requested make it in a way that the markdown output is only and optional additional output, but that would require more work for little benefit IMO. For now, lintcheck has two output types. Here are the examples (best viewed on desktop): <details> <summary>`cargo lintcheck --only anyhow`</summary> ```txt clippy 0.1.59 (460bef2 2021-12-08) ### Reports target/lintcheck/sources/anyhow-1.0.38/build.rs:1:null clippy::cargo_common_metadata "package `anyhow` is missing `package.keywords` metadata" target/lintcheck/sources/anyhow-1.0.38/src/error.rs:350:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" target/lintcheck/sources/anyhow-1.0.38/src/lib.rs:1:null clippy::cargo_common_metadata "package `anyhow` is missing `package.keywords` metadata" ### Stats: | lint | count | | -------------------------------------------------- | ----- | | clippy::missing_panics_doc | 1 | | clippy::cargo_common_metadata | 2 | ### ICEs: ``` </details> <details> <summary>`cargo lintcheck --only anyhow --markdown` (The file links only work locally)</summary> clippy 0.1.59 (460bef2 2021-12-08) ### Reports | file | lint | message | | --- | --- | --- | | [`anyhow-1.0.38/build.rs:1:null`](../target/lintcheck/sources/anyhow-1.0.38/build.rs#L1) | `clippy::cargo_common_metadata` | "package `anyhow` is missing `package.keywords` metadata" | | [`anyhow-1.0.38/src/error.rs:350:5`](../target/lintcheck/sources/anyhow-1.0.38/src/error.rs#L350) | `clippy::missing_panics_doc` | "docs for function which may panic missing `# Panics` section" | | [`anyhow-1.0.38/src/lib.rs:1:null`](../target/lintcheck/sources/anyhow-1.0.38/src/lib.rs#L1) | `clippy::cargo_common_metadata` | "package `anyhow` is missing `package.keywords` metadata" | ### Stats: | lint | count | | -------------------------------------------------- | ----- | | clippy::missing_panics_doc | 1 | | clippy::cargo_common_metadata | 2 | ### ICEs: </details> The table margins are so large to keep the table inline for long file names and lint names --- changelog: none r? `@matthiaskrgr`
2 parents 4a4f092 + d6fca46 commit d27f598

File tree

1 file changed

+70
-28
lines changed

1 file changed

+70
-28
lines changed

lintcheck/src/main.rs

Lines changed: 70 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use std::process::Command;
1212
use std::sync::atomic::{AtomicUsize, Ordering};
1313
use std::{collections::HashMap, io::ErrorKind};
1414
use std::{
15-
env, fmt,
15+
env,
1616
fs::write,
1717
path::{Path, PathBuf},
1818
thread,
@@ -101,13 +101,28 @@ struct ClippyWarning {
101101
is_ice: bool,
102102
}
103103

104-
impl std::fmt::Display for ClippyWarning {
105-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106-
writeln!(
107-
f,
108-
r#"target/lintcheck/sources/{}-{}/{}:{}:{} {} "{}""#,
109-
&self.crate_name, &self.crate_version, &self.file, &self.line, &self.column, &self.linttype, &self.message
110-
)
104+
#[allow(unused)]
105+
impl ClippyWarning {
106+
fn to_output(&self, markdown: bool) -> String {
107+
let file = format!("{}-{}/{}", &self.crate_name, &self.crate_version, &self.file);
108+
let file_with_pos = format!("{}:{}:{}", &file, &self.line, &self.column);
109+
if markdown {
110+
let lint = format!("`{}`", self.linttype);
111+
112+
let mut output = String::from("| ");
113+
output.push_str(&format!(
114+
"[`{}`](../target/lintcheck/sources/{}#L{})",
115+
file_with_pos, file, self.line
116+
));
117+
output.push_str(&format!(r#" | {:<50} | "{}" |"#, lint, self.message));
118+
output.push('\n');
119+
output
120+
} else {
121+
format!(
122+
"target/lintcheck/sources/{} {} \"{}\"\n",
123+
file_with_pos, self.linttype, self.message
124+
)
125+
}
111126
}
112127
}
113128

@@ -378,6 +393,8 @@ struct LintcheckConfig {
378393
fix: bool,
379394
/// A list of lint that this lintcheck run shound focus on
380395
lint_filter: Vec<String>,
396+
/// Indicate if the output should support markdown syntax
397+
markdown: bool,
381398
}
382399

383400
impl LintcheckConfig {
@@ -393,12 +410,17 @@ impl LintcheckConfig {
393410
.to_string()
394411
});
395412

413+
let markdown = clap_config.is_present("markdown");
396414
let sources_toml_path = PathBuf::from(sources_toml);
397415

398416
// for the path where we save the lint results, get the filename without extension (so for
399417
// wasd.toml, use "wasd"...)
400418
let filename: PathBuf = sources_toml_path.file_stem().unwrap().into();
401-
let lintcheck_results_path = PathBuf::from(format!("lintcheck-logs/{}_logs.txt", filename.display()));
419+
let lintcheck_results_path = PathBuf::from(format!(
420+
"lintcheck-logs/{}_logs.{}",
421+
filename.display(),
422+
if markdown { "md" } else { "txt" }
423+
));
402424

403425
// look at the --threads arg, if 0 is passed, ask rayon rayon how many threads it would spawn and
404426
// use half of that for the physical core count
@@ -440,6 +462,7 @@ impl LintcheckConfig {
440462
lintcheck_results_path,
441463
fix,
442464
lint_filter,
465+
markdown,
443466
}
444467
}
445468
}
@@ -601,10 +624,15 @@ fn gather_stats(clippy_warnings: &[ClippyWarning]) -> (String, HashMap<&String,
601624
// to not have a lint with 200 and 2 warnings take the same spot
602625
stats.sort_by_key(|(lint, count)| format!("{:0>4}, {}", count, lint));
603626

627+
let mut header = String::from("| lint | count |\n");
628+
header.push_str("| -------------------------------------------------- | ----- |\n");
604629
let stats_string = stats
605630
.iter()
606-
.map(|(lint, count)| format!("{} {}\n", lint, count))
607-
.collect::<String>();
631+
.map(|(lint, count)| format!("| {:<50} | {:>4} |\n", lint, count))
632+
.fold(header, |mut table, line| {
633+
table.push_str(&line);
634+
table
635+
});
608636

609637
(stats_string, counter)
610638
}
@@ -802,15 +830,23 @@ pub fn main() {
802830
.map(|w| (&w.crate_name, &w.message))
803831
.collect();
804832

805-
let mut all_msgs: Vec<String> = clippy_warnings.iter().map(ToString::to_string).collect();
833+
let mut all_msgs: Vec<String> = clippy_warnings
834+
.iter()
835+
.map(|warn| warn.to_output(config.markdown))
836+
.collect();
806837
all_msgs.sort();
807-
all_msgs.push("\n\n\n\nStats:\n".into());
838+
all_msgs.push("\n\n### Stats:\n\n".into());
808839
all_msgs.push(stats_formatted);
809840

810841
// save the text into lintcheck-logs/logs.txt
811842
let mut text = clippy_ver; // clippy version number on top
812-
text.push_str(&format!("\n{}", all_msgs.join("")));
813-
text.push_str("ICEs:\n");
843+
text.push_str("\n### Reports\n\n");
844+
if config.markdown {
845+
text.push_str("| file | lint | message |\n");
846+
text.push_str("| --- | --- | --- |\n");
847+
}
848+
text.push_str(&format!("{}", all_msgs.join("")));
849+
text.push_str("\n\n### ICEs:\n");
814850
ices.iter()
815851
.for_each(|(cratename, msg)| text.push_str(&format!("{}: '{}'", cratename, msg)));
816852

@@ -832,20 +868,21 @@ fn read_stats_from_file(file_path: &Path) -> HashMap<String, usize> {
832868

833869
let lines: Vec<String> = file_content.lines().map(ToString::to_string).collect();
834870

835-
// search for the beginning "Stats:" and the end "ICEs:" of the section we want
836-
let start = lines.iter().position(|line| line == "Stats:").unwrap();
837-
let end = lines.iter().position(|line| line == "ICEs:").unwrap();
838-
839-
let stats_lines = &lines[start + 1..end];
840-
841-
stats_lines
871+
lines
842872
.iter()
843-
.map(|line| {
844-
let mut spl = line.split(' ');
845-
(
846-
spl.next().unwrap().to_string(),
847-
spl.next().unwrap().parse::<usize>().unwrap(),
848-
)
873+
.skip_while(|line| line.as_str() != "### Stats:")
874+
// Skipping the table header and the `Stats:` label
875+
.skip(4)
876+
.take_while(|line| line.starts_with("| "))
877+
.filter_map(|line| {
878+
let mut spl = line.split('|');
879+
// Skip the first `|` symbol
880+
spl.next();
881+
if let (Some(lint), Some(count)) = (spl.next(), spl.next()) {
882+
Some((lint.trim().to_string(), count.trim().parse::<usize>().unwrap()))
883+
} else {
884+
None
885+
}
849886
})
850887
.collect::<HashMap<String, usize>>()
851888
}
@@ -957,6 +994,11 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
957994
.value_name("clippy_lint_name")
958995
.help("apply a filter to only collect specified lints, this also overrides `allow` attributes"),
959996
)
997+
.arg(
998+
Arg::with_name("markdown")
999+
.long("--markdown")
1000+
.help("change the reports table to use markdown links"),
1001+
)
9601002
.get_matches()
9611003
}
9621004

0 commit comments

Comments
 (0)