Skip to content

Commit dae7abb

Browse files
authored
Merge pull request #3325 from phansch/riir_update_lints_first_replace_region
RIIR update_lints: Replace lint count in README.md
2 parents 6ae89c4 + 05ffc2d commit dae7abb

File tree

2 files changed

+153
-9
lines changed

2 files changed

+153
-9
lines changed

clippy_dev/src/lib.rs

+128-6
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,9 @@ impl Lint {
5757
}
5858
}
5959

60-
/// Returns all non-deprecated lints
61-
pub fn active_lints(lints: impl Iterator<Item=Self>) -> impl Iterator<Item=Self> {
62-
lints.filter(|l| l.deprecation.is_none())
60+
/// Returns all non-deprecated lints and non-internal lints
61+
pub fn usable_lints(lints: impl Iterator<Item=Self>) -> impl Iterator<Item=Self> {
62+
lints.filter(|l| l.deprecation.is_none() && !l.group.starts_with("internal"))
6363
}
6464

6565
/// Returns the lints in a HashMap, grouped by the different lint groups
@@ -101,6 +101,89 @@ fn lint_files() -> impl Iterator<Item=walkdir::DirEntry> {
101101
.filter(|f| f.path().extension() == Some(OsStr::new("rs")))
102102
}
103103

104+
/// Replace a region in a file delimited by two lines matching regexes.
105+
///
106+
/// `path` is the relative path to the file on which you want to perform the replacement.
107+
///
108+
/// See `replace_region_in_text` for documentation of the other options.
109+
#[allow(clippy::expect_fun_call)]
110+
pub fn replace_region_in_file<F>(path: &str, start: &str, end: &str, replace_start: bool, replacements: F) where F: Fn() -> Vec<String> {
111+
let mut f = fs::File::open(path).expect(&format!("File not found: {}", path));
112+
let mut contents = String::new();
113+
f.read_to_string(&mut contents).expect("Something went wrong reading the file");
114+
let replaced = replace_region_in_text(&contents, start, end, replace_start, replacements);
115+
116+
let mut f = fs::File::create(path).expect(&format!("File not found: {}", path));
117+
f.write_all(replaced.as_bytes()).expect("Unable to write file");
118+
// Ensure we write the changes with a trailing newline so that
119+
// the file has the proper line endings.
120+
f.write_all(b"\n").expect("Unable to write file");
121+
}
122+
123+
/// Replace a region in a text delimited by two lines matching regexes.
124+
///
125+
/// * `text` is the input text on which you want to perform the replacement
126+
/// * `start` is a `&str` that describes the delimiter line before the region you want to replace.
127+
/// As the `&str` will be converted to a `Regex`, this can contain regex syntax, too.
128+
/// * `end` is a `&str` that describes the delimiter line until where the replacement should
129+
/// happen. As the `&str` will be converted to a `Regex`, this can contain regex syntax, too.
130+
/// * If `replace_start` is true, the `start` delimiter line is replaced as well.
131+
/// The `end` delimiter line is never replaced.
132+
/// * `replacements` is a closure that has to return a `Vec<String>` which contains the new text.
133+
///
134+
/// If you want to perform the replacement on files instead of already parsed text,
135+
/// use `replace_region_in_file`.
136+
///
137+
/// # Example
138+
///
139+
/// ```
140+
/// let the_text = "replace_start\nsome text\nthat will be replaced\nreplace_end";
141+
/// let result = clippy_dev::replace_region_in_text(
142+
/// the_text,
143+
/// r#"replace_start"#,
144+
/// r#"replace_end"#,
145+
/// false,
146+
/// || {
147+
/// vec!["a different".to_string(), "text".to_string()]
148+
/// }
149+
/// );
150+
/// assert_eq!("replace_start\na different\ntext\nreplace_end", result);
151+
/// ```
152+
pub fn replace_region_in_text<F>(text: &str, start: &str, end: &str, replace_start: bool, replacements: F) -> String where F: Fn() -> Vec<String> {
153+
let lines = text.lines();
154+
let mut in_old_region = false;
155+
let mut found = false;
156+
let mut new_lines = vec![];
157+
let start = Regex::new(start).unwrap();
158+
let end = Regex::new(end).unwrap();
159+
160+
for line in lines {
161+
if in_old_region {
162+
if end.is_match(&line) {
163+
in_old_region = false;
164+
new_lines.extend(replacements());
165+
new_lines.push(line.to_string());
166+
}
167+
} else if start.is_match(&line) {
168+
if !replace_start {
169+
new_lines.push(line.to_string());
170+
}
171+
in_old_region = true;
172+
found = true;
173+
} else {
174+
new_lines.push(line.to_string());
175+
}
176+
}
177+
178+
if !found {
179+
// This happens if the provided regex in `clippy_dev/src/main.rs` is not found in the
180+
// given text or file. Most likely this is an error on the programmer's side and the Regex
181+
// is incorrect.
182+
println!("regex {:?} not found. You may have to update it.", start);
183+
}
184+
new_lines.join("\n")
185+
}
186+
104187
#[test]
105188
fn test_parse_contents() {
106189
let result: Vec<Lint> = parse_contents(
@@ -141,15 +224,54 @@ declare_deprecated_lint! {
141224
}
142225

143226
#[test]
144-
fn test_active_lints() {
227+
fn test_replace_region() {
228+
let text = r#"
229+
abc
230+
123
231+
789
232+
def
233+
ghi"#;
234+
let expected = r#"
235+
abc
236+
hello world
237+
def
238+
ghi"#;
239+
let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, false, || {
240+
vec!["hello world".to_string()]
241+
});
242+
assert_eq!(expected, result);
243+
}
244+
245+
#[test]
246+
fn test_replace_region_with_start() {
247+
let text = r#"
248+
abc
249+
123
250+
789
251+
def
252+
ghi"#;
253+
let expected = r#"
254+
hello world
255+
def
256+
ghi"#;
257+
let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, true, || {
258+
vec!["hello world".to_string()]
259+
});
260+
assert_eq!(expected, result);
261+
}
262+
263+
#[test]
264+
fn test_usable_lints() {
145265
let lints = vec![
146266
Lint::new("should_assert_eq", "Deprecated", "abc", Some("Reason"), "module_name"),
147-
Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name")
267+
Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name"),
268+
Lint::new("should_assert_eq2", "internal", "abc", None, "module_name"),
269+
Lint::new("should_assert_eq2", "internal_style", "abc", None, "module_name")
148270
];
149271
let expected = vec![
150272
Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name")
151273
];
152-
assert_eq!(expected, Lint::active_lints(lints.into_iter()).collect::<Vec<Lint>>());
274+
assert_eq!(expected, Lint::usable_lints(lints.into_iter()).collect::<Vec<Lint>>());
153275
}
154276

155277
#[test]

clippy_dev/src/main.rs

+25-3
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,17 @@ fn main() {
3232
if let Some(matches) = matches.subcommand_matches("update_lints") {
3333
if matches.is_present("print-only") {
3434
print_lints();
35+
} else {
36+
update_lints();
3537
}
3638
}
3739
}
3840

3941
fn print_lints() {
40-
let lint_list = gather_all().collect::<Vec<Lint>>();
41-
let grouped_by_lint_group = Lint::by_lint_group(&lint_list);
42+
let lint_list = gather_all();
43+
let usable_lints: Vec<Lint> = Lint::usable_lints(lint_list).collect();
44+
let lint_count = usable_lints.len();
45+
let grouped_by_lint_group = Lint::by_lint_group(&usable_lints);
4246

4347
for (lint_group, mut lints) in grouped_by_lint_group {
4448
if lint_group == "Deprecated" { continue; }
@@ -51,5 +55,23 @@ fn print_lints() {
5155
}
5256
}
5357

54-
println!("there are {} lints", Lint::active_lints(lint_list.into_iter()).count());
58+
println!("there are {} lints", lint_count);
59+
}
60+
61+
fn update_lints() {
62+
let lint_list = gather_all();
63+
let usable_lints: Vec<Lint> = Lint::usable_lints(lint_list).collect();
64+
let lint_count = usable_lints.len();
65+
66+
replace_region_in_file(
67+
"../README.md",
68+
r#"\[There are \d+ lints included in this crate!\]\(https://rust-lang-nursery.github.io/rust-clippy/master/index.html\)"#,
69+
"",
70+
true,
71+
|| {
72+
vec![
73+
format!("[There are {} lints included in this crate!](https://rust-lang-nursery.github.io/rust-clippy/master/index.html)", lint_count)
74+
]
75+
}
76+
);
5577
}

0 commit comments

Comments
 (0)