@@ -10,80 +10,80 @@ use lintrunner::{
1010 path:: AbsPath ,
1111 persistent_data:: PersistentDataStore ,
1212 render:: print_error,
13- PathsToLint , RenderOpt , RevisionOpt ,
13+ PathsOpt , RenderOpt , RevisionOpt ,
1414} ;
1515
1616#[ derive( Debug , Parser ) ]
17- #[ structopt ( name = "lintrunner" , about = "A lint runner" ) ]
17+ #[ clap ( name = "lintrunner" , about = "A lint runner" , infer_subcommands ( true ) ) ]
1818struct Args {
1919 /// Verbose mode (-v, or -vv to show full list of paths being linted)
20- #[ clap( short, long, parse( from_occurrences) ) ]
20+ #[ clap( short, long, parse( from_occurrences) , global = true ) ]
2121 verbose : u8 ,
2222
2323 /// Path to a toml file defining which linters to run
24- #[ clap( long, default_value = ".lintrunner.toml" ) ]
24+ #[ clap( long, default_value = ".lintrunner.toml" , global = true ) ]
2525 config : String ,
2626
2727 /// If set, any suggested patches will be applied
28- #[ clap( short, long) ]
28+ #[ clap( short, long, global = true ) ]
2929 apply_patches : bool ,
3030
3131 /// Shell command that returns new-line separated paths to lint
3232 ///
3333 /// Example: To run on all files in the repo, use `--paths-cmd='git grep -Il .'`.
34- #[ clap( long, conflicts_with = "paths-from" ) ]
34+ #[ clap( long, conflicts_with = "paths-from" , global = true ) ]
3535 paths_cmd : Option < String > ,
3636
3737 /// File with new-line separated paths to lint
38- #[ clap( long) ]
38+ #[ clap( long, global = true ) ]
3939 paths_from : Option < String > ,
4040
4141 /// Lint all files that differ between the working directory and the
4242 /// specified revision. This argument can be any <tree-ish> that is accepted
4343 /// by `git diff-tree`
44- #[ clap( long, short, conflicts_with_all=& [ "paths" , "paths-cmd" , "paths-from" ] ) ]
44+ #[ clap( long, short, conflicts_with_all=& [ "paths" , "paths-cmd" , "paths-from" ] , global = true ) ]
4545 revision : Option < String > ,
4646
4747 /// Lint all files that differ between the merge base of HEAD with the
4848 /// specified revision and HEAD. This argument can be any <tree-sh> that is
4949 /// accepted by `git diff-tree`
5050 ///
5151 /// Example: lintrunner -m master
52- #[ clap( long, short, conflicts_with_all=& [ "paths" , "paths-cmd" , "paths-from" , "revision" ] ) ]
52+ #[ clap( long, short, conflicts_with_all=& [ "paths" , "paths-cmd" , "paths-from" , "revision" ] , global = true ) ]
5353 merge_base_with : Option < String > ,
5454
5555 /// Comma-separated list of linters to skip (e.g. --skip CLANGFORMAT,NOQA)
56- #[ clap( long) ]
56+ #[ clap( long, global = true ) ]
5757 skip : Option < String > ,
5858
5959 /// Comma-separated list of linters to run (opposite of --skip)
60- #[ clap( long) ]
60+ #[ clap( long, global = true ) ]
6161 take : Option < String > ,
6262
6363 /// With 'default' show lint issues in human-readable format, for interactive use.
6464 /// With 'json', show lint issues as machine-readable JSON (one per line)
6565 /// With 'oneline', show lint issues in compact format (one per line)
66- #[ clap( long, arg_enum, default_value_t = RenderOpt :: Default ) ]
66+ #[ clap( long, arg_enum, default_value_t = RenderOpt :: Default , global= true ) ]
6767 output : RenderOpt ,
6868
69+ /// Paths to lint. lintrunner will still respect the inclusions and
6970 #[ clap( subcommand) ]
7071 cmd : Option < SubCommand > ,
7172
72- /// Paths to lint. lintrunner will still respect the inclusions and
7373 /// exclusions defined in .lintrunner.toml; manually specifying a path will
7474 /// not override them.
75- #[ clap( conflicts_with_all = & [ "paths-cmd" , "paths-from" ] ) ]
75+ #[ clap( conflicts_with_all = & [ "paths-cmd" , "paths-from" ] , global = true ) ]
7676 paths : Vec < String > ,
7777
7878 /// If set, always output with ANSI colors, even if we detect the output is
7979 /// not a user-attended terminal.
80- #[ clap( long) ]
80+ #[ clap( long, global = true ) ]
8181 force_color : bool ,
8282
8383 /// If set, use ths provided path to store any metadata generated by
8484 /// lintrunner. By default, this is a platform-specific location for
8585 /// application data (e.g. $XDG_DATA_HOME for UNIX systems.)
86- #[ clap( long) ]
86+ #[ clap( long, global = true ) ]
8787 data_path : Option < String > ,
8888}
8989
@@ -95,6 +95,12 @@ enum SubCommand {
9595 #[ clap( long, short) ]
9696 dry_run : bool ,
9797 } ,
98+ /// Run and accept changes for formatting linters only. Equivalent to
99+ /// `lintrunner --apply-patches --take <formatters>`.
100+ Format ,
101+
102+ /// Run linters. This is the default if no subcommand is provided.
103+ Lint ,
98104}
99105
100106fn do_main ( ) -> Result < i32 > {
@@ -120,6 +126,10 @@ fn do_main() -> Result<i32> {
120126
121127 let config_path = AbsPath :: try_from ( & args. config )
122128 . with_context ( || format ! ( "Could not read lintrunner config at: '{}'" , args. config) ) ?;
129+
130+ let cmd = args. cmd . unwrap_or ( SubCommand :: Lint ) ;
131+ let lint_runner_config = LintRunnerConfig :: new ( & config_path) ?;
132+
123133 let skipped_linters = args. skip . map ( |linters| {
124134 linters
125135 . split ( ',' )
@@ -133,28 +143,32 @@ fn do_main() -> Result<i32> {
133143 . collect :: < HashSet < _ > > ( )
134144 } ) ;
135145
136- let lint_runner_config = LintRunnerConfig :: new ( & config_path) ?;
146+ // If we are formatting, the universe of linters to select from should be
147+ // restricted to only formatters.
148+ // (NOTE: we pay an allocation for `placeholder` even in cases where we are
149+ // just passing through a reference in the else-branch. This doesn't matter,
150+ // but if we want to fix it we should impl Cow for LintConfig and use that
151+ // instead.).
152+ let mut placeholder = Vec :: new ( ) ;
153+ let all_linters = if let SubCommand :: Format = & cmd {
154+ let iter = lint_runner_config
155+ . linters
156+ . iter ( )
157+ . filter ( |l| l. is_formatter )
158+ . map ( |l| l. clone ( ) ) ;
159+ placeholder. extend ( iter) ;
160+ & placeholder
161+ } else {
162+ // If we're not formatting, all linters defined in the config are
163+ // eligible to run.
164+ & lint_runner_config. linters
165+ } ;
137166
138- let linters = get_linters_from_config (
139- & lint_runner_config. linters ,
140- skipped_linters,
141- taken_linters,
142- & config_path,
143- ) ?;
167+ let linters =
168+ get_linters_from_config ( all_linters, skipped_linters, taken_linters, & config_path) ?;
144169
145170 let enable_spinners = args. verbose == 0 && args. output == RenderOpt :: Default ;
146-
147- let paths_to_lint = if let Some ( paths_file) = args. paths_from {
148- let path_file = AbsPath :: try_from ( & paths_file)
149- . with_context ( || format ! ( "Failed to find `--paths-from` file '{}'" , paths_file) ) ?;
150- PathsToLint :: PathsFile ( path_file)
151- } else if let Some ( paths_cmd) = args. paths_cmd {
152- PathsToLint :: PathsCmd ( paths_cmd)
153- } else if !args. paths . is_empty ( ) {
154- PathsToLint :: Paths ( args. paths )
155- } else {
156- PathsToLint :: Auto
157- } ;
171+ let persistent_data_store = PersistentDataStore :: new ( & config_path) ?;
158172
159173 let revision_opt = if let Some ( revision) = args. revision {
160174 RevisionOpt :: Revision ( revision)
@@ -164,19 +178,40 @@ fn do_main() -> Result<i32> {
164178 RevisionOpt :: Head
165179 } ;
166180
167- let persistent_data_store = PersistentDataStore :: new ( & config_path) ?;
181+ let paths_opt = if let Some ( paths_file) = args. paths_from {
182+ let path_file = AbsPath :: try_from ( & paths_file)
183+ . with_context ( || format ! ( "Failed to find `--paths-from` file '{}'" , paths_file) ) ?;
184+ PathsOpt :: PathsFile ( path_file)
185+ } else if let Some ( paths_cmd) = args. paths_cmd {
186+ PathsOpt :: PathsCmd ( paths_cmd)
187+ } else if !args. paths . is_empty ( ) {
188+ PathsOpt :: Paths ( args. paths )
189+ } else {
190+ PathsOpt :: Auto
191+ } ;
168192
169- match args . cmd {
170- Some ( SubCommand :: Init { dry_run } ) => {
193+ match cmd {
194+ SubCommand :: Init { dry_run } => {
171195 // Just run initialization commands, don't actually lint.
172196 do_init ( linters, dry_run, & persistent_data_store, & config_path)
173197 }
174- None => {
198+ SubCommand :: Format => {
199+ check_init_changed ( & persistent_data_store, & lint_runner_config) ?;
200+ do_lint (
201+ linters,
202+ paths_opt,
203+ true , // always apply patches when we use the format command
204+ args. output ,
205+ enable_spinners,
206+ revision_opt,
207+ )
208+ }
209+ SubCommand :: Lint => {
175210 // Default command is to just lint.
176211 check_init_changed ( & persistent_data_store, & lint_runner_config) ?;
177212 do_lint (
178213 linters,
179- paths_to_lint ,
214+ paths_opt ,
180215 args. apply_patches ,
181216 args. output ,
182217 enable_spinners,
0 commit comments