source/interprocedural/analysisResult.mli (145 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 Ast module Kind = AnalysisKind (* Implemented by an individual analysis to be plugged into the global fixpoint. *) module type ANALYSIS_PROVIDED = sig (* Registration info. *) type call_model [@@deriving show] (* Used in fixpoint computation (widened), used at call sites. *) type result (* Produced in each iteration (replaced), not used at call sites. *) (* Used as part of a filename, so avoid spaces and slashes *) val name : string (* Functions to construct global operations. *) val empty_model : call_model val obscure_model : call_model (* For call sites where we don't know the targets. *) val join : iteration:int -> call_model -> call_model -> call_model val widen : iteration:int -> previous:call_model -> next:call_model -> call_model val reached_fixpoint : iteration:int -> previous:call_model -> next:call_model -> bool (* remove aspects from the model that are not needed at call sites. Just for optimization. *) val strip_for_callsite : call_model -> call_model end (* Representation of the kind of data manipulated by each individual analysis. NOTE, we use an object type, but never any actual objects, since object types are more flexible for specifying a set of labeled types than other mechanisms. *) type ('result, 'model) analysis_data = < result : 'result ; model : 'model > (* Stored kind (storable in the shared heap). *) type 'a storable_kind = 'a Kind.storable_kind constraint 'a = ('result, 'model) analysis_data (* External kind (can't be stored in the shared heap). *) type 'a kind = 'a Kind.kind constraint 'a = ('result, 'model) analysis_data (* Type markers to distinguish model, result parts in generic data structures statically. E.g. model pkg vs. result pkg below. This allows splitting off more distinct parts from the analysis data without having to write new code to package and access the new parts. *) type model = MK type result = RK (* Abstract a full kind to just the part necessary. The model and result markers allow the static type system to avoid having to pattern match parts that are not used. *) type ('part, _) partial_kind = | ModelPart : < model : 'model ; .. > storable_kind -> (model, 'model) partial_kind | ResultPart : < result : 'result ; .. > storable_kind -> (result, 'result) partial_kind (* Abstracts part of an analysis' data value by storing it along with its kind. Used for both model and result parts from each analysis. *) type 'part pkg = | Pkg : { kind: ('part, 'value) partial_kind; value: 'value; } -> 'part pkg [@@deriving show] type result_pkg = result pkg [@@deriving show] type model_pkg = model pkg [@@deriving show] type model_t = { models: model_pkg Kind.Map.t; is_obscure: bool; } [@@deriving show] type result_t = result_pkg Kind.Map.t [@@deriving show] type 'call_model initialize_result = { initial_models: 'call_model Target.Map.t; skip_overrides: Ast.Reference.Set.t; } val empty_initialize_result : 'call_model initialize_result module type ANALYZER = sig type result type call_model val analyze : environment:Analysis.TypeEnvironment.ReadOnly.t -> callable:Target.callable_t -> qualifier:Reference.t -> define:Statement.Define.t Node.t -> existing:call_model option -> result * call_model (* Called prior to typechecking, so we can fail fast on bad config *) val initialize_configuration : static_analysis_configuration:Configuration.StaticAnalysis.t -> unit (* Called once on master before analysis of individual callables. *) val initialize_models : scheduler:Scheduler.t -> static_analysis_configuration:Configuration.StaticAnalysis.t -> environment:Analysis.TypeEnvironment.ReadOnly.t -> callables:Target.callable_t list -> stubs:Target.callable_t list -> call_model initialize_result val report : scheduler:Scheduler.t -> static_analysis_configuration:Configuration.StaticAnalysis.t -> environment:Analysis.TypeEnvironment.ReadOnly.t -> filename_lookup:(Ast.Reference.t -> string option) -> callables:Target.Set.t -> skipped_overrides:Ast.Reference.t list -> fixpoint_timer:Timer.t -> fixpoint_iterations:int option -> Yojson.Safe.json list end module type ANALYSIS_RESULT = sig include ANALYSIS_PROVIDED val kind : (result, call_model) analysis_data Kind.kind end module type ANALYSIS_RESULT_WITH_REGISTRATION = sig include ANALYSIS_RESULT module Register (Analyzer : ANALYZER with type result := result and type call_model := call_model) : sig val abstract_kind : Kind.abstract end end (* The full signature of an individual analysis. This is what the general framework stores per analysis and uses to manipulate its data. NOTE: this gets built in 2 steps, a) from the ANALYSIS_DATA provided to create the result kind, and b) from the analyzer function. This split breaks the recursion between analyses and their results, allowing analysis A to refer to ResultB, where ResultB is the result of analysis B, and vice-versa. *) module type ANALYSIS = sig include ANALYSIS_RESULT include ANALYZER with type result := result and type call_model := call_model end type 'a analysis_module = (module ANALYSIS with type result = 'result and type call_model = 'model) constraint 'a = ('result, 'model) analysis_data type 'a analysis = { kind: 'a Kind.storable_kind; analysis: 'a analysis_module; } type abstract_analysis = Analysis : 'a analysis -> abstract_analysis val get_abstract_analysis : Kind.abstract -> abstract_analysis val get_analysis : ('result, 'model) analysis_data Kind.storable_kind -> ('result, 'model) analysis_data analysis_module (* To add a new analysis, the analysis should invoke the Make functor below. The functor adds a new constant constructor to 'analysis kind. This Kind constructor can then be used by analyses to lookup their own results/fixpoint information (or from another analysis). To register the analysis, call the Register functor in the resulting module. *) module Make (Analysis : ANALYSIS_PROVIDED) : ANALYSIS_RESULT_WITH_REGISTRATION with type result := Analysis.result and type call_model := Analysis.call_model val empty_model : model_t val obscure_model : model_t val empty_result : result_t val get : ('part, 'value) partial_kind -> 'part pkg Kind.Map.t -> 'value option val get_model : ('result, 'model) analysis_data Kind.kind -> model_t -> 'model option val make_model : ('result, 'model) analysis_data Kind.kind -> 'model -> model_t val with_model : ('result, 'model) analysis_data Kind.kind -> 'model -> model_t -> model_t val get_result : ('result, 'model) analysis_data Kind.kind -> result_t -> 'result option