source/service/incrementalCheck.ml (179 lines of code) (raw):

(* * 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. *) open Core open Ast open Analysis open Configuration.Analysis open Pyre type errors = Analysis.AnalysisError.t list [@@deriving show] let recheck ~configuration:({ incremental_style; _ } as configuration) ~scheduler ~environment ~errors paths = let timer = Timer.start () in let ast_environment = TypeEnvironment.ast_environment environment in let module_tracker = AstEnvironment.module_tracker ast_environment in let module_updates = ModuleTracker.update module_tracker ~configuration ~paths in Scheduler.once_per_worker scheduler ~configuration ~f:SharedMemory.invalidate_caches; SharedMemory.invalidate_caches (); SharedMemory.collect `aggressive; (* Repopulate the environment. *) Log.info "Repopulating the environment..."; let annotated_global_environment_update_result = let annotated_global_environment = AnnotatedGlobalEnvironment.create ast_environment in AnnotatedGlobalEnvironment.update_this_and_all_preceding_environments annotated_global_environment ~configuration ~scheduler (Update module_updates) in let invalidated_modules = AnnotatedGlobalEnvironment.UpdateResult.ast_environment_update_result annotated_global_environment_update_result |> AstEnvironment.UpdateResult.invalidated_modules in let recheck_modules, new_errors, total_rechecked_functions = match incremental_style with | FineGrained -> let unannotated_global_environment_update_result = AnnotatedGlobalEnvironment.UpdateResult.unannotated_global_environment_update_result annotated_global_environment_update_result in let function_triggers = let filter_union sofar keyset = let filter registered sofar = match SharedMemoryKeys.DependencyKey.get_key registered with | SharedMemoryKeys.TypeCheckDefine name -> ( match Reference.Map.add sofar ~key:name ~data:registered with | `Duplicate -> sofar | `Ok updated -> updated) | _ -> sofar in SharedMemoryKeys.DependencyKey.RegisteredSet.fold filter keyset sofar in AnnotatedGlobalEnvironment.UpdateResult.all_triggered_dependencies annotated_global_environment_update_result |> List.fold ~init:Reference.Map.empty ~f:filter_union in let recheck_functions = let register_and_add sofar trigger = let register = function | Some existing -> existing | None -> SharedMemoryKeys.DependencyKey.Registry.register (SharedMemoryKeys.TypeCheckDefine trigger) in Reference.Map.update sofar trigger ~f:register in UnannotatedGlobalEnvironment.UpdateResult.define_additions unannotated_global_environment_update_result |> Set.fold ~init:function_triggers ~f:register_and_add in let recheck_functions_list = Map.to_alist recheck_functions in let recheck_function_names = List.map recheck_functions_list ~f:fst in (* Rerun type checking for triggered functions. *) TypeEnvironment.invalidate environment recheck_function_names; recheck_functions_list |> List.map ~f:(fun (define, registered) -> define, Some registered) |> TypeCheck.run_on_defines ~scheduler ~configuration ~environment; (* Rerun postprocessing for triggered modules. *) let recheck_modules = (* For each rechecked function, its containing module needs to be included in postprocessing *) List.fold ~init:(Reference.Set.of_list invalidated_modules) (Reference.Map.keys function_triggers) ~f:(fun sofar define_name -> let unannotated_global_environment = UnannotatedGlobalEnvironment.UpdateResult.read_only unannotated_global_environment_update_result in match UnannotatedGlobalEnvironment.ReadOnly.get_define unannotated_global_environment define_name with | None -> sofar | Some { FunctionDefinition.qualifier; _ } -> Set.add sofar qualifier) |> Set.to_list in let errors = Analysis.Postprocessing.run ~scheduler ~configuration ~environment:(Analysis.TypeEnvironment.read_only environment) recheck_modules in recheck_modules, errors, Map.length recheck_functions | Shallow -> let total_rechecked_functions = let unannotated_global_environment_update_result = AnnotatedGlobalEnvironment.UpdateResult.unannotated_global_environment_update_result annotated_global_environment_update_result in let previous_defines = UnannotatedGlobalEnvironment.UpdateResult.previous_defines unannotated_global_environment_update_result |> Set.to_list in let current_defines = let unannotated_global_environment = UnannotatedGlobalEnvironment.UpdateResult.read_only unannotated_global_environment_update_result in List.concat_map invalidated_modules ~f: (UnannotatedGlobalEnvironment.ReadOnly.all_defines_in_module unannotated_global_environment) in TypeEnvironment.invalidate environment previous_defines; TypeEnvironment.invalidate environment current_defines; List.length current_defines in let errors = Analysis.TypeCheck.legacy_run_on_modules ~scheduler ~configuration ~environment invalidated_modules; Analysis.Postprocessing.run ~scheduler ~configuration ~environment:(Analysis.TypeEnvironment.read_only environment) invalidated_modules in invalidated_modules, errors, total_rechecked_functions in Statistics.event ~section:`Memory ~name:"shared memory size" ~integers:["size", Memory.heap_size ()] (); (* Kill all previous errors for new files we just checked *) List.iter ~f:(Hashtbl.remove errors) recheck_modules; (* Associate the new errors with new files *) Log.info "Number of new errors = %d" (List.length new_errors); List.iter new_errors ~f:(fun error -> let key = AnalysisError.module_reference error in Hashtbl.add_multi errors ~key ~data:error); Statistics.performance ~name:"incremental check" ~timer ~integers: [ "number of changed files", List.length paths; "number of module tracker updates", List.length module_updates; "number of parser updates", List.length invalidated_modules; "number of rechecked modules", List.length recheck_modules; "number of re-checked functions", total_rechecked_functions; ] (); recheck_modules, new_errors