Skip to content

Commit

Permalink
Merge pull request rust-lang#3325 from phansch/riir_update_lints_firs…
Browse files Browse the repository at this point in the history
…t_replace_region

RIIR update_lints: Replace lint count in README.md
  • Loading branch information
phansch authored Oct 16, 2018
2 parents 6ae89c4 + 05ffc2d commit dae7abb
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 9 deletions.
134 changes: 128 additions & 6 deletions clippy_dev/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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]
Expand Down
28 changes: 25 additions & 3 deletions clippy_dev/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand All @@ -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)
]
}
);
}

0 comments on commit dae7abb

Please sign in to comment.