Skip to content

Commit 30e4532

Browse files
committed
Auto merge of #8952 - rust-lang:explain, r=xFredNet
add `--explain` subcommand This closes #8291. --- changelog: add `cargo clippy -- --explain <lintname>` subcommand
2 parents c36696a + ad72aee commit 30e4532

File tree

575 files changed

+13817
-28
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

575 files changed

+13817
-28
lines changed

clippy_dev/src/update_lints.rs

+160-28
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use aho_corasick::AhoCorasickBuilder;
33
use indoc::writedoc;
44
use itertools::Itertools;
55
use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
6-
use std::collections::{HashMap, HashSet};
6+
use std::collections::{BTreeSet, HashMap, HashSet};
77
use std::ffi::OsStr;
88
use std::fmt::Write;
99
use std::fs::{self, OpenOptions};
@@ -124,6 +124,8 @@ fn generate_lint_files(
124124
let content = gen_lint_group_list("all", all_group_lints);
125125
process_file("clippy_lints/src/lib.register_all.rs", update_mode, &content);
126126

127+
update_docs(update_mode, &usable_lints);
128+
127129
for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) {
128130
let content = gen_lint_group_list(&lint_group, lints.iter());
129131
process_file(
@@ -140,6 +142,62 @@ fn generate_lint_files(
140142
process_file("tests/ui/rename.rs", update_mode, &content);
141143
}
142144

145+
fn update_docs(update_mode: UpdateMode, usable_lints: &[Lint]) {
146+
replace_region_in_file(update_mode, Path::new("src/docs.rs"), "docs! {\n", "\n}\n", |res| {
147+
for name in usable_lints.iter().map(|lint| lint.name.clone()).sorted() {
148+
writeln!(res, r#" "{name}","#).unwrap();
149+
}
150+
});
151+
152+
if update_mode == UpdateMode::Check {
153+
let mut extra = BTreeSet::new();
154+
let mut lint_names = usable_lints
155+
.iter()
156+
.map(|lint| lint.name.clone())
157+
.collect::<BTreeSet<_>>();
158+
for file in std::fs::read_dir("src/docs").unwrap() {
159+
let filename = file.unwrap().file_name().into_string().unwrap();
160+
if let Some(name) = filename.strip_suffix(".txt") {
161+
if !lint_names.remove(name) {
162+
extra.insert(name.to_string());
163+
}
164+
}
165+
}
166+
167+
let failed = print_lint_names("extra lint docs:", &extra) | print_lint_names("missing lint docs:", &lint_names);
168+
169+
if failed {
170+
exit_with_failure();
171+
}
172+
} else {
173+
if std::fs::remove_dir_all("src/docs").is_err() {
174+
eprintln!("could not remove src/docs directory");
175+
}
176+
if std::fs::create_dir("src/docs").is_err() {
177+
eprintln!("could not recreate src/docs directory");
178+
}
179+
}
180+
for lint in usable_lints {
181+
process_file(
182+
Path::new("src/docs").join(lint.name.clone() + ".txt"),
183+
update_mode,
184+
&lint.documentation,
185+
);
186+
}
187+
}
188+
189+
fn print_lint_names(header: &str, lints: &BTreeSet<String>) -> bool {
190+
if lints.is_empty() {
191+
return false;
192+
}
193+
println!("{}", header);
194+
for lint in lints.iter().sorted() {
195+
println!(" {}", lint);
196+
}
197+
println!();
198+
true
199+
}
200+
143201
pub fn print_lints() {
144202
let (lint_list, _, _) = gather_all();
145203
let usable_lints = Lint::usable_lints(&lint_list);
@@ -589,17 +647,26 @@ struct Lint {
589647
desc: String,
590648
module: String,
591649
declaration_range: Range<usize>,
650+
documentation: String,
592651
}
593652

594653
impl Lint {
595654
#[must_use]
596-
fn new(name: &str, group: &str, desc: &str, module: &str, declaration_range: Range<usize>) -> Self {
655+
fn new(
656+
name: &str,
657+
group: &str,
658+
desc: &str,
659+
module: &str,
660+
declaration_range: Range<usize>,
661+
documentation: String,
662+
) -> Self {
597663
Self {
598664
name: name.to_lowercase(),
599665
group: group.into(),
600666
desc: remove_line_splices(desc),
601667
module: module.into(),
602668
declaration_range,
669+
documentation,
603670
}
604671
}
605672

@@ -852,27 +919,35 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
852919
}| token_kind == &TokenKind::Ident && *content == "declare_clippy_lint",
853920
) {
854921
let start = range.start;
855-
856-
let mut iter = iter
857-
.by_ref()
858-
.filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
922+
let mut docs = String::with_capacity(128);
923+
let mut iter = iter.by_ref().filter(|t| !matches!(t.token_kind, TokenKind::Whitespace));
859924
// matches `!{`
860925
match_tokens!(iter, Bang OpenBrace);
861-
match iter.next() {
862-
// #[clippy::version = "version"] pub
863-
Some(LintDeclSearchResult {
864-
token_kind: TokenKind::Pound,
865-
..
866-
}) => {
867-
match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident);
868-
},
869-
// pub
870-
Some(LintDeclSearchResult {
871-
token_kind: TokenKind::Ident,
872-
..
873-
}) => (),
874-
_ => continue,
926+
let mut in_code = false;
927+
while let Some(t) = iter.next() {
928+
match t.token_kind {
929+
TokenKind::LineComment { .. } => {
930+
if let Some(line) = t.content.strip_prefix("/// ").or_else(|| t.content.strip_prefix("///")) {
931+
if line.starts_with("```") {
932+
docs += "```\n";
933+
in_code = !in_code;
934+
} else if !(in_code && line.starts_with("# ")) {
935+
docs += line;
936+
docs.push('\n');
937+
}
938+
}
939+
},
940+
TokenKind::Pound => {
941+
match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident);
942+
break;
943+
},
944+
TokenKind::Ident => {
945+
break;
946+
},
947+
_ => {},
948+
}
875949
}
950+
docs.pop(); // remove final newline
876951

877952
let (name, group, desc) = match_tokens!(
878953
iter,
@@ -890,7 +965,7 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
890965
..
891966
}) = iter.next()
892967
{
893-
lints.push(Lint::new(name, group, desc, module, start..range.end));
968+
lints.push(Lint::new(name, group, desc, module, start..range.end, docs));
894969
}
895970
}
896971
}
@@ -1120,13 +1195,15 @@ mod tests {
11201195
"\"really long text\"",
11211196
"module_name",
11221197
Range::default(),
1198+
String::new(),
11231199
),
11241200
Lint::new(
11251201
"doc_markdown",
11261202
"pedantic",
11271203
"\"single line\"",
11281204
"module_name",
11291205
Range::default(),
1206+
String::new(),
11301207
),
11311208
];
11321209
assert_eq!(expected, result);
@@ -1166,20 +1243,23 @@ mod tests {
11661243
"\"abc\"",
11671244
"module_name",
11681245
Range::default(),
1246+
String::new(),
11691247
),
11701248
Lint::new(
11711249
"should_assert_eq2",
11721250
"internal",
11731251
"\"abc\"",
11741252
"module_name",
11751253
Range::default(),
1254+
String::new(),
11761255
),
11771256
Lint::new(
11781257
"should_assert_eq2",
11791258
"internal_style",
11801259
"\"abc\"",
11811260
"module_name",
11821261
Range::default(),
1262+
String::new(),
11831263
),
11841264
];
11851265
let expected = vec![Lint::new(
@@ -1188,29 +1268,59 @@ mod tests {
11881268
"\"abc\"",
11891269
"module_name",
11901270
Range::default(),
1271+
String::new(),
11911272
)];
11921273
assert_eq!(expected, Lint::usable_lints(&lints));
11931274
}
11941275

11951276
#[test]
11961277
fn test_by_lint_group() {
11971278
let lints = vec![
1198-
Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
1279+
Lint::new(
1280+
"should_assert_eq",
1281+
"group1",
1282+
"\"abc\"",
1283+
"module_name",
1284+
Range::default(),
1285+
String::new(),
1286+
),
11991287
Lint::new(
12001288
"should_assert_eq2",
12011289
"group2",
12021290
"\"abc\"",
12031291
"module_name",
12041292
Range::default(),
1293+
String::new(),
1294+
),
1295+
Lint::new(
1296+
"incorrect_match",
1297+
"group1",
1298+
"\"abc\"",
1299+
"module_name",
1300+
Range::default(),
1301+
String::new(),
12051302
),
1206-
Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
12071303
];
12081304
let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
12091305
expected.insert(
12101306
"group1".to_string(),
12111307
vec![
1212-
Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
1213-
Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
1308+
Lint::new(
1309+
"should_assert_eq",
1310+
"group1",
1311+
"\"abc\"",
1312+
"module_name",
1313+
Range::default(),
1314+
String::new(),
1315+
),
1316+
Lint::new(
1317+
"incorrect_match",
1318+
"group1",
1319+
"\"abc\"",
1320+
"module_name",
1321+
Range::default(),
1322+
String::new(),
1323+
),
12141324
],
12151325
);
12161326
expected.insert(
@@ -1221,6 +1331,7 @@ mod tests {
12211331
"\"abc\"",
12221332
"module_name",
12231333
Range::default(),
1334+
String::new(),
12241335
)],
12251336
);
12261337
assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
@@ -1259,9 +1370,30 @@ mod tests {
12591370
#[test]
12601371
fn test_gen_lint_group_list() {
12611372
let lints = vec![
1262-
Lint::new("abc", "group1", "\"abc\"", "module_name", Range::default()),
1263-
Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
1264-
Lint::new("internal", "internal_style", "\"abc\"", "module_name", Range::default()),
1373+
Lint::new(
1374+
"abc",
1375+
"group1",
1376+
"\"abc\"",
1377+
"module_name",
1378+
Range::default(),
1379+
String::new(),
1380+
),
1381+
Lint::new(
1382+
"should_assert_eq",
1383+
"group1",
1384+
"\"abc\"",
1385+
"module_name",
1386+
Range::default(),
1387+
String::new(),
1388+
),
1389+
Lint::new(
1390+
"internal",
1391+
"internal_style",
1392+
"\"abc\"",
1393+
"module_name",
1394+
Range::default(),
1395+
String::new(),
1396+
),
12651397
];
12661398
let expected = GENERATED_FILE_COMMENT.to_string()
12671399
+ &[

0 commit comments

Comments
 (0)