[litho] Distinguish builder object by latest callsite

Reviewed By: ezgicicek

Differential Revision: D19147057

fbshipit-source-id: f76d84da1
master
Sungkeun Cho 5 years ago committed by Facebook Github Bot
parent 805e3f17fc
commit 78ff7f7942

@ -49,18 +49,39 @@ end
module CreatedLocation = struct module CreatedLocation = struct
type t = type t =
| ByCreateMethod of {location: Location.t; typ_name: Typ.name} | ByCreateMethod of {location: Location.t; typ_name: Typ.name; latest_callsite: Location.t}
| ByParameter of LocalAccessPath.t | ByParameter of LocalAccessPath.t
[@@deriving compare] [@@deriving compare]
let pp fmt = function let pp =
| ByCreateMethod {location; typ_name} -> let pp_latest_callsite location latest_callsite fmt =
F.fprintf fmt "Created at %a with type %a" Location.pp location Typ.Name.pp typ_name if not (Location.equal location latest_callsite) then
F.fprintf fmt "(via %a)" Location.pp latest_callsite
in
fun fmt -> function
| ByCreateMethod {location; typ_name; latest_callsite} ->
F.fprintf fmt "Created at %a%t with type %a" Location.pp location
(pp_latest_callsite location latest_callsite)
Typ.Name.pp typ_name
| ByParameter path -> | ByParameter path ->
F.fprintf fmt "Given by parameter %a" LocalAccessPath.pp path F.fprintf fmt "Given by parameter %a" LocalAccessPath.pp path
let update_latest_callsite callsite x =
match x with ByCreateMethod x -> ByCreateMethod {x with latest_callsite= callsite} | _ -> x
end end
module CreatedLocations = AbstractDomain.FiniteSet (CreatedLocation) module CreatedLocations = struct
include AbstractDomain.FiniteSet (CreatedLocation)
let update_latest_callsite callsite ~to_update x =
map
(fun created_location ->
if mem created_location to_update then
CreatedLocation.update_latest_callsite callsite created_location
else created_location )
x
end
(** Map from access paths of callee parameters and return variable to caller's corresponding access (** Map from access paths of callee parameters and return variable to caller's corresponding access
paths *) paths *)
@ -108,6 +129,14 @@ module Created = struct
append caller_return caller_created acc ) append caller_return caller_created acc )
in in
CreatedLocations.fold accum_subst callee_returns acc ) CreatedLocations.fold accum_subst callee_returns acc )
let get_all_created_locations x =
fold (fun _ v acc -> CreatedLocations.union acc v) x CreatedLocations.empty
let update_latest_callsite callsite to_update x =
map (fun v -> CreatedLocations.update_latest_callsite callsite ~to_update v) x
end end
module MethodCalls = struct module MethodCalls = struct
@ -184,6 +213,11 @@ module MethodCalled = struct
let no_build_called created_location = {created_location; is_build_called= false} let no_build_called created_location = {created_location; is_build_called= false}
let build_called created_location = {created_location; is_build_called= true} let build_called created_location = {created_location; is_build_called= true}
let update_latest_callsite callsite to_update k =
if CreatedLocations.mem k.created_location to_update then
{k with created_location= CreatedLocation.update_latest_callsite callsite k.created_location}
else k
end end
include AbstractDomain.Map (Key) (MethodCalls) include AbstractDomain.Map (Key) (MethodCalls)
@ -274,6 +308,20 @@ module MethodCalled = struct
in in
let caller' = fold accum_substed callee empty in let caller' = fold accum_substed callee empty in
merge (fun _ v v' -> match v' with Some _ -> v' | None -> v) caller caller' merge (fun _ v v' -> match v' with Some _ -> v' | None -> v) caller caller'
let get_all_created_locations x =
fold
(fun {created_location} _ acc -> CreatedLocations.add created_location acc)
x CreatedLocations.empty
let update_latest_callsite callsite to_update x =
fold
(fun k v acc ->
let k = Key.update_latest_callsite callsite to_update k in
add k v acc )
x empty
end end
module Mem = struct module Mem = struct
@ -332,7 +380,9 @@ module Mem = struct
let call_create lhs typ_name location ({created} as x) = let call_create lhs typ_name location ({created} as x) =
let created_location = CreatedLocation.ByCreateMethod {location; typ_name} in let created_location =
CreatedLocation.ByCreateMethod {location; typ_name; latest_callsite= location}
in
{ created= Created.add lhs (CreatedLocations.singleton created_location) created { created= Created.add lhs (CreatedLocations.singleton created_location) created
; method_called= ; method_called=
MethodCalled.add MethodCalled.add
@ -356,8 +406,25 @@ module Mem = struct
{x with method_called= MethodCalled.check_required_props ~check_on_string_set method_called} {x with method_called= MethodCalled.check_required_props ~check_on_string_set method_called}
let subst ~formals ~actuals ~ret_id_typ:(ret_var, ret_typ) ~caller_pname ~callee_pname ~caller let get_all_created_locations {created; method_called} =
~callee = CreatedLocations.union
(Created.get_all_created_locations created)
(MethodCalled.get_all_created_locations method_called)
let update_latest_callsite callsite ~prev ~next =
let prev_created_locations = get_all_created_locations prev in
let next_created_locations = get_all_created_locations next in
let new_created_locations =
CreatedLocations.diff next_created_locations prev_created_locations
in
{ created= Created.update_latest_callsite callsite new_created_locations next.created
; method_called=
MethodCalled.update_latest_callsite callsite new_created_locations next.method_called }
let subst ~callsite ~formals ~actuals ~ret_id_typ:(ret_var, ret_typ) ~caller_pname ~callee_pname
~caller ~callee =
let callee_return = let callee_return =
LocalAccessPath.make_from_pvar (Pvar.get_ret_pvar callee_pname) ret_typ callee_pname LocalAccessPath.make_from_pvar (Pvar.get_ret_pvar callee_pname) ret_typ callee_pname
in in
@ -394,7 +461,8 @@ module Mem = struct
MethodCalled.subst ~is_reachable map ~find_caller_created ~caller:caller.method_called MethodCalled.subst ~is_reachable map ~find_caller_created ~caller:caller.method_called
~callee:callee.method_called ~callee:callee.method_called
in in
{created; method_called} let next = {created; method_called} in
update_latest_callsite callsite ~prev:caller ~next
end end
type t = {no_return_called: Mem.t; return_called: Mem.t} type t = {no_return_called: Mem.t; return_called: Mem.t}
@ -441,10 +509,10 @@ let call_return {no_return_called; return_called} =
{no_return_called= Mem.empty; return_called= Mem.join no_return_called return_called} {no_return_called= Mem.empty; return_called= Mem.join no_return_called return_called}
let subst ~formals ~actuals ~ret_id_typ ~caller_pname ~callee_pname ~caller ~callee = let subst ~callsite ~formals ~actuals ~ret_id_typ ~caller_pname ~callee_pname ~caller ~callee =
{ caller with { caller with
no_return_called= no_return_called=
Mem.subst ~formals ~actuals ~ret_id_typ ~caller_pname ~callee_pname Mem.subst ~callsite ~formals ~actuals ~ret_id_typ ~caller_pname ~callee_pname
~caller:caller.no_return_called ~callee } ~caller:caller.no_return_called ~callee }

@ -34,7 +34,8 @@ end
include AbstractDomain.S include AbstractDomain.S
val subst : val subst :
formals:(Pvar.t * Typ.t) list callsite:Location.t
-> formals:(Pvar.t * Typ.t) list
-> actuals:HilExp.t list -> actuals:HilExp.t list
-> ret_id_typ:AccessPath.base -> ret_id_typ:AccessPath.base
-> caller_pname:Typ.Procname.t -> caller_pname:Typ.Procname.t

@ -205,11 +205,11 @@ module TransferFunctions = struct
type extras = {get_proc_summary_and_formals: get_proc_summary_and_formals} type extras = {get_proc_summary_and_formals: get_proc_summary_and_formals}
let apply_callee_summary summary_opt ~caller_pname ~callee_pname ret_id_typ formals actuals astate let apply_callee_summary summary_opt callsite ~caller_pname ~callee_pname ret_id_typ formals
= actuals astate =
Option.value_map summary_opt ~default:astate ~f:(fun callee_summary -> Option.value_map summary_opt ~default:astate ~f:(fun callee_summary ->
Domain.subst ~formals ~actuals ~ret_id_typ ~caller_pname ~callee_pname ~caller:astate Domain.subst ~callsite ~formals ~actuals ~ret_id_typ ~caller_pname ~callee_pname
~callee:callee_summary ) ~caller:astate ~callee:callee_summary )
let exec_instr astate ProcData.{summary; tenv; extras= {get_proc_summary_and_formals}} _ let exec_instr astate ProcData.{summary; tenv; extras= {get_proc_summary_and_formals}} _
@ -246,14 +246,14 @@ module TransferFunctions = struct
else else
(* treat it like a normal call *) (* treat it like a normal call *)
Option.value_map callee_summary_and_formals_opt ~default:astate ~f:(fun (_, formals) -> Option.value_map callee_summary_and_formals_opt ~default:astate ~f:(fun (_, formals) ->
apply_callee_summary callee_summary_opt ~caller_pname ~callee_pname return_base apply_callee_summary callee_summary_opt location ~caller_pname ~callee_pname
formals actuals astate ) return_base formals actuals astate )
| Call (ret_id_typ, Direct callee_pname, actuals, _, _) -> | Call (ret_id_typ, Direct callee_pname, actuals, _, location) ->
let callee_summary_and_formals_opt = get_proc_summary_and_formals callee_pname in let callee_summary_and_formals_opt = get_proc_summary_and_formals callee_pname in
let callee_summary_opt = Option.map callee_summary_and_formals_opt ~f:fst in let callee_summary_opt = Option.map callee_summary_and_formals_opt ~f:fst in
Option.value_map callee_summary_and_formals_opt ~default:astate ~f:(fun (_, formals) -> Option.value_map callee_summary_and_formals_opt ~default:astate ~f:(fun (_, formals) ->
apply_callee_summary callee_summary_opt ~caller_pname ~callee_pname ret_id_typ formals apply_callee_summary callee_summary_opt location ~caller_pname ~callee_pname ret_id_typ
actuals astate ) formals actuals astate )
| Assign (lhs_ae, rhs, _) -> | Assign (lhs_ae, rhs, _) ->
let astate = let astate =
match rhs with match rhs with

@ -500,7 +500,7 @@ public class RequiredProps {
builder2.prop3(new Object()).build(); builder2.prop3(new Object()).build();
} }
public void twoBuildersBad_FN() { public void twoBuildersBad() {
MyComponent.Builder builder1 = createWrapper(); MyComponent.Builder builder1 = createWrapper();
MyComponent.Builder builder2 = createWrapper(); MyComponent.Builder builder2 = createWrapper();
builder1.prop1(new Object()).prop3(new Object()).build(); builder1.prop1(new Object()).prop3(new Object()).build();

@ -32,3 +32,4 @@ codetoanalyze/java/litho-required-props/RequiredProps.java, codetoanalyze.java.l
codetoanalyze/java/litho-required-props/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.setRequiredOnOneBothBranchesWithCreateOk_FP(boolean):com.facebook.litho.Component, 1, MISSING_REQUIRED_PROP, no_bucket, ERROR, [**@Prop prop1** is required for component codetoanalyze.java.litho.MyComponent, but is not set before the call to build(),calls codetoanalyze.java.litho.MyComponent.create(...),calls prop2(...),calls prop3(...)] codetoanalyze/java/litho-required-props/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.setRequiredOnOneBothBranchesWithCreateOk_FP(boolean):com.facebook.litho.Component, 1, MISSING_REQUIRED_PROP, no_bucket, ERROR, [**@Prop prop1** is required for component codetoanalyze.java.litho.MyComponent, but is not set before the call to build(),calls codetoanalyze.java.litho.MyComponent.create(...),calls prop2(...),calls prop3(...)]
codetoanalyze/java/litho-required-props/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.setRequiredOnOneBranchBad(boolean):com.facebook.litho.Component, 1, MISSING_REQUIRED_PROP, no_bucket, ERROR, [**@Prop prop1** is required for component codetoanalyze.java.litho.MyComponent, but is not set before the call to build(),calls codetoanalyze.java.litho.MyComponent.create(...),calls prop2(...),calls prop3(...)] codetoanalyze/java/litho-required-props/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.setRequiredOnOneBranchBad(boolean):com.facebook.litho.Component, 1, MISSING_REQUIRED_PROP, no_bucket, ERROR, [**@Prop prop1** is required for component codetoanalyze.java.litho.MyComponent, but is not set before the call to build(),calls codetoanalyze.java.litho.MyComponent.create(...),calls prop2(...),calls prop3(...)]
codetoanalyze/java/litho-required-props/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.setRequiredOnOneBranchEffectfulBad(boolean):com.facebook.litho.Component, 1, MISSING_REQUIRED_PROP, no_bucket, ERROR, [**@Prop prop1** is required for component codetoanalyze.java.litho.MyComponent, but is not set before the call to build(),calls codetoanalyze.java.litho.MyComponent.create(...),calls prop2(...),calls prop3(...)] codetoanalyze/java/litho-required-props/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.setRequiredOnOneBranchEffectfulBad(boolean):com.facebook.litho.Component, 1, MISSING_REQUIRED_PROP, no_bucket, ERROR, [**@Prop prop1** is required for component codetoanalyze.java.litho.MyComponent, but is not set before the call to build(),calls codetoanalyze.java.litho.MyComponent.create(...),calls prop2(...),calls prop3(...)]
codetoanalyze/java/litho-required-props/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.twoBuildersBad():void, -10, MISSING_REQUIRED_PROP, no_bucket, ERROR, [**@Prop prop1** is required for component codetoanalyze.java.litho.MyComponent, but is not set before the call to build(),calls codetoanalyze.java.litho.MyComponent.create(...),calls prop3(...)]

Loading…
Cancel
Save