[litho] Remove old domain

Summary: The new domain is much better than the old one. Let's kill the old one (along with old litho tests) and simplify things.

Reviewed By: skcho

Differential Revision: D18959627

fbshipit-source-id: df77ae20e
master
Ezgi Çiçek 5 years ago committed by Facebook Github Bot
parent e42bd8cd6c
commit 38421e2735

@ -160,7 +160,6 @@ DIRECT_TESTS += \
java_impurity \
java_inefficientKeysetIterator \
java_infer \
java_litho \
java_litho-required-props \
java_performance \
java_purity \

@ -1529,10 +1529,6 @@ INTERNAL OPTIONS
--never-returning-null json
Matcher or list of matchers for functions that never return null.
--new-litho-domain
Activates: [EXPERIMENTAL] Use new litho domain (Conversely:
--no-new-litho-domain)
--nullable-annotation-name string
Specify custom nullable annotation name

@ -1734,8 +1734,6 @@ and monitor_prop_size =
and nelseg = CLOpt.mk_bool ~deprecated:["nelseg"] ~long:"nelseg" "Use only nonempty lsegs"
and new_litho_domain = CLOpt.mk_bool ~long:"new-litho-domain" "[EXPERIMENTAL] Use new litho domain"
and nullable_annotation =
CLOpt.mk_string_opt ~long:"nullable-annotation-name" "Specify custom nullable annotation name"
@ -3013,8 +3011,6 @@ and monitor_prop_size = !monitor_prop_size
and nelseg = !nelseg
and new_litho_domain = !new_litho_domain
and nullable_annotation = !nullable_annotation
and nullsafe_optimistic_third_party_params_in_non_strict =

@ -490,8 +490,6 @@ val monitor_prop_size : bool
val nelseg : bool
val new_litho_domain : bool
val no_translate_libs : bool
val nullable_annotation : string option

@ -11,8 +11,6 @@ module F = Format
module LocalAccessPath = struct
type t = {access_path: AccessPath.t; parent: Typ.Procname.t} [@@deriving compare]
let equal = [%compare.equal: t]
let make access_path parent = {access_path; parent}
let make_from_access_expression ae parent =
@ -21,14 +19,6 @@ module LocalAccessPath = struct
let make_from_pvar pvar typ parent = make (AccessPath.of_pvar pvar typ) parent
let to_formal_option {access_path= ((_, base_typ) as base), accesses; parent} formal_map =
match FormalMap.get_formal_index base formal_map with
| Some formal_index ->
Some (make ((Var.of_formal_index formal_index, base_typ), accesses) parent)
| None ->
None
let pp fmt t = AccessPath.pp fmt t.access_path
end
@ -36,19 +26,6 @@ module LocalAccessPathSet = PrettyPrintable.MakePPSet (LocalAccessPath)
let suffixes = String.Set.of_list ["Attr"; "Dip"; "Px"; "Res"; "Sp"]
module MethodCall = struct
type t = {receiver: LocalAccessPath.t; procname: Typ.Procname.t; location: Location.t}
[@@deriving compare]
let make receiver procname location = {receiver; procname; location}
let pp fmt {receiver; procname} =
F.fprintf fmt "%a.%a" LocalAccessPath.pp receiver Typ.Procname.pp procname
let procname_to_string {procname} = Typ.Procname.get_method procname
end
module MethodCallPrefix = struct
type t =
{ (* TODO: We can remove the [receiver] field after we replace the old checker *)
@ -72,509 +49,410 @@ module MethodCallPrefix = struct
let procname_to_string {procname} = Typ.Procname.get_method procname
(* NOTE: This is only for sharing some code with the previous version of the checker. *)
let to_method_call {receiver; procname; location} = MethodCall.make receiver procname location
end
module CallSet = AbstractDomain.FiniteSet (MethodCall)
module OldDomain = AbstractDomain.Map (LocalAccessPath) (CallSet)
module CreatedLocation = struct
type t =
| ByCreateMethod of {location: Location.t; typ_name: Typ.name}
| ByParameter of LocalAccessPath.t
[@@deriving compare]
module NewDomain = struct
module CreatedLocation = struct
type t =
| ByCreateMethod of {location: Location.t; typ_name: Typ.name}
| ByParameter of LocalAccessPath.t
[@@deriving compare]
let pp fmt = function
| ByCreateMethod {location; typ_name} ->
F.fprintf fmt "Created at %a with type %a" Location.pp location Typ.Name.pp typ_name
| ByParameter path ->
F.fprintf fmt "Given by parameter %a" LocalAccessPath.pp path
end
let pp fmt = function
| ByCreateMethod {location; typ_name} ->
F.fprintf fmt "Created at %a with type %a" Location.pp location Typ.Name.pp typ_name
| ByParameter path ->
F.fprintf fmt "Given by parameter %a" LocalAccessPath.pp path
end
module CreatedLocations = AbstractDomain.FiniteSet (CreatedLocation)
(** Map from access paths of callee parameters and return variable to caller's corresponding access
paths *)
module SubstPathMap = struct
include PrettyPrintable.MakePPMonoMap (LocalAccessPath) (LocalAccessPath)
let make ~formals ~actuals ~caller_return ~callee_return =
let map = singleton callee_return caller_return in
match
List.fold2 formals actuals ~init:map ~f:(fun acc formal actual ->
Option.fold actual ~init:acc ~f:(fun acc actual -> add formal actual acc) )
with
| Ok map ->
map
| Unequal_lengths ->
map
end
module CreatedLocations = AbstractDomain.FiniteSet (CreatedLocation)
(** Map from access paths of callee parameters and return variable to caller's corresponding
access paths *)
module SubstPathMap = struct
include PrettyPrintable.MakePPMonoMap (LocalAccessPath) (LocalAccessPath)
let make ~formals ~actuals ~caller_return ~callee_return =
let map = singleton callee_return caller_return in
match
List.fold2 formals actuals ~init:map ~f:(fun acc formal actual ->
Option.fold actual ~init:acc ~f:(fun acc actual -> add formal actual acc) )
with
| Ok map ->
map
| Unequal_lengths ->
map
end
module Created = struct
include AbstractDomain.Map (LocalAccessPath) (CreatedLocations)
module Created = struct
include AbstractDomain.Map (LocalAccessPath) (CreatedLocations)
let lookup k x = Option.value (find_opt k x) ~default:CreatedLocations.empty
let lookup k x = Option.value (find_opt k x) ~default:CreatedLocations.empty
let append path locations x =
let append_created_locations = function
| None ->
Some locations
| Some pre_locations ->
Some (CreatedLocations.join pre_locations locations)
in
update path append_created_locations x
let append path locations x =
let append_created_locations = function
| None ->
Some locations
| Some pre_locations ->
Some (CreatedLocations.join pre_locations locations)
in
update path append_created_locations x
let append_one path location x = append path (CreatedLocations.singleton location) x
let subst map ~caller_return ~callee_return ~callee ~caller =
Option.fold (find_opt callee_return callee) ~init:caller ~f:(fun acc callee_returns ->
let accum_subst callee_return acc =
match callee_return with
| CreatedLocation.ByCreateMethod _ ->
append_one caller_return callee_return acc
| CreatedLocation.ByParameter path ->
let caller_path = SubstPathMap.find path map in
Option.value_map (find_opt caller_path caller) ~default:acc
~f:(fun caller_created -> append caller_return caller_created acc)
in
CreatedLocations.fold accum_subst callee_returns acc )
end
module MethodCalls = struct
module IsChecked = AbstractDomain.BooleanOr
(** if the method calls are checked and reported *)
let append_one path location x = append path (CreatedLocations.singleton location) x
module S = AbstractDomain.InvertedSet (MethodCallPrefix)
let subst map ~caller_return ~callee_return ~callee ~caller =
Option.fold (find_opt callee_return callee) ~init:caller ~f:(fun acc callee_returns ->
let accum_subst callee_return acc =
match callee_return with
| CreatedLocation.ByCreateMethod _ ->
append_one caller_return callee_return acc
| CreatedLocation.ByParameter path ->
let caller_path = SubstPathMap.find path map in
Option.value_map (find_opt caller_path caller) ~default:acc ~f:(fun caller_created ->
append caller_return caller_created acc )
in
CreatedLocations.fold accum_subst callee_returns acc )
end
type t = {is_checked: IsChecked.t; method_calls: S.t}
module MethodCalls = struct
module IsChecked = AbstractDomain.BooleanOr
(** if the method calls are checked and reported *)
let pp fmt {is_checked; method_calls} =
F.fprintf fmt "%a%s" S.pp method_calls (if is_checked then " checked" else "")
module S = AbstractDomain.InvertedSet (MethodCallPrefix)
type t = {is_checked: IsChecked.t; method_calls: S.t}
let leq ~lhs ~rhs =
IsChecked.leq ~lhs:lhs.is_checked ~rhs:rhs.is_checked
&& S.leq ~lhs:lhs.method_calls ~rhs:rhs.method_calls
let pp fmt {is_checked; method_calls} =
F.fprintf fmt "%a%s" S.pp method_calls (if is_checked then " checked" else "")
let join x y =
{ is_checked= IsChecked.join x.is_checked y.is_checked
; method_calls= S.join x.method_calls y.method_calls }
let leq ~lhs ~rhs =
IsChecked.leq ~lhs:lhs.is_checked ~rhs:rhs.is_checked
&& S.leq ~lhs:lhs.method_calls ~rhs:rhs.method_calls
let widen ~prev ~next ~num_iters =
{ is_checked= IsChecked.widen ~prev:prev.is_checked ~next:next.is_checked ~num_iters
; method_calls= S.widen ~prev:prev.method_calls ~next:next.method_calls ~num_iters }
let join x y =
{ is_checked= IsChecked.join x.is_checked y.is_checked
; method_calls= S.join x.method_calls y.method_calls }
let empty = {is_checked= false; method_calls= S.empty}
let widen ~prev ~next ~num_iters =
{ is_checked= IsChecked.widen ~prev:prev.is_checked ~next:next.is_checked ~num_iters
; method_calls= S.widen ~prev:prev.method_calls ~next:next.method_calls ~num_iters }
let singleton e = {is_checked= false; method_calls= S.singleton e}
let add e ({method_calls} as x) = {x with method_calls= S.add e method_calls}
let empty = {is_checked= false; method_calls= S.empty}
let merge x y =
{is_checked= x.is_checked || y.is_checked; method_calls= S.union x.method_calls y.method_calls}
let singleton e = {is_checked= false; method_calls= S.singleton e}
let add e ({method_calls} as x) = {x with method_calls= S.add e method_calls}
let to_string_set method_calls =
let accum_as_string method_call acc =
String.Set.add acc (MethodCallPrefix.procname_to_string method_call)
in
S.fold accum_as_string method_calls String.Set.empty
let merge x y =
{is_checked= x.is_checked || y.is_checked; method_calls= S.union x.method_calls y.method_calls}
let get_call_chain method_calls =
(* TODO: sort chain by inserted order *)
S.elements method_calls
let to_string_set method_calls =
let accum_as_string method_call acc =
String.Set.add acc (MethodCallPrefix.procname_to_string method_call)
in
S.fold accum_as_string method_calls String.Set.empty
let check_required_props ~check_on_string_set parent_typename ({is_checked; method_calls} as x)
=
if not is_checked then (
let prop_set = to_string_set method_calls in
let call_chain = get_call_chain method_calls in
check_on_string_set parent_typename call_chain prop_set ;
{x with is_checked= true} )
else x
end
let get_call_chain method_calls =
(* TODO: sort chain by inserted order *)
S.elements method_calls
module MethodCalled = struct
module Key = struct
type t =
{ created_location: CreatedLocation.t
; is_build_called: bool (** if the build method has been called on the builder object *) }
[@@deriving compare]
let pp fmt {created_location; is_build_called} =
F.fprintf fmt "%a%s" CreatedLocation.pp created_location
(if is_build_called then " with build() called" else "")
let check_required_props ~check_on_string_set parent_typename ({is_checked; method_calls} as x) =
if not is_checked then (
let prop_set = to_string_set method_calls in
let call_chain = get_call_chain method_calls in
check_on_string_set parent_typename call_chain prop_set ;
{x with is_checked= true} )
else x
end
module MethodCalled = struct
module Key = struct
type t =
{ created_location: CreatedLocation.t
; is_build_called: bool (** if the build method has been called on the builder object *) }
[@@deriving compare]
let no_build_called created_location = {created_location; is_build_called= false}
let pp fmt {created_location; is_build_called} =
F.fprintf fmt "%a%s" CreatedLocation.pp created_location
(if is_build_called then " with build() called" else "")
let build_called created_location = {created_location; is_build_called= true}
end
include AbstractDomain.Map (Key) (MethodCalls)
let no_build_called created_location = {created_location; is_build_called= false}
let add_one created_location v x =
let f = function
| None ->
Some (MethodCalls.singleton v)
| Some method_calls ->
Some (MethodCalls.add v method_calls)
in
update (Key.no_build_called created_location) f x
let build_called created_location = {created_location; is_build_called= true}
end
include AbstractDomain.Map (Key) (MethodCalls)
let add_all created_locations callee x =
CreatedLocations.fold
(fun created_location acc -> add_one created_location callee acc)
created_locations x
let add_one created_location v x =
let f = function
| None ->
Some (MethodCalls.singleton v)
| Some method_calls ->
Some (MethodCalls.add v method_calls)
in
update (Key.no_build_called created_location) f x
let add_all created_locations callee x =
CreatedLocations.fold
(fun created_location acc -> add_one created_location callee acc)
created_locations x
let build_method_called_one created_location x =
let k_no_build_called = Key.no_build_called created_location in
let k_build_called = Key.build_called created_location in
let method_calls =
match (find_opt k_no_build_called x, find_opt k_build_called x) with
| None, None ->
MethodCalls.empty
| Some x, None | None, Some x ->
x
| Some method_calls_no_build_called, Some method_calls_build_called ->
MethodCalls.join method_calls_no_build_called method_calls_build_called
in
remove k_no_build_called x |> add k_build_called method_calls
let build_method_called_one created_location x =
let k_no_build_called = Key.no_build_called created_location in
let k_build_called = Key.build_called created_location in
let method_calls =
match (find_opt k_no_build_called x, find_opt k_build_called x) with
| None, None ->
MethodCalls.empty
| Some x, None | None, Some x ->
x
| Some method_calls_no_build_called, Some method_calls_build_called ->
MethodCalls.join method_calls_no_build_called method_calls_build_called
in
remove k_no_build_called x |> add k_build_called method_calls
let build_method_called created_locations x =
CreatedLocations.fold build_method_called_one created_locations x
let build_method_called created_locations x =
CreatedLocations.fold build_method_called_one created_locations x
let check_required_props ~check_on_string_set x =
let f {Key.created_location; is_build_called} method_calls =
if is_build_called then
match created_location with
| CreatedLocation.ByCreateMethod {typ_name} ->
MethodCalls.check_required_props ~check_on_string_set typ_name method_calls
| CreatedLocation.ByParameter _ ->
method_calls
else method_calls
in
mapi f x
let check_required_props ~check_on_string_set x =
let f {Key.created_location; is_build_called} method_calls =
if is_build_called then
match created_location with
| CreatedLocation.ByCreateMethod {typ_name} ->
MethodCalls.check_required_props ~check_on_string_set typ_name method_calls
| CreatedLocation.ByParameter _ ->
method_calls
else method_calls
let subst ~is_reachable map ~find_caller_created ~caller ~callee =
let merge_method_calls ~callee_method_calls ({Key.created_location} as caller_key) acc =
let method_calls =
Option.value_map
(find_opt (Key.no_build_called created_location) caller)
~default:callee_method_calls
~f:(fun caller_method_calls -> MethodCalls.merge caller_method_calls callee_method_calls)
in
mapi f x
update caller_key
(function
| None ->
Some method_calls
| Some acc_method_calls ->
Some (MethodCalls.merge acc_method_calls method_calls) )
acc
in
let merge_method_calls_on_substed ~callee_method_calls ~is_build_called caller_created acc =
CreatedLocations.fold
(fun created_location acc ->
merge_method_calls ~callee_method_calls {Key.created_location; is_build_called} acc )
caller_created acc
in
let accum_substed ({Key.created_location; is_build_called} as callee_key) callee_method_calls
acc =
match created_location with
| CreatedLocation.ByCreateMethod _ ->
if is_reachable created_location then
merge_method_calls ~callee_method_calls callee_key acc
else acc
| CreatedLocation.ByParameter path ->
Option.value_map (SubstPathMap.find_opt path map) ~default:acc ~f:(fun caller_path ->
Option.value_map (find_caller_created caller_path) ~default:acc
~f:(fun caller_created ->
merge_method_calls_on_substed ~callee_method_calls ~is_build_called caller_created
acc ) )
in
let caller' = fold accum_substed callee empty in
merge (fun _ v v' -> match v' with Some _ -> v' | None -> v) caller caller'
end
module Mem = struct
type t = {created: Created.t; method_called: MethodCalled.t}
let subst ~is_reachable map ~find_caller_created ~caller ~callee =
let merge_method_calls ~callee_method_calls ({Key.created_location} as caller_key) acc =
let method_calls =
Option.value_map
(find_opt (Key.no_build_called created_location) caller)
~default:callee_method_calls
~f:(fun caller_method_calls -> MethodCalls.merge caller_method_calls callee_method_calls)
in
update caller_key
(function
| None ->
Some method_calls
| Some acc_method_calls ->
Some (MethodCalls.merge acc_method_calls method_calls) )
acc
in
let merge_method_calls_on_substed ~callee_method_calls ~is_build_called caller_created acc =
CreatedLocations.fold
(fun created_location acc ->
merge_method_calls ~callee_method_calls {Key.created_location; is_build_called} acc )
caller_created acc
in
let accum_substed ({Key.created_location; is_build_called} as callee_key) callee_method_calls
acc =
match created_location with
| CreatedLocation.ByCreateMethod _ ->
if is_reachable created_location then
merge_method_calls ~callee_method_calls callee_key acc
else acc
| CreatedLocation.ByParameter path ->
Option.value_map (SubstPathMap.find_opt path map) ~default:acc ~f:(fun caller_path ->
Option.value_map (find_caller_created caller_path) ~default:acc
~f:(fun caller_created ->
merge_method_calls_on_substed ~callee_method_calls ~is_build_called
caller_created acc ) )
in
let caller' = fold accum_substed callee empty in
merge (fun _ v v' -> match v' with Some _ -> v' | None -> v) caller caller'
end
let pp fmt {created; method_called} =
F.fprintf fmt "@[<v 0>@[Created:@;%a@]@,@[MethodCalled:@;%a@]@]" Created.pp created
MethodCalled.pp method_called
module Mem = struct
type t = {created: Created.t; method_called: MethodCalled.t}
let pp fmt {created; method_called} =
F.fprintf fmt "@[<v 0>@[Created:@;%a@]@,@[MethodCalled:@;%a@]@]" Created.pp created
MethodCalled.pp method_called
let leq ~lhs ~rhs =
Created.leq ~lhs:lhs.created ~rhs:rhs.created
&& MethodCalled.leq ~lhs:lhs.method_called ~rhs:rhs.method_called
let leq ~lhs ~rhs =
Created.leq ~lhs:lhs.created ~rhs:rhs.created
&& MethodCalled.leq ~lhs:lhs.method_called ~rhs:rhs.method_called
let join x y =
{ created= Created.join x.created y.created
; method_called= MethodCalled.join x.method_called y.method_called }
let join x y =
{ created= Created.join x.created y.created
; method_called= MethodCalled.join x.method_called y.method_called }
let widen ~prev ~next ~num_iters =
{ created= Created.widen ~prev:prev.created ~next:next.created ~num_iters
; method_called= MethodCalled.widen ~prev:prev.method_called ~next:next.method_called ~num_iters
}
let widen ~prev ~next ~num_iters =
{ created= Created.widen ~prev:prev.created ~next:next.created ~num_iters
; method_called=
MethodCalled.widen ~prev:prev.method_called ~next:next.method_called ~num_iters }
let contains_build {method_called} =
MethodCalled.exists (fun MethodCalled.Key.{is_build_called} _ -> is_build_called) method_called
let empty = {created= Created.empty; method_called= MethodCalled.empty}
let empty = {created= Created.empty; method_called= MethodCalled.empty}
let init tenv pname formals =
List.fold formals ~init:empty ~f:(fun ({created; method_called} as acc) (pvar, ptr_typ) ->
match ptr_typ with
| Typ.{desc= Tptr (typ, _)} -> (
match Typ.name typ with
| Some typ_name
when PatternMatch.is_subtype_of_str tenv typ_name
"com.facebook.litho.Component$Builder" ->
let formal_ae = LocalAccessPath.make_from_pvar pvar ptr_typ pname in
let created_location = CreatedLocation.ByParameter formal_ae in
{ created=
Created.add formal_ae (CreatedLocations.singleton created_location) created
; method_called=
MethodCalled.add
(MethodCalled.Key.no_build_called created_location)
MethodCalls.empty method_called }
| _ ->
acc )
let init tenv pname formals =
List.fold formals ~init:empty ~f:(fun ({created; method_called} as acc) (pvar, ptr_typ) ->
match ptr_typ with
| Typ.{desc= Tptr (typ, _)} -> (
match Typ.name typ with
| Some typ_name
when PatternMatch.is_subtype_of_str tenv typ_name "com.facebook.litho.Component$Builder"
->
let formal_ae = LocalAccessPath.make_from_pvar pvar ptr_typ pname in
let created_location = CreatedLocation.ByParameter formal_ae in
{ created= Created.add formal_ae (CreatedLocations.singleton created_location) created
; method_called=
MethodCalled.add
(MethodCalled.Key.no_build_called created_location)
MethodCalls.empty method_called }
| _ ->
acc )
| _ ->
acc )
let assign ~lhs ~rhs ({created} as x) =
{x with created= Created.add lhs (Created.lookup rhs created) created}
let assign ~lhs ~rhs ({created} as x) =
{x with created= Created.add lhs (Created.lookup rhs created) created}
let call_create lhs typ_name location ({created} as x) =
let created_location = CreatedLocation.ByCreateMethod {location; typ_name} in
{ created= Created.add lhs (CreatedLocations.singleton created_location) created
; method_called=
MethodCalled.add
(MethodCalled.Key.no_build_called created_location)
MethodCalls.empty x.method_called }
let call_create lhs typ_name location ({created} as x) =
let created_location = CreatedLocation.ByCreateMethod {location; typ_name} in
{ created= Created.add lhs (CreatedLocations.singleton created_location) created
; method_called=
MethodCalled.add
(MethodCalled.Key.no_build_called created_location)
MethodCalls.empty x.method_called }
let call_builder ~ret ~receiver callee {created; method_called} =
let created_locations = Created.lookup receiver created in
{ created= Created.add ret created_locations created
; method_called= MethodCalled.add_all created_locations callee method_called }
let call_builder ~ret ~receiver callee {created; method_called} =
let created_locations = Created.lookup receiver created in
{ created= Created.add ret created_locations created
; method_called= MethodCalled.add_all created_locations callee method_called }
let call_build_method ~ret ~receiver {created; method_called} =
let created_locations = Created.lookup receiver created in
{ created= Created.add ret created_locations created
; method_called= MethodCalled.build_method_called created_locations method_called }
let call_build_method ~ret ~receiver {created; method_called} =
let created_locations = Created.lookup receiver created in
{ created= Created.add ret created_locations created
; method_called= MethodCalled.build_method_called created_locations method_called }
let check_required_props ~check_on_string_set ({method_called} as x) =
{x with method_called= MethodCalled.check_required_props ~check_on_string_set method_called}
let check_required_props ~check_on_string_set ({method_called} as x) =
{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
~callee =
let callee_return =
LocalAccessPath.make_from_pvar (Pvar.get_ret_pvar callee_pname) ret_typ callee_pname
in
let caller_return = LocalAccessPath.make (AccessPath.of_var ret_var ret_typ) caller_pname in
let formals =
List.map formals ~f:(fun (pvar, typ) -> LocalAccessPath.make_from_pvar pvar typ callee_pname)
in
let actuals =
List.map actuals ~f:(function
| HilExp.AccessExpression actual ->
Some (LocalAccessPath.make_from_access_expression actual caller_pname)
| _ ->
None )
in
let map = SubstPathMap.make ~formals ~actuals ~caller_return ~callee_return in
let created =
Created.subst map ~caller_return ~callee_return ~caller:caller.created
~callee:callee.created
let subst ~formals ~actuals ~ret_id_typ:(ret_var, ret_typ) ~caller_pname ~callee_pname ~caller
~callee =
let callee_return =
LocalAccessPath.make_from_pvar (Pvar.get_ret_pvar callee_pname) ret_typ callee_pname
in
let caller_return = LocalAccessPath.make (AccessPath.of_var ret_var ret_typ) caller_pname in
let formals =
List.map formals ~f:(fun (pvar, typ) -> LocalAccessPath.make_from_pvar pvar typ callee_pname)
in
let actuals =
List.map actuals ~f:(function
| HilExp.AccessExpression actual ->
Some (LocalAccessPath.make_from_access_expression actual caller_pname)
| _ ->
None )
in
let map = SubstPathMap.make ~formals ~actuals ~caller_return ~callee_return in
let created =
Created.subst map ~caller_return ~callee_return ~caller:caller.created ~callee:callee.created
in
let is_reachable =
let reachable_paths =
LocalAccessPathSet.of_list formals |> LocalAccessPathSet.add callee_return
in
let is_reachable =
let reachable_paths =
LocalAccessPathSet.of_list formals |> LocalAccessPathSet.add callee_return
let reachable_locations =
let accum_reachable_location path locations acc =
if LocalAccessPathSet.mem path reachable_paths then CreatedLocations.union acc locations
else acc
in
let reachable_locations =
let accum_reachable_location path locations acc =
if LocalAccessPathSet.mem path reachable_paths then CreatedLocations.union acc locations
else acc
in
Created.fold accum_reachable_location callee.created CreatedLocations.empty
in
fun created_location -> CreatedLocations.mem created_location reachable_locations
in
let method_called =
let find_caller_created path = Created.find_opt path caller.created in
MethodCalled.subst ~is_reachable map ~find_caller_created ~caller:caller.method_called
~callee:callee.method_called
Created.fold accum_reachable_location callee.created CreatedLocations.empty
in
{created; method_called}
end
type t = {no_return_called: Mem.t; return_called: Mem.t}
let pp fmt {no_return_called; return_called} =
F.fprintf fmt "@[<v 0>@[NoReturnCalled:@;%a@]@,@[ReturnCalled:@;%a@]@]" Mem.pp no_return_called
Mem.pp return_called
let get_summary ~is_void_func x = if is_void_func then x.no_return_called else x.return_called
let leq ~lhs ~rhs =
Mem.leq ~lhs:lhs.no_return_called ~rhs:rhs.no_return_called
&& Mem.leq ~lhs:lhs.return_called ~rhs:rhs.return_called
let join x y =
{ no_return_called= Mem.join x.no_return_called y.no_return_called
; return_called= Mem.join x.return_called y.return_called }
let widen ~prev ~next ~num_iters =
{ no_return_called= Mem.widen ~prev:prev.no_return_called ~next:next.no_return_called ~num_iters
; return_called= Mem.widen ~prev:prev.return_called ~next:next.return_called ~num_iters }
let init tenv pname formals =
{no_return_called= Mem.init tenv pname formals; return_called= Mem.empty}
let map_no_return_called f x = {x with no_return_called= f x.no_return_called}
let assign ~lhs ~rhs = map_no_return_called (Mem.assign ~lhs ~rhs)
let call_create lhs typ_name location =
map_no_return_called (Mem.call_create lhs typ_name location)
let call_builder ~ret ~receiver callee =
map_no_return_called (Mem.call_builder ~ret ~receiver callee)
let call_build_method ~ret ~receiver = map_no_return_called (Mem.call_build_method ~ret ~receiver)
fun created_location -> CreatedLocations.mem created_location reachable_locations
in
let method_called =
let find_caller_created path = Created.find_opt path caller.created in
MethodCalled.subst ~is_reachable map ~find_caller_created ~caller:caller.method_called
~callee:callee.method_called
in
{created; method_called}
end
let call_return {no_return_called; return_called} =
{no_return_called= Mem.empty; return_called= Mem.join no_return_called return_called}
type t = {no_return_called: Mem.t; return_called: Mem.t}
let pp fmt {no_return_called; return_called} =
F.fprintf fmt "@[<v 0>@[NoReturnCalled:@;%a@]@,@[ReturnCalled:@;%a@]@]" Mem.pp no_return_called
Mem.pp return_called
let subst ~formals ~actuals ~ret_id_typ ~caller_pname ~callee_pname ~caller ~callee =
{ caller with
no_return_called=
Mem.subst ~formals ~actuals ~ret_id_typ ~caller_pname ~callee_pname
~caller:caller.no_return_called ~callee }
end
include struct
include AbstractDomain.Pair (OldDomain) (NewDomain)
let get_summary ~is_void_func x = if is_void_func then x.no_return_called else x.return_called
let lift_old f (o, _) = f o
let leq ~lhs ~rhs =
Mem.leq ~lhs:lhs.no_return_called ~rhs:rhs.no_return_called
&& Mem.leq ~lhs:lhs.return_called ~rhs:rhs.return_called
let map_old f (o, n) = (f o, n)
let map_new f (o, n) = (o, f n)
let join x y =
{ no_return_called= Mem.join x.no_return_called y.no_return_called
; return_called= Mem.join x.return_called y.return_called }
let init tenv pname formals = (OldDomain.empty, NewDomain.init tenv pname formals)
let add k v = map_old (OldDomain.add k v)
let widen ~prev ~next ~num_iters =
{ no_return_called= Mem.widen ~prev:prev.no_return_called ~next:next.no_return_called ~num_iters
; return_called= Mem.widen ~prev:prev.return_called ~next:next.return_called ~num_iters }
let remove k = map_old (OldDomain.remove k)
let bindings = lift_old OldDomain.bindings
let init tenv pname formals =
{no_return_called= Mem.init tenv pname formals; return_called= Mem.empty}
let find k = lift_old (OldDomain.find k)
let mem k = lift_old (OldDomain.mem k)
let map_no_return_called f x = {x with no_return_called= f x.no_return_called}
let iter f = lift_old (OldDomain.iter f)
let assign ~lhs ~rhs = map_no_return_called (Mem.assign ~lhs ~rhs)
let assign ~lhs ~rhs = map_new (NewDomain.assign ~lhs ~rhs)
let call_create lhs typ_name location = map_no_return_called (Mem.call_create lhs typ_name location)
let call_create ret typ_name location = map_new (NewDomain.call_create ret typ_name location)
let call_builder ~ret ~receiver callee =
map_no_return_called (Mem.call_builder ~ret ~receiver callee)
let call_builder ~ret ~receiver callee = map_new (NewDomain.call_builder ~ret ~receiver callee)
let call_build_method ~ret ~receiver = map_new (NewDomain.call_build_method ~ret ~receiver)
let call_build_method ~ret ~receiver = map_no_return_called (Mem.call_build_method ~ret ~receiver)
let call_return = map_new NewDomain.call_return
let call_return {no_return_called; return_called} =
{no_return_called= Mem.empty; return_called= Mem.join no_return_called return_called}
type summary = OldDomain.t * NewDomain.Mem.t
let pp_summary fmt (o, n) =
F.fprintf fmt "@[<v 2>@[Old:@;%a@]@,@[New:@;%a@]@]" OldDomain.pp o NewDomain.Mem.pp n
let subst ~formals ~actuals ~ret_id_typ ~caller_pname ~callee_pname ~caller ~callee =
{ caller with
no_return_called=
Mem.subst ~formals ~actuals ~ret_id_typ ~caller_pname ~callee_pname
~caller:caller.no_return_called ~callee }
let get_summary ~is_void_func = map_new (NewDomain.get_summary ~is_void_func)
type summary = Mem.t
let check_required_props ~check_on_string_set =
map_new (NewDomain.Mem.check_required_props ~check_on_string_set)
end
let pp_summary fmt = F.fprintf fmt "@[<v 2>@[Litho Summary:@;%a@]@]" Mem.pp
let substitute ~(f_sub : LocalAccessPath.t -> LocalAccessPath.t option) old_astate =
OldDomain.fold
(fun original_access_path call_set acc ->
let access_path' =
match f_sub original_access_path with
| Some access_path ->
access_path
| None ->
original_access_path
in
let call_set' =
CallSet.fold
(fun ({procname; location} as call) call_set_acc ->
let receiver =
match f_sub call.receiver with Some receiver' -> receiver' | None -> call.receiver
in
CallSet.add {receiver; procname; location} call_set_acc )
call_set CallSet.empty
in
OldDomain.add access_path' call_set' acc )
old_astate OldDomain.empty
(** Unroll the domain to enumerate all the call chains ending in [call] and apply [f] to each
maximal chain. For example, if the domain encodes the chains foo().bar().goo() and foo().baz(),
[f] will be called once on foo().bar().goo() and once on foo().baz() *)
let iter_call_chains_with_suffix ~f call_suffix astate =
let rec unroll_call_ ({receiver; procname} as call : MethodCall.t) (acc, visited) =
let is_cycle (call : MethodCall.t) =
(* detect direct cycles and cycles due to mutual recursion *)
LocalAccessPath.equal call.receiver receiver || Typ.Procname.Set.mem call.procname visited
in
let acc' = call :: acc in
let visited' = Typ.Procname.Set.add procname visited in
try
let calls' = find receiver astate in
CallSet.iter
(fun call ->
if not (is_cycle call) then unroll_call_ call (acc', visited')
else f receiver.access_path acc' )
calls'
with Caml.Not_found -> f receiver.access_path acc'
in
unroll_call_ call_suffix ([], Typ.Procname.Set.empty)
let iter_call_chains ~f astate =
iter
(fun _ call_set ->
CallSet.iter (fun call -> iter_call_chains_with_suffix ~f call astate) call_set )
astate
let check_required_props ~check_on_string_set = Mem.check_required_props ~check_on_string_set

@ -6,7 +6,6 @@
*)
open! IStd
module F = Format
(** Access path + its parent procedure *)
module LocalAccessPath : sig
@ -15,73 +14,41 @@ module LocalAccessPath : sig
val make : AccessPath.t -> Typ.Procname.t -> t
val make_from_access_expression : HilExp.AccessExpression.t -> Typ.Procname.t -> t
val to_formal_option : t -> FormalMap.t -> t option
val pp : F.formatter -> t -> unit
end
val suffixes : String.Set.t
(** Called procedure & location + its receiver *)
module MethodCall : sig
type t = private {receiver: LocalAccessPath.t; procname: Typ.Procname.t; location: Location.t}
[@@deriving compare]
module MethodCallPrefix : sig
type t = private
{receiver: LocalAccessPath.t; prefix: string; procname: Typ.Procname.t; location: Location.t}
val make : LocalAccessPath.t -> Typ.Procname.t -> Location.t -> t
val pp : F.formatter -> t -> unit
val procname_to_string : t -> string
end
module MethodCallPrefix : sig
module Mem : sig
type t
val make : LocalAccessPath.t -> Typ.Procname.t -> Location.t -> t
val to_method_call : t -> MethodCall.t
val contains_build : t -> bool
end
module CallSet : module type of AbstractDomain.FiniteSet (MethodCall)
module OldDomain : module type of AbstractDomain.Map (LocalAccessPath) (CallSet)
module NewDomain : sig
module Mem : sig
type t
end
include AbstractDomain.S
include AbstractDomain.S
val subst :
formals:(Pvar.t * Typ.t) list
-> actuals:HilExp.t list
-> ret_id_typ:AccessPath.base
-> caller_pname:Typ.Procname.t
-> callee_pname:Typ.Procname.t
-> caller:t
-> callee:Mem.t
-> t
end
include module type of AbstractDomain.Pair (OldDomain) (NewDomain)
val subst :
formals:(Pvar.t * Typ.t) list
-> actuals:HilExp.t list
-> ret_id_typ:AccessPath.base
-> caller_pname:Typ.Procname.t
-> callee_pname:Typ.Procname.t
-> caller:t
-> callee:Mem.t
-> t
(** type for saving in summary payload *)
type summary = OldDomain.t * NewDomain.Mem.t
type summary = Mem.t
val init : Tenv.t -> Typ.Procname.t -> (Pvar.t * Typ.t) list -> t
val add : LocalAccessPath.t -> CallSet.t -> t -> t
val remove : LocalAccessPath.t -> t -> t
val mem : LocalAccessPath.t -> t -> bool
val find : LocalAccessPath.t -> t -> CallSet.t
val bindings : summary -> (LocalAccessPath.t * CallSet.t) list
val assign : lhs:LocalAccessPath.t -> rhs:LocalAccessPath.t -> t -> t
val call_create : LocalAccessPath.t -> Typ.name -> Location.t -> t -> t
@ -105,10 +72,3 @@ val check_required_props :
check_on_string_set:(Typ.name -> MethodCallPrefix.t list -> String.Set.t -> unit)
-> summary
-> summary
val substitute : f_sub:(LocalAccessPath.t -> LocalAccessPath.t option) -> OldDomain.t -> OldDomain.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 *)
val iter_call_chains : f:(AccessPath.t -> MethodCall.t list -> unit) -> summary -> unit
(** Apply [f] to each maximal call chain encoded in [t] *)

@ -100,32 +100,11 @@ struct
type nonrec extras = extras
let apply_callee_summary summary_opt ~caller_pname ~callee_pname ret_id_typ formals actuals
((old_domain, new_domain) as astate) =
Option.value_map summary_opt ~default:astate ~f:(fun (old_callee, new_callee) ->
(* TODO: append paths if the footprint access path is an actual path instead of a var *)
let f_sub {Domain.LocalAccessPath.access_path= (var, _), _} =
match Var.get_footprint_index var with
| Some footprint_index -> (
match List.nth actuals footprint_index with
| Some (HilExp.AccessExpression actual_access_expr) ->
Some
(Domain.LocalAccessPath.make
(HilExp.AccessExpression.to_access_path actual_access_expr)
caller_pname)
| _ ->
None )
| None ->
if Var.is_return var then
Some (Domain.LocalAccessPath.make (ret_id_typ, []) caller_pname)
else None
in
let astate_old = Domain.substitute ~f_sub old_callee |> Domain.OldDomain.join old_domain in
let astate_new =
Domain.NewDomain.subst ~formals ~actuals ~ret_id_typ ~caller_pname ~callee_pname
~caller:new_domain ~callee:new_callee
in
(astate_old, astate_new) )
let apply_callee_summary summary_opt ~caller_pname ~callee_pname ret_id_typ formals actuals astate
=
Option.value_map summary_opt ~default:astate ~f:(fun callee_summary ->
Domain.subst ~formals ~actuals ~ret_id_typ ~caller_pname ~callee_pname ~caller:astate
~callee:callee_summary )
let exec_instr astate ProcData.{summary; tenv; extras= {get_proc_summary_and_formals}} _
@ -144,19 +123,11 @@ struct
Domain.LocalAccessPath.make_from_access_expression receiver_ae caller_pname
in
if
( LithoContext.check_callee ~callee_pname ~tenv callee_summary_opt
|| (* track callee in order to report respective errors *)
Domain.mem receiver astate
(* track anything called on a receiver we're already tracking *) )
LithoContext.check_callee ~callee_pname ~tenv callee_summary_opt
(* track callee in order to report respective errors *)
&& LithoContext.satisfies_heuristic ~callee_pname ~callee_summary_opt tenv
then
let return_access_path = Domain.LocalAccessPath.make (return_base, []) caller_pname in
let callee = Domain.MethodCall.make receiver callee_pname location in
let return_calls =
(try Domain.find return_access_path astate with Caml.Not_found -> Domain.CallSet.empty)
|> Domain.CallSet.add callee
in
let astate = Domain.add return_access_path return_calls astate in
match get_component_create_typ_opt callee_pname tenv with
| Some create_typ ->
Domain.call_create return_access_path create_typ location astate
@ -195,12 +166,6 @@ struct
(HilExp.AccessExpression.to_access_path rhs_ae)
caller_pname
in
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
@ -242,12 +207,7 @@ module MakeAnalyzer (LithoContext : LithoContext with type summary = Domain.summ
if LithoContext.should_report proc_desc tenv then LithoContext.report post tenv summary
else post
in
let postprocess (old_astate, new_astate) formal_map : Domain.summary =
let f_sub access_path = Domain.LocalAccessPath.to_formal_option access_path formal_map in
(Domain.substitute ~f_sub old_astate, new_astate)
in
let payload = postprocess post (FormalMap.make proc_desc) in
TF.Payload.update_summary payload summary
TF.Payload.update_summary post summary
| None ->
summary
end

@ -72,26 +72,13 @@ let report_missing_required_prop summary prop parent_typename loc call_chain =
in
let ltr =
Errlog.make_trace_element 0 loc message []
:: List.map call_chain ~f:(fun Domain.MethodCall.{procname; location} ->
:: List.map call_chain ~f:(fun Domain.MethodCallPrefix.{procname; location} ->
let call_msg = F.asprintf "calls %a" Typ.Procname.pp procname in
Errlog.make_trace_element 0 location call_msg [] )
in
Reporting.log_error summary ~loc ~ltr IssueType.missing_required_prop message
(* walk backward through [call_chain] and return the first type T <: Component that is not part of
the Litho framework (i.e., is client code) *)
let find_client_component_type call_chain =
List.find_map
~f:(fun Domain.MethodCall.{procname} ->
match procname with
| Typ.Procname.Java java_pname ->
Typ.Name.Java.get_outer_class (Typ.Procname.Java.get_class_type_name java_pname)
| _ ->
None )
call_chain
let has_prop prop_set prop =
let check prop =
String.Set.mem prop_set prop
@ -124,19 +111,13 @@ module LithoContext = struct
let satisfies_heuristic ~callee_pname ~callee_summary_opt tenv =
(* If the method is build() or create() itself or doesn't contain a build() in
its summary, we want to track it in the domain. *)
let build_exists_in_callees =
Option.value_map ~default:false callee_summary_opt ~f:(fun sum ->
LithoDomain.Mem.contains_build sum )
in
LithoFramework.is_component_build_method callee_pname tenv
|| LithoFramework.is_component_create_method callee_pname tenv
||
(* check if build()/create() exists in callees *)
let build_exists_in_callees =
Option.value_map callee_summary_opt ~default:[] ~f:Domain.bindings
|> List.exists ~f:(fun (_, call_set) ->
LithoDomain.CallSet.exists
(fun LithoDomain.MethodCall.{procname} ->
LithoFramework.is_component_build_method procname tenv
|| LithoFramework.is_component_create_method procname tenv )
call_set )
in
match callee_pname with
| Typ.Procname.Java java_callee_procname ->
not (Typ.Procname.Java.is_static java_callee_procname || build_exists_in_callees)
@ -153,49 +134,15 @@ module LithoContext = struct
&& Procdesc.get_access proc_desc <> PredSymb.Private
let check_on_string_set tenv summary parent_typename call_chain prop_set =
let required_props = get_required_props parent_typename tenv in
List.iter required_props ~f:(fun required_prop ->
if not (has_prop prop_set required_prop) then
report_missing_required_prop summary required_prop parent_typename
(Summary.get_loc summary) call_chain )
let check_on_string_set_prefix tenv summary parent_typename call_chain_prefix prop_set =
let call_chain = List.map call_chain_prefix ~f:Domain.MethodCallPrefix.to_method_call in
check_on_string_set tenv summary parent_typename call_chain prop_set
let report astate tenv summary =
let check_required_prop_chain _ call_chain =
let call_chain =
List.drop_while call_chain ~f:(fun Domain.MethodCall.{procname} ->
not (LithoFramework.is_component_create_method procname tenv) )
in
let rev_chain = List.rev call_chain in
match rev_chain with
| Domain.MethodCall.{procname} :: _
when LithoFramework.is_component_build_method procname tenv -> (
(* Here, we'll have a type name like MyComponent$Builder in hand. Truncate the $Builder
part from the typename, then look at the fields of MyComponent to figure out which
ones are annotated with @Prop *)
match find_client_component_type call_chain with
| Some parent_typename ->
let prop_set =
List.map ~f:Domain.MethodCall.procname_to_string call_chain |> String.Set.of_list
in
check_on_string_set tenv summary parent_typename call_chain prop_set
| _ ->
() )
| _ ->
()
let check_on_string_set parent_typename call_chain prop_set =
let required_props = get_required_props parent_typename tenv in
List.iter required_props ~f:(fun required_prop ->
if not (has_prop prop_set required_prop) then
report_missing_required_prop summary required_prop parent_typename
(Summary.get_loc summary) call_chain )
in
if Config.new_litho_domain then
let check_on_string_set_prefix = check_on_string_set_prefix tenv summary in
Domain.check_required_props ~check_on_string_set:check_on_string_set_prefix astate
else (
Domain.iter_call_chains ~f:check_required_prop_chain astate ;
astate )
Domain.check_required_props ~check_on_string_set astate
let session_name = "litho required props"

@ -5,7 +5,7 @@
TESTS_DIR = ../../..
INFER_OPTIONS = --litho-required-props-only --new-litho-domain --debug-exceptions
INFER_OPTIONS = --litho-required-props-only --debug-exceptions
INFERPRINT_OPTIONS = --issues-tests
SOURCES = $(wildcard *.java)

@ -1,50 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.litho;
public class Column extends Component {
static native Builder acquire();
public static Builder create() {
Builder builder = acquire();
if (builder == null) {
builder = new Builder();
}
return builder;
}
public static class Builder extends Component.Builder {
public Builder child(Component child) {
if (child == null) {
return this;
}
return this;
}
public Builder child(Component.Builder child) {
if (child == null) {
return this;
}
return child(child.build());
}
Column mColumn;
@Override
public Column build() {
return mColumn;
}
@Override
public Builder getThis() {
return this;
}
}
}

@ -1,22 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.litho;
public class Component {
public abstract static class Builder<T extends Builder<T>> {
public abstract Component build();
public abstract T getThis();
public T commonProp(Object prop) {
return getThis();
}
}
}

@ -1,12 +0,0 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
TESTS_DIR = ../../..
INFER_OPTIONS = --litho-required-props-only --debug-exceptions
INFERPRINT_OPTIONS = --issues-tests
SOURCES = $(wildcard *.java)
include $(TESTS_DIR)/javac.make

@ -1,54 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package codetoanalyze.java.litho;
import com.facebook.litho.Component;
import com.facebook.litho.annotations.Prop;
public class MyComponent extends Component {
@Prop Object prop1; // implicitly non-optional
@Prop(optional = true)
Object prop2; // explicitly optional
@Prop(optional = false)
Object prop3; // explicitly non-optional
Object nonProp;
public Builder create() {
return new Builder();
}
public static class Builder extends Component.Builder<Builder> {
MyComponent mMyComponent;
public Builder prop1(Object o) {
this.mMyComponent.prop1 = o;
return this;
}
public Builder prop2(Object o) {
this.mMyComponent.prop2 = o;
return this;
}
public Builder prop3(Object o) {
this.mMyComponent.prop3 = o;
return this;
}
public MyComponent build() {
return mMyComponent;
}
@Override
public Builder getThis() {
return this;
}
}
}

@ -1,43 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.litho;
import com.facebook.litho.annotations.Prop;
public class MyLithoComponent extends Component {
@Prop Object prop1; // implicitly non-optional
@Prop(optional = false)
Object prop2; // explicitly non-optional
public Builder create() {
return new Builder();
}
public static class Builder extends Component.Builder<Builder> {
MyLithoComponent mMyLithoComponent;
public Builder prop1(Object o) {
this.mMyLithoComponent.prop1 = o;
return this;
}
public Builder prop2(Object o) {
this.mMyLithoComponent.prop2 = o;
return this;
}
public MyLithoComponent build() {
return mMyLithoComponent;
}
@Override
public Builder getThis() {
return this;
}
}
}

@ -1,38 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package codetoanalyze.java.litho;
import com.facebook.litho.Component;
import com.facebook.litho.annotations.TreeProp;
class MyTreeComponent extends Component {
@TreeProp Object prop1; // implicitly non-optional
Object nonProp;
public Builder create() {
return new Builder();
}
static class Builder extends Component.Builder<Builder> {
MyTreeComponent mMyTreeComponent;
public Builder prop1(Object o) {
this.mMyTreeComponent.prop1 = o;
return this;
}
public MyTreeComponent build() {
return mMyTreeComponent;
}
@Override
public Builder getThis() {
return this;
}
}
}

@ -1,22 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.litho.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
public @interface Prop {
ResType resType() default ResType.NONE;
boolean optional() default false;
String varArg() default "";
}

@ -1,418 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package codetoanalyze.java.litho;
import com.facebook.litho.Column;
import com.facebook.litho.Component;
import com.facebook.litho.MyLithoComponent;
import java.util.ArrayList;
public class RequiredProps {
public MyComponent mMyComponent;
public MyLithoComponent mMyLithoComponent;
public ResPropComponent mResPropComponent;
public VarArgPropComponent mVarArgPropComponent;
public Component buildWithAllOk() {
return mMyComponent
.create()
.prop1(new Object())
.prop2(new Object())
.prop3(new Object())
.build();
}
// prop 2 is optional
public Component buildWithout2Ok() {
return mMyComponent.create().prop1(new Object()).prop3(new Object()).build();
}
// prop 1 is required
public Component buildWithout1Bad() {
return mMyComponent.create().prop2(new Object()).prop3(new Object()).build();
}
// prop3 is required
public Component buildWithout3Bad() {
return mMyComponent.create().prop1(new Object()).prop2(new Object()).build();
}
public Component buildWithCommonPropOk() {
return mMyComponent
.create()
.prop1(new Object())
.commonProp(new Object())
.prop3(new Object())
.build();
}
private static MyComponent.Builder setProp1(MyComponent.Builder builder) {
return builder.prop1(new Object());
}
private static MyComponent.Builder setProp3(MyComponent.Builder builder) {
return builder.prop3(new Object());
}
public Component setProp1InCalleeOk() {
return setProp1(mMyComponent.create().prop2(new Object())).prop3(new Object()).build();
}
public Component setProp3InCalleeOk() {
return setProp3(mMyComponent.create().prop1(new Object()).prop2(new Object())).build();
}
public Component setProp3InCalleeButForgetProp1Bad() {
return setProp3(mMyComponent.create()).prop2(new Object()).build();
}
public Component setRequiredOnOneBranchBad(boolean b) {
MyComponent.Builder builder = mMyComponent.create();
if (b) {
builder = builder.prop1(new Object());
}
return builder.prop2(new Object()).prop3(new Object()).build();
}
public Component setRequiredOnBothBranchesOk(boolean b) {
MyComponent.Builder builder = mMyComponent.create();
if (b) {
builder = builder.prop1(new Object());
} else {
builder = builder.prop1(new Object());
}
return builder.prop2(new Object()).prop3(new Object()).build();
}
// current domain can't handle implicit calls like this
public Component setRequiredOnBothBranchesNoAssignOk_FP(boolean b) {
MyComponent.Builder builder = mMyComponent.create();
if (b) {
builder.prop1(new Object());
} else {
builder.prop1(new Object());
}
return builder.prop2(new Object()).prop3(new Object()).build();
}
// gets confused at cyclic dependency to builder when setting prop1
public Component setRequiredOk(boolean b) {
MyComponent.Builder builder = mMyComponent.create();
builder = builder.prop1(new Object());
return builder.prop2(new Object()).prop3(new Object()).build();
}
// gets confused at side-effectfull prop setting
public Component setRequiredEffectful_FP(boolean b) {
MyComponent.Builder builder = mMyComponent.create();
builder.prop1(new Object());
return builder.prop2(new Object()).prop3(new Object()).build();
}
public Component setRequiredOnOneBranchEffectfulBad(boolean b) {
MyComponent.Builder builder = mMyComponent.create();
if (b) {
builder.prop1(new Object());
}
return builder.prop2(new Object()).prop3(new Object()).build();
}
public void buildPropLithoMissingOneInLoopBad(int x) {
for (int i = 0; i < x; i++) {
Column.create()
.child(mMyLithoComponent.create().prop1(new Object()).commonProp(new Object()))
.build();
}
}
// due to mutual recursion check, we break cycle at seen props
public Component doubleSetMissingBad_FN() {
Component.Builder builder =
mMyComponent.create().commonProp(new Object()).prop3(new Object()).commonProp(new Object());
return builder.build();
}
// due to mutual recursion check, we break cycle at seen props
public Component doubleSetCommonOk() {
Component.Builder builder =
mMyComponent
.create()
.prop1(new Object())
.commonProp(new Object())
.prop3(new Object())
.commonProp(new Object());
return builder.build();
}
// only missing prop3
public Component setRequiredOnBothBranchesMissingProp3Bad(boolean b) {
MyComponent.Builder builder = mMyComponent.create();
if (b) {
builder.prop1(new Object());
} else {
builder.prop1(new Object());
}
return builder.prop2(new Object()).build();
}
public void buildPropInConditionalOk_FP(boolean b) {
MyComponent.Builder builder = mMyComponent.create();
if (b) {
builder.prop1(new Object()).prop3(new Object());
} else {
builder.prop1(new Object()).prop3(new Object());
}
builder.build();
}
// should be only missing prop3
public void buildPropMissingInConditionalBad(boolean b) {
MyComponent.Builder builder = mMyComponent.create();
if (b) {
builder.prop1(new Object()).prop3(new Object());
} else {
builder.prop2(new Object()).prop1(new Object());
}
builder.build();
}
public boolean isEmptyOrNull(String str) {
return str == null || str.isEmpty();
}
public void buildInterProcUnrelatedBad(boolean b, String s) {
MyComponent.Builder builder = mMyComponent.create().prop1(new Object());
if (!isEmptyOrNull(s)) {
builder.prop3(new Object());
}
builder.build();
}
// don't want to report here; want to report at clients that don't pass prop1
private MyComponent buildSuffix(MyComponent.Builder builder) {
return builder.prop2(new Object()).prop3(new Object()).build();
}
// shouldn't report here; prop 1 passed
public Component callBuildSuffixWithRequiredOk() {
return buildSuffix(mMyComponent.create().prop1(new Object()));
}
// should report here; forgot prop 1
public Component callBuildSuffixWithoutRequiredBad() {
return buildSuffix(mMyComponent.create());
}
public Object generalTypeWithout2Ok() {
Component.Builder builder = mMyComponent.create().prop1(new Object()).prop3(new Object());
return builder.build();
}
public Object generalTypeForgot3Bad() {
MyComponent.Builder builder1 = mMyComponent.create();
Component.Builder builder2 = (Component.Builder) builder1.prop1(new Object());
// don't fail to find required @Prop's for MyComponent.Builder even though the static type that
// build is invoked on is [builder2]
return builder2.build();
}
public void buildWithColumnChildBad() {
Column.Builder builder = Column.create();
Component.Builder childBuilder = mMyComponent.create().prop1(new Object());
// forgot prop 3, and builder.child() will invoke build() on childBuilder
builder.child(childBuilder);
}
public Component buildWithColumnChildOk() {
return Column.create()
.child(mMyComponent.create().prop1(new Object()).prop3(new Object()))
.build();
}
public void buildPropResWithNormalOk() {
mResPropComponent.create().prop(new Object()).build();
}
public void buildPropResWithResOk() {
mResPropComponent.create().propRes(new Object()).build();
}
public void buildPropResWithAttrOk() {
mResPropComponent.create().propAttr(new Object()).build();
}
public void buildPropResWithDipOk() {
mResPropComponent.create().propDip(new Object()).build();
}
public void buildPropResWithPxOk() {
mResPropComponent.create().propPx(new Object()).build();
}
public void buildPropResWithSpOk() {
mResPropComponent.create().propSp(new Object()).build();
}
public void buildPropResMissingBad() {
mResPropComponent.create().build();
}
public void buildPropResInCondOk_FP(boolean b) {
ResPropComponent.Builder builder = mResPropComponent.create();
if (b) {
builder.propAttr(new Object());
} else {
builder.propDip(new Object());
}
builder.build();
}
public void buildPropResInCondOneNormalOk_FP(boolean b) {
ResPropComponent.Builder builder = mResPropComponent.create();
if (b) {
builder.propAttr(new Object());
} else {
builder.prop(new Object());
}
builder.build();
}
public void buildPropVarArgNormalOk() {
mVarArgPropComponent.create().props(new ArrayList<Object>()).build();
}
public void buildPropVarArgElementOk() {
mVarArgPropComponent.create().prop(new Object()).build();
}
public void buildPropVarArgAttrElementOk() {
mVarArgPropComponent.create().propAttr(new Object()).build();
}
public void buildPropVarArgNormalAttrElementOk() {
mVarArgPropComponent.create().propsAttr(new ArrayList<Object>()).build();
}
public void buildPropVarArgMissingBad() {
mVarArgPropComponent.create().build();
}
public Component buildPropLithoMissingBothBad() {
return mMyLithoComponent.create().build();
}
public void buildPropLithoMissingOneBad() {
Column.create()
.child(mMyLithoComponent.create().prop1(new Object()).commonProp(new Object()))
.build();
}
public Component buildPropLithoOK() {
Component.Builder layoutBuilder =
mMyLithoComponent.create().prop1(new Object()).prop2(new Object());
return layoutBuilder.build();
}
public void castImpossibleOk_FP(Object o1) {
Component.Builder builder = mMyLithoComponent.create();
if (builder instanceof MyComponent.Builder)
((MyComponent.Builder) builder)
.build(); // this branch will never be taken but we can't detect it yet
}
void castOk(Object o1) {
Component.Builder builder = mMyLithoComponent.create().prop1(new Object()).prop2(new Object());
if (builder instanceof MyLithoComponent.Builder)
((MyLithoComponent.Builder) builder).build(); // this branch will be taken
}
Component.Builder createBuilder() {
return mMyLithoComponent.create();
}
void castMissingOneBad(Object o1) {
Component.Builder builder = createBuilder();
if (builder instanceof MyLithoComponent.Builder)
((MyLithoComponent.Builder) builder).prop2(new Object()).build(); // this branch will be taken
}
void buildMissingProp3_FN() {
Component.Builder builder = mMyComponent.create();
((MyLithoComponent.Builder) builder).prop1(new Object()).prop2(new Object()).build();
}
public class NonRequiredTreeProps {
public MyTreeComponent mMyTreeComponent;
public MyTreeComponent buildWithoutOk() {
return mMyTreeComponent.create().build();
}
}
public Component buildPropLithoMissingInOneBranchBad(boolean b) {
if (b) {
return mMyLithoComponent.create().prop1(new Object()).build();
} else {
return mMyLithoComponent.create().prop1(new Object()).prop2(new Object()).build();
}
}
public Component buildPropLithoMissingInOneBranchBeforeBuildBad(boolean b) {
MyLithoComponent.Builder builder =
b
? mMyLithoComponent.create().prop1(new Object())
: mMyLithoComponent.create().prop1(new Object()).prop2(new Object());
return builder.build();
}
public Component setRequiredOnOneBothBranchesWithCreateOk_FP(boolean b) {
MyComponent.Builder builder = mMyComponent.create();
if (b) {
builder.prop1(new Object());
} else {
builder = mMyComponent.create().prop1(new Object());
}
return builder.prop2(new Object()).prop3(new Object()).build();
}
public Component missingProp3InOneBranchBeforeBuildBad(boolean b) {
Component.Builder builder =
b
? mMyComponent.create().prop1(new Object())
: mMyLithoComponent.create().prop1(new Object()).prop2(new Object());
return builder.build();
}
public Component missingProp2InOneBranchBeforeBuildBad(boolean b) {
Component.Builder builder =
b
? mMyComponent.create().prop1(new Object()).prop3(new Object())
: mMyLithoComponent.create().prop1(new Object());
return builder.build();
}
public Component missingProp1InBothBranchesBeforeBuildBad(boolean b) {
Component.Builder builder =
b
? mMyComponent.create().prop3(new Object())
: mMyLithoComponent.create().prop2(new Object());
return builder.build();
}
public Component createDiffferentInBranchesBeforeBuildOk(boolean b) {
Component.Builder builder =
b
? mMyComponent.create().prop1(new Object()).prop3(new Object())
: mMyLithoComponent.create().prop1(new Object()).prop2(new Object());
return builder.build();
}
}

@ -1,69 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package codetoanalyze.java.litho;
import com.facebook.litho.Component;
import com.facebook.litho.annotations.Prop;
import com.facebook.litho.annotations.ResType;
/**
* using @Prop(resType = ..) allows you to set the Prop with any of .propname, .propnameRes, or
* .propnameAttr
*/
public class ResPropComponent extends Component {
@Prop(resType = ResType.SOME)
Object prop; // implicitly non-optional with resType
public Builder create() {
return new Builder();
}
public static class Builder extends Component.Builder<Builder> {
ResPropComponent mResPropComponent;
public Builder prop(Object o) {
this.mResPropComponent.prop = o;
return this;
}
public Builder propRes(Object o) {
this.mResPropComponent.prop = o;
return this;
}
public Builder propAttr(Object o) {
this.mResPropComponent.prop = o;
return this;
}
public Builder propDip(Object o) {
this.mResPropComponent.prop = o;
return this;
}
public Builder propPx(Object o) {
this.mResPropComponent.prop = o;
return this;
}
public Builder propSp(Object o) {
this.mResPropComponent.prop = o;
return this;
}
public ResPropComponent build() {
return mResPropComponent;
}
@Override
public Builder getThis() {
return this;
}
}
}

@ -1,12 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.litho.annotations;
public enum ResType {
SOME,
NONE
}

@ -1,20 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.litho.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
public @interface TreeProp {
ResType resType() default ResType.NONE;
boolean optional() default false;
}

@ -1,83 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package codetoanalyze.java.litho;
import com.facebook.litho.Component;
import com.facebook.litho.annotations.Prop;
import java.util.ArrayList;
import java.util.List;
/** varArg test */
class VarArgPropComponent extends Component {
@Prop(varArg = "prop")
List<Object> props;
public Builder create() {
return new Builder();
}
static class Builder extends Component.Builder<Builder> {
VarArgPropComponent mVarArgPropComponent;
public Builder prop(Object prop) {
if (prop == null) {
return this;
}
if (this.mVarArgPropComponent.props == null) {
this.mVarArgPropComponent.props = new ArrayList<Object>();
}
this.mVarArgPropComponent.props.add(prop);
return this;
}
public Builder propAttr(Object prop) {
if (prop == null) {
return this;
}
if (this.mVarArgPropComponent.props == null) {
this.mVarArgPropComponent.props = new ArrayList<Object>();
}
this.mVarArgPropComponent.props.add(prop);
return this;
}
public Builder propsAttr(List<Object> props) {
if (props == null) {
return this;
}
if (this.mVarArgPropComponent.props == null || this.mVarArgPropComponent.props.isEmpty()) {
this.mVarArgPropComponent.props = props;
} else {
this.mVarArgPropComponent.props.addAll(props);
}
return this;
}
public Builder props(List<Object> props) {
if (props == null) {
return this;
}
if (this.mVarArgPropComponent.props == null || this.mVarArgPropComponent.props.isEmpty()) {
this.mVarArgPropComponent.props = props;
} else {
this.mVarArgPropComponent.props.addAll(props);
}
return this;
}
public VarArgPropComponent build() {
return mVarArgPropComponent;
}
@Override
public Builder getThis() {
return this;
}
}
}

@ -1,35 +0,0 @@
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.buildInterProcUnrelatedBad(boolean,java.lang.String):void, 0, MISSING_REQUIRED_PROP, no_bucket, ERROR, [@Prop prop3 is required for component codetoanalyze.java.litho.MyComponent, but is not set before the call to build(),calls MyComponent$Builder MyComponent.create(),calls MyComponent$Builder MyComponent$Builder.prop1(Object),calls MyComponent MyComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.buildPropInConditionalOk_FP(boolean):void, 0, MISSING_REQUIRED_PROP, no_bucket, ERROR, [@Prop prop3 is required for component codetoanalyze.java.litho.MyComponent, but is not set before the call to build(),calls MyComponent$Builder MyComponent.create(),calls MyComponent MyComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.buildPropInConditionalOk_FP(boolean):void, 0, 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 MyComponent$Builder MyComponent.create(),calls MyComponent MyComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.buildPropLithoMissingBothBad():com.facebook.litho.Component, 0, MISSING_REQUIRED_PROP, no_bucket, ERROR, [@Prop prop2 is required for component com.facebook.litho.MyLithoComponent, but is not set before the call to build(),calls MyLithoComponent$Builder MyLithoComponent.create(),calls MyLithoComponent MyLithoComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.buildPropLithoMissingBothBad():com.facebook.litho.Component, 0, MISSING_REQUIRED_PROP, no_bucket, ERROR, [@Prop prop1 is required for component com.facebook.litho.MyLithoComponent, but is not set before the call to build(),calls MyLithoComponent$Builder MyLithoComponent.create(),calls MyLithoComponent MyLithoComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.buildPropLithoMissingInOneBranchBad(boolean):com.facebook.litho.Component, 0, MISSING_REQUIRED_PROP, no_bucket, ERROR, [@Prop prop2 is required for component com.facebook.litho.MyLithoComponent, but is not set before the call to build(),calls MyLithoComponent$Builder MyLithoComponent.create(),calls MyLithoComponent$Builder MyLithoComponent$Builder.prop1(Object),calls MyLithoComponent MyLithoComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.buildPropLithoMissingInOneBranchBeforeBuildBad(boolean):com.facebook.litho.Component, 0, MISSING_REQUIRED_PROP, no_bucket, ERROR, [@Prop prop2 is required for component com.facebook.litho.MyLithoComponent, but is not set before the call to build(),calls MyLithoComponent$Builder MyLithoComponent.create(),calls MyLithoComponent$Builder MyLithoComponent$Builder.prop1(Object),calls MyLithoComponent MyLithoComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.buildPropLithoMissingOneBad():void, 0, MISSING_REQUIRED_PROP, no_bucket, ERROR, [@Prop prop2 is required for component com.facebook.litho.MyLithoComponent, but is not set before the call to build(),calls MyLithoComponent$Builder MyLithoComponent.create(),calls MyLithoComponent$Builder MyLithoComponent$Builder.prop1(Object),calls Component$Builder Component$Builder.commonProp(Object),calls Component Component$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.buildPropLithoMissingOneInLoopBad(int):void, 0, MISSING_REQUIRED_PROP, no_bucket, ERROR, [@Prop prop2 is required for component com.facebook.litho.MyLithoComponent, but is not set before the call to build(),calls MyLithoComponent$Builder MyLithoComponent.create(),calls MyLithoComponent$Builder MyLithoComponent$Builder.prop1(Object),calls Component$Builder Component$Builder.commonProp(Object),calls Component Component$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.buildPropMissingInConditionalBad(boolean):void, 0, 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 MyComponent$Builder MyComponent.create(),calls MyComponent MyComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.buildPropMissingInConditionalBad(boolean):void, 0, MISSING_REQUIRED_PROP, no_bucket, ERROR, [@Prop prop3 is required for component codetoanalyze.java.litho.MyComponent, but is not set before the call to build(),calls MyComponent$Builder MyComponent.create(),calls MyComponent MyComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.buildPropResInCondOk_FP(boolean):void, 0, MISSING_REQUIRED_PROP, no_bucket, ERROR, [@Prop prop is required for component codetoanalyze.java.litho.ResPropComponent, but is not set before the call to build(),calls ResPropComponent$Builder ResPropComponent.create(),calls ResPropComponent ResPropComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.buildPropResInCondOneNormalOk_FP(boolean):void, 0, MISSING_REQUIRED_PROP, no_bucket, ERROR, [@Prop prop is required for component codetoanalyze.java.litho.ResPropComponent, but is not set before the call to build(),calls ResPropComponent$Builder ResPropComponent.create(),calls ResPropComponent ResPropComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.buildPropResMissingBad():void, 0, MISSING_REQUIRED_PROP, no_bucket, ERROR, [@Prop prop is required for component codetoanalyze.java.litho.ResPropComponent, but is not set before the call to build(),calls ResPropComponent$Builder ResPropComponent.create(),calls ResPropComponent ResPropComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.buildPropVarArgMissingBad():void, 0, MISSING_REQUIRED_PROP, no_bucket, ERROR, [Either @Prop props or @Prop(varArg = prop) is required for component codetoanalyze.java.litho.VarArgPropComponent, but is not set before the call to build(),calls VarArgPropComponent$Builder VarArgPropComponent.create(),calls VarArgPropComponent VarArgPropComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.buildWithColumnChildBad():void, 0, MISSING_REQUIRED_PROP, no_bucket, ERROR, [@Prop prop3 is required for component codetoanalyze.java.litho.MyComponent, but is not set before the call to build(),calls MyComponent$Builder MyComponent.create(),calls MyComponent$Builder MyComponent$Builder.prop1(Object),calls Component Component$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.buildWithout1Bad():com.facebook.litho.Component, 0, 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 MyComponent$Builder MyComponent.create(),calls MyComponent$Builder MyComponent$Builder.prop2(Object),calls MyComponent$Builder MyComponent$Builder.prop3(Object),calls MyComponent MyComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.buildWithout3Bad():com.facebook.litho.Component, 0, MISSING_REQUIRED_PROP, no_bucket, ERROR, [@Prop prop3 is required for component codetoanalyze.java.litho.MyComponent, but is not set before the call to build(),calls MyComponent$Builder MyComponent.create(),calls MyComponent$Builder MyComponent$Builder.prop1(Object),calls MyComponent$Builder MyComponent$Builder.prop2(Object),calls MyComponent MyComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.callBuildSuffixWithoutRequiredBad():com.facebook.litho.Component, 0, 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 MyComponent$Builder MyComponent.create(),calls MyComponent$Builder MyComponent$Builder.prop2(Object),calls MyComponent$Builder MyComponent$Builder.prop3(Object),calls MyComponent MyComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.castImpossibleOk_FP(java.lang.Object):void, 0, MISSING_REQUIRED_PROP, no_bucket, ERROR, [@Prop prop3 is required for component codetoanalyze.java.litho.MyComponent, but is not set before the call to build(),calls MyLithoComponent$Builder MyLithoComponent.create(),calls MyComponent MyComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.castImpossibleOk_FP(java.lang.Object):void, 0, 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 MyLithoComponent$Builder MyLithoComponent.create(),calls MyComponent MyComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.castMissingOneBad(java.lang.Object):void, 0, MISSING_REQUIRED_PROP, no_bucket, ERROR, [@Prop prop1 is required for component com.facebook.litho.MyLithoComponent, but is not set before the call to build(),calls MyLithoComponent$Builder MyLithoComponent.create(),calls MyLithoComponent$Builder MyLithoComponent$Builder.prop2(Object),calls MyLithoComponent MyLithoComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.generalTypeForgot3Bad():java.lang.Object, 0, MISSING_REQUIRED_PROP, no_bucket, ERROR, [@Prop prop3 is required for component codetoanalyze.java.litho.MyComponent, but is not set before the call to build(),calls MyComponent$Builder MyComponent.create(),calls MyComponent$Builder MyComponent$Builder.prop1(Object),calls Component MyComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.missingProp1InBothBranchesBeforeBuildBad(boolean):com.facebook.litho.Component, 0, 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 MyComponent$Builder MyComponent.create(),calls MyComponent$Builder MyComponent$Builder.prop3(Object),calls Component MyLithoComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.missingProp1InBothBranchesBeforeBuildBad(boolean):com.facebook.litho.Component, 0, MISSING_REQUIRED_PROP, no_bucket, ERROR, [@Prop prop1 is required for component com.facebook.litho.MyLithoComponent, but is not set before the call to build(),calls MyLithoComponent$Builder MyLithoComponent.create(),calls MyLithoComponent$Builder MyLithoComponent$Builder.prop2(Object),calls Component MyLithoComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.missingProp2InOneBranchBeforeBuildBad(boolean):com.facebook.litho.Component, 0, MISSING_REQUIRED_PROP, no_bucket, ERROR, [@Prop prop2 is required for component com.facebook.litho.MyLithoComponent, but is not set before the call to build(),calls MyLithoComponent$Builder MyLithoComponent.create(),calls MyLithoComponent$Builder MyLithoComponent$Builder.prop1(Object),calls Component MyLithoComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.missingProp3InOneBranchBeforeBuildBad(boolean):com.facebook.litho.Component, 0, MISSING_REQUIRED_PROP, no_bucket, ERROR, [@Prop prop3 is required for component codetoanalyze.java.litho.MyComponent, but is not set before the call to build(),calls MyComponent$Builder MyComponent.create(),calls MyComponent$Builder MyComponent$Builder.prop1(Object),calls Component MyLithoComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.setProp3InCalleeButForgetProp1Bad():com.facebook.litho.Component, 0, 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 MyComponent$Builder MyComponent.create(),calls MyComponent$Builder MyComponent$Builder.prop3(Object),calls MyComponent$Builder MyComponent$Builder.prop2(Object),calls MyComponent MyComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.setRequiredEffectful_FP(boolean):com.facebook.litho.Component, 0, 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 MyComponent$Builder MyComponent.create(),calls MyComponent$Builder MyComponent$Builder.prop2(Object),calls MyComponent$Builder MyComponent$Builder.prop3(Object),calls MyComponent MyComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.setRequiredOnBothBranchesMissingProp3Bad(boolean):com.facebook.litho.Component, 0, 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 MyComponent$Builder MyComponent.create(),calls MyComponent$Builder MyComponent$Builder.prop2(Object),calls MyComponent MyComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.setRequiredOnBothBranchesMissingProp3Bad(boolean):com.facebook.litho.Component, 0, MISSING_REQUIRED_PROP, no_bucket, ERROR, [@Prop prop3 is required for component codetoanalyze.java.litho.MyComponent, but is not set before the call to build(),calls MyComponent$Builder MyComponent.create(),calls MyComponent$Builder MyComponent$Builder.prop2(Object),calls MyComponent MyComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.setRequiredOnBothBranchesNoAssignOk_FP(boolean):com.facebook.litho.Component, 0, 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 MyComponent$Builder MyComponent.create(),calls MyComponent$Builder MyComponent$Builder.prop2(Object),calls MyComponent$Builder MyComponent$Builder.prop3(Object),calls MyComponent MyComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.setRequiredOnOneBothBranchesWithCreateOk_FP(boolean):com.facebook.litho.Component, 0, 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 MyComponent$Builder MyComponent.create(),calls MyComponent$Builder MyComponent$Builder.prop2(Object),calls MyComponent$Builder MyComponent$Builder.prop3(Object),calls MyComponent MyComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.setRequiredOnOneBranchBad(boolean):com.facebook.litho.Component, 0, 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 MyComponent$Builder MyComponent.create(),calls MyComponent$Builder MyComponent$Builder.prop2(Object),calls MyComponent$Builder MyComponent$Builder.prop3(Object),calls MyComponent MyComponent$Builder.build()]
codetoanalyze/java/litho/RequiredProps.java, codetoanalyze.java.litho.RequiredProps.setRequiredOnOneBranchEffectfulBad(boolean):com.facebook.litho.Component, 0, 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 MyComponent$Builder MyComponent.create(),calls MyComponent$Builder MyComponent$Builder.prop2(Object),calls MyComponent$Builder MyComponent$Builder.prop3(Object),calls MyComponent MyComponent$Builder.build()]
Loading…
Cancel
Save