diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs
index 8477183ae56..dcb2de2b1f8 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<Item=Self>) -> impl Iterator<Item=Self> {
-        lints.filter(|l| l.deprecation.is_none())
+    /// Returns all non-deprecated lints and non-internal lints
+    pub fn usable_lints(lints: impl Iterator<Item=Self>) -> impl Iterator<Item=Self> {
+        lints.filter(|l| l.deprecation.is_none() && !l.group.starts_with("internal"))
     }
 
     /// Returns the lints in a HashMap, grouped by the different lint groups
@@ -101,6 +101,89 @@ 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.
+#[allow(clippy::expect_fun_call)]
+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_all(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,15 +224,54 @@ declare_deprecated_lint! {
 }
 
 #[test]
-fn test_active_lints() {
+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"),
-        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::<Vec<Lint>>());
+    assert_eq!(expected, Lint::usable_lints(lints.into_iter()).collect::<Vec<Lint>>());
 }
 
 #[test]
diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs
index 9e78def78fe..7b688836a95 100644
--- a/clippy_dev/src/main.rs
+++ b/clippy_dev/src/main.rs
@@ -32,13 +32,17 @@ fn main() {
     if let Some(matches) = matches.subcommand_matches("update_lints") {
         if matches.is_present("print-only") {
             print_lints();
+        } else {
+            update_lints();
         }
     }
 }
 
 fn print_lints() {
-    let lint_list = gather_all().collect::<Vec<Lint>>();
-    let grouped_by_lint_group = Lint::by_lint_group(&lint_list);
+    let lint_list = gather_all();
+    let usable_lints: Vec<Lint> = 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 +55,23 @@ fn print_lints() {
         }
     }
 
-    println!("there are {} lints", Lint::active_lints(lint_list.into_iter()).count());
+    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)
+            ]
+        }
+    );
 }