diff --git a/infer/src/backend/utils.ml b/infer/src/backend/utils.ml index 9f9751c13..17fdd518b 100644 --- a/infer/src/backend/utils.ml +++ b/infer/src/backend/utils.ml @@ -97,6 +97,16 @@ module StringSet = Set.Make(String) let pp_stringset fmt ss = StringSet.iter (fun s -> F.fprintf fmt "%s " s) ss +(** string list -> StringSet.t + from http://stackoverflow.com/a/2382330 *) +let string_set_of_list list = + IList.fold_left (fun acc x -> StringSet.add x acc) StringSet.empty list + +(** intersection of two string lists, as a StringSet.t + from http://stackoverflow.com/a/2382330 *) +let string_list_intersection a b = + StringSet.inter (string_set_of_list a) (string_set_of_list b) + (** Maps from integers *) module IntMap = Map.Make (struct type t = int diff --git a/infer/src/backend/utils.mli b/infer/src/backend/utils.mli index bae3a7384..8e801eff4 100644 --- a/infer/src/backend/utils.mli +++ b/infer/src/backend/utils.mli @@ -92,6 +92,12 @@ module StringSet : Set.S with type elt = string (** Pretty print a set of strings *) val pp_stringset : Format.formatter -> StringSet.t -> unit +(** list of strings -> set of strings *) +val string_set_of_list : string list -> StringSet.t + +(** List intersection *) +val string_list_intersection : string list -> string list -> StringSet.t + (** Maps from integers *) module IntMap : Map.S with type key = int diff --git a/infer/src/clang/cFrontend_utils.ml b/infer/src/clang/cFrontend_utils.ml index 06616e287..dd0de7cef 100644 --- a/infer/src/clang/cFrontend_utils.ml +++ b/infer/src/clang/cFrontend_utils.ml @@ -442,22 +442,29 @@ struct let file_opt = (fst decl_info.Clang_ast_t.di_source_range).Clang_ast_t.sl_file in opt_equal string_equal file_opt Config.source_file && Option.is_some file_opt - let rec is_objc_if_descendant if_decl ancestors = - match if_decl with - | Some Clang_ast_t.ObjCInterfaceDecl (_, ndi, _, _, _)-> - let open CFrontend_config in - let blacklist = [nsobject_cl; nsproxy_cl] in - let in_list some_list = IList.mem string_equal ndi.Clang_ast_t.ni_name some_list in - if in_list ancestors then - true - else if in_list blacklist then - false - else - (match get_super_if if_decl with - | Some super_decl -> - is_objc_if_descendant (Some super_decl) ancestors - | None -> false) - | _ -> false + let default_blacklist = + let open CFrontend_config in + [nsobject_cl; nsproxy_cl] + + let rec is_objc_if_descendant ?(blacklist = default_blacklist) if_decl ancestors = + (* List of ancestors to check for and list of classes to short-circuit to + false can't intersect *) + if not (StringSet.is_empty (string_list_intersection blacklist ancestors)) then + failwith "Blacklist and ancestors must be mutually exclusive." + else + match if_decl with + | Some Clang_ast_t.ObjCInterfaceDecl (_, ndi, _, _, _) -> + let in_list some_list = IList.mem string_equal ndi.Clang_ast_t.ni_name some_list in + if in_list ancestors then + true + else if in_list blacklist then + false + else + (match get_super_if if_decl with + | Some super_decl -> + is_objc_if_descendant (Some super_decl) ancestors + | None -> false) + | _ -> false (* let rec getter_attribute_opt attributes = diff --git a/infer/src/clang/cFrontend_utils.mli b/infer/src/clang/cFrontend_utils.mli index 5bd63ad15..2fa1d0d41 100644 --- a/infer/src/clang/cFrontend_utils.mli +++ b/infer/src/clang/cFrontend_utils.mli @@ -165,8 +165,11 @@ sig eventual descendant of one of the classes passed in. Ancestors param is a list of strings that represent the class names. Will short-circuit on NSObject and NSProxy since those are known to be - common base classes. *) - val is_objc_if_descendant : Clang_ast_t.decl option -> (string) list -> bool + common base classes. + The list of classes to short-circuit on can be overridden via specifying + the named `blacklist` argument. *) + val is_objc_if_descendant : + ?blacklist:string list -> Clang_ast_t.decl option -> string list -> bool end