From 38421e27356b673e3c9dc73eebce49dba0d37095 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ezgi=20=C3=87i=C3=A7ek?= Date: Fri, 13 Dec 2019 03:45:15 -0800 Subject: [PATCH] [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 --- Makefile | 1 - infer/man/man1/infer-full.txt | 4 - infer/src/base/Config.ml | 4 - infer/src/base/Config.mli | 2 - infer/src/checkers/LithoDomain.ml | 736 ++++++++---------- infer/src/checkers/LithoDomain.mli | 72 +- infer/src/checkers/LithoFramework.ml | 56 +- infer/src/checkers/RequiredProps.ml | 77 +- .../java/litho-required-props/Makefile | 2 +- .../codetoanalyze/java/litho/Column.java | 50 -- .../codetoanalyze/java/litho/Component.java | 22 - infer/tests/codetoanalyze/java/litho/Makefile | 12 - .../codetoanalyze/java/litho/MyComponent.java | 54 -- .../java/litho/MyLithoComponent.java | 43 - .../java/litho/MyTreeComponent.java | 38 - .../tests/codetoanalyze/java/litho/Prop.java | 22 - .../java/litho/RequiredProps.java | 418 ---------- .../java/litho/ResPropComponent.java | 69 -- .../codetoanalyze/java/litho/ResType.java | 12 - .../codetoanalyze/java/litho/TreeProp.java | 20 - .../java/litho/VarArgPropComponent.java | 83 -- .../tests/codetoanalyze/java/litho/issues.exp | 35 - 22 files changed, 344 insertions(+), 1488 deletions(-) delete mode 100644 infer/tests/codetoanalyze/java/litho/Column.java delete mode 100644 infer/tests/codetoanalyze/java/litho/Component.java delete mode 100644 infer/tests/codetoanalyze/java/litho/Makefile delete mode 100644 infer/tests/codetoanalyze/java/litho/MyComponent.java delete mode 100644 infer/tests/codetoanalyze/java/litho/MyLithoComponent.java delete mode 100644 infer/tests/codetoanalyze/java/litho/MyTreeComponent.java delete mode 100644 infer/tests/codetoanalyze/java/litho/Prop.java delete mode 100644 infer/tests/codetoanalyze/java/litho/RequiredProps.java delete mode 100644 infer/tests/codetoanalyze/java/litho/ResPropComponent.java delete mode 100644 infer/tests/codetoanalyze/java/litho/ResType.java delete mode 100644 infer/tests/codetoanalyze/java/litho/TreeProp.java delete mode 100644 infer/tests/codetoanalyze/java/litho/VarArgPropComponent.java delete mode 100644 infer/tests/codetoanalyze/java/litho/issues.exp diff --git a/Makefile b/Makefile index f2fefc9f9..542dc228b 100644 --- a/Makefile +++ b/Makefile @@ -160,7 +160,6 @@ DIRECT_TESTS += \ java_impurity \ java_inefficientKeysetIterator \ java_infer \ - java_litho \ java_litho-required-props \ java_performance \ java_purity \ diff --git a/infer/man/man1/infer-full.txt b/infer/man/man1/infer-full.txt index dd39a31ed..18a770d8d 100644 --- a/infer/man/man1/infer-full.txt +++ b/infer/man/man1/infer-full.txt @@ -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 diff --git a/infer/src/base/Config.ml b/infer/src/base/Config.ml index dd33c3774..0096cc296 100644 --- a/infer/src/base/Config.ml +++ b/infer/src/base/Config.ml @@ -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 = diff --git a/infer/src/base/Config.mli b/infer/src/base/Config.mli index 5cb09b507..4895dc4bb 100644 --- a/infer/src/base/Config.mli +++ b/infer/src/base/Config.mli @@ -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 diff --git a/infer/src/checkers/LithoDomain.ml b/infer/src/checkers/LithoDomain.ml index 97190a6a4..8b46aacab 100644 --- a/infer/src/checkers/LithoDomain.ml +++ b/infer/src/checkers/LithoDomain.ml @@ -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 "@[@[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 "@[@[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 "@[@[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 "@[@[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 "@[@[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 "@[@[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 diff --git a/infer/src/checkers/LithoDomain.mli b/infer/src/checkers/LithoDomain.mli index 20d06746d..1abb43da3 100644 --- a/infer/src/checkers/LithoDomain.mli +++ b/infer/src/checkers/LithoDomain.mli @@ -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] *) diff --git a/infer/src/checkers/LithoFramework.ml b/infer/src/checkers/LithoFramework.ml index e1770dc38..bae2e745d 100644 --- a/infer/src/checkers/LithoFramework.ml +++ b/infer/src/checkers/LithoFramework.ml @@ -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 diff --git a/infer/src/checkers/RequiredProps.ml b/infer/src/checkers/RequiredProps.ml index 1f36d965a..1d1397dcd 100644 --- a/infer/src/checkers/RequiredProps.ml +++ b/infer/src/checkers/RequiredProps.ml @@ -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" diff --git a/infer/tests/codetoanalyze/java/litho-required-props/Makefile b/infer/tests/codetoanalyze/java/litho-required-props/Makefile index da74bc966..5fd31c783 100644 --- a/infer/tests/codetoanalyze/java/litho-required-props/Makefile +++ b/infer/tests/codetoanalyze/java/litho-required-props/Makefile @@ -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) diff --git a/infer/tests/codetoanalyze/java/litho/Column.java b/infer/tests/codetoanalyze/java/litho/Column.java deleted file mode 100644 index b63386004..000000000 --- a/infer/tests/codetoanalyze/java/litho/Column.java +++ /dev/null @@ -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; - } - } -} diff --git a/infer/tests/codetoanalyze/java/litho/Component.java b/infer/tests/codetoanalyze/java/litho/Component.java deleted file mode 100644 index a72ae84b9..000000000 --- a/infer/tests/codetoanalyze/java/litho/Component.java +++ /dev/null @@ -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> { - - public abstract Component build(); - - public abstract T getThis(); - - public T commonProp(Object prop) { - return getThis(); - } - } -} diff --git a/infer/tests/codetoanalyze/java/litho/Makefile b/infer/tests/codetoanalyze/java/litho/Makefile deleted file mode 100644 index f9dc69635..000000000 --- a/infer/tests/codetoanalyze/java/litho/Makefile +++ /dev/null @@ -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 diff --git a/infer/tests/codetoanalyze/java/litho/MyComponent.java b/infer/tests/codetoanalyze/java/litho/MyComponent.java deleted file mode 100644 index 6bb4e7c86..000000000 --- a/infer/tests/codetoanalyze/java/litho/MyComponent.java +++ /dev/null @@ -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 { - 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; - } - } -} diff --git a/infer/tests/codetoanalyze/java/litho/MyLithoComponent.java b/infer/tests/codetoanalyze/java/litho/MyLithoComponent.java deleted file mode 100644 index a4acb10e5..000000000 --- a/infer/tests/codetoanalyze/java/litho/MyLithoComponent.java +++ /dev/null @@ -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 { - 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; - } - } -} diff --git a/infer/tests/codetoanalyze/java/litho/MyTreeComponent.java b/infer/tests/codetoanalyze/java/litho/MyTreeComponent.java deleted file mode 100644 index addc1d849..000000000 --- a/infer/tests/codetoanalyze/java/litho/MyTreeComponent.java +++ /dev/null @@ -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 { - MyTreeComponent mMyTreeComponent; - - public Builder prop1(Object o) { - this.mMyTreeComponent.prop1 = o; - return this; - } - - public MyTreeComponent build() { - return mMyTreeComponent; - } - - @Override - public Builder getThis() { - return this; - } - } -} diff --git a/infer/tests/codetoanalyze/java/litho/Prop.java b/infer/tests/codetoanalyze/java/litho/Prop.java deleted file mode 100644 index cf49d01e3..000000000 --- a/infer/tests/codetoanalyze/java/litho/Prop.java +++ /dev/null @@ -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 ""; -} diff --git a/infer/tests/codetoanalyze/java/litho/RequiredProps.java b/infer/tests/codetoanalyze/java/litho/RequiredProps.java deleted file mode 100644 index 68225adb1..000000000 --- a/infer/tests/codetoanalyze/java/litho/RequiredProps.java +++ /dev/null @@ -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()).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()).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(); - } - -} diff --git a/infer/tests/codetoanalyze/java/litho/ResPropComponent.java b/infer/tests/codetoanalyze/java/litho/ResPropComponent.java deleted file mode 100644 index 2a93a1e98..000000000 --- a/infer/tests/codetoanalyze/java/litho/ResPropComponent.java +++ /dev/null @@ -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 { - - 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; - } - } -} diff --git a/infer/tests/codetoanalyze/java/litho/ResType.java b/infer/tests/codetoanalyze/java/litho/ResType.java deleted file mode 100644 index 200b1066b..000000000 --- a/infer/tests/codetoanalyze/java/litho/ResType.java +++ /dev/null @@ -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 -} diff --git a/infer/tests/codetoanalyze/java/litho/TreeProp.java b/infer/tests/codetoanalyze/java/litho/TreeProp.java deleted file mode 100644 index 2560f427f..000000000 --- a/infer/tests/codetoanalyze/java/litho/TreeProp.java +++ /dev/null @@ -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; -} diff --git a/infer/tests/codetoanalyze/java/litho/VarArgPropComponent.java b/infer/tests/codetoanalyze/java/litho/VarArgPropComponent.java deleted file mode 100644 index 89a696619..000000000 --- a/infer/tests/codetoanalyze/java/litho/VarArgPropComponent.java +++ /dev/null @@ -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 props; - - public Builder create() { - return new Builder(); - } - - static class Builder extends Component.Builder { - - VarArgPropComponent mVarArgPropComponent; - - public Builder prop(Object prop) { - if (prop == null) { - return this; - } - if (this.mVarArgPropComponent.props == null) { - this.mVarArgPropComponent.props = new ArrayList(); - } - 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(); - } - this.mVarArgPropComponent.props.add(prop); - return this; - } - - public Builder propsAttr(List 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 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; - } - } -} diff --git a/infer/tests/codetoanalyze/java/litho/issues.exp b/infer/tests/codetoanalyze/java/litho/issues.exp deleted file mode 100644 index 5cc6b67e9..000000000 --- a/infer/tests/codetoanalyze/java/litho/issues.exp +++ /dev/null @@ -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()]