about summary refs log tree commit diff
path: root/clippy_dev
diff options
context:
space:
mode:
authorPhilipp Hansch <dev@phansch.net>2018-10-16 08:00:31 +0200
committerPhilipp Hansch <dev@phansch.net>2018-10-16 08:14:32 +0200
commit956987f43e3012c3487973cc0dd47f7c4eaa7942 (patch)
tree0402d867dafce56deb2225a7abbaab252ac53893 /clippy_dev
parentaf441b5b072eb01c939d2e92a3496dbedf6d412b (diff)
downloadrust-956987f43e3012c3487973cc0dd47f7c4eaa7942.tar.gz
rust-956987f43e3012c3487973cc0dd47f7c4eaa7942.zip
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`.
Diffstat (limited to 'clippy_dev')
-rw-r--r--clippy_dev/src/lib.rs119
-rw-r--r--clippy_dev/src/main.rs20
2 files changed, 139 insertions, 0 deletions
diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs
index d4b74f55717..1c303d180d2 100644
--- a/clippy_dev/src/lib.rs
+++ b/clippy_dev/src/lib.rs
@@ -101,6 +101,88 @@ fn lint_files() -> impl Iterator<Item=walkdir::DirEntry> {
         .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<F>(path: &str, start: &str, end: &str, replace_start: bool, replacements: F) where F: Fn() -> Vec<String> {
+    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<String>` 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<F>(text: &str, start: &str, end: &str, replace_start: bool, replacements: F) -> String where F: Fn() -> Vec<String> {
+    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<Lint> = parse_contents(
@@ -141,6 +223,43 @@ declare_deprecated_lint! {
 }
 
 #[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![
         Lint::new("should_assert_eq", "Deprecated", "abc", Some("Reason"), "module_name"),
diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs
index ff7d47366ae..7b688836a95 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> = 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)
+            ]
+        }
+    );
+}