From dcee48a68b0eaafffaf9a4e8ca34243a913fe1cc Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Thu, 20 Feb 2025 12:16:47 -0800 Subject: [PATCH] [flow] Add support for backup flowconfigs on ignored files Summary: This diff allows you to use our typical module-mapping syntax in the flowconfig to specify a backup for explicitly ignored files. It does not provide the same support when these files are passed on the command line. Changelog: [internal] Reviewed By: SamChou19815 Differential Revision: D69858997 fbshipit-source-id: 74497e7a70ec9a22c2274babe34fef220a933d5c --- src/commands/commandUtils.ml | 27 ++++++++++++++----- src/commands/config/flowConfig.ml | 26 +++++++++++++++--- src/commands/config/flowConfig.mli | 2 +- src/commands/initCommand.ml | 11 +++++++- src/commands/lsCommand.ml | 4 ++- src/common/files.ml | 6 ++--- src/common/files.mli | 4 +-- tests/flowconfig_ignore/.flowconfig | 1 + .../my_ignores_with_backup/with_backup.js | 2 ++ 9 files changed, 64 insertions(+), 19 deletions(-) create mode 100644 tests/flowconfig_ignore/my_ignores_with_backup/with_backup.js diff --git a/src/commands/commandUtils.ml b/src/commands/commandUtils.ml index c49d42998bd..a3bdeecf5d8 100644 --- a/src/commands/commandUtils.ml +++ b/src/commands/commandUtils.ml @@ -594,7 +594,7 @@ let assert_version flowconfig = | Error msg -> Exit.(exit ~msg Invalid_flowconfig) type flowconfig_params = { - ignores: string list; + ignores: (string * string option) list; untyped: string list; declarations: string list; includes: string list; @@ -611,7 +611,7 @@ let list_of_string_arg = function let collect_flowconfig_flags main ignores_str untyped_str declarations_str includes_str lib_str lints_str = - let ignores = list_of_string_arg ignores_str in + let ignores = List.map (fun ignore -> (ignore, None)) (list_of_string_arg ignores_str) in let untyped = list_of_string_arg untyped_str in let declarations = list_of_string_arg declarations_str in let includes = list_of_string_arg includes_str in @@ -626,15 +626,26 @@ let remove_exclusion pattern = pattern let file_options = - let ignores_of_arg root patterns extras = + let path_pattern_of_arg root pattern = let expand_project_root_token = Files.expand_project_root_token ~root in + pattern |> remove_exclusion |> expand_project_root_token |> Str.regexp + in + let path_patterns_of_args root patterns extras = let patterns = Base.List.rev_append extras patterns in Base.List.map ~f:(fun s -> - let reg = s |> remove_exclusion |> expand_project_root_token |> Str.regexp in + let reg = path_pattern_of_arg root s in (s, reg)) patterns in + let ignores_of_args root patterns extras = + let patterns = Base.List.rev_append extras patterns in + Base.List.map + ~f:(fun (path, backup) -> + let reg = path_pattern_of_arg root path in + ((path, backup), reg)) + patterns + in let includes_of_arg ~implicitly_include_root ~root ~lib_paths paths = (* Explicitly included paths are always added to the path_matcher *) let path_matcher = @@ -703,9 +714,11 @@ let file_options = in Some libdir in - let ignores = ignores_of_arg root (FlowConfig.ignores flowconfig) ignores in - let untyped = ignores_of_arg root (FlowConfig.untyped flowconfig) untyped in - let declarations = ignores_of_arg root (FlowConfig.declarations flowconfig) declarations in + let ignores = ignores_of_args root (FlowConfig.ignores flowconfig) ignores in + let untyped = path_patterns_of_args root (FlowConfig.untyped flowconfig) untyped in + let declarations = + path_patterns_of_args root (FlowConfig.declarations flowconfig) declarations + in let lib_paths = lib_paths ~root flowconfig libs in let includes = includes diff --git a/src/commands/config/flowConfig.ml b/src/commands/config/flowConfig.ml index 6b871368cdd..71f718e5149 100644 --- a/src/commands/config/flowConfig.ml +++ b/src/commands/config/flowConfig.ml @@ -1201,8 +1201,12 @@ type rollout = { type config = { rollouts: rollout SMap.t; - (* completely ignored files (both module resolving and typing) *) - ignores: string list; + (* completely ignored files (both module resolving and typing) + * This type *should* just be a string list, but we have an undocumented feature that allows + * you to specify a backup flowconfig to use if a file is ignored using our module-mapper syntax. + * This should be reverted to string list after we properly support multiplatform flow roots. + *) + ignores: (string * string option) list; (* files that should be treated as untyped *) untyped: string list; (* files that should be treated as declarations *) @@ -1230,7 +1234,12 @@ end = struct let section_header o section = fprintf o "[%s]\n" section - let ignores o = Base.List.iter ~f:(fprintf o "%s\n") + let ignores o = + Base.List.iter ~f:(fun (ignore, backup_opt) -> + match backup_opt with + | None -> fprintf o "%s\n" ignore + | Some backup -> fprintf o "%s -> %s\n" ignore backup + ) let untyped o = Base.List.iter ~f:(fprintf o "%s\n") @@ -1368,7 +1377,16 @@ let parse_libs lines config : (config * warning list, error) result = Ok ({ config with libs }, []) let parse_ignores lines config = - let ignores = trim_lines lines in + let raw_ignores = trim_lines lines in + let ignores = + raw_ignores + |> Base.List.map ~f:(fun ignore -> + if Str.string_match Opts.mapping_regexp ignore 0 then + (Str.matched_group 1 ignore, Some (Str.matched_group 2 ignore)) + else + (ignore, None) + ) + in Ok ({ config with ignores }, []) let parse_untyped lines config = diff --git a/src/commands/config/flowConfig.mli b/src/commands/config/flowConfig.mli index 20483a809f4..f305085546d 100644 --- a/src/commands/config/flowConfig.mli +++ b/src/commands/config/flowConfig.mli @@ -46,7 +46,7 @@ val write : config -> out_channel -> unit (* Accessors *) (* completely ignored files (both module resolving and typing) *) -val ignores : config -> string list +val ignores : config -> (string * string option) list (* files that should be treated as untyped *) val untyped : config -> string list diff --git a/src/commands/initCommand.ml b/src/commands/initCommand.ml index 8e502feb926..c28f025abbe 100644 --- a/src/commands/initCommand.ml +++ b/src/commands/initCommand.ml @@ -64,7 +64,16 @@ let main base_flags flowconfig_flags options root () = Exit.(exit ~msg Invalid_flowconfig) ); - let config = FlowConfig.init ~ignores ~untyped ~declarations ~includes ~libs ~options ~lints in + let config = + FlowConfig.init + ~ignores:(List.map fst ignores) + ~untyped + ~declarations + ~includes + ~libs + ~options + ~lints + in let config = match config with | Ok (config, []) -> config diff --git a/src/commands/lsCommand.ml b/src/commands/lsCommand.ml index 019ed1d9ebb..5b97a4aa1c1 100644 --- a/src/commands/lsCommand.ml +++ b/src/commands/lsCommand.ml @@ -119,7 +119,9 @@ let rec iter_get_next ~f get_next = let make_options ~flowconfig ~root ~ignore_flag ~include_flag ~untyped_flag ~declaration_flag = let includes = CommandUtils.list_of_string_arg include_flag in - let ignores = CommandUtils.list_of_string_arg ignore_flag in + let ignores = + List.map (fun ignore -> (ignore, None)) (CommandUtils.list_of_string_arg ignore_flag) + in let untyped = CommandUtils.list_of_string_arg untyped_flag in let declarations = CommandUtils.list_of_string_arg declaration_flag in let libs = [] in diff --git a/src/common/files.ml b/src/common/files.ml index f922661b098..02f1f0505b0 100644 --- a/src/common/files.ml +++ b/src/common/files.ml @@ -13,7 +13,7 @@ type lib_dir = type options = { default_lib_dir: lib_dir option; - ignores: (string * Str.regexp) list; + ignores: ((string * string option) * Str.regexp) list; untyped: (string * Str.regexp) list; declarations: (string * Str.regexp) list; implicitly_include_root: bool; @@ -521,7 +521,7 @@ let dir_filter_of_options (options : options) f = to be un-ignored. similarly, if an ignore ends in $, we could still prune if the current path isn't a prefix of it. *) Base.List.for_all - ~f:(fun (pattern, _rx) -> + ~f:(fun ((pattern, _), _rx) -> (not (String.starts_with ~prefix:"!" pattern)) && not (String.ends_with ~suffix:"$" pattern)) options.ignores in @@ -615,7 +615,7 @@ let is_ignored (options : options) path = (* On Windows, the path may use \ instead of /, but let's standardize the * ignore regex to use / *) let path = Sys_utils.normalize_filename_dir_sep path in - is_matching path options.ignores + is_matching path (options.ignores |> List.map (fun ((x, _y), z) -> (x, z))) (* true if a file path matches an [untyped] entry in config *) let is_untyped (options : options) path = diff --git a/src/common/files.mli b/src/common/files.mli index a440552998f..3d46e7332c8 100644 --- a/src/common/files.mli +++ b/src/common/files.mli @@ -15,7 +15,7 @@ type options val mk_options : default_lib_dir:lib_dir option -> - ignores:(string * Str.regexp) list -> + ignores:((string * string option) * Str.regexp) list -> untyped:(string * Str.regexp) list -> declarations:(string * Str.regexp) list -> implicitly_include_root:bool -> @@ -35,7 +35,7 @@ val default_options : options val default_lib_dir : options -> lib_dir option -val ignores : options -> (string * Str.regexp) list +val ignores : options -> ((string * string option) * Str.regexp) list val untyped : options -> (string * Str.regexp) list diff --git a/tests/flowconfig_ignore/.flowconfig b/tests/flowconfig_ignore/.flowconfig index a0c7186edc8..af5ed618b4d 100644 --- a/tests/flowconfig_ignore/.flowconfig +++ b/tests/flowconfig_ignore/.flowconfig @@ -6,6 +6,7 @@ !/my_declarations/root_typecheck/.* [ignore] +'.*/my_ignores_with_backup/.*' -> '.flowconfig' .*/my_ignores/.* !.*/my_ignores/typecheck/.* !.*/my_ignores/actually_typecheck\.js diff --git a/tests/flowconfig_ignore/my_ignores_with_backup/with_backup.js b/tests/flowconfig_ignore/my_ignores_with_backup/with_backup.js new file mode 100644 index 00000000000..c6a95d72760 --- /dev/null +++ b/tests/flowconfig_ignore/my_ignores_with_backup/with_backup.js @@ -0,0 +1,2 @@ +// @flow +const x: string = 3; // NO ERROR