Skip to content

Commit e2907ff

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

File tree

570 files changed

+13672
-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

+13672
-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
}
@@ -1116,13 +1191,15 @@ mod tests {
11161191
"\"really long text\"",
11171192
"module_name",
11181193
Range::default(),
1194+
String::new(),
11191195
),
11201196
Lint::new(
11211197
"doc_markdown",
11221198
"pedantic",
11231199
"\"single line\"",
11241200
"module_name",
11251201
Range::default(),
1202+
String::new(),
11261203
),
11271204
];
11281205
assert_eq!(expected, result);
@@ -1162,20 +1239,23 @@ mod tests {
11621239
"\"abc\"",
11631240
"module_name",
11641241
Range::default(),
1242+
String::new(),
11651243
),
11661244
Lint::new(
11671245
"should_assert_eq2",
11681246
"internal",
11691247
"\"abc\"",
11701248
"module_name",
11711249
Range::default(),
1250+
String::new(),
11721251
),
11731252
Lint::new(
11741253
"should_assert_eq2",
11751254
"internal_style",
11761255
"\"abc\"",
11771256
"module_name",
11781257
Range::default(),
1258+
String::new(),
11791259
),
11801260
];
11811261
let expected = vec![Lint::new(
@@ -1184,29 +1264,59 @@ mod tests {
11841264
"\"abc\"",
11851265
"module_name",
11861266
Range::default(),
1267+
String::new(),
11871268
)];
11881269
assert_eq!(expected, Lint::usable_lints(&lints));
11891270
}
11901271

11911272
#[test]
11921273
fn test_by_lint_group() {
11931274
let lints = vec![
1194-
Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
1275+
Lint::new(
1276+
"should_assert_eq",
1277+
"group1",
1278+
"\"abc\"",
1279+
"module_name",
1280+
Range::default(),
1281+
String::new(),
1282+
),
11951283
Lint::new(
11961284
"should_assert_eq2",
11971285
"group2",
11981286
"\"abc\"",
11991287
"module_name",
12001288
Range::default(),
1289+
String::new(),
1290+
),
1291+
Lint::new(
1292+
"incorrect_match",
1293+
"group1",
1294+
"\"abc\"",
1295+
"module_name",
1296+
Range::default(),
1297+
String::new(),
12011298
),
1202-
Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
12031299
];
12041300
let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
12051301
expected.insert(
12061302
"group1".to_string(),
12071303
vec![
1208-
Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
1209-
Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
1304+
Lint::new(
1305+
"should_assert_eq",
1306+
"group1",
1307+
"\"abc\"",
1308+
"module_name",
1309+
Range::default(),
1310+
String::new(),
1311+
),
1312+
Lint::new(
1313+
"incorrect_match",
1314+
"group1",
1315+
"\"abc\"",
1316+
"module_name",
1317+
Range::default(),
1318+
String::new(),
1319+
),
12101320
],
12111321
);
12121322
expected.insert(
@@ -1217,6 +1327,7 @@ mod tests {
12171327
"\"abc\"",
12181328
"module_name",
12191329
Range::default(),
1330+
String::new(),
12201331
)],
12211332
);
12221333
assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
@@ -1255,9 +1366,30 @@ mod tests {
12551366
#[test]
12561367
fn test_gen_lint_group_list() {
12571368
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()),
1369+
Lint::new(
1370+
"abc",
1371+
"group1",
1372+
"\"abc\"",
1373+
"module_name",
1374+
Range::default(),
1375+
String::new(),
1376+
),
1377+
Lint::new(
1378+
"should_assert_eq",
1379+
"group1",
1380+
"\"abc\"",
1381+
"module_name",
1382+
Range::default(),
1383+
String::new(),
1384+
),
1385+
Lint::new(
1386+
"internal",
1387+
"internal_style",
1388+
"\"abc\"",
1389+
"module_name",
1390+
Range::default(),
1391+
String::new(),
1392+
),
12611393
];
12621394
let expected = GENERATED_FILE_COMMENT.to_string()
12631395
+ &[

0 commit comments

Comments
 (0)