Auto merge of #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 (460bef22a
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 (460bef22a
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`
This commit is contained in:
commit
d27f598651
@ -12,7 +12,7 @@
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::{collections::HashMap, io::ErrorKind};
|
||||
use std::{
|
||||
env, fmt,
|
||||
env,
|
||||
fs::write,
|
||||
path::{Path, PathBuf},
|
||||
thread,
|
||||
@ -101,15 +101,30 @@ struct ClippyWarning {
|
||||
is_ice: bool,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ClippyWarning {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
writeln!(
|
||||
f,
|
||||
r#"target/lintcheck/sources/{}-{}/{}:{}:{} {} "{}""#,
|
||||
&self.crate_name, &self.crate_version, &self.file, &self.line, &self.column, &self.linttype, &self.message
|
||||
#[allow(unused)]
|
||||
impl ClippyWarning {
|
||||
fn to_output(&self, markdown: bool) -> String {
|
||||
let file = format!("{}-{}/{}", &self.crate_name, &self.crate_version, &self.file);
|
||||
let file_with_pos = format!("{}:{}:{}", &file, &self.line, &self.column);
|
||||
if markdown {
|
||||
let lint = format!("`{}`", self.linttype);
|
||||
|
||||
let mut output = String::from("| ");
|
||||
output.push_str(&format!(
|
||||
"[`{}`](../target/lintcheck/sources/{}#L{})",
|
||||
file_with_pos, file, self.line
|
||||
));
|
||||
output.push_str(&format!(r#" | {:<50} | "{}" |"#, lint, self.message));
|
||||
output.push('\n');
|
||||
output
|
||||
} else {
|
||||
format!(
|
||||
"target/lintcheck/sources/{} {} \"{}\"\n",
|
||||
file_with_pos, self.linttype, self.message
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get(path: &str) -> Result<ureq::Response, ureq::Error> {
|
||||
const MAX_RETRIES: u8 = 4;
|
||||
@ -378,6 +393,8 @@ struct LintcheckConfig {
|
||||
fix: bool,
|
||||
/// A list of lint that this lintcheck run shound focus on
|
||||
lint_filter: Vec<String>,
|
||||
/// Indicate if the output should support markdown syntax
|
||||
markdown: bool,
|
||||
}
|
||||
|
||||
impl LintcheckConfig {
|
||||
@ -393,12 +410,17 @@ fn from_clap(clap_config: &ArgMatches) -> Self {
|
||||
.to_string()
|
||||
});
|
||||
|
||||
let markdown = clap_config.is_present("markdown");
|
||||
let sources_toml_path = PathBuf::from(sources_toml);
|
||||
|
||||
// for the path where we save the lint results, get the filename without extension (so for
|
||||
// wasd.toml, use "wasd"...)
|
||||
let filename: PathBuf = sources_toml_path.file_stem().unwrap().into();
|
||||
let lintcheck_results_path = PathBuf::from(format!("lintcheck-logs/{}_logs.txt", filename.display()));
|
||||
let lintcheck_results_path = PathBuf::from(format!(
|
||||
"lintcheck-logs/{}_logs.{}",
|
||||
filename.display(),
|
||||
if markdown { "md" } else { "txt" }
|
||||
));
|
||||
|
||||
// look at the --threads arg, if 0 is passed, ask rayon rayon how many threads it would spawn and
|
||||
// use half of that for the physical core count
|
||||
@ -440,6 +462,7 @@ fn from_clap(clap_config: &ArgMatches) -> Self {
|
||||
lintcheck_results_path,
|
||||
fix,
|
||||
lint_filter,
|
||||
markdown,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -601,10 +624,15 @@ fn gather_stats(clippy_warnings: &[ClippyWarning]) -> (String, HashMap<&String,
|
||||
// to not have a lint with 200 and 2 warnings take the same spot
|
||||
stats.sort_by_key(|(lint, count)| format!("{:0>4}, {}", count, lint));
|
||||
|
||||
let mut header = String::from("| lint | count |\n");
|
||||
header.push_str("| -------------------------------------------------- | ----- |\n");
|
||||
let stats_string = stats
|
||||
.iter()
|
||||
.map(|(lint, count)| format!("{} {}\n", lint, count))
|
||||
.collect::<String>();
|
||||
.map(|(lint, count)| format!("| {:<50} | {:>4} |\n", lint, count))
|
||||
.fold(header, |mut table, line| {
|
||||
table.push_str(&line);
|
||||
table
|
||||
});
|
||||
|
||||
(stats_string, counter)
|
||||
}
|
||||
@ -802,15 +830,23 @@ pub fn main() {
|
||||
.map(|w| (&w.crate_name, &w.message))
|
||||
.collect();
|
||||
|
||||
let mut all_msgs: Vec<String> = clippy_warnings.iter().map(ToString::to_string).collect();
|
||||
let mut all_msgs: Vec<String> = clippy_warnings
|
||||
.iter()
|
||||
.map(|warn| warn.to_output(config.markdown))
|
||||
.collect();
|
||||
all_msgs.sort();
|
||||
all_msgs.push("\n\n\n\nStats:\n".into());
|
||||
all_msgs.push("\n\n### Stats:\n\n".into());
|
||||
all_msgs.push(stats_formatted);
|
||||
|
||||
// save the text into lintcheck-logs/logs.txt
|
||||
let mut text = clippy_ver; // clippy version number on top
|
||||
text.push_str(&format!("\n{}", all_msgs.join("")));
|
||||
text.push_str("ICEs:\n");
|
||||
text.push_str("\n### Reports\n\n");
|
||||
if config.markdown {
|
||||
text.push_str("| file | lint | message |\n");
|
||||
text.push_str("| --- | --- | --- |\n");
|
||||
}
|
||||
text.push_str(&format!("{}", all_msgs.join("")));
|
||||
text.push_str("\n\n### ICEs:\n");
|
||||
ices.iter()
|
||||
.for_each(|(cratename, msg)| text.push_str(&format!("{}: '{}'", cratename, msg)));
|
||||
|
||||
@ -832,20 +868,21 @@ fn read_stats_from_file(file_path: &Path) -> HashMap<String, usize> {
|
||||
|
||||
let lines: Vec<String> = file_content.lines().map(ToString::to_string).collect();
|
||||
|
||||
// search for the beginning "Stats:" and the end "ICEs:" of the section we want
|
||||
let start = lines.iter().position(|line| line == "Stats:").unwrap();
|
||||
let end = lines.iter().position(|line| line == "ICEs:").unwrap();
|
||||
|
||||
let stats_lines = &lines[start + 1..end];
|
||||
|
||||
stats_lines
|
||||
lines
|
||||
.iter()
|
||||
.map(|line| {
|
||||
let mut spl = line.split(' ');
|
||||
(
|
||||
spl.next().unwrap().to_string(),
|
||||
spl.next().unwrap().parse::<usize>().unwrap(),
|
||||
)
|
||||
.skip_while(|line| line.as_str() != "### Stats:")
|
||||
// Skipping the table header and the `Stats:` label
|
||||
.skip(4)
|
||||
.take_while(|line| line.starts_with("| "))
|
||||
.filter_map(|line| {
|
||||
let mut spl = line.split('|');
|
||||
// Skip the first `|` symbol
|
||||
spl.next();
|
||||
if let (Some(lint), Some(count)) = (spl.next(), spl.next()) {
|
||||
Some((lint.trim().to_string(), count.trim().parse::<usize>().unwrap()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<HashMap<String, usize>>()
|
||||
}
|
||||
@ -957,6 +994,11 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
|
||||
.value_name("clippy_lint_name")
|
||||
.help("apply a filter to only collect specified lints, this also overrides `allow` attributes"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("markdown")
|
||||
.long("--markdown")
|
||||
.help("change the reports table to use markdown links"),
|
||||
)
|
||||
.get_matches()
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user