diff --git a/infer/src/checkers/LithoDomain.ml b/infer/src/checkers/LithoDomain.ml index 6888cd3e7..2cec5cd39 100644 --- a/infer/src/checkers/LithoDomain.ml +++ b/infer/src/checkers/LithoDomain.ml @@ -40,31 +40,59 @@ module CallSet = AbstractDomain.FiniteSet (MethodCall) module OldDomain = AbstractDomain.Map (LocalAccessPath) (CallSet) module NewDomain = struct - include AbstractDomain.Empty + module CreatedLocation = struct + type t = Location.t [@@deriving compare] + + let pp fmt location = F.fprintf fmt "Created at %a" Location.pp location + end + + module CreatedLocations = AbstractDomain.InvertedSet (CreatedLocation) + + module Created = struct + include AbstractDomain.InvertedMap (LocalAccessPath) (CreatedLocations) + + let lookup k x = Option.value (find_opt k x) ~default:CreatedLocations.empty + end + + include Created + + let assign ~lhs ~rhs x = Created.add lhs (Created.lookup rhs x) x + + let call_create lhs location x = Created.add lhs (CreatedLocations.singleton location) x + + let call_builder ~ret ~receiver x = Created.add ret (Created.lookup receiver x) x end include struct include AbstractDomain.Pair (OldDomain) (NewDomain) - let lift f (o, _) = f o + let lift_old f (o, _) = f o let map_old f (o, n) = (f o, n) - let empty = (OldDomain.empty, ()) + let map_new f (o, n) = (o, f n) + + let empty = (OldDomain.empty, NewDomain.empty) let add k v = map_old (OldDomain.add k v) let remove k = map_old (OldDomain.remove k) - let bindings = lift OldDomain.bindings + let bindings = lift_old OldDomain.bindings - let find k = lift (OldDomain.find k) + let find k = lift_old (OldDomain.find k) - let mem k = lift (OldDomain.mem k) + let mem k = lift_old (OldDomain.mem k) - let iter f = lift (OldDomain.iter f) + let iter f = lift_old (OldDomain.iter f) let fold f (o, _) init = OldDomain.fold f o init + + let assign ~lhs ~rhs = map_new (NewDomain.assign ~lhs ~rhs) + + let call_create ret location = map_new (NewDomain.call_create ret location) + + let call_builder ~ret ~receiver = map_new (NewDomain.call_builder ~ret ~receiver) end let substitute ~(f_sub : LocalAccessPath.t -> LocalAccessPath.t option) astate = diff --git a/infer/src/checkers/LithoDomain.mli b/infer/src/checkers/LithoDomain.mli index 24810d19a..d17eac4b3 100644 --- a/infer/src/checkers/LithoDomain.mli +++ b/infer/src/checkers/LithoDomain.mli @@ -33,7 +33,7 @@ module CallSet : module type of AbstractDomain.FiniteSet (MethodCall) module OldDomain : module type of AbstractDomain.Map (LocalAccessPath) (CallSet) -module NewDomain : module type of AbstractDomain.Empty +module NewDomain : AbstractDomain.S include module type of AbstractDomain.Pair (OldDomain) (NewDomain) @@ -49,6 +49,12 @@ val find : LocalAccessPath.t -> t -> CallSet.t val bindings : t -> (LocalAccessPath.t * CallSet.t) list +val assign : lhs:LocalAccessPath.t -> rhs:LocalAccessPath.t -> t -> t + +val call_create : LocalAccessPath.t -> Location.t -> t -> t + +val call_builder : ret:LocalAccessPath.t -> receiver:LocalAccessPath.t -> t -> t + val substitute : f_sub:(LocalAccessPath.t -> LocalAccessPath.t option) -> t -> t (** Substitute each access path in the domain using [f_sub]. If [f_sub] returns None, the original access path is retained; otherwise, the new one is used *) diff --git a/infer/src/checkers/LithoFramework.ml b/infer/src/checkers/LithoFramework.ml index d27f8f212..c33bed626 100644 --- a/infer/src/checkers/LithoFramework.ml +++ b/infer/src/checkers/LithoFramework.ml @@ -137,7 +137,12 @@ struct (try Domain.find return_access_path astate with Caml.Not_found -> Domain.CallSet.empty) |> Domain.CallSet.add (Domain.MethodCall.make receiver callee_pname location) in - Domain.add return_access_path return_calls astate + let astate = Domain.add return_access_path return_calls astate in + if is_component_create_method callee_pname tenv then + Domain.call_create return_access_path location astate + else if is_component_builder callee_pname tenv then + Domain.call_builder ~ret:return_access_path ~receiver astate + else astate else (* treat it like a normal call *) apply_callee_summary callee_summary_opt caller_pname return_base actuals astate @@ -146,7 +151,7 @@ struct Payload.read ~caller_summary:summary ~callee_pname:callee_procname in apply_callee_summary callee_summary_opt caller_pname ret_id_typ actuals astate - | Assign (lhs_ae, HilExp.AccessExpression rhs_ae, _) -> ( + | Assign (lhs_ae, HilExp.AccessExpression rhs_ae, _) -> (* creating an alias for the rhs binding; assume all reads will now occur through the alias. this helps us keep track of chains in cases like tmp = getFoo(); x = tmp; tmp.getBar() *) @@ -156,10 +161,13 @@ struct let rhs_access_path = Domain.LocalAccessPath.make (HilExp.AccessExpression.to_access_path rhs_ae) caller_pname in - try - let call_set = Domain.find rhs_access_path astate in - Domain.remove rhs_access_path astate |> Domain.add lhs_access_path call_set - with Caml.Not_found -> astate ) + let astate = + try + let call_set = Domain.find rhs_access_path astate in + Domain.remove rhs_access_path astate |> Domain.add lhs_access_path call_set + with Caml.Not_found -> astate + in + Domain.assign ~lhs:lhs_access_path ~rhs:rhs_access_path astate | _ -> astate