diff --git a/infer/man/man1/infer-full.txt b/infer/man/man1/infer-full.txt index 26e7b9c63..fa994f1ba 100644 --- a/infer/man/man1/infer-full.txt +++ b/infer/man/man1/infer-full.txt @@ -2002,6 +2002,15 @@ INTERNAL OPTIONS --incremental-analysis and --continue-analysis. (Conversely: --no-reanalyze) + --remodel-class string + Specify a Remodel class name. For sub-classes of the Remodel class + in ObjC, setters and getters for properties are auto-generated and + they store/load values into/from field names of "_". + + --remodel-class-reset + Cancel the effect of --remodel-class. + --report-blacklist-files-containing-reset Set --report-blacklist-files-containing to the empty list. diff --git a/infer/src/IR/Tenv.ml b/infer/src/IR/Tenv.ml index e96ac8e85..97f81f4b5 100644 --- a/infer/src/IR/Tenv.ml +++ b/infer/src/IR/Tenv.ml @@ -63,6 +63,17 @@ let add_field tenv class_tn_name field = () +let implements_remodel_class tenv name = + Option.exists Typ.Name.Objc.remodel_class ~f:(fun remodel_class -> + let rec implements_remodel_class_helper name = + Typ.Name.equal name remodel_class + || lookup tenv name + |> Option.exists ~f:(fun {Struct.supers} -> + List.exists supers ~f:implements_remodel_class_helper ) + in + implements_remodel_class_helper name ) + + type per_file = Global | FileLocal of t let pp_per_file fmt = function diff --git a/infer/src/IR/Tenv.mli b/infer/src/IR/Tenv.mli index 5911a184c..998d3c9b3 100644 --- a/infer/src/IR/Tenv.mli +++ b/infer/src/IR/Tenv.mli @@ -54,6 +54,9 @@ val add_field : t -> Typ.Name.t -> Struct.field -> unit val pp : Format.formatter -> t -> unit (** print a type environment *) +val implements_remodel_class : t -> Typ.Name.t -> bool +(** Check if a class implements the Remodel class *) + type per_file = Global | FileLocal of t val pp_per_file : Format.formatter -> per_file -> unit diff --git a/infer/src/IR/Typ.ml b/infer/src/IR/Typ.ml index 64cafcdb0..9e409955d 100644 --- a/infer/src/IR/Typ.ml +++ b/infer/src/IR/Typ.ml @@ -570,6 +570,9 @@ module Name = struct in function | ObjcClass (name, _) -> not (QualifiedCppName.Set.mem name tagged_classes) | _ -> false + + + let remodel_class = Option.map Config.remodel_class ~f:from_string end module Set = PrettyPrintable.MakePPSet (struct diff --git a/infer/src/IR/Typ.mli b/infer/src/IR/Typ.mli index 05e636b20..bd46a4a6f 100644 --- a/infer/src/IR/Typ.mli +++ b/infer/src/IR/Typ.mli @@ -236,6 +236,8 @@ module Name : sig val from_qual_name : QualifiedCppName.t -> t val protocol_from_qual_name : QualifiedCppName.t -> t + + val remodel_class : t option end module Set : PrettyPrintable.PPSet with type elt = t diff --git a/infer/src/base/Config.ml b/infer/src/base/Config.ml index 851f3e780..c6e220cd4 100644 --- a/infer/src/base/Config.ml +++ b/infer/src/base/Config.ml @@ -2168,6 +2168,13 @@ and relative_path_backtrack = convert /my/source/File.java with project root /my/root into ../source/File.java" +and remodel_class = + CLOpt.mk_string_opt ~long:"remodel-class" + "Specify a Remodel class name. For sub-classes of the Remodel class in ObjC, setters and \ + getters for properties are auto-generated and they store/load values into/from field names of \ + \"_\"." + + and report = CLOpt.mk_bool ~long:"report" ~default:true ~in_help:InferCommand.[(Analyze, manual_generic); (Run, manual_generic)] @@ -3353,6 +3360,8 @@ and reanalyze = !reanalyze and relative_path_backtrack = !relative_path_backtrack +and remodel_class = !remodel_class + and report = !report and report_blacklist_files_containing = RevList.to_list !report_blacklist_files_containing diff --git a/infer/src/base/Config.mli b/infer/src/base/Config.mli index a53fe398e..4828c7360 100644 --- a/infer/src/base/Config.mli +++ b/infer/src/base/Config.mli @@ -546,6 +546,8 @@ val reanalyze : bool val relative_path_backtrack : int +val remodel_class : string option + val report : bool val report_blacklist_files_containing : string list diff --git a/infer/src/clang/cField_decl.ml b/infer/src/clang/cField_decl.ml index ad58941d0..7b1b49039 100644 --- a/infer/src/clang/cField_decl.ml +++ b/infer/src/clang/cField_decl.ml @@ -39,7 +39,7 @@ let fields_superclass tenv interface_decl_info = [] -let build_sil_field qual_type_to_sil_type tenv class_tname field_name qual_type prop_attributes = +let build_sil_field qual_type_to_sil_type tenv class_tname ni_name qual_type prop_attributes = let prop_atts = List.map ~f:(fun att -> Annot.{name= None; value= Str (Clang_ast_j.string_of_property_attribute att)}) @@ -54,7 +54,7 @@ let build_sil_field qual_type_to_sil_type tenv class_tname field_name qual_type | _ -> [] in - let fname = CGeneral_utils.mk_class_field_name class_tname field_name.Clang_ast_t.ni_name in + let fname = CGeneral_utils.mk_class_field_name class_tname ni_name in let typ = qual_type_to_sil_type tenv qual_type in let item_annotations = match prop_atts with @@ -68,22 +68,23 @@ let build_sil_field qual_type_to_sil_type tenv class_tname field_name qual_type (* Given a list of declarations in an interface returns a list of fields *) -let get_fields qual_type_to_sil_type tenv class_tname decl_list = +let get_fields ~implements_remodel_class qual_type_to_sil_type tenv class_tname decl_list = let open Clang_ast_t in - let get_sil_field name_info (qt : qual_type) property_attributes = - build_sil_field qual_type_to_sil_type tenv class_tname name_info qt property_attributes + let get_sil_field ni_name (qt : qual_type) property_attributes = + build_sil_field qual_type_to_sil_type tenv class_tname ni_name qt property_attributes in let rec get_field fields decl = match decl with - | ObjCPropertyDecl (_, name_info, {opdi_qual_type; opdi_ivar_decl; opdi_property_attributes}) -> - let name_info, qual_type = + | ObjCPropertyDecl (_, {ni_name}, {opdi_qual_type; opdi_ivar_decl; opdi_property_attributes}) -> + let ni_name, qual_type = match CAst_utils.get_decl_opt_with_decl_ref_opt opdi_ivar_decl with - | Some (ObjCIvarDecl (_, name_info, qual_type, _, _)) -> - (name_info, qual_type) + | Some (ObjCIvarDecl (_, {ni_name}, qual_type, _, _)) -> + (ni_name, qual_type) | _ -> - (name_info, opdi_qual_type) + let ni_name = if implements_remodel_class then "_" ^ ni_name else ni_name in + (ni_name, opdi_qual_type) in - let field = get_sil_field name_info qual_type opdi_property_attributes in + let field = get_sil_field ni_name qual_type opdi_property_attributes in CGeneral_utils.add_no_duplicates_fields field fields | ObjCPropertyImplDecl (_, obj_c_property_impl_decl_info) -> ( let property_decl_opt = obj_c_property_impl_decl_info.Clang_ast_t.opidi_property_decl in @@ -92,8 +93,8 @@ let get_fields qual_type_to_sil_type tenv class_tname decl_list = get_field fields decl | None -> fields ) - | ObjCIvarDecl (_, name_info, qual_type, _, _) -> - let field = get_sil_field name_info qual_type [] in + | ObjCIvarDecl (_, {ni_name}, qual_type, _, _) -> + let field = get_sil_field ni_name qual_type [] in CGeneral_utils.add_no_duplicates_fields field fields | _ -> fields diff --git a/infer/src/clang/cField_decl.mli b/infer/src/clang/cField_decl.mli index ce2e6a047..b842d7c4d 100644 --- a/infer/src/clang/cField_decl.mli +++ b/infer/src/clang/cField_decl.mli @@ -12,7 +12,8 @@ open! IStd type field_type = Fieldname.t * Typ.t * (Annot.t * bool) list val get_fields : - CAst_utils.qual_type_to_sil_type + implements_remodel_class:bool + -> CAst_utils.qual_type_to_sil_type -> Tenv.t -> Typ.Name.t -> Clang_ast_t.decl list diff --git a/infer/src/clang/objcCategory_decl.ml b/infer/src/clang/objcCategory_decl.ml index 48bfd46ea..9d8ed6498 100644 --- a/infer/src/clang/objcCategory_decl.ml +++ b/infer/src/clang/objcCategory_decl.ml @@ -65,7 +65,10 @@ let get_base_class_name_from_category decl = (* to the corresponding class. Update the tenv accordingly.*) let process_category qual_type_to_sil_type procname_from_decl tenv class_name decl_info decl_list = let class_tn_name = Typ.Name.Objc.from_qual_name class_name in - let decl_fields = CField_decl.get_fields qual_type_to_sil_type tenv class_tn_name decl_list in + let decl_fields = + CField_decl.get_fields ~implements_remodel_class:false qual_type_to_sil_type tenv class_tn_name + decl_list + in let decl_methods = ObjcMethod_decl.get_methods procname_from_decl tenv decl_list in let class_tn_desc = Typ.Tstruct class_tn_name in let decl_key = Clang_ast_extend.DeclPtr decl_info.Clang_ast_t.di_pointer in diff --git a/infer/src/clang/objcInterface_decl.ml b/infer/src/clang/objcInterface_decl.ml index 13a9c20cd..100541927 100644 --- a/infer/src/clang/objcInterface_decl.ml +++ b/infer/src/clang/objcInterface_decl.ml @@ -54,7 +54,15 @@ let create_supers_fields qual_type_to_sil_type tenv class_tname decl_list otdi_s let supers = match super_opt with None -> [] | Some super -> [Typ.Name.Objc.from_qual_name super] in - let fields = CField_decl.get_fields qual_type_to_sil_type tenv class_tname decl_list in + let implements_remodel_class = + Option.exists Typ.Name.Objc.remodel_class ~f:(fun remodel_class -> + Typ.Name.equal class_tname remodel_class + || List.exists supers ~f:(Tenv.implements_remodel_class tenv) ) + in + let fields = + CField_decl.get_fields ~implements_remodel_class qual_type_to_sil_type tenv class_tname + decl_list + in (supers, fields) @@ -136,7 +144,11 @@ let interface_impl_declaration qual_type_to_sil_type procname_from_decl tenv dec "ADDING: ObjCImplementationDecl for class '%a'@\n" QualifiedCppName.pp class_name ; add_class_decl qual_type_to_sil_type tenv idi ; let class_tn_name = Typ.Name.Objc.from_qual_name class_name in - let fields = CField_decl.get_fields qual_type_to_sil_type tenv class_tn_name decl_list in + let implements_remodel_class = Tenv.implements_remodel_class tenv class_tn_name in + let fields = + CField_decl.get_fields ~implements_remodel_class qual_type_to_sil_type tenv class_tn_name + decl_list + in CField_decl.add_missing_fields tenv class_name fields ; let methods = ObjcMethod_decl.get_methods procname_from_decl tenv decl_list in ObjcMethod_decl.add_missing_methods tenv class_tn_name methods ;