From af441b5b072eb01c939d2e92a3496dbedf6d412b Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Tue, 16 Oct 2018 07:24:32 +0200 Subject: [PATCH 1/3] Rename `active_lints` to `usable_lints` Because now `usable_lints` will also exclude internal lints. --- clippy_dev/src/lib.rs | 14 ++++++++------ clippy_dev/src/main.rs | 8 +++++--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 8477183ae56c..d4b74f557170 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -57,9 +57,9 @@ impl Lint { } } - /// Returns all non-deprecated lints - pub fn active_lints(lints: impl Iterator) -> impl Iterator { - lints.filter(|l| l.deprecation.is_none()) + /// Returns all non-deprecated lints and non-internal lints + pub fn usable_lints(lints: impl Iterator) -> impl Iterator { + lints.filter(|l| l.deprecation.is_none() && !l.group.starts_with("internal")) } /// Returns the lints in a HashMap, grouped by the different lint groups @@ -141,15 +141,17 @@ declare_deprecated_lint! { } #[test] -fn test_active_lints() { +fn test_usable_lints() { let lints = vec![ Lint::new("should_assert_eq", "Deprecated", "abc", Some("Reason"), "module_name"), - Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name") + Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name"), + Lint::new("should_assert_eq2", "internal", "abc", None, "module_name"), + Lint::new("should_assert_eq2", "internal_style", "abc", None, "module_name") ]; let expected = vec![ Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name") ]; - assert_eq!(expected, Lint::active_lints(lints.into_iter()).collect::>()); + assert_eq!(expected, Lint::usable_lints(lints.into_iter()).collect::>()); } #[test] diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 9e78def78fe4..ff7d47366ae5 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -37,8 +37,10 @@ fn main() { } fn print_lints() { - let lint_list = gather_all().collect::>(); - let grouped_by_lint_group = Lint::by_lint_group(&lint_list); + let lint_list = gather_all(); + let usable_lints: Vec = Lint::usable_lints(lint_list).collect(); + let lint_count = usable_lints.len(); + let grouped_by_lint_group = Lint::by_lint_group(&usable_lints); for (lint_group, mut lints) in grouped_by_lint_group { if lint_group == "Deprecated" { continue; } @@ -51,5 +53,5 @@ fn print_lints() { } } - println!("there are {} lints", Lint::active_lints(lint_list.into_iter()).count()); + println!("there are {} lints", lint_count); } From 956987f43e3012c3487973cc0dd47f7c4eaa7942 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Tue, 16 Oct 2018 08:00:31 +0200 Subject: [PATCH 2/3] RIIR update_lints: Replace lint count in README.md This allows the usage of `util/dev update_lints` which will write the new lint_count to the `README.md`. --- clippy_dev/src/lib.rs | 119 +++++++++++++++++++++++++++++++++++++++++ clippy_dev/src/main.rs | 20 +++++++ 2 files changed, 139 insertions(+) diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index d4b74f557170..1c303d180d2c 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -101,6 +101,88 @@ fn lint_files() -> impl Iterator { .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) } +/// Replace a region in a file delimited by two lines matching regexes. +/// +/// `path` is the relative path to the file on which you want to perform the replacement. +/// +/// See `replace_region_in_text` for documentation of the other options. +pub fn replace_region_in_file(path: &str, start: &str, end: &str, replace_start: bool, replacements: F) where F: Fn() -> Vec { + let mut f = fs::File::open(path).expect(&format!("File not found: {}", path)); + let mut contents = String::new(); + f.read_to_string(&mut contents).expect("Something went wrong reading the file"); + let replaced = replace_region_in_text(&contents, start, end, replace_start, replacements); + + let mut f = fs::File::create(path).expect(&format!("File not found: {}", path)); + f.write_all(replaced.as_bytes()).expect("Unable to write file"); + // Ensure we write the changes with a trailing newline so that + // the file has the proper line endings. + f.write(b"\n").expect("Unable to write file"); +} + +/// Replace a region in a text delimited by two lines matching regexes. +/// +/// * `text` is the input text on which you want to perform the replacement +/// * `start` is a `&str` that describes the delimiter line before the region you want to replace. +/// As the `&str` will be converted to a `Regex`, this can contain regex syntax, too. +/// * `end` is a `&str` that describes the delimiter line until where the replacement should +/// happen. As the `&str` will be converted to a `Regex`, this can contain regex syntax, too. +/// * If `replace_start` is true, the `start` delimiter line is replaced as well. +/// The `end` delimiter line is never replaced. +/// * `replacements` is a closure that has to return a `Vec` which contains the new text. +/// +/// If you want to perform the replacement on files instead of already parsed text, +/// use `replace_region_in_file`. +/// +/// # Example +/// +/// ``` +/// let the_text = "replace_start\nsome text\nthat will be replaced\nreplace_end"; +/// let result = clippy_dev::replace_region_in_text( +/// the_text, +/// r#"replace_start"#, +/// r#"replace_end"#, +/// false, +/// || { +/// vec!["a different".to_string(), "text".to_string()] +/// } +/// ); +/// assert_eq!("replace_start\na different\ntext\nreplace_end", result); +/// ``` +pub fn replace_region_in_text(text: &str, start: &str, end: &str, replace_start: bool, replacements: F) -> String where F: Fn() -> Vec { + let lines = text.lines(); + let mut in_old_region = false; + let mut found = false; + let mut new_lines = vec![]; + let start = Regex::new(start).unwrap(); + let end = Regex::new(end).unwrap(); + + for line in lines { + if in_old_region { + if end.is_match(&line) { + in_old_region = false; + new_lines.extend(replacements()); + new_lines.push(line.to_string()); + } + } else if start.is_match(&line) { + if !replace_start { + new_lines.push(line.to_string()); + } + in_old_region = true; + found = true; + } else { + new_lines.push(line.to_string()); + } + } + + if !found { + // This happens if the provided regex in `clippy_dev/src/main.rs` is not found in the + // given text or file. Most likely this is an error on the programmer's side and the Regex + // is incorrect. + println!("regex {:?} not found. You may have to update it.", start); + } + new_lines.join("\n") +} + #[test] fn test_parse_contents() { let result: Vec = parse_contents( @@ -140,6 +222,43 @@ declare_deprecated_lint! { assert_eq!(expected, result); } +#[test] +fn test_replace_region() { + let text = r#" +abc +123 +789 +def +ghi"#; + let expected = r#" +abc +hello world +def +ghi"#; + let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, false, || { + vec!["hello world".to_string()] + }); + assert_eq!(expected, result); +} + +#[test] +fn test_replace_region_with_start() { + let text = r#" +abc +123 +789 +def +ghi"#; + let expected = r#" +hello world +def +ghi"#; + let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, true, || { + vec!["hello world".to_string()] + }); + assert_eq!(expected, result); +} + #[test] fn test_usable_lints() { let lints = vec![ diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index ff7d47366ae5..7b688836a95a 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -32,6 +32,8 @@ fn main() { if let Some(matches) = matches.subcommand_matches("update_lints") { if matches.is_present("print-only") { print_lints(); + } else { + update_lints(); } } } @@ -55,3 +57,21 @@ fn print_lints() { println!("there are {} lints", lint_count); } + +fn update_lints() { + let lint_list = gather_all(); + let usable_lints: Vec = Lint::usable_lints(lint_list).collect(); + let lint_count = usable_lints.len(); + + replace_region_in_file( + "../README.md", + r#"\[There are \d+ lints included in this crate!\]\(https://rust-lang-nursery.github.io/rust-clippy/master/index.html\)"#, + "", + true, + || { + vec![ + format!("[There are {} lints included in this crate!](https://rust-lang-nursery.github.io/rust-clippy/master/index.html)", lint_count) + ] + } + ); +} From 05ffc2d05743c0cc4ad5e551c147dc8385e67bac Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Tue, 16 Oct 2018 20:58:00 +0200 Subject: [PATCH 3/3] Fix dogfood `expect_fun_call` causes a false-positive, so I disabled it for now. --- clippy_dev/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 1c303d180d2c..dcb2de2b1f8e 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -106,6 +106,7 @@ fn lint_files() -> impl Iterator { /// `path` is the relative path to the file on which you want to perform the replacement. /// /// See `replace_region_in_text` for documentation of the other options. +#[allow(clippy::expect_fun_call)] pub fn replace_region_in_file(path: &str, start: &str, end: &str, replace_start: bool, replacements: F) where F: Fn() -> Vec { let mut f = fs::File::open(path).expect(&format!("File not found: {}", path)); let mut contents = String::new(); @@ -116,7 +117,7 @@ pub fn replace_region_in_file(path: &str, start: &str, end: &str, replace_sta f.write_all(replaced.as_bytes()).expect("Unable to write file"); // Ensure we write the changes with a trailing newline so that // the file has the proper line endings. - f.write(b"\n").expect("Unable to write file"); + f.write_all(b"\n").expect("Unable to write file"); } /// Replace a region in a text delimited by two lines matching regexes.