Skip to content

Commit

Permalink
Auto merge of #8952 - rust-lang:explain, r=xFredNet
Browse files Browse the repository at this point in the history
add `--explain` subcommand

This closes #8291.

---

changelog: add `cargo clippy -- --explain <lintname>` subcommand
  • Loading branch information
bors committed Sep 2, 2022
2 parents c36696a + ad72aee commit 30e4532
Show file tree
Hide file tree
Showing 575 changed files with 13,817 additions and 28 deletions.
188 changes: 160 additions & 28 deletions clippy_dev/src/update_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use aho_corasick::AhoCorasickBuilder;
use indoc::writedoc;
use itertools::Itertools;
use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
use std::collections::{HashMap, HashSet};
use std::collections::{BTreeSet, HashMap, HashSet};
use std::ffi::OsStr;
use std::fmt::Write;
use std::fs::{self, OpenOptions};
Expand Down Expand Up @@ -124,6 +124,8 @@ fn generate_lint_files(
let content = gen_lint_group_list("all", all_group_lints);
process_file("clippy_lints/src/lib.register_all.rs", update_mode, &content);

update_docs(update_mode, &usable_lints);

for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) {
let content = gen_lint_group_list(&lint_group, lints.iter());
process_file(
Expand All @@ -140,6 +142,62 @@ fn generate_lint_files(
process_file("tests/ui/rename.rs", update_mode, &content);
}

fn update_docs(update_mode: UpdateMode, usable_lints: &[Lint]) {
replace_region_in_file(update_mode, Path::new("src/docs.rs"), "docs! {\n", "\n}\n", |res| {
for name in usable_lints.iter().map(|lint| lint.name.clone()).sorted() {
writeln!(res, r#" "{name}","#).unwrap();
}
});

if update_mode == UpdateMode::Check {
let mut extra = BTreeSet::new();
let mut lint_names = usable_lints
.iter()
.map(|lint| lint.name.clone())
.collect::<BTreeSet<_>>();
for file in std::fs::read_dir("src/docs").unwrap() {
let filename = file.unwrap().file_name().into_string().unwrap();
if let Some(name) = filename.strip_suffix(".txt") {
if !lint_names.remove(name) {
extra.insert(name.to_string());
}
}
}

let failed = print_lint_names("extra lint docs:", &extra) | print_lint_names("missing lint docs:", &lint_names);

if failed {
exit_with_failure();
}
} else {
if std::fs::remove_dir_all("src/docs").is_err() {
eprintln!("could not remove src/docs directory");
}
if std::fs::create_dir("src/docs").is_err() {
eprintln!("could not recreate src/docs directory");
}
}
for lint in usable_lints {
process_file(
Path::new("src/docs").join(lint.name.clone() + ".txt"),
update_mode,
&lint.documentation,
);
}
}

fn print_lint_names(header: &str, lints: &BTreeSet<String>) -> bool {
if lints.is_empty() {
return false;
}
println!("{}", header);
for lint in lints.iter().sorted() {
println!(" {}", lint);
}
println!();
true
}

pub fn print_lints() {
let (lint_list, _, _) = gather_all();
let usable_lints = Lint::usable_lints(&lint_list);
Expand Down Expand Up @@ -589,17 +647,26 @@ struct Lint {
desc: String,
module: String,
declaration_range: Range<usize>,
documentation: String,
}

impl Lint {
#[must_use]
fn new(name: &str, group: &str, desc: &str, module: &str, declaration_range: Range<usize>) -> Self {
fn new(
name: &str,
group: &str,
desc: &str,
module: &str,
declaration_range: Range<usize>,
documentation: String,
) -> Self {
Self {
name: name.to_lowercase(),
group: group.into(),
desc: remove_line_splices(desc),
module: module.into(),
declaration_range,
documentation,
}
}

Expand Down Expand Up @@ -852,27 +919,35 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
}| token_kind == &TokenKind::Ident && *content == "declare_clippy_lint",
) {
let start = range.start;

let mut iter = iter
.by_ref()
.filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
let mut docs = String::with_capacity(128);
let mut iter = iter.by_ref().filter(|t| !matches!(t.token_kind, TokenKind::Whitespace));
// matches `!{`
match_tokens!(iter, Bang OpenBrace);
match iter.next() {
// #[clippy::version = "version"] pub
Some(LintDeclSearchResult {
token_kind: TokenKind::Pound,
..
}) => {
match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident);
},
// pub
Some(LintDeclSearchResult {
token_kind: TokenKind::Ident,
..
}) => (),
_ => continue,
let mut in_code = false;
while let Some(t) = iter.next() {
match t.token_kind {
TokenKind::LineComment { .. } => {
if let Some(line) = t.content.strip_prefix("/// ").or_else(|| t.content.strip_prefix("///")) {
if line.starts_with("```") {
docs += "```\n";
in_code = !in_code;
} else if !(in_code && line.starts_with("# ")) {
docs += line;
docs.push('\n');
}
}
},
TokenKind::Pound => {
match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident);
break;
},
TokenKind::Ident => {
break;
},
_ => {},
}
}
docs.pop(); // remove final newline

let (name, group, desc) = match_tokens!(
iter,
Expand All @@ -890,7 +965,7 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
..
}) = iter.next()
{
lints.push(Lint::new(name, group, desc, module, start..range.end));
lints.push(Lint::new(name, group, desc, module, start..range.end, docs));
}
}
}
Expand Down Expand Up @@ -1120,13 +1195,15 @@ mod tests {
"\"really long text\"",
"module_name",
Range::default(),
String::new(),
),
Lint::new(
"doc_markdown",
"pedantic",
"\"single line\"",
"module_name",
Range::default(),
String::new(),
),
];
assert_eq!(expected, result);
Expand Down Expand Up @@ -1166,20 +1243,23 @@ mod tests {
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
Lint::new(
"should_assert_eq2",
"internal",
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
Lint::new(
"should_assert_eq2",
"internal_style",
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
];
let expected = vec![Lint::new(
Expand All @@ -1188,29 +1268,59 @@ mod tests {
"\"abc\"",
"module_name",
Range::default(),
String::new(),
)];
assert_eq!(expected, Lint::usable_lints(&lints));
}

#[test]
fn test_by_lint_group() {
let lints = vec![
Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
Lint::new(
"should_assert_eq",
"group1",
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
Lint::new(
"should_assert_eq2",
"group2",
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
Lint::new(
"incorrect_match",
"group1",
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
];
let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
expected.insert(
"group1".to_string(),
vec![
Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
Lint::new(
"should_assert_eq",
"group1",
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
Lint::new(
"incorrect_match",
"group1",
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
],
);
expected.insert(
Expand All @@ -1221,6 +1331,7 @@ mod tests {
"\"abc\"",
"module_name",
Range::default(),
String::new(),
)],
);
assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
Expand Down Expand Up @@ -1259,9 +1370,30 @@ mod tests {
#[test]
fn test_gen_lint_group_list() {
let lints = vec![
Lint::new("abc", "group1", "\"abc\"", "module_name", Range::default()),
Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
Lint::new("internal", "internal_style", "\"abc\"", "module_name", Range::default()),
Lint::new(
"abc",
"group1",
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
Lint::new(
"should_assert_eq",
"group1",
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
Lint::new(
"internal",
"internal_style",
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
];
let expected = GENERATED_FILE_COMMENT.to_string()
+ &[
Expand Down

0 comments on commit 30e4532

Please sign in to comment.