From 46881dd40ac38b84c5932985e430651d63e612d9 Mon Sep 17 00:00:00 2001 From: Sam Zhou Date: Wed, 5 Mar 2025 16:52:25 -0800 Subject: [PATCH] [flow] Scoped libdef support 2/n: Filter out unavailable results for IDE services Summary: In this diff, we will address the shortcoming from the previous diff, where we index every single globals, even if the scoped libdef config says that it won't be active for the current file. This diff addresses it in a simple way: we will post-process the auto-import results to filter these away, based on `Context.active_global_builtins.` Changelog: [internal] Reviewed By: panagosg7 Differential Revision: D70532319 fbshipit-source-id: f213d61f70e97ec7e94bf8fce5177f5d54932655 --- .../autocomplete/autocompleteService_js.ml | 16 +++++- .../code_action/code_action_service.ml | 5 ++ src/services/code_action/lsp_import_edits.ml | 12 +++++ src/services/code_action/lsp_import_edits.mli | 2 + src/typing/builtins.ml | 3 ++ src/typing/builtins.mli | 2 + tests/libdef_scoped_autocomplete/.flowconfig | 7 +++ tests/libdef_scoped_autocomplete/.testconfig | 1 + tests/libdef_scoped_autocomplete/bar/test.js | 6 +++ tests/libdef_scoped_autocomplete/bar_lib.js | 2 + .../libdef_scoped_autocomplete/common_lib.js | 2 + .../libdef_scoped_autocomplete/common_test.js | 6 +++ tests/libdef_scoped_autocomplete/foo/test.js | 6 +++ tests/libdef_scoped_autocomplete/foo_lib.js | 2 + .../libdef_scoped_autocomplete.exp | 51 +++++++++++++++++++ tests/libdef_scoped_autocomplete/test.sh | 11 ++++ 16 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 tests/libdef_scoped_autocomplete/.flowconfig create mode 100644 tests/libdef_scoped_autocomplete/.testconfig create mode 100644 tests/libdef_scoped_autocomplete/bar/test.js create mode 100644 tests/libdef_scoped_autocomplete/bar_lib.js create mode 100644 tests/libdef_scoped_autocomplete/common_lib.js create mode 100644 tests/libdef_scoped_autocomplete/common_test.js create mode 100644 tests/libdef_scoped_autocomplete/foo/test.js create mode 100644 tests/libdef_scoped_autocomplete/foo_lib.js create mode 100644 tests/libdef_scoped_autocomplete/libdef_scoped_autocomplete.exp create mode 100644 tests/libdef_scoped_autocomplete/test.sh diff --git a/src/services/autocomplete/autocompleteService_js.ml b/src/services/autocomplete/autocompleteService_js.ml index b030c6b4424..592a225b676 100644 --- a/src/services/autocomplete/autocompleteService_js.ml +++ b/src/services/autocomplete/autocompleteService_js.ml @@ -358,6 +358,18 @@ type typing = { canonical: Autocomplete_sigil.Canonical.token option; } +let search_with_filtered_auto_import_results cx = + let is_available_autoimport_result = Lsp_import_edits.is_available_autoimport_result cx in + fun f ~ac_options name -> + let open Export_search_types in + let { results; is_incomplete } = f ~ac_options name in + let results = + Base.List.filter results ~f:(fun { search_result = { name; source; _ }; _ } -> + is_available_autoimport_result ~name ~source + ) + in + { results; is_incomplete } + let mk_typing_artifacts ~layout_options ~loc_of_aloc @@ -378,8 +390,8 @@ let mk_typing_artifacts loc_of_aloc; get_ast_from_shared_mem; module_system_info; - search_exported_values; - search_exported_types; + search_exported_values = search_with_filtered_auto_import_results cx search_exported_values; + search_exported_types = search_with_filtered_auto_import_results cx search_exported_types; cx; file_sig; ast; diff --git a/src/services/code_action/code_action_service.ml b/src/services/code_action/code_action_service.ml index da69fca7a46..081de614487 100644 --- a/src/services/code_action/code_action_service.ml +++ b/src/services/code_action/code_action_service.ml @@ -476,6 +476,7 @@ let maybe_sort_by_usage ~imports_ranked_usage imports = imports let suggest_imports + ~cx ~layout_options ~module_system_info ~ast @@ -504,8 +505,10 @@ let suggest_imports source = Some "Flow" && code = lsp_code && Lsp_helpers.ranges_overlap range error_range ) in + let is_available_autoimport_result = Lsp_import_edits.is_available_autoimport_result cx in files |> Export_index.ExportMap.bindings + |> Base.List.filter ~f:(fun ((source, _), _) -> is_available_autoimport_result ~name ~source) |> maybe_sort_by_usage ~imports_ranked_usage |> Base.List.fold ~init:[] ~f:(fun acc ((source, export_kind), _num) -> match @@ -1154,6 +1157,7 @@ let code_actions_of_errors let actions = if include_quick_fixes && Loc.intersects error_loc loc then suggest_imports + ~cx ~layout_options:(Code_action_utils.layout_options options) ~module_system_info ~ast @@ -1632,6 +1636,7 @@ let suggest_imports_cli when Options.autoimports options -> let ranked_imports = suggest_imports + ~cx ~layout_options:(Code_action_utils.layout_options options) ~module_system_info ~ast diff --git a/src/services/code_action/lsp_import_edits.ml b/src/services/code_action/lsp_import_edits.ml index 6db26cc3a2e..45d1633cf72 100644 --- a/src/services/code_action/lsp_import_edits.ml +++ b/src/services/code_action/lsp_import_edits.ml @@ -8,6 +8,18 @@ open Code_action_text_edits open Lsp_module_system_info +let is_available_autoimport_result cx = + let (available_globals, available_modules) = + let builtins = Context.active_global_builtins cx in + (Builtins.builtin_ordinary_name_set builtins, Builtins.builtin_modules_set builtins) + in + fun ~name ~source -> + let open Export_index in + match source with + | Global -> SSet.mem name available_globals + | Builtin mref -> SSet.mem mref available_modules + | File_key _ -> true + let main_of_package ~get_package_info package_dir = let file_key = File_key.JsonFile (package_dir ^ Filename.dir_sep ^ "package.json") in match get_package_info file_key with diff --git a/src/services/code_action/lsp_import_edits.mli b/src/services/code_action/lsp_import_edits.mli index 1efade802ef..8161b4eae91 100644 --- a/src/services/code_action/lsp_import_edits.mli +++ b/src/services/code_action/lsp_import_edits.mli @@ -5,6 +5,8 @@ * LICENSE file in the root directory of this source tree. *) +val is_available_autoimport_result : Context.t -> name:string -> source:Export_index.source -> bool + (** Generates the 'from' part of 'import ... from ...' required to import [source] from a file in [src_dir] *) val from_of_source : diff --git a/src/typing/builtins.ml b/src/typing/builtins.ml index 21c6631cbb7..0399459d4dd 100644 --- a/src/typing/builtins.ml +++ b/src/typing/builtins.ml @@ -20,6 +20,9 @@ let builtin_ordinary_name_set { original_global_values; original_global_types; _ let add = SMap.fold (fun k _ acc -> SSet.add k acc) in SSet.empty |> add original_global_values |> add original_global_types +let builtin_modules_set { original_global_modules; _ } = + SMap.fold (fun k _ acc -> SSet.add k acc) original_global_modules SSet.empty + let get_builtin_value_opt { original_global_values; type_mapper; mapped_global_names; _ } name = match Hashtbl.find_opt mapped_global_names name with | Some v -> Some v diff --git a/src/typing/builtins.mli b/src/typing/builtins.mli index 18564e7340a..42f9defa47e 100644 --- a/src/typing/builtins.mli +++ b/src/typing/builtins.mli @@ -9,6 +9,8 @@ type t val builtin_ordinary_name_set : t -> SSet.t +val builtin_modules_set : t -> SSet.t + val get_builtin_value_opt : t -> string -> (ALoc.t * Type.t) option val get_builtin_type_opt : t -> string -> (ALoc.t * Type.t) option diff --git a/tests/libdef_scoped_autocomplete/.flowconfig b/tests/libdef_scoped_autocomplete/.flowconfig new file mode 100644 index 00000000000..3c7d895dfa7 --- /dev/null +++ b/tests/libdef_scoped_autocomplete/.flowconfig @@ -0,0 +1,7 @@ +[libs] +common_lib.js +'/foo' -> 'foo_lib.js' +'/bar' -> 'bar_lib.js' + +[options] +all=true diff --git a/tests/libdef_scoped_autocomplete/.testconfig b/tests/libdef_scoped_autocomplete/.testconfig new file mode 100644 index 00000000000..5a3e9f8a853 --- /dev/null +++ b/tests/libdef_scoped_autocomplete/.testconfig @@ -0,0 +1 @@ +shell: test.sh diff --git a/tests/libdef_scoped_autocomplete/bar/test.js b/tests/libdef_scoped_autocomplete/bar/test.js new file mode 100644 index 00000000000..de1f02c2075 --- /dev/null +++ b/tests/libdef_scoped_autocomplete/bar/test.js @@ -0,0 +1,6 @@ +common +// ^ +fooS +// ^ +barS +// ^ diff --git a/tests/libdef_scoped_autocomplete/bar_lib.js b/tests/libdef_scoped_autocomplete/bar_lib.js new file mode 100644 index 00000000000..d4a43cbbe6f --- /dev/null +++ b/tests/libdef_scoped_autocomplete/bar_lib.js @@ -0,0 +1,2 @@ +declare const commonValueWithOverride: boolean; +declare const barSpecific: string; diff --git a/tests/libdef_scoped_autocomplete/common_lib.js b/tests/libdef_scoped_autocomplete/common_lib.js new file mode 100644 index 00000000000..abc7255392d --- /dev/null +++ b/tests/libdef_scoped_autocomplete/common_lib.js @@ -0,0 +1,2 @@ +declare const commonValueNoOverride: string; +declare const commonValueWithOverride: string; diff --git a/tests/libdef_scoped_autocomplete/common_test.js b/tests/libdef_scoped_autocomplete/common_test.js new file mode 100644 index 00000000000..de1f02c2075 --- /dev/null +++ b/tests/libdef_scoped_autocomplete/common_test.js @@ -0,0 +1,6 @@ +common +// ^ +fooS +// ^ +barS +// ^ diff --git a/tests/libdef_scoped_autocomplete/foo/test.js b/tests/libdef_scoped_autocomplete/foo/test.js new file mode 100644 index 00000000000..de1f02c2075 --- /dev/null +++ b/tests/libdef_scoped_autocomplete/foo/test.js @@ -0,0 +1,6 @@ +common +// ^ +fooS +// ^ +barS +// ^ diff --git a/tests/libdef_scoped_autocomplete/foo_lib.js b/tests/libdef_scoped_autocomplete/foo_lib.js new file mode 100644 index 00000000000..3ed2be90000 --- /dev/null +++ b/tests/libdef_scoped_autocomplete/foo_lib.js @@ -0,0 +1,2 @@ +declare const commonValueWithOverride: number; +declare const fooSpecific: string; diff --git a/tests/libdef_scoped_autocomplete/libdef_scoped_autocomplete.exp b/tests/libdef_scoped_autocomplete/libdef_scoped_autocomplete.exp new file mode 100644 index 00000000000..5023c259d39 --- /dev/null +++ b/tests/libdef_scoped_autocomplete/libdef_scoped_autocomplete.exp @@ -0,0 +1,51 @@ +common_test.js:1:7 +Flags: --pretty --imports +{ + "result":[ + {"name":"commonValueNoOverride","type":"(global)"}, + {"name":"commonValueWithOverride","type":"(global)"} + ] +} + +common_test.js:3:5 +Flags: --pretty --imports +{"result":[]} + +common_test.js:5:5 +Flags: --pretty --imports +{"result":[]} + +foo/test.js:1:7 +Flags: --pretty --imports +{ + "result":[ + {"name":"commonValueNoOverride","type":"(global)"}, + {"name":"commonValueWithOverride","type":"(global)"} + ] +} + +foo/test.js:3:5 +Flags: --pretty --imports +{"result":[{"name":"fooSpecific","type":"(global)"}]} + +foo/test.js:5:5 +Flags: --pretty --imports +{"result":[]} + +bar/test.js:1:7 +Flags: --pretty --imports +{ + "result":[ + {"name":"commonValueNoOverride","type":"(global)"}, + {"name":"commonValueWithOverride","type":"(global)"} + ] +} + +bar/test.js:3:5 +Flags: --pretty --imports +{"result":[]} + +bar/test.js:5:5 +Flags: --pretty --imports +{"result":[{"name":"barSpecific","type":"(global)"}]} + diff --git a/tests/libdef_scoped_autocomplete/test.sh b/tests/libdef_scoped_autocomplete/test.sh new file mode 100644 index 00000000000..2d390677df6 --- /dev/null +++ b/tests/libdef_scoped_autocomplete/test.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +# shellcheck disable=SC2094 + +queries_in_file autocomplete "common_test.js" --pretty --imports +queries_in_file autocomplete "foo/test.js" --pretty --imports +queries_in_file autocomplete "bar/test.js" --pretty --imports