Skip to content

Commit d44f5da

Browse files
committed
add --explain subcommand
1 parent cc637ba commit d44f5da

File tree

570 files changed

+14252
-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.

570 files changed

+14252
-28
lines changed

clippy_dev/src/update_lints.rs

+156-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,26 +919,30 @@ 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+
while let Some(t) = iter.next() {
927+
match t.token_kind {
928+
TokenKind::LineComment { .. } => {
929+
let line = t.content.trim_start_matches(['/', ' ']);
930+
if line.starts_with("```") {
931+
docs += "```\n";
932+
} else {
933+
docs += line;
934+
docs.push('\n');
935+
}
936+
},
937+
TokenKind::Pound => {
938+
match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident);
939+
break;
940+
},
941+
TokenKind::Ident => {
942+
break;
943+
},
944+
_ => {},
945+
}
875946
}
876947

877948
let (name, group, desc) = match_tokens!(
@@ -890,7 +961,7 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
890961
..
891962
}) = iter.next()
892963
{
893-
lints.push(Lint::new(name, group, desc, module, start..range.end));
964+
lints.push(Lint::new(name, group, desc, module, start..range.end, docs));
894965
}
895966
}
896967
}
@@ -1116,13 +1187,15 @@ mod tests {
11161187
"\"really long text\"",
11171188
"module_name",
11181189
Range::default(),
1190+
String::new(),
11191191
),
11201192
Lint::new(
11211193
"doc_markdown",
11221194
"pedantic",
11231195
"\"single line\"",
11241196
"module_name",
11251197
Range::default(),
1198+
String::new(),
11261199
),
11271200
];
11281201
assert_eq!(expected, result);
@@ -1162,20 +1235,23 @@ mod tests {
11621235
"\"abc\"",
11631236
"module_name",
11641237
Range::default(),
1238+
String::new(),
11651239
),
11661240
Lint::new(
11671241
"should_assert_eq2",
11681242
"internal",
11691243
"\"abc\"",
11701244
"module_name",
11711245
Range::default(),
1246+
String::new(),
11721247
),
11731248
Lint::new(
11741249
"should_assert_eq2",
11751250
"internal_style",
11761251
"\"abc\"",
11771252
"module_name",
11781253
Range::default(),
1254+
String::new(),
11791255
),
11801256
];
11811257
let expected = vec![Lint::new(
@@ -1184,29 +1260,59 @@ mod tests {
11841260
"\"abc\"",
11851261
"module_name",
11861262
Range::default(),
1263+
String::new(),
11871264
)];
11881265
assert_eq!(expected, Lint::usable_lints(&lints));
11891266
}
11901267

11911268
#[test]
11921269
fn test_by_lint_group() {
11931270
let lints = vec![
1194-
Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
1271+
Lint::new(
1272+
"should_assert_eq",
1273+
"group1",
1274+
"\"abc\"",
1275+
"module_name",
1276+
Range::default(),
1277+
String::new(),
1278+
),
11951279
Lint::new(
11961280
"should_assert_eq2",
11971281
"group2",
11981282
"\"abc\"",
11991283
"module_name",
12001284
Range::default(),
1285+
String::new(),
1286+
),
1287+
Lint::new(
1288+
"incorrect_match",
1289+
"group1",
1290+
"\"abc\"",
1291+
"module_name",
1292+
Range::default(),
1293+
String::new(),
12011294
),
1202-
Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
12031295
];
12041296
let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
12051297
expected.insert(
12061298
"group1".to_string(),
12071299
vec![
1208-
Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
1209-
Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
1300+
Lint::new(
1301+
"should_assert_eq",
1302+
"group1",
1303+
"\"abc\"",
1304+
"module_name",
1305+
Range::default(),
1306+
String::new(),
1307+
),
1308+
Lint::new(
1309+
"incorrect_match",
1310+
"group1",
1311+
"\"abc\"",
1312+
"module_name",
1313+
Range::default(),
1314+
String::new(),
1315+
),
12101316
],
12111317
);
12121318
expected.insert(
@@ -1217,6 +1323,7 @@ mod tests {
12171323
"\"abc\"",
12181324
"module_name",
12191325
Range::default(),
1326+
String::new(),
12201327
)],
12211328
);
12221329
assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
@@ -1255,9 +1362,30 @@ mod tests {
12551362
#[test]
12561363
fn test_gen_lint_group_list() {
12571364
let lints = vec![
1258-
Lint::new("abc", "group1", "\"abc\"", "module_name", Range::default()),
1259-
Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
1260-
Lint::new("internal", "internal_style", "\"abc\"", "module_name", Range::default()),
1365+
Lint::new(
1366+
"abc",
1367+
"group1",
1368+
"\"abc\"",
1369+
"module_name",
1370+
Range::default(),
1371+
String::new(),
1372+
),
1373+
Lint::new(
1374+
"should_assert_eq",
1375+
"group1",
1376+
"\"abc\"",
1377+
"module_name",
1378+
Range::default(),
1379+
String::new(),
1380+
),
1381+
Lint::new(
1382+
"internal",
1383+
"internal_style",
1384+
"\"abc\"",
1385+
"module_name",
1386+
Range::default(),
1387+
String::new(),
1388+
),
12611389
];
12621390
let expected = GENERATED_FILE_COMMENT.to_string()
12631391
+ &[

0 commit comments

Comments
 (0)