Skip to content

Commit ff21eb2

Browse files
committed
Merge bitcoin/bitcoin#30219: Lint: Support running individual lint checks
0fcbfdb Support running individual lint checks (David Gumberg) Pull request description: This PR was split out from #29965: Adds support for running individual tests in the rust lint suite by passing `--lint=LINT_TO_RUN` to the lint runner. This PR also adds a corresponding help message. When running with `cargo run`, arguments after a double dash (`--`) are passed to the binary instead of the cargo command. For example, in order to run the linter check that tabs are not used as whitespace: ```console cd test/lint/test_runner && cargo run -- --lint=tabs_whitespace ``` ACKs for top commit: maflcko: ACK 0fcbfdb achow101: ACK 0fcbfdb marcofleon: Tested ACK 0fcbfdb. Ran `cargo run` with various of the individual tests and with bad input. Also ran it with no arguments. Everything works as expected and help message looks good. Tree-SHA512: 48fe4aa9fbb2acef5f8e3c17382ae22e0e350ae6ad9aeeb1a3c0a9192de98809f98728e32b8db24a36906ace999e35626ebd6cb2ca05f74146d21e9b6fb14615
2 parents 011a895 + 0fcbfdb commit ff21eb2

File tree

2 files changed

+138
-15
lines changed

2 files changed

+138
-15
lines changed

test/lint/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,21 @@ Then you can use:
2626
( cd ./test/lint/test_runner/ && cargo fmt && cargo clippy && RUST_BACKTRACE=1 cargo run )
2727
```
2828

29+
If you wish to run individual lint checks, run the test_runner with
30+
`--lint=TEST_TO_RUN` arguments. If running with `cargo run`, arguments after
31+
`--` are passed to the binary you are running e.g.:
32+
33+
```sh
34+
( cd ./test/lint/test_runner/ && RUST_BACKTRACE=1 cargo run -- --lint=doc --lint=trailing_whitespace )
35+
```
36+
37+
To see a list of all individual lint checks available in test_runner, use `-h`
38+
or `--help`:
39+
40+
```sh
41+
( cd ./test/lint/test_runner/ && RUST_BACKTRACE=1 cargo run -- --help )
42+
```
43+
2944
#### Dependencies
3045

3146
| Lint test | Dependency |

test/lint/test_runner/src/main.rs

Lines changed: 123 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,113 @@ type LintError = String;
1212
type LintResult = Result<(), LintError>;
1313
type LintFn = fn() -> LintResult;
1414

15+
struct Linter {
16+
pub description: &'static str,
17+
pub name: &'static str,
18+
pub lint_fn: LintFn,
19+
}
20+
21+
fn get_linter_list() -> Vec<&'static Linter> {
22+
vec![
23+
&Linter {
24+
description: "Check that all command line arguments are documented.",
25+
name: "doc",
26+
lint_fn: lint_doc
27+
},
28+
&Linter {
29+
description: "Check that no symbol from bitcoin-config.h is used without the header being included",
30+
name: "includes_build_config",
31+
lint_fn: lint_includes_build_config
32+
},
33+
&Linter {
34+
description: "Check that markdown links resolve",
35+
name: "markdown",
36+
lint_fn: lint_markdown
37+
},
38+
&Linter {
39+
description: "Check that std::filesystem is not used directly",
40+
name: "std_filesystem",
41+
lint_fn: lint_std_filesystem
42+
},
43+
&Linter {
44+
description: "Check that subtrees are pure subtrees",
45+
name: "subtree",
46+
lint_fn: lint_subtree
47+
},
48+
&Linter {
49+
description: "Check that tabs are not used as whitespace",
50+
name: "tabs_whitespace",
51+
lint_fn: lint_tabs_whitespace
52+
},
53+
&Linter {
54+
description: "Check for trailing whitespace",
55+
name: "trailing_whitespace",
56+
lint_fn: lint_trailing_whitespace
57+
},
58+
&Linter {
59+
description: "Run all linters of the form: test/lint/lint-*.py",
60+
name: "all_python_linters",
61+
lint_fn: run_all_python_linters
62+
},
63+
]
64+
}
65+
66+
fn print_help_and_exit() {
67+
print!(
68+
r#"
69+
Usage: test_runner [--lint=LINTER_TO_RUN]
70+
Runs all linters in the lint test suite, printing any errors
71+
they detect.
72+
73+
If you wish to only run some particular lint tests, pass
74+
'--lint=' with the name of the lint test you wish to run.
75+
You can set as many '--lint=' values as you wish, e.g.:
76+
test_runner --lint=doc --lint=subtree
77+
78+
The individual linters available to run are:
79+
"#
80+
);
81+
for linter in get_linter_list() {
82+
println!("{}: \"{}\"", linter.name, linter.description)
83+
}
84+
85+
std::process::exit(1);
86+
}
87+
88+
fn parse_lint_args(args: &[String]) -> Vec<&'static Linter> {
89+
let linter_list = get_linter_list();
90+
let mut lint_values = Vec::new();
91+
92+
for arg in args {
93+
#[allow(clippy::if_same_then_else)]
94+
if arg.starts_with("--lint=") {
95+
let lint_arg_value = arg
96+
.trim_start_matches("--lint=")
97+
.trim_matches('"')
98+
.trim_matches('\'');
99+
100+
let try_find_linter = linter_list
101+
.iter()
102+
.find(|linter| linter.name == lint_arg_value);
103+
match try_find_linter {
104+
Some(linter) => {
105+
lint_values.push(*linter);
106+
}
107+
None => {
108+
println!("No linter {lint_arg_value} found!");
109+
print_help_and_exit();
110+
}
111+
}
112+
} else if arg.eq("--help") || arg.eq("-h") {
113+
print_help_and_exit();
114+
} else {
115+
print_help_and_exit();
116+
}
117+
}
118+
119+
lint_values
120+
}
121+
15122
/// Return the git command
16123
fn git() -> Command {
17124
let mut git = Command::new("git");
@@ -337,7 +444,7 @@ Markdown link errors found:
337444
}
338445
}
339446

340-
fn lint_all() -> LintResult {
447+
fn run_all_python_linters() -> LintResult {
341448
let mut good = true;
342449
let lint_dir = get_git_root().join("test/lint");
343450
for entry in fs::read_dir(lint_dir).unwrap() {
@@ -352,7 +459,7 @@ fn lint_all() -> LintResult {
352459
.success()
353460
{
354461
good = false;
355-
println!("^---- failure generated from {}", entry_fn);
462+
println!("^---- ⚠️ Failure generated from {}", entry_fn);
356463
}
357464
}
358465
if good {
@@ -363,25 +470,26 @@ fn lint_all() -> LintResult {
363470
}
364471

365472
fn main() -> ExitCode {
366-
let test_list: Vec<(&str, LintFn)> = vec![
367-
("subtree check", lint_subtree),
368-
("std::filesystem check", lint_std_filesystem),
369-
("trailing whitespace check", lint_trailing_whitespace),
370-
("no-tabs check", lint_tabs_whitespace),
371-
("build config includes check", lint_includes_build_config),
372-
("-help=1 documentation check", lint_doc),
373-
("markdown hyperlink check", lint_markdown),
374-
("lint-*.py scripts", lint_all),
375-
];
473+
let linters_to_run: Vec<&Linter> = if env::args().count() > 1 {
474+
let args: Vec<String> = env::args().skip(1).collect();
475+
parse_lint_args(&args)
476+
} else {
477+
// If no arguments are passed, run all linters.
478+
get_linter_list()
479+
};
376480

377481
let git_root = get_git_root();
378482

379483
let mut test_failed = false;
380-
for (lint_name, lint_fn) in test_list {
484+
for linter in linters_to_run {
381485
// chdir to root before each lint test
382486
env::set_current_dir(&git_root).unwrap();
383-
if let Err(err) = lint_fn() {
384-
println!("{err}\n^---- ⚠️ Failure generated from {lint_name}!");
487+
if let Err(err) = (linter.lint_fn)() {
488+
println!(
489+
"{err}\n^---- ⚠️ Failure generated from lint check '{}'!",
490+
linter.name
491+
);
492+
println!("{}", linter.description);
385493
test_failed = true;
386494
}
387495
}

0 commit comments

Comments
 (0)