RIIR update lints: Add check mode (update_lints.py rewrite complete)
This finishes up the rewrite of `update_lints.py` in Rust. More
specifically, this
* adds the `--check` flag and handling to clippy_dev
* tracks file changes over the different calls to `replace_region_in_file`
* only writes changes to files if the `--check` flag is *not* used
* runs `./util/dev update_lints --check` on CI instead of the old script
* replaces usage of the `update_lints.py` script with an error
`./util/dev update_lints` behaves 99% the same as the python script.
The only difference that I'm aware of is an ordering change to
`clippy_lints/src/lib.rs` because underscores seem to be sorted
differently in Rust and in Python.
🏁
This commit is contained in:
parent
7e417d4cbd
commit
90f31e21ab
@ -180,7 +180,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
|
|||||||
|
|
||||||
The [`rustc_plugin::PluginRegistry`][plugin_registry] provides two methods to register lints: [register_early_lint_pass][reg_early_lint_pass] and [register_late_lint_pass][reg_late_lint_pass].
|
The [`rustc_plugin::PluginRegistry`][plugin_registry] provides two methods to register lints: [register_early_lint_pass][reg_early_lint_pass] and [register_late_lint_pass][reg_late_lint_pass].
|
||||||
Both take an object that implements an [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass] respectively. This is done in every single lint.
|
Both take an object that implements an [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass] respectively. This is done in every single lint.
|
||||||
It's worth noting that the majority of `clippy_lints/src/lib.rs` is autogenerated by `util/update_lints.py` and you don't have to add anything by hand. When you are writing your own lint, you can use that script to save you some time.
|
It's worth noting that the majority of `clippy_lints/src/lib.rs` is autogenerated by `util/dev update_lints` and you don't have to add anything by hand. When you are writing your own lint, you can use that script to save you some time.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// ./clippy_lints/src/else_if_without_else.rs
|
// ./clippy_lints/src/else_if_without_else.rs
|
||||||
|
@ -23,8 +23,9 @@ cargo test --features debugging
|
|||||||
cd clippy_lints && cargo test && cd ..
|
cd clippy_lints && cargo test && cd ..
|
||||||
cd rustc_tools_util && cargo test && cd ..
|
cd rustc_tools_util && cargo test && cd ..
|
||||||
cd clippy_dev && cargo test && cd ..
|
cd clippy_dev && cargo test && cd ..
|
||||||
# check that the lint lists are up-to-date
|
|
||||||
./util/update_lints.py -c
|
# Perform various checks for lint registration
|
||||||
|
./util/dev update_lints --check
|
||||||
|
|
||||||
CLIPPY="`pwd`/target/debug/cargo-clippy clippy"
|
CLIPPY="`pwd`/target/debug/cargo-clippy clippy"
|
||||||
# run clippy on its own codebase...
|
# run clippy on its own codebase...
|
||||||
|
@ -168,23 +168,33 @@ fn lint_files() -> impl Iterator<Item=walkdir::DirEntry> {
|
|||||||
.filter(|f| f.path().extension() == Some(OsStr::new("rs")))
|
.filter(|f| f.path().extension() == Some(OsStr::new("rs")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether a file has had its text changed or not
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
pub struct FileChange {
|
||||||
|
pub changed: bool,
|
||||||
|
pub new_lines: String,
|
||||||
|
}
|
||||||
|
|
||||||
/// Replace a region in a file delimited by two lines matching regexes.
|
/// 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.
|
/// `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.
|
/// See `replace_region_in_text` for documentation of the other options.
|
||||||
#[allow(clippy::expect_fun_call)]
|
#[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> {
|
pub fn replace_region_in_file<F>(path: &str, start: &str, end: &str, replace_start: bool, write_back: bool, replacements: F) -> FileChange where F: Fn() -> Vec<String> {
|
||||||
let mut f = fs::File::open(path).expect(&format!("File not found: {}", path));
|
let mut f = fs::File::open(path).expect(&format!("File not found: {}", path));
|
||||||
let mut contents = String::new();
|
let mut contents = String::new();
|
||||||
f.read_to_string(&mut contents).expect("Something went wrong reading the file");
|
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 file_change = replace_region_in_text(&contents, start, end, replace_start, replacements);
|
||||||
|
|
||||||
let mut f = fs::File::create(path).expect(&format!("File not found: {}", path));
|
if write_back {
|
||||||
f.write_all(replaced.as_bytes()).expect("Unable to write file");
|
let mut f = fs::File::create(path).expect(&format!("File not found: {}", path));
|
||||||
// Ensure we write the changes with a trailing newline so that
|
f.write_all(file_change.new_lines.as_bytes()).expect("Unable to write file");
|
||||||
// the file has the proper line endings.
|
// Ensure we write the changes with a trailing newline so that
|
||||||
f.write_all(b"\n").expect("Unable to write file");
|
// the file has the proper line endings.
|
||||||
|
f.write_all(b"\n").expect("Unable to write file");
|
||||||
|
}
|
||||||
|
file_change
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replace a region in a text delimited by two lines matching regexes.
|
/// Replace a region in a text delimited by two lines matching regexes.
|
||||||
@ -213,10 +223,10 @@ pub fn replace_region_in_file<F>(path: &str, start: &str, end: &str, replace_sta
|
|||||||
/// || {
|
/// || {
|
||||||
/// vec!["a different".to_string(), "text".to_string()]
|
/// vec!["a different".to_string(), "text".to_string()]
|
||||||
/// }
|
/// }
|
||||||
/// );
|
/// ).new_lines;
|
||||||
/// assert_eq!("replace_start\na different\ntext\nreplace_end", result);
|
/// 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> {
|
pub fn replace_region_in_text<F>(text: &str, start: &str, end: &str, replace_start: bool, replacements: F) -> FileChange where F: Fn() -> Vec<String> {
|
||||||
let lines = text.lines();
|
let lines = text.lines();
|
||||||
let mut in_old_region = false;
|
let mut in_old_region = false;
|
||||||
let mut found = false;
|
let mut found = false;
|
||||||
@ -224,7 +234,7 @@ pub fn replace_region_in_text<F>(text: &str, start: &str, end: &str, replace_sta
|
|||||||
let start = Regex::new(start).unwrap();
|
let start = Regex::new(start).unwrap();
|
||||||
let end = Regex::new(end).unwrap();
|
let end = Regex::new(end).unwrap();
|
||||||
|
|
||||||
for line in lines {
|
for line in lines.clone() {
|
||||||
if in_old_region {
|
if in_old_region {
|
||||||
if end.is_match(&line) {
|
if end.is_match(&line) {
|
||||||
in_old_region = false;
|
in_old_region = false;
|
||||||
@ -248,7 +258,10 @@ pub fn replace_region_in_text<F>(text: &str, start: &str, end: &str, replace_sta
|
|||||||
// is incorrect.
|
// is incorrect.
|
||||||
eprintln!("error: regex `{:?}` not found. You may have to update it.", start);
|
eprintln!("error: regex `{:?}` not found. You may have to update it.", start);
|
||||||
}
|
}
|
||||||
new_lines.join("\n")
|
FileChange {
|
||||||
|
changed: lines.ne(new_lines.clone()),
|
||||||
|
new_lines: new_lines.join("\n")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -305,7 +318,7 @@ def
|
|||||||
ghi"#;
|
ghi"#;
|
||||||
let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, false, || {
|
let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, false, || {
|
||||||
vec!["hello world".to_string()]
|
vec!["hello world".to_string()]
|
||||||
});
|
}).new_lines;
|
||||||
assert_eq!(expected, result);
|
assert_eq!(expected, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,7 +336,7 @@ def
|
|||||||
ghi"#;
|
ghi"#;
|
||||||
let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, true, || {
|
let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, true, || {
|
||||||
vec!["hello world".to_string()]
|
vec!["hello world".to_string()]
|
||||||
});
|
}).new_lines;
|
||||||
assert_eq!(expected, result);
|
assert_eq!(expected, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,12 @@ extern crate regex;
|
|||||||
use clap::{App, Arg, SubCommand};
|
use clap::{App, Arg, SubCommand};
|
||||||
use clippy_dev::*;
|
use clippy_dev::*;
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
enum UpdateMode {
|
||||||
|
Check,
|
||||||
|
Change
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let matches = App::new("Clippy developer tooling")
|
let matches = App::new("Clippy developer tooling")
|
||||||
.subcommand(
|
.subcommand(
|
||||||
@ -28,17 +34,23 @@ fn main() {
|
|||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("print-only")
|
Arg::with_name("print-only")
|
||||||
.long("print-only")
|
.long("print-only")
|
||||||
.short("p")
|
.help("Print a table of lints to STDOUT. This does not include deprecated and internal lints. (Does not modify any files)")
|
||||||
.help("Print a table of lints to STDOUT. This does not include deprecated and internal lints. (Does not modify any files)"),
|
|
||||||
)
|
)
|
||||||
)
|
.arg(
|
||||||
.get_matches();
|
Arg::with_name("check")
|
||||||
|
.long("check")
|
||||||
|
.help("Checks that util/dev update_lints has been run. Used on CI."),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.get_matches();
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("update_lints") {
|
if let Some(matches) = matches.subcommand_matches("update_lints") {
|
||||||
if matches.is_present("print-only") {
|
if matches.is_present("print-only") {
|
||||||
print_lints();
|
print_lints();
|
||||||
|
} else if matches.is_present("check") {
|
||||||
|
update_lints(UpdateMode::Check);
|
||||||
} else {
|
} else {
|
||||||
update_lints();
|
update_lints(UpdateMode::Change);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,53 +75,58 @@ fn print_lints() {
|
|||||||
println!("there are {} lints", lint_count);
|
println!("there are {} lints", lint_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_lints() {
|
fn update_lints(update_mode: UpdateMode) {
|
||||||
let lint_list: Vec<Lint> = gather_all().collect();
|
let lint_list: Vec<Lint> = gather_all().collect();
|
||||||
let usable_lints: Vec<Lint> = Lint::usable_lints(lint_list.clone().into_iter()).collect();
|
let usable_lints: Vec<Lint> = Lint::usable_lints(lint_list.clone().into_iter()).collect();
|
||||||
let lint_count = usable_lints.len();
|
let lint_count = usable_lints.len();
|
||||||
|
|
||||||
replace_region_in_file(
|
let mut file_change = replace_region_in_file(
|
||||||
"../README.md",
|
"../README.md",
|
||||||
r#"\[There are \d+ lints included in this crate!\]\(https://rust-lang-nursery.github.io/rust-clippy/master/index.html\)"#,
|
r#"\[There are \d+ lints included in this crate!\]\(https://rust-lang-nursery.github.io/rust-clippy/master/index.html\)"#,
|
||||||
"",
|
"",
|
||||||
true,
|
true,
|
||||||
|
update_mode == UpdateMode::Change,
|
||||||
|| {
|
|| {
|
||||||
vec![
|
vec![
|
||||||
format!("[There are {} lints included in this crate!](https://rust-lang-nursery.github.io/rust-clippy/master/index.html)", lint_count)
|
format!("[There are {} lints included in this crate!](https://rust-lang-nursery.github.io/rust-clippy/master/index.html)", lint_count)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
);
|
).changed;
|
||||||
|
|
||||||
replace_region_in_file(
|
file_change |= replace_region_in_file(
|
||||||
"../CHANGELOG.md",
|
"../CHANGELOG.md",
|
||||||
"<!-- begin autogenerated links to lint list -->",
|
"<!-- begin autogenerated links to lint list -->",
|
||||||
"<!-- end autogenerated links to lint list -->",
|
"<!-- end autogenerated links to lint list -->",
|
||||||
false,
|
false,
|
||||||
|
update_mode == UpdateMode::Change,
|
||||||
|| { gen_changelog_lint_list(lint_list.clone()) }
|
|| { gen_changelog_lint_list(lint_list.clone()) }
|
||||||
);
|
).changed;
|
||||||
|
|
||||||
replace_region_in_file(
|
file_change |= replace_region_in_file(
|
||||||
"../clippy_lints/src/lib.rs",
|
"../clippy_lints/src/lib.rs",
|
||||||
"begin deprecated lints",
|
"begin deprecated lints",
|
||||||
"end deprecated lints",
|
"end deprecated lints",
|
||||||
false,
|
false,
|
||||||
|
update_mode == UpdateMode::Change,
|
||||||
|| { gen_deprecated(&lint_list) }
|
|| { gen_deprecated(&lint_list) }
|
||||||
);
|
).changed;
|
||||||
|
|
||||||
replace_region_in_file(
|
file_change |= replace_region_in_file(
|
||||||
"../clippy_lints/src/lib.rs",
|
"../clippy_lints/src/lib.rs",
|
||||||
"begin lints modules",
|
"begin lints modules",
|
||||||
"end lints modules",
|
"end lints modules",
|
||||||
false,
|
false,
|
||||||
|
update_mode == UpdateMode::Change,
|
||||||
|| { gen_modules_list(lint_list.clone()) }
|
|| { gen_modules_list(lint_list.clone()) }
|
||||||
);
|
).changed;
|
||||||
|
|
||||||
// Generate lists of lints in the clippy::all lint group
|
// Generate lists of lints in the clippy::all lint group
|
||||||
replace_region_in_file(
|
file_change |= replace_region_in_file(
|
||||||
"../clippy_lints/src/lib.rs",
|
"../clippy_lints/src/lib.rs",
|
||||||
r#"reg.register_lint_group\("clippy::all""#,
|
r#"reg.register_lint_group\("clippy::all""#,
|
||||||
r#"\]\);"#,
|
r#"\]\);"#,
|
||||||
false,
|
false,
|
||||||
|
update_mode == UpdateMode::Change,
|
||||||
|| {
|
|| {
|
||||||
// clippy::all should only include the following lint groups:
|
// clippy::all should only include the following lint groups:
|
||||||
let all_group_lints = usable_lints.clone().into_iter().filter(|l| {
|
let all_group_lints = usable_lints.clone().into_iter().filter(|l| {
|
||||||
@ -121,16 +138,22 @@ fn update_lints() {
|
|||||||
|
|
||||||
gen_lint_group_list(all_group_lints)
|
gen_lint_group_list(all_group_lints)
|
||||||
}
|
}
|
||||||
);
|
).changed;
|
||||||
|
|
||||||
// Generate the list of lints for all other lint groups
|
// Generate the list of lints for all other lint groups
|
||||||
for (lint_group, lints) in Lint::by_lint_group(&usable_lints) {
|
for (lint_group, lints) in Lint::by_lint_group(&usable_lints) {
|
||||||
replace_region_in_file(
|
file_change |= replace_region_in_file(
|
||||||
"../clippy_lints/src/lib.rs",
|
"../clippy_lints/src/lib.rs",
|
||||||
&format!("reg.register_lint_group\\(\"clippy::{}\"", lint_group),
|
&format!("reg.register_lint_group\\(\"clippy::{}\"", lint_group),
|
||||||
r#"\]\);"#,
|
r#"\]\);"#,
|
||||||
false,
|
false,
|
||||||
|
update_mode == UpdateMode::Change,
|
||||||
|| { gen_lint_group_list(lints.clone()) }
|
|| { gen_lint_group_list(lints.clone()) }
|
||||||
);
|
).changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if update_mode == UpdateMode::Check && file_change {
|
||||||
|
println!("Not all lints defined properly. Please run util/dev update_lints to make sure all lints are defined properly.");
|
||||||
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -548,8 +548,8 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||||||
bytecount::NAIVE_BYTECOUNT,
|
bytecount::NAIVE_BYTECOUNT,
|
||||||
collapsible_if::COLLAPSIBLE_IF,
|
collapsible_if::COLLAPSIBLE_IF,
|
||||||
const_static_lifetime::CONST_STATIC_LIFETIME,
|
const_static_lifetime::CONST_STATIC_LIFETIME,
|
||||||
copies::IF_SAME_THEN_ELSE,
|
|
||||||
copies::IFS_SAME_COND,
|
copies::IFS_SAME_COND,
|
||||||
|
copies::IF_SAME_THEN_ELSE,
|
||||||
cyclomatic_complexity::CYCLOMATIC_COMPLEXITY,
|
cyclomatic_complexity::CYCLOMATIC_COMPLEXITY,
|
||||||
derive::DERIVE_HASH_XOR_EQ,
|
derive::DERIVE_HASH_XOR_EQ,
|
||||||
double_comparison::DOUBLE_COMPARISONS,
|
double_comparison::DOUBLE_COMPARISONS,
|
||||||
@ -743,12 +743,12 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||||||
unused_io_amount::UNUSED_IO_AMOUNT,
|
unused_io_amount::UNUSED_IO_AMOUNT,
|
||||||
unused_label::UNUSED_LABEL,
|
unused_label::UNUSED_LABEL,
|
||||||
vec::USELESS_VEC,
|
vec::USELESS_VEC,
|
||||||
|
write::PRINTLN_EMPTY_STRING,
|
||||||
write::PRINT_LITERAL,
|
write::PRINT_LITERAL,
|
||||||
write::PRINT_WITH_NEWLINE,
|
write::PRINT_WITH_NEWLINE,
|
||||||
write::PRINTLN_EMPTY_STRING,
|
write::WRITELN_EMPTY_STRING,
|
||||||
write::WRITE_LITERAL,
|
write::WRITE_LITERAL,
|
||||||
write::WRITE_WITH_NEWLINE,
|
write::WRITE_WITH_NEWLINE,
|
||||||
write::WRITELN_EMPTY_STRING,
|
|
||||||
zero_div_zero::ZERO_DIVIDED_BY_ZERO,
|
zero_div_zero::ZERO_DIVIDED_BY_ZERO,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -831,12 +831,12 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||||||
types::IMPLICIT_HASHER,
|
types::IMPLICIT_HASHER,
|
||||||
types::LET_UNIT_VALUE,
|
types::LET_UNIT_VALUE,
|
||||||
unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
|
unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
|
||||||
|
write::PRINTLN_EMPTY_STRING,
|
||||||
write::PRINT_LITERAL,
|
write::PRINT_LITERAL,
|
||||||
write::PRINT_WITH_NEWLINE,
|
write::PRINT_WITH_NEWLINE,
|
||||||
write::PRINTLN_EMPTY_STRING,
|
write::WRITELN_EMPTY_STRING,
|
||||||
write::WRITE_LITERAL,
|
write::WRITE_LITERAL,
|
||||||
write::WRITE_WITH_NEWLINE,
|
write::WRITE_WITH_NEWLINE,
|
||||||
write::WRITELN_EMPTY_STRING,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
reg.register_lint_group("clippy::complexity", Some("clippy_complexity"), vec![
|
reg.register_lint_group("clippy::complexity", Some("clippy_complexity"), vec![
|
||||||
@ -916,8 +916,8 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||||||
bit_mask::BAD_BIT_MASK,
|
bit_mask::BAD_BIT_MASK,
|
||||||
bit_mask::INEFFECTIVE_BIT_MASK,
|
bit_mask::INEFFECTIVE_BIT_MASK,
|
||||||
booleans::LOGIC_BUG,
|
booleans::LOGIC_BUG,
|
||||||
copies::IF_SAME_THEN_ELSE,
|
|
||||||
copies::IFS_SAME_COND,
|
copies::IFS_SAME_COND,
|
||||||
|
copies::IF_SAME_THEN_ELSE,
|
||||||
derive::DERIVE_HASH_XOR_EQ,
|
derive::DERIVE_HASH_XOR_EQ,
|
||||||
drop_forget_ref::DROP_COPY,
|
drop_forget_ref::DROP_COPY,
|
||||||
drop_forget_ref::DROP_REF,
|
drop_forget_ref::DROP_REF,
|
||||||
|
@ -10,245 +10,11 @@
|
|||||||
# option. This file may not be copied, modified, or distributed
|
# option. This file may not be copied, modified, or distributed
|
||||||
# except according to those terms.
|
# except according to those terms.
|
||||||
|
|
||||||
|
|
||||||
# Generate a Markdown table of all lints, and put it in README.md.
|
|
||||||
# With -n option, only print the new table to stdout.
|
|
||||||
# With -c option, print a warning and set exit status to 1 if a file would be
|
|
||||||
# changed.
|
|
||||||
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
import sys
|
||||||
from subprocess import call
|
|
||||||
|
|
||||||
declare_deprecated_lint_re = re.compile(r'''
|
|
||||||
declare_deprecated_lint! \s* [{(] \s*
|
|
||||||
pub \s+ (?P<name>[A-Z_][A-Z_0-9]*) \s*,\s*
|
|
||||||
" (?P<desc>(?:[^"\\]+|\\.)*) " \s* [})]
|
|
||||||
''', re.VERBOSE | re.DOTALL)
|
|
||||||
|
|
||||||
declare_clippy_lint_re = re.compile(r'''
|
|
||||||
declare_clippy_lint! \s* [{(] \s*
|
|
||||||
pub \s+ (?P<name>[A-Z_][A-Z_0-9]*) \s*,\s*
|
|
||||||
(?P<cat>[a-z_]+) \s*,\s*
|
|
||||||
" (?P<desc>(?:[^"\\]+|\\.)*) " \s* [})]
|
|
||||||
''', re.VERBOSE | re.DOTALL)
|
|
||||||
|
|
||||||
nl_escape_re = re.compile(r'\\\n\s*')
|
|
||||||
|
|
||||||
docs_link = 'https://rust-lang-nursery.github.io/rust-clippy/master/index.html'
|
|
||||||
|
|
||||||
|
|
||||||
def collect(deprecated_lints, clippy_lints, fn):
|
|
||||||
"""Collect all lints from a file.
|
|
||||||
|
|
||||||
Adds entries to the lints list as `(module, name, level, desc)`.
|
|
||||||
"""
|
|
||||||
with open(fn) as fp:
|
|
||||||
code = fp.read()
|
|
||||||
|
|
||||||
for match in declare_deprecated_lint_re.finditer(code):
|
|
||||||
# remove \-newline escapes from description string
|
|
||||||
desc = nl_escape_re.sub('', match.group('desc'))
|
|
||||||
deprecated_lints.append((os.path.splitext(os.path.basename(fn))[0],
|
|
||||||
match.group('name').lower(),
|
|
||||||
desc.replace('\\"', '"')))
|
|
||||||
|
|
||||||
for match in declare_clippy_lint_re.finditer(code):
|
|
||||||
# remove \-newline escapes from description string
|
|
||||||
desc = nl_escape_re.sub('', match.group('desc'))
|
|
||||||
cat = match.group('cat')
|
|
||||||
if cat in ('internal', 'internal_warn'):
|
|
||||||
continue
|
|
||||||
module_name = os.path.splitext(os.path.basename(fn))[0]
|
|
||||||
if module_name == 'mod':
|
|
||||||
module_name = os.path.basename(os.path.dirname(fn))
|
|
||||||
clippy_lints[cat].append((module_name,
|
|
||||||
match.group('name').lower(),
|
|
||||||
"allow",
|
|
||||||
desc.replace('\\"', '"')))
|
|
||||||
|
|
||||||
|
|
||||||
def gen_group(lints):
|
|
||||||
"""Write lint group (list of all lints in the form module::NAME)."""
|
|
||||||
for (module, name, _, _) in sorted(lints):
|
|
||||||
yield ' %s::%s,\n' % (module, name.upper())
|
|
||||||
|
|
||||||
|
|
||||||
def gen_mods(lints):
|
|
||||||
"""Declare modules"""
|
|
||||||
|
|
||||||
for module in sorted(set(lint[0] for lint in lints)):
|
|
||||||
yield 'pub mod %s;\n' % module
|
|
||||||
|
|
||||||
|
|
||||||
def gen_deprecated(lints):
|
|
||||||
"""Declare deprecated lints"""
|
|
||||||
|
|
||||||
for lint in lints:
|
|
||||||
yield ' store.register_removed(\n'
|
|
||||||
yield ' "%s",\n' % lint[1]
|
|
||||||
yield ' "%s",\n' % lint[2]
|
|
||||||
yield ' );\n'
|
|
||||||
|
|
||||||
|
|
||||||
def replace_region(fn, region_start, region_end, callback,
|
|
||||||
replace_start=True, write_back=True):
|
|
||||||
"""Replace a region in a file delimited by two lines matching regexes.
|
|
||||||
|
|
||||||
A callback is called to write the new region. If `replace_start` is true,
|
|
||||||
the start delimiter line is replaced as well. The end delimiter line is
|
|
||||||
never replaced.
|
|
||||||
"""
|
|
||||||
# read current content
|
|
||||||
with open(fn) as fp:
|
|
||||||
lines = list(fp)
|
|
||||||
|
|
||||||
found = False
|
|
||||||
|
|
||||||
# replace old region with new region
|
|
||||||
new_lines = []
|
|
||||||
in_old_region = False
|
|
||||||
for line in lines:
|
|
||||||
if in_old_region:
|
|
||||||
if re.search(region_end, line):
|
|
||||||
in_old_region = False
|
|
||||||
new_lines.extend(callback())
|
|
||||||
new_lines.append(line)
|
|
||||||
elif re.search(region_start, line):
|
|
||||||
if not replace_start:
|
|
||||||
new_lines.append(line)
|
|
||||||
# old region starts here
|
|
||||||
in_old_region = True
|
|
||||||
found = True
|
|
||||||
else:
|
|
||||||
new_lines.append(line)
|
|
||||||
|
|
||||||
if not found:
|
|
||||||
print("regex " + region_start + " not found")
|
|
||||||
|
|
||||||
# write back to file
|
|
||||||
if write_back:
|
|
||||||
with open(fn, 'w') as fp:
|
|
||||||
fp.writelines(new_lines)
|
|
||||||
|
|
||||||
# if something changed, return true
|
|
||||||
return lines != new_lines
|
|
||||||
|
|
||||||
|
|
||||||
def main(print_only=False, check=False):
|
|
||||||
deprecated_lints = []
|
|
||||||
clippy_lints = {
|
|
||||||
"correctness": [],
|
|
||||||
"style": [],
|
|
||||||
"complexity": [],
|
|
||||||
"perf": [],
|
|
||||||
"restriction": [],
|
|
||||||
"pedantic": [],
|
|
||||||
"cargo": [],
|
|
||||||
"nursery": [],
|
|
||||||
}
|
|
||||||
|
|
||||||
# check directory
|
|
||||||
if not os.path.isfile('clippy_lints/src/lib.rs'):
|
|
||||||
print('Error: call this script from clippy checkout directory!')
|
|
||||||
return
|
|
||||||
|
|
||||||
# collect all lints from source files
|
|
||||||
for root, dirs, files in os.walk('clippy_lints/src'):
|
|
||||||
for fn in files:
|
|
||||||
if fn.endswith('.rs'):
|
|
||||||
collect(deprecated_lints, clippy_lints,
|
|
||||||
os.path.join(root, fn))
|
|
||||||
|
|
||||||
# determine version
|
|
||||||
with open('Cargo.toml') as fp:
|
|
||||||
for line in fp:
|
|
||||||
if line.startswith('version ='):
|
|
||||||
clippy_version = line.split()[2].strip('"')
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
print('Error: version not found in Cargo.toml!')
|
|
||||||
return
|
|
||||||
|
|
||||||
all_lints = []
|
|
||||||
clippy_lint_groups = [
|
|
||||||
"correctness",
|
|
||||||
"style",
|
|
||||||
"complexity",
|
|
||||||
"perf",
|
|
||||||
]
|
|
||||||
clippy_lint_list = []
|
|
||||||
for x in clippy_lint_groups:
|
|
||||||
clippy_lint_list += clippy_lints[x]
|
|
||||||
for _, value in clippy_lints.iteritems():
|
|
||||||
all_lints += value
|
|
||||||
|
|
||||||
if print_only:
|
|
||||||
call(["./util/dev", "update_lints", "--print-only"])
|
|
||||||
return
|
|
||||||
|
|
||||||
# update the lint counter in README.md
|
|
||||||
changed = replace_region(
|
|
||||||
'README.md',
|
|
||||||
r'^\[There are \d+ lints included in this crate!\]\(https://rust-lang-nursery.github.io/rust-clippy/master/index.html\)$', "",
|
|
||||||
lambda: ['[There are %d lints included in this crate!](https://rust-lang-nursery.github.io/rust-clippy/master/index.html)\n' %
|
|
||||||
(len(all_lints))],
|
|
||||||
write_back=not check)
|
|
||||||
|
|
||||||
# update the links in the CHANGELOG
|
|
||||||
changed |= replace_region(
|
|
||||||
'CHANGELOG.md',
|
|
||||||
"<!-- begin autogenerated links to lint list -->",
|
|
||||||
"<!-- end autogenerated links to lint list -->",
|
|
||||||
lambda: ["[`{0}`]: {1}#{0}\n".format(l[1], docs_link) for l in
|
|
||||||
sorted(all_lints + deprecated_lints,
|
|
||||||
key=lambda l: l[1])],
|
|
||||||
replace_start=False, write_back=not check)
|
|
||||||
|
|
||||||
# update version of clippy_lints in Cargo.toml
|
|
||||||
changed |= replace_region(
|
|
||||||
'Cargo.toml', r'# begin automatic update', '# end automatic update',
|
|
||||||
lambda: ['clippy_lints = { version = "%s", path = "clippy_lints" }\n' %
|
|
||||||
clippy_version],
|
|
||||||
replace_start=False, write_back=not check)
|
|
||||||
|
|
||||||
# update version of clippy_lints in Cargo.toml
|
|
||||||
changed |= replace_region(
|
|
||||||
'clippy_lints/Cargo.toml', r'# begin automatic update', '# end automatic update',
|
|
||||||
lambda: ['version = "%s"\n' % clippy_version],
|
|
||||||
replace_start=False, write_back=not check)
|
|
||||||
|
|
||||||
# update the `pub mod` list
|
|
||||||
changed |= replace_region(
|
|
||||||
'clippy_lints/src/lib.rs', r'begin lints modules', r'end lints modules',
|
|
||||||
lambda: gen_mods(all_lints),
|
|
||||||
replace_start=False, write_back=not check)
|
|
||||||
|
|
||||||
# same for "clippy::*" lint collections
|
|
||||||
changed |= replace_region(
|
|
||||||
'clippy_lints/src/lib.rs', r'reg.register_lint_group\("clippy::all"', r'\]\);',
|
|
||||||
lambda: gen_group(clippy_lint_list),
|
|
||||||
replace_start=False, write_back=not check)
|
|
||||||
|
|
||||||
for key, value in clippy_lints.iteritems():
|
|
||||||
# same for "clippy::*" lint collections
|
|
||||||
changed |= replace_region(
|
|
||||||
'clippy_lints/src/lib.rs', r'reg.register_lint_group\("clippy::' + key + r'"', r'\]\);',
|
|
||||||
lambda: gen_group(value),
|
|
||||||
replace_start=False, write_back=not check)
|
|
||||||
|
|
||||||
# same for "deprecated" lint collection
|
|
||||||
changed |= replace_region(
|
|
||||||
'clippy_lints/src/lib.rs', r'begin deprecated lints', r'end deprecated lints',
|
|
||||||
lambda: gen_deprecated(deprecated_lints),
|
|
||||||
replace_start=False,
|
|
||||||
write_back=not check)
|
|
||||||
|
|
||||||
if check and changed:
|
|
||||||
print('Please run util/update_lints.py to regenerate lints lists.')
|
|
||||||
return 1
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print('Error: Please use `util/dev` to update lints')
|
||||||
|
return 1
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
sys.exit(main(print_only='-n' in sys.argv, check='-c' in sys.argv))
|
sys.exit(main())
|
||||||
|
Loading…
Reference in New Issue
Block a user