From bedf32bed58919eb377b8ba89b55e6f44beaa7cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ezgi=20=C3=87i=C3=A7ek?= Date: Tue, 14 Aug 2018 04:49:25 -0700 Subject: [PATCH] [Cost, InferBo] generalize ArrayLists to Collections and Iterators Reviewed By: mbouaziz Differential Revision: D9150202 fbshipit-source-id: 0b7f60058 --- infer/src/IR/ProcnameDispatcher.ml | 27 +++++++++ infer/src/IR/ProcnameDispatcher.mli | 4 ++ infer/src/absint/PatternMatch.ml | 9 +++ infer/src/absint/PatternMatch.mli | 6 ++ .../src/bufferoverrun/bufferOverrunChecker.ml | 8 +-- .../src/bufferoverrun/bufferOverrunModels.ml | 59 ++++++++++--------- .../bufferOverrunProofObligations.ml | 32 +++++----- infer/src/bufferoverrun/bufferOverrunUtils.ml | 13 ++-- .../src/bufferoverrun/bufferOverrunUtils.mli | 8 +-- .../java/performance/CollectionTest.java | 34 +++++++++++ .../codetoanalyze/java/performance/issues.exp | 5 ++ 11 files changed, 147 insertions(+), 58 deletions(-) create mode 100644 infer/tests/codetoanalyze/java/performance/CollectionTest.java diff --git a/infer/src/IR/ProcnameDispatcher.ml b/infer/src/IR/ProcnameDispatcher.ml index af8b4c2ad..9f4db8c3d 100644 --- a/infer/src/IR/ProcnameDispatcher.ml +++ b/infer/src/IR/ProcnameDispatcher.ml @@ -144,6 +144,27 @@ let name_cons {on_objc_cpp; on_qual_name; get_markers} +let name_cons_f + : ('context, 'f_in, 'f_out, 'captured_types, 'markers_in, 'markers_out, _) path_matcher + -> ('context -> string -> bool) + -> ('context, 'f_in, 'f_out, 'captured_types, 'markers_in, 'markers_out) name_matcher = + fun m f_name -> + let {on_templated_name; get_markers} = m in + let on_qual_name context f qual_name = + match QualifiedCppName.extract_last qual_name with + | Some (last, rest) when f_name context last -> + on_templated_name context f (rest, []) + | _ -> + None + in + let on_objc_cpp context f (objc_cpp: Typ.Procname.ObjC_Cpp.t) = + if f_name context objc_cpp.method_name then + on_templated_name context f (templated_name_of_class_name objc_cpp.class_name) + else None + in + {on_objc_cpp; on_qual_name; get_markers} + + let all_names_cons : ('context, 'f_in, 'f_out, 'captured_types, 'markers_in, 'markers_out, non_empty) path_matcher -> ( 'context @@ -321,6 +342,10 @@ module type Common = sig val ( ~- ) : string -> ('context, 'f, 'f, unit, 'markers, 'markers) name_matcher (** Starts a path with a name *) + val ( ~+ ) : + ('context -> string -> bool) -> ('context, 'f, 'f, unit, 'markers, 'markers) name_matcher + (** Starts a path with a matching name that satisfies the given function *) + val ( &+ ) : ( 'context , 'f_in @@ -469,6 +494,8 @@ module Common = struct let ( ~- ) name = empty &::! name + let ( ~+ ) f_name = name_cons_f empty f_name + let ( &+ ) templ_matcher template_arg = templ_cons templ_matcher template_arg let ( &+...>! ) templ_matcher () = templ_matcher &+ any_template_args >! () diff --git a/infer/src/IR/ProcnameDispatcher.mli b/infer/src/IR/ProcnameDispatcher.mli index 49d8b3905..d9bd3aab6 100644 --- a/infer/src/IR/ProcnameDispatcher.mli +++ b/infer/src/IR/ProcnameDispatcher.mli @@ -103,6 +103,10 @@ module type Common = sig val ( ~- ) : string -> ('context, 'f, 'f, unit, 'markers, 'markers) name_matcher (** Starts a path with a name *) + val ( ~+ ) : + ('context -> string -> bool) -> ('context, 'f, 'f, unit, 'markers, 'markers) name_matcher + (** Starts a path with a matching name that satisfies the given function *) + val ( &+ ) : ( 'context , 'f_in diff --git a/infer/src/absint/PatternMatch.ml b/infer/src/absint/PatternMatch.ml index 5b591f03f..455e475cd 100644 --- a/infer/src/absint/PatternMatch.ml +++ b/infer/src/absint/PatternMatch.ml @@ -49,6 +49,15 @@ let is_subtype_of_str tenv cn1 classname_str = is_subtype tenv cn1 typename +let implements interface tenv typename = + let is_interface s _ = String.equal interface (Typ.Name.name s) in + supertype_exists tenv is_interface (Typ.Name.Java.from_string typename) + + +let implements_iterator = implements "java.util.Iterator" + +let implements_collection = implements "java.util.Collection" + (** The type the method is invoked on *) let get_this_type proc_attributes = match proc_attributes.ProcAttributes.formals with (_, t) :: _ -> Some t | _ -> None diff --git a/infer/src/absint/PatternMatch.mli b/infer/src/absint/PatternMatch.mli index 7f5e0b516..71ddc4bb9 100644 --- a/infer/src/absint/PatternMatch.mli +++ b/infer/src/absint/PatternMatch.mli @@ -30,6 +30,12 @@ val is_subtype : Tenv.t -> Typ.Name.t -> Typ.Name.t -> bool val is_subtype_of_str : Tenv.t -> Typ.Name.t -> string -> bool (** Resolve [typ_str] in [tenv], then check [typ] <: [typ_str] *) +val implements_iterator : Tenv.t -> string -> bool +(** Check whether class implements Java's Iterator *) + +val implements_collection : Tenv.t -> string -> bool +(** Check whether class implements a Java's Collection *) + val supertype_exists : Tenv.t -> (Typ.Name.t -> Typ.Struct.t -> bool) -> Typ.Name.t -> bool (** Holds iff the predicate holds on a supertype of the named type, including the type itself *) diff --git a/infer/src/bufferoverrun/bufferOverrunChecker.ml b/infer/src/bufferoverrun/bufferOverrunChecker.ml index 4646cfd17..524866662 100644 --- a/infer/src/bufferoverrun/bufferOverrunChecker.ml +++ b/infer/src/bufferoverrun/bufferOverrunChecker.ml @@ -176,7 +176,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct | Prune (exp, _, _, _) -> Sem.Prune.prune exp mem | Call (ret, Const (Cfun callee_pname), params, location, _) -> ( - match Models.Call.dispatch () callee_pname params with + match Models.Call.dispatch tenv callee_pname params with | Some {Models.exec} -> let node_hash = CFG.Node.hash node in let model_env = @@ -268,7 +268,7 @@ module Init = struct pname symbol_table path tenv ~node_hash location ~depth loc elt ~offset ?size ~inst_num ~new_sym_num ~new_alloc_num mem | Typ.Tstruct typename -> ( - match Models.TypName.dispatch () typename with + match Models.TypName.dispatch tenv typename with | Some {Models.declare_symbolic} -> let model_env = Models.mk_model_env pname node_hash location tenv symbol_table in declare_symbolic ~decl_sym_val:(decl_sym_val ~may_last_field) path model_env ~depth @@ -325,7 +325,7 @@ module Init = struct BoUtils.Exec.decl_local_array ~decl_local pname ~node_hash location loc typ ~length ?stride ~inst_num ~dimension mem | Typ.Tstruct typname -> ( - match Models.TypName.dispatch () typname with + match Models.TypName.dispatch tenv typname with | Some {Models.declare_local} -> let model_env = Models.mk_model_env pname node_hash location tenv symbol_table in declare_local ~decl_local model_env loc ~inst_num ~dimension mem @@ -477,7 +477,7 @@ module Report = struct | Sil.Load (_, exp, _, location) | Sil.Store (exp, _, _, location) -> check_expr pname exp location mem cond_set | Sil.Call (_, Const (Cfun callee_pname), params, location, _) -> ( - match Models.Call.dispatch () callee_pname params with + match Models.Call.dispatch tenv callee_pname params with | Some {Models.check} -> let node_hash = CFG.Node.hash node in check (Models.mk_model_env pname node_hash location tenv symbol_table) mem cond_set diff --git a/infer/src/bufferoverrun/bufferOverrunModels.ml b/infer/src/bufferoverrun/bufferOverrunModels.ml index 40bf3eb16..546d56336 100644 --- a/infer/src/bufferoverrun/bufferOverrunModels.ml +++ b/infer/src/bufferoverrun/bufferOverrunModels.ml @@ -303,18 +303,18 @@ module StdArray = struct {declare_local; declare_symbolic} end -(* Java's ArrayLists are represented by their size. We don't care about the elements. +(* Java's Collections are represented by their size. We don't care about the elements. - when they are constructed, we set the size to 0 - each time we add an element, we increase the length of the array - each time we delete an element, we decrease the length of the array *) -module ArrayList = struct +module Collection = struct let typ = let declare_local ~decl_local:_ {pname; node_hash; location} loc ~inst_num ~dimension mem = - BoUtils.Exec.decl_local_arraylist pname ~node_hash location loc ~inst_num ~dimension mem + BoUtils.Exec.decl_local_collection pname ~node_hash location loc ~inst_num ~dimension mem in let declare_symbolic ~decl_sym_val:_ path {pname; location; symbol_table} ~depth:_ loc ~inst_num:_ ~new_sym_num ~new_alloc_num:_ mem = - BoUtils.Exec.decl_sym_arraylist pname symbol_table path location loc ~new_sym_num mem + BoUtils.Exec.decl_sym_collection pname symbol_table path location loc ~new_sym_num mem in {declare_local; declare_symbolic} @@ -383,7 +383,7 @@ module ArrayList = struct let add_at_index (alist_id: Ident.t) index_exp = let check {pname; location} mem cond_set = let array_exp = Exp.Var alist_id in - BoUtils.Check.arraylist_access ~array_exp ~index_exp ~is_arraylist_add:true mem pname + BoUtils.Check.collection_access ~array_exp ~index_exp ~is_collection_add:true mem pname location cond_set in {exec= change_size_by ~size_f:incr_size alist_id; check} @@ -392,7 +392,7 @@ module ArrayList = struct let remove_at_index alist_id index_exp = let check {pname; location} mem cond_set = let array_exp = Exp.Var alist_id in - BoUtils.Check.arraylist_access ~array_exp ~index_exp mem pname location cond_set + BoUtils.Check.collection_access ~array_exp ~index_exp mem pname location cond_set in {exec= change_size_by ~size_f:decr_size alist_id; check} @@ -404,7 +404,7 @@ module ArrayList = struct in let check {pname; location} mem cond_set = let array_exp = Exp.Var alist_id in - BoUtils.Check.arraylist_access ~index_exp ~array_exp ~is_arraylist_add:true mem pname + BoUtils.Check.collection_access ~index_exp ~array_exp ~is_collection_add:true mem pname location cond_set in {exec; check} @@ -414,13 +414,13 @@ module ArrayList = struct let exec _model_env ~ret:_ mem = mem in let check {pname; location} mem cond_set = let array_exp = Exp.Var alist_id in - BoUtils.Check.arraylist_access ~index_exp ~array_exp mem pname location cond_set + BoUtils.Check.collection_access ~index_exp ~array_exp mem pname location cond_set in {exec; check} end module Call = struct - let dispatch : (unit, model) ProcnameDispatcher.Call.dispatcher = + let dispatch : (Tenv.t, model) ProcnameDispatcher.Call.dispatcher = let open ProcnameDispatcher.Call in let mk_std_array () = -"std" &:: "array" < any_typ &+ capt_int in let std_array0 = mk_std_array () in @@ -433,7 +433,8 @@ module Call = struct ; -"fgetc" <>--> by_value Dom.Val.Itv.m1_255 ; -"infer_print" <>$ capt_exp $!--> infer_print ; -"malloc" <>$ capt_exp $+...$--> malloc - ; -"__new" <>$ capt_exp_of_typ (-"java.util.ArrayList") $+...$--> ArrayList.new_list + ; -"__new" <>$ capt_exp_of_typ (+PatternMatch.implements_collection) + $+...$--> Collection.new_list ; -"__new" <>$ capt_exp $+...$--> malloc ; -"__new_array" <>$ capt_exp $+...$--> malloc ; -"__placement_new" <>$ any_arg $+ capt_exp $!--> placement_new @@ -449,29 +450,31 @@ module Call = struct ; std_array2 >:: "at" $ capt_arg $+ capt_arg $!--> StdArray.at ; std_array2 >:: "operator[]" $ capt_arg $+ capt_arg $!--> StdArray.at ; -"std" &:: "array" &::.*--> StdArray.no_model - ; -"java.util.ArrayList" &:: "get" <>$ capt_var_exn $+ capt_exp - $--> ArrayList.get_or_set_at_index - ; -"java.util.ArrayList" &:: "set" <>$ capt_var_exn $+ capt_exp $+ any_arg - $--> ArrayList.get_or_set_at_index - ; -"java.util.ArrayList" &:: "remove" <>$ capt_var_exn $+ capt_exp - $--> ArrayList.remove_at_index - ; -"java.util.ArrayList" &:: "add" <>$ capt_var_exn $+ any_arg $--> ArrayList.add - ; -"java.util.ArrayList" &:: "add" <>$ capt_var_exn $+ capt_exp $+ any_arg - $!--> ArrayList.add_at_index - ; -"java.util.ArrayList" &:: "iterator" <>$ capt_exp $!--> ArrayList.iterator - ; -"java.util.Iterator" &:: "hasNext" <>$ capt_exp $!--> ArrayList.hasNext - ; -"java.util.ArrayList" &:: "addAll" <>$ capt_var_exn $+ capt_exp $--> ArrayList.addAll - ; -"java.util.ArrayList" &:: "addAll" <>$ capt_var_exn $+ capt_exp $+ capt_exp - $!--> ArrayList.addAll_at_index - ; -"java.util.ArrayList" &:: "size" <>$ capt_exp $!--> ArrayList.size ] + ; +PatternMatch.implements_collection &:: "get" <>$ capt_var_exn $+ capt_exp + $--> Collection.get_or_set_at_index + ; +PatternMatch.implements_collection &:: "set" <>$ capt_var_exn $+ capt_exp $+ any_arg + $--> Collection.get_or_set_at_index + ; +PatternMatch.implements_collection &:: "remove" <>$ capt_var_exn $+ capt_exp + $--> Collection.remove_at_index + ; +PatternMatch.implements_collection &:: "add" <>$ capt_var_exn $+ any_arg + $--> Collection.add + ; +PatternMatch.implements_collection &:: "add" <>$ capt_var_exn $+ capt_exp $+ any_arg + $!--> Collection.add_at_index + ; +PatternMatch.implements_collection &:: "iterator" <>$ capt_exp $!--> Collection.iterator + ; +PatternMatch.implements_iterator &:: "hasNext" <>$ capt_exp $!--> Collection.hasNext + ; +PatternMatch.implements_collection &:: "addAll" <>$ capt_var_exn $+ capt_exp + $--> Collection.addAll + ; +PatternMatch.implements_collection &:: "addAll" <>$ capt_var_exn $+ capt_exp $+ capt_exp + $!--> Collection.addAll_at_index + ; +PatternMatch.implements_collection &:: "size" <>$ capt_exp $!--> Collection.size ] end module TypName = struct - let dispatch : (unit, typ_model) ProcnameDispatcher.TypName.dispatcher = + let dispatch : (Tenv.t, typ_model) ProcnameDispatcher.TypName.dispatcher = let open ProcnameDispatcher.TypName in make_dispatcher [ -"std" &:: "array" < capt_typ `T &+ capt_int >--> StdArray.typ - ; -"java.util.Iterator" &::.*--> ArrayList.typ - ; -"java.util.ArrayList" &::.*--> ArrayList.typ + ; +PatternMatch.implements_collection &::.*--> Collection.typ + ; +PatternMatch.implements_iterator &::.*--> Collection.typ ; -"std" &:: "array" &::.*--> StdArray.no_typ_model ] end diff --git a/infer/src/bufferoverrun/bufferOverrunProofObligations.ml b/infer/src/bufferoverrun/bufferOverrunProofObligations.ml index 10000675f..b8b5e53fa 100644 --- a/infer/src/bufferoverrun/bufferOverrunProofObligations.ml +++ b/infer/src/bufferoverrun/bufferOverrunProofObligations.ml @@ -215,15 +215,15 @@ module ArrayAccessCondition = struct (* check buffer overrun and return its confidence *) - let check : is_arraylist_add:bool -> t -> checked_condition = - fun ~is_arraylist_add c -> - (* idx = [il, iu], size = [sl, su], + let check : is_collection_add:bool -> t -> checked_condition = + fun ~is_collection_add c -> + (* idx = [il, iu], size = [sl, su], For arrays : we want to check that 0 <= idx < size For adding into arraylists: we want to check that 0 <= idx <= size *) let c' = set_size_pos c in (* if sl < 0, use sl' = 0 *) let not_overrun = - if is_arraylist_add then ItvPure.le_sem c'.idx c'.size + if is_collection_add then ItvPure.le_sem c'.idx c'.size else if Relation.lt_sat_opt c'.idx_sym_exp c'.size_sym_exp c'.relation then Itv.Boolean.true_ else ItvPure.lt_sem c'.idx c'.size in @@ -278,12 +278,12 @@ end module Condition = struct type t = | AllocSize of AllocSizeCondition.t - | ArrayAccess of {is_arraylist_add: bool; c: ArrayAccessCondition.t} + | ArrayAccess of {is_collection_add: bool; c: ArrayAccessCondition.t} let make_alloc_size = Option.map ~f:(fun c -> AllocSize c) - let make_array_access ~is_arraylist_add = - Option.map ~f:(fun c -> ArrayAccess {is_arraylist_add; c}) + let make_array_access ~is_collection_add = + Option.map ~f:(fun c -> ArrayAccess {is_collection_add; c}) let get_symbols = function @@ -296,9 +296,9 @@ module Condition = struct let subst bound_map rel_map caller_relation = function | AllocSize c -> AllocSizeCondition.subst bound_map c |> make_alloc_size - | ArrayAccess {is_arraylist_add; c} -> + | ArrayAccess {is_collection_add; c} -> ArrayAccessCondition.subst bound_map rel_map caller_relation c - |> make_array_access ~is_arraylist_add + |> make_array_access ~is_collection_add let have_similar_bounds c1 c2 = @@ -317,7 +317,7 @@ module Condition = struct match (lhs, rhs) with | AllocSize lhs, AllocSize rhs -> AllocSizeCondition.xcompare ~lhs ~rhs - | ArrayAccess {is_arraylist_add= b1; c= lhs}, ArrayAccess {is_arraylist_add= b2; c= rhs} + | ArrayAccess {is_collection_add= b1; c= lhs}, ArrayAccess {is_collection_add= b2; c= rhs} when Bool.equal b1 b2 -> ArrayAccessCondition.xcompare ~lhs ~rhs | _ -> @@ -341,14 +341,14 @@ module Condition = struct let check = function | AllocSize c -> AllocSizeCondition.check c - | ArrayAccess {is_arraylist_add; c} -> - ArrayAccessCondition.check ~is_arraylist_add c + | ArrayAccess {is_collection_add; c} -> + ArrayAccessCondition.check ~is_collection_add c let forget_locs locs x = match x with - | ArrayAccess {is_arraylist_add; c} -> - ArrayAccess {is_arraylist_add; c= ArrayAccessCondition.forget_locs locs c} + | ArrayAccess {is_collection_add; c} -> + ArrayAccess {is_collection_add; c= ArrayAccessCondition.forget_locs locs c} | AllocSize _ -> x end @@ -534,10 +534,10 @@ module ConditionSet = struct join [cwt] condset - let add_array_access pname location ~idx ~size ~is_arraylist_add ~idx_sym_exp ~size_sym_exp + let add_array_access pname location ~idx ~size ~is_collection_add ~idx_sym_exp ~size_sym_exp ~relation val_traces condset = ArrayAccessCondition.make ~idx ~size ~idx_sym_exp ~size_sym_exp ~relation - |> Condition.make_array_access ~is_arraylist_add |> add_opt pname location val_traces condset + |> Condition.make_array_access ~is_collection_add |> add_opt pname location val_traces condset let add_alloc_size pname location ~length val_traces condset = diff --git a/infer/src/bufferoverrun/bufferOverrunUtils.ml b/infer/src/bufferoverrun/bufferOverrunUtils.ml index 031e0dfe6..49423a700 100644 --- a/infer/src/bufferoverrun/bufferOverrunUtils.ml +++ b/infer/src/bufferoverrun/bufferOverrunUtils.ml @@ -57,7 +57,7 @@ module Exec = struct (mem, inst_num + 1) - let decl_local_arraylist + let decl_local_collection : Typ.Procname.t -> node_hash:int -> Location.t -> Loc.t -> inst_num:int -> dimension:int -> Dom.Mem.astate -> Dom.Mem.astate * int = fun pname ~node_hash location loc ~inst_num ~dimension mem -> @@ -128,7 +128,7 @@ module Exec = struct decl_sym_val pname path tenv ~node_hash location ~depth alloc_loc typ mem - let decl_sym_arraylist + let decl_sym_collection : Typ.Procname.t -> Itv.SymbolTable.t -> Itv.SymbolPath.partial -> Location.t -> Loc.t -> new_sym_num:Itv.Counter.t -> Dom.Mem.astate -> Dom.Mem.astate = fun pname symbol_table path location loc ~new_sym_num mem -> @@ -199,13 +199,13 @@ end module Check = struct let check_access ~size ~idx ~size_sym_exp ~idx_sym_exp ~relation ~arr ~idx_traces - ?(is_arraylist_add= false) pname location cond_set = + ?(is_collection_add= false) pname location cond_set = let arr_traces = Dom.Val.get_traces arr in match (size, idx) with | NonBottom length, NonBottom idx -> let traces = TraceSet.merge ~arr_traces ~idx_traces location in PO.ConditionSet.add_array_access pname location ~size:length ~idx ~size_sym_exp - ~idx_sym_exp ~relation ~is_arraylist_add traces cond_set + ~idx_sym_exp ~relation ~is_collection_add traces cond_set | _ -> cond_set @@ -230,7 +230,8 @@ module Check = struct location cond_set - let arraylist_access ~array_exp ~index_exp ?(is_arraylist_add= false) mem pname location cond_set = + let collection_access ~array_exp ~index_exp ?(is_collection_add= false) mem pname location + cond_set = let idx = Sem.eval index_exp mem in let arr = Sem.eval array_exp mem in let idx_traces = Dom.Val.get_traces idx in @@ -238,7 +239,7 @@ module Check = struct let idx = Dom.Val.get_itv idx in let relation = Dom.Mem.get_relation mem in check_access ~size ~idx ~size_sym_exp:None ~idx_sym_exp:None ~relation ~arr ~idx_traces - ~is_arraylist_add pname location cond_set + ~is_collection_add pname location cond_set let lindex ~array_exp ~index_exp mem pname location cond_set = diff --git a/infer/src/bufferoverrun/bufferOverrunUtils.mli b/infer/src/bufferoverrun/bufferOverrunUtils.mli index d5d3277ac..4b19d647b 100644 --- a/infer/src/bufferoverrun/bufferOverrunUtils.mli +++ b/infer/src/bufferoverrun/bufferOverrunUtils.mli @@ -25,7 +25,7 @@ module Exec : sig -> length:IntLit.t option -> ?stride:int -> inst_num:int -> dimension:int -> Dom.Mem.astate -> Dom.Mem.astate * int - val decl_local_arraylist : + val decl_local_collection : Typ.Procname.t -> node_hash:int -> Location.t -> Loc.t -> inst_num:int -> dimension:int -> Dom.Mem.astate -> Dom.Mem.astate * int @@ -44,7 +44,7 @@ module Exec : sig -> node_hash:int -> Location.t -> depth:int -> Loc.t -> Typ.t -> inst_num:int -> new_alloc_num:Itv.Counter.t -> Dom.Mem.astate -> Dom.Mem.astate - val decl_sym_arraylist : + val decl_sym_collection : Typ.Procname.t -> Itv.SymbolTable.t -> Itv.SymbolPath.partial -> Location.t -> Loc.t -> new_sym_num:Itv.Counter.t -> Dom.Mem.astate -> Dom.Mem.astate @@ -65,7 +65,7 @@ module Check : sig array_exp:Exp.t -> index_exp:Exp.t -> Dom.Mem.astate -> Typ.Procname.t -> Location.t -> PO.ConditionSet.t -> PO.ConditionSet.t - val arraylist_access : - array_exp:Exp.t -> index_exp:Exp.t -> ?is_arraylist_add:bool -> Dom.Mem.astate + val collection_access : + array_exp:Exp.t -> index_exp:Exp.t -> ?is_collection_add:bool -> Dom.Mem.astate -> Typ.Procname.t -> Location.t -> PO.ConditionSet.t -> PO.ConditionSet.t end diff --git a/infer/tests/codetoanalyze/java/performance/CollectionTest.java b/infer/tests/codetoanalyze/java/performance/CollectionTest.java new file mode 100644 index 000000000..0a93bff41 --- /dev/null +++ b/infer/tests/codetoanalyze/java/performance/CollectionTest.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import java.util.Collection; +import java.util.Iterator; +import java.util.concurrent.ConcurrentLinkedQueue; + +public class CollectionTest { + + interface MyCollection extends Collection {} + + void iterate_over_mycollection(MyCollection list) { + for (int i = 0, size = list.size(); i < size; ++i) {} + } + + void iterate_over_some_java_collection( + ConcurrentLinkedQueue> mSubscribers) { + for (MyCollection list : mSubscribers) { + } + } + + // Expected |mSubscribers| * |list| but we get T + // because we are not tracking elements of collections + void iterate_over_mycollection_quad_FP( + ConcurrentLinkedQueue> mSubscribers) { + for (MyCollection list : mSubscribers) { + iterate_over_mycollection(list); + } + } +} diff --git a/infer/tests/codetoanalyze/java/performance/issues.exp b/infer/tests/codetoanalyze/java/performance/issues.exp index 8d7eb9f19..0a07fd63a 100644 --- a/infer/tests/codetoanalyze/java/performance/issues.exp +++ b/infer/tests/codetoanalyze/java/performance/issues.exp @@ -38,6 +38,11 @@ codetoanalyze/java/performance/Break.java, int Break.break_constant(int), 2, EXP codetoanalyze/java/performance/Break.java, int Break.break_loop(int,int), 1, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 1 + 7 * p.ub, degree = 1] codetoanalyze/java/performance/Break.java, int Break.break_loop(int,int), 1, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 2 + 7 * p.ub, degree = 1] codetoanalyze/java/performance/Break.java, void Break.break_outer_loop_FN(int,int), 0, INFINITE_EXECUTION_TIME_CALL, no_bucket, ERROR, [] +codetoanalyze/java/performance/CollectionTest.java, void CollectionTest.iterate_over_mycollection(CollectionTest$MyCollection), 1, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 5 + 5 * list.length.ub, degree = 1] +codetoanalyze/java/performance/CollectionTest.java, void CollectionTest.iterate_over_mycollection(CollectionTest$MyCollection), 1, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 6 + 5 * list.length.ub, degree = 1] +codetoanalyze/java/performance/CollectionTest.java, void CollectionTest.iterate_over_mycollection_quad_FP(ConcurrentLinkedQueue), 0, INFINITE_EXECUTION_TIME_CALL, no_bucket, ERROR, [] +codetoanalyze/java/performance/CollectionTest.java, void CollectionTest.iterate_over_some_java_collection(ConcurrentLinkedQueue), 2, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 4 + 9 * (mSubscribers.length.ub + -1) + 4 * mSubscribers.length.ub, degree = 1] +codetoanalyze/java/performance/CollectionTest.java, void CollectionTest.iterate_over_some_java_collection(ConcurrentLinkedQueue), 2, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 5 + 9 * (mSubscribers.length.ub + -1) + 4 * mSubscribers.length.ub, degree = 1] codetoanalyze/java/performance/Compound_loop.java, int Compound_loop.compound_while(int), 3, CONDITION_ALWAYS_TRUE, no_bucket, WARNING, [] codetoanalyze/java/performance/Compound_loop.java, int Compound_loop.compound_while(int), 3, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 5 + 7 * m.ub, degree = 1] codetoanalyze/java/performance/Compound_loop.java, int Compound_loop.compound_while(int), 4, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 5 + 7 * m.ub, degree = 1]