[frontend] Add support for fields of Remodel-generated class in ObjC

Summary: For Remodel-generated class (https://github.com/facebook/remodel), their properties are stored/loaded at internal fields named "_<property name>".  This diff prepends "_" to property names when writing field info to the type environment when the field is of Remodel-generated class.

Reviewed By: ezgicicek

Differential Revision: D28541495

fbshipit-source-id: d0a1e5a4f
master
Sungkeun Cho 4 years ago committed by Facebook GitHub Bot
parent d8d2f2b23d
commit 7e58392558

@ -2002,6 +2002,15 @@ INTERNAL OPTIONS
--incremental-analysis and --continue-analysis. (Conversely: --incremental-analysis and --continue-analysis. (Conversely:
--no-reanalyze) --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 "_<property
name>".
--remodel-class-reset
Cancel the effect of --remodel-class.
--report-blacklist-files-containing-reset --report-blacklist-files-containing-reset
Set --report-blacklist-files-containing to the empty list. Set --report-blacklist-files-containing to the empty list.

@ -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 type per_file = Global | FileLocal of t
let pp_per_file fmt = function let pp_per_file fmt = function

@ -54,6 +54,9 @@ val add_field : t -> Typ.Name.t -> Struct.field -> unit
val pp : Format.formatter -> t -> unit val pp : Format.formatter -> t -> unit
(** print a type environment *) (** 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 type per_file = Global | FileLocal of t
val pp_per_file : Format.formatter -> per_file -> unit val pp_per_file : Format.formatter -> per_file -> unit

@ -570,6 +570,9 @@ module Name = struct
in in
function function
| ObjcClass (name, _) -> not (QualifiedCppName.Set.mem name tagged_classes) | _ -> false | ObjcClass (name, _) -> not (QualifiedCppName.Set.mem name tagged_classes) | _ -> false
let remodel_class = Option.map Config.remodel_class ~f:from_string
end end
module Set = PrettyPrintable.MakePPSet (struct module Set = PrettyPrintable.MakePPSet (struct

@ -236,6 +236,8 @@ module Name : sig
val from_qual_name : QualifiedCppName.t -> t val from_qual_name : QualifiedCppName.t -> t
val protocol_from_qual_name : QualifiedCppName.t -> t val protocol_from_qual_name : QualifiedCppName.t -> t
val remodel_class : t option
end end
module Set : PrettyPrintable.PPSet with type elt = t module Set : PrettyPrintable.PPSet with type elt = t

@ -2168,6 +2168,13 @@ and relative_path_backtrack =
convert /my/source/File.java with project root /my/root into ../source/File.java" 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 \
\"_<property name>\"."
and report = and report =
CLOpt.mk_bool ~long:"report" ~default:true CLOpt.mk_bool ~long:"report" ~default:true
~in_help:InferCommand.[(Analyze, manual_generic); (Run, manual_generic)] ~in_help:InferCommand.[(Analyze, manual_generic); (Run, manual_generic)]
@ -3353,6 +3360,8 @@ and reanalyze = !reanalyze
and relative_path_backtrack = !relative_path_backtrack and relative_path_backtrack = !relative_path_backtrack
and remodel_class = !remodel_class
and report = !report and report = !report
and report_blacklist_files_containing = RevList.to_list !report_blacklist_files_containing and report_blacklist_files_containing = RevList.to_list !report_blacklist_files_containing

@ -546,6 +546,8 @@ val reanalyze : bool
val relative_path_backtrack : int val relative_path_backtrack : int
val remodel_class : string option
val report : bool val report : bool
val report_blacklist_files_containing : string list val report_blacklist_files_containing : string list

@ -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 = let prop_atts =
List.map List.map
~f:(fun att -> Annot.{name= None; value= Str (Clang_ast_j.string_of_property_attribute att)}) ~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 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 typ = qual_type_to_sil_type tenv qual_type in
let item_annotations = let item_annotations =
match prop_atts with 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 *) (* 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 open Clang_ast_t in
let get_sil_field name_info (qt : qual_type) property_attributes = let get_sil_field ni_name (qt : qual_type) property_attributes =
build_sil_field qual_type_to_sil_type tenv class_tname name_info qt property_attributes build_sil_field qual_type_to_sil_type tenv class_tname ni_name qt property_attributes
in in
let rec get_field fields decl = let rec get_field fields decl =
match decl with match decl with
| ObjCPropertyDecl (_, name_info, {opdi_qual_type; opdi_ivar_decl; opdi_property_attributes}) -> | ObjCPropertyDecl (_, {ni_name}, {opdi_qual_type; opdi_ivar_decl; opdi_property_attributes}) ->
let name_info, qual_type = let ni_name, qual_type =
match CAst_utils.get_decl_opt_with_decl_ref_opt opdi_ivar_decl with match CAst_utils.get_decl_opt_with_decl_ref_opt opdi_ivar_decl with
| Some (ObjCIvarDecl (_, name_info, qual_type, _, _)) -> | Some (ObjCIvarDecl (_, {ni_name}, qual_type, _, _)) ->
(name_info, 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 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 CGeneral_utils.add_no_duplicates_fields field fields
| ObjCPropertyImplDecl (_, obj_c_property_impl_decl_info) -> ( | ObjCPropertyImplDecl (_, obj_c_property_impl_decl_info) -> (
let property_decl_opt = obj_c_property_impl_decl_info.Clang_ast_t.opidi_property_decl in 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 get_field fields decl
| None -> | None ->
fields ) fields )
| ObjCIvarDecl (_, name_info, qual_type, _, _) -> | ObjCIvarDecl (_, {ni_name}, qual_type, _, _) ->
let field = get_sil_field name_info qual_type [] in let field = get_sil_field ni_name qual_type [] in
CGeneral_utils.add_no_duplicates_fields field fields CGeneral_utils.add_no_duplicates_fields field fields
| _ -> | _ ->
fields fields

@ -12,7 +12,8 @@ open! IStd
type field_type = Fieldname.t * Typ.t * (Annot.t * bool) list type field_type = Fieldname.t * Typ.t * (Annot.t * bool) list
val get_fields : val get_fields :
CAst_utils.qual_type_to_sil_type implements_remodel_class:bool
-> CAst_utils.qual_type_to_sil_type
-> Tenv.t -> Tenv.t
-> Typ.Name.t -> Typ.Name.t
-> Clang_ast_t.decl list -> Clang_ast_t.decl list

@ -65,7 +65,10 @@ let get_base_class_name_from_category decl =
(* to the corresponding class. Update the tenv accordingly.*) (* 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 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 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 decl_methods = ObjcMethod_decl.get_methods procname_from_decl tenv decl_list in
let class_tn_desc = Typ.Tstruct class_tn_name 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 let decl_key = Clang_ast_extend.DeclPtr decl_info.Clang_ast_t.di_pointer in

@ -54,7 +54,15 @@ let create_supers_fields qual_type_to_sil_type tenv class_tname decl_list otdi_s
let supers = let supers =
match super_opt with None -> [] | Some super -> [Typ.Name.Objc.from_qual_name super] match super_opt with None -> [] | Some super -> [Typ.Name.Objc.from_qual_name super]
in 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) (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 ; "ADDING: ObjCImplementationDecl for class '%a'@\n" QualifiedCppName.pp class_name ;
add_class_decl qual_type_to_sil_type tenv idi ; add_class_decl qual_type_to_sil_type tenv idi ;
let class_tn_name = Typ.Name.Objc.from_qual_name class_name in 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 ; CField_decl.add_missing_fields tenv class_name fields ;
let methods = ObjcMethod_decl.get_methods procname_from_decl tenv decl_list in let methods = ObjcMethod_decl.get_methods procname_from_decl tenv decl_list in
ObjcMethod_decl.add_missing_methods tenv class_tn_name methods ; ObjcMethod_decl.add_missing_methods tenv class_tn_name methods ;

Loading…
Cancel
Save