diff options
Diffstat (limited to 'src/tools/clippy/clippy_dev')
-rw-r--r-- | src/tools/clippy/clippy_dev/src/bless.rs | 2 | ||||
-rw-r--r-- | src/tools/clippy/clippy_dev/src/fmt.rs | 8 | ||||
-rw-r--r-- | src/tools/clippy/clippy_dev/src/lib.rs | 2 | ||||
-rw-r--r-- | src/tools/clippy/clippy_dev/src/new_lint.rs | 10 | ||||
-rw-r--r-- | src/tools/clippy/clippy_dev/src/update_lints.rs | 204 |
5 files changed, 182 insertions, 44 deletions
diff --git a/src/tools/clippy/clippy_dev/src/bless.rs b/src/tools/clippy/clippy_dev/src/bless.rs index f5c51b947..92b2771f3 100644 --- a/src/tools/clippy/clippy_dev/src/bless.rs +++ b/src/tools/clippy/clippy_dev/src/bless.rs @@ -37,7 +37,7 @@ fn update_reference_file(test_output_entry: &DirEntry, ignore_timestamp: bool) { return; } - let test_output_file = fs::read(&test_output_path).expect("Unable to read test output file"); + let test_output_file = fs::read(test_output_path).expect("Unable to read test output file"); let reference_file = fs::read(&reference_file_path).unwrap_or_default(); if test_output_file != reference_file { diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs index 3b27f061e..357cf6fc4 100644 --- a/src/tools/clippy/clippy_dev/src/fmt.rs +++ b/src/tools/clippy/clippy_dev/src/fmt.rs @@ -46,7 +46,7 @@ pub fn run(check: bool, verbose: bool) { // dependency if fs::read_to_string(project_root.join("Cargo.toml")) .expect("Failed to read clippy Cargo.toml") - .contains(&"[target.'cfg(NOT_A_PLATFORM)'.dependencies]") + .contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") { return Err(CliError::IntellijSetupActive); } @@ -193,10 +193,10 @@ fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> { let args = &["--version"]; if context.verbose { - println!("{}", format_command(&program, &dir, args)); + println!("{}", format_command(program, &dir, args)); } - let output = Command::new(&program).current_dir(&dir).args(args.iter()).output()?; + let output = Command::new(program).current_dir(&dir).args(args.iter()).output()?; if output.status.success() { Ok(()) @@ -207,7 +207,7 @@ fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> { Err(CliError::RustfmtNotInstalled) } else { Err(CliError::CommandFailed( - format_command(&program, &dir, args), + format_command(program, &dir, args), std::str::from_utf8(&output.stderr).unwrap_or("").to_string(), )) } diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs index 82574a8e6..54c7456a2 100644 --- a/src/tools/clippy/clippy_dev/src/lib.rs +++ b/src/tools/clippy/clippy_dev/src/lib.rs @@ -1,5 +1,5 @@ #![feature(let_chains)] -#![feature(let_else)] +#![cfg_attr(bootstrap, feature(let_else))] #![feature(once_cell)] #![feature(rustc_private)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index 03d2ef3d1..331b76484 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -120,15 +120,17 @@ fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { let new_lint = if enable_msrv { format!( - "store.register_{lint_pass}_pass(move || Box::new({module_name}::{camel_name}::new(msrv)));\n ", + "store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(msrv)));\n ", lint_pass = lint.pass, + ctor_arg = if lint.pass == "late" { "_" } else { "" }, module_name = lint.name, camel_name = to_camel_case(lint.name), ) } else { format!( - "store.register_{lint_pass}_pass(|| Box::new({module_name}::{camel_name}));\n ", + "store.register_{lint_pass}_pass(|{ctor_arg}| Box::new({module_name}::{camel_name}));\n ", lint_pass = lint.pass, + ctor_arg = if lint.pass == "late" { "_" } else { "" }, module_name = lint.name, camel_name = to_camel_case(lint.name), ) @@ -155,7 +157,7 @@ fn to_camel_case(name: &str) -> String { name.split('_') .map(|s| { if s.is_empty() { - String::from("") + String::new() } else { [&s[0..1].to_uppercase(), &s[1..]].concat() } @@ -438,7 +440,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> let mut lint_context = None; let mut iter = rustc_lexer::tokenize(&file_contents).map(|t| { - let range = offset..offset + t.len; + let range = offset..offset + t.len as usize; offset = range.end; LintDeclSearchResult { diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index aed38bc28..b95061bf8 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -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}; @@ -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( @@ -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); @@ -418,7 +476,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io .expect("failed to find `impl_lint_pass` terminator"); impl_lint_pass_end += impl_lint_pass_start; - if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(&lint_name_upper) { + if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(lint_name_upper) { let mut lint_name_end = impl_lint_pass_start + (lint_name_pos + lint_name_upper.len()); for c in content[lint_name_end..impl_lint_pass_end].chars() { // Remove trailing whitespace @@ -451,7 +509,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io } let mut content = - fs::read_to_string(&path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy())); + fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy())); eprintln!( "warn: you will have to manually remove any code related to `{}` from `{}`", @@ -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, } } @@ -836,7 +903,7 @@ pub(crate) struct LintDeclSearchResult<'a> { fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) { let mut offset = 0usize; let mut iter = tokenize(contents).map(|t| { - let range = offset..offset + t.len; + let range = offset..offset + t.len as usize; offset = range.end; LintDeclSearchResult { @@ -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, @@ -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)); } } } @@ -899,7 +974,7 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) { fn parse_deprecated_contents(contents: &str, lints: &mut Vec<DeprecatedLint>) { let mut offset = 0usize; let mut iter = tokenize(contents).map(|t| { - let range = offset..offset + t.len; + let range = offset..offset + t.len as usize; offset = range.end; LintDeclSearchResult { @@ -946,7 +1021,7 @@ fn parse_renamed_contents(contents: &str, lints: &mut Vec<RenamedLint>) { for line in contents.lines() { let mut offset = 0usize; let mut iter = tokenize(line).map(|t| { - let range = offset..offset + t.len; + let range = offset..offset + t.len as usize; offset = range.end; LintDeclSearchResult { @@ -977,7 +1052,11 @@ fn remove_line_splices(s: &str) -> String { .and_then(|s| s.strip_suffix('"')) .unwrap_or_else(|| panic!("expected quoted string, found `{}`", s)); let mut res = String::with_capacity(s.len()); - unescape::unescape_literal(s, unescape::Mode::Str, &mut |range, _| res.push_str(&s[range])); + unescape::unescape_literal(s, unescape::Mode::Str, &mut |range, ch| { + if ch.is_ok() { + res.push_str(&s[range]); + } + }); res } @@ -1116,6 +1195,7 @@ mod tests { "\"really long text\"", "module_name", Range::default(), + String::new(), ), Lint::new( "doc_markdown", @@ -1123,6 +1203,7 @@ mod tests { "\"single line\"", "module_name", Range::default(), + String::new(), ), ]; assert_eq!(expected, result); @@ -1162,6 +1243,7 @@ mod tests { "\"abc\"", "module_name", Range::default(), + String::new(), ), Lint::new( "should_assert_eq2", @@ -1169,6 +1251,7 @@ mod tests { "\"abc\"", "module_name", Range::default(), + String::new(), ), Lint::new( "should_assert_eq2", @@ -1176,6 +1259,7 @@ mod tests { "\"abc\"", "module_name", Range::default(), + String::new(), ), ]; let expected = vec![Lint::new( @@ -1184,6 +1268,7 @@ mod tests { "\"abc\"", "module_name", Range::default(), + String::new(), )]; assert_eq!(expected, Lint::usable_lints(&lints)); } @@ -1191,22 +1276,51 @@ mod tests { #[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( @@ -1217,6 +1331,7 @@ mod tests { "\"abc\"", "module_name", Range::default(), + String::new(), )], ); assert_eq!(expected, Lint::by_lint_group(lints.into_iter())); @@ -1255,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() + &[ |