@@ -12,6 +12,113 @@ type LintError = String;
1212type LintResult = Result < ( ) , LintError > ;
1313type 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
16123fn 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
365472fn 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