diff --git a/infer/src/clang/CProcname.ml b/infer/src/clang/CProcname.ml new file mode 100644 index 000000000..fa956a559 --- /dev/null +++ b/infer/src/clang/CProcname.ml @@ -0,0 +1,125 @@ +(* + * Copyright (c) 2017 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + *) + +open! IStd + +let rec get_mangled_method_name function_decl_info method_decl_info = + (* For virtual methods return mangled name of the method from most base class + Go recursively until there is no method in any parent class. All names + of the same method need to be the same, otherwise dynamic dispatch won't + work. *) + let open Clang_ast_t in + match method_decl_info.xmdi_overriden_methods with + | [] -> function_decl_info.fdi_mangled_name + | base1_dr :: _ -> + (let base1 = match CAst_utils.get_decl base1_dr.dr_decl_pointer with + | Some b -> b + | _ -> assert false in + match base1 with + | CXXMethodDecl (_, _, _, fdi, mdi) + | CXXConstructorDecl (_, _, _, fdi, mdi) + | CXXConversionDecl (_, _, _, fdi, mdi) + | CXXDestructorDecl (_, _, _, fdi, mdi) -> + get_mangled_method_name fdi mdi + | _ -> assert false) + +let mk_c_function translation_unit_context name function_decl_info_opt = + let file = + match function_decl_info_opt with + | Some (decl_info, function_decl_info) -> + (match function_decl_info.Clang_ast_t.fdi_storage_class with + | Some "static" -> + let file_opt = (fst decl_info.Clang_ast_t.di_source_range).Clang_ast_t.sl_file in + Option.value_map ~f:SourceFile.to_string ~default:"" file_opt + | _ -> "") + | None -> "" in + let mangled_opt = match function_decl_info_opt with + | Some (_, function_decl_info) -> function_decl_info.Clang_ast_t.fdi_mangled_name + | _ -> None in + let mangled_name = match mangled_opt with + | Some m when CGeneral_utils.is_cpp_translation translation_unit_context -> m + | _ -> "" in + let mangled = (Utils.string_crc_hex32 file) ^ mangled_name in + if String.is_empty file && String.is_empty mangled_name then + Procname.from_string_c_fun name + else + Procname.C (Procname.c name mangled) + +let mk_cpp_method class_name method_name ?meth_decl mangled = + let method_kind = match meth_decl with + | Some (Clang_ast_t.CXXConstructorDecl (_, _, _, _, {xmdi_is_constexpr})) -> + Procname.CPPConstructor (mangled, xmdi_is_constexpr) + | _ -> + Procname.CPPMethod mangled in + Procname.ObjC_Cpp + (Procname.objc_cpp class_name method_name method_kind) + +let block_procname_with_index defining_proc i = + Config.anonymous_block_prefix ^ + (Procname.to_string defining_proc) ^ + Config.anonymous_block_num_sep ^ + (string_of_int i) + +(* Global counter for anonymous block*) +let block_counter = ref 0 + +let get_next_block_pvar defining_proc = + let name = block_procname_with_index defining_proc (!block_counter +1) in + Pvar.mk_tmp name defining_proc + +let reset_block_counter () = + block_counter := 0 + +let get_fresh_block_index () = + block_counter := !block_counter +1; + !block_counter + +let mk_fresh_block_procname defining_proc = + let name = block_procname_with_index defining_proc (get_fresh_block_index ()) in + Procname.mangled_objc_block name + + +module NoAstDecl = struct + let c_function_of_string translation_unit_context name = + mk_c_function translation_unit_context name None + + let cpp_method_of_string class_name method_name = + mk_cpp_method class_name method_name None + + let objc_method_of_string_kind class_name method_name method_kind = + Procname.ObjC_Cpp + (Procname.objc_cpp class_name method_name method_kind) +end + +let from_decl translation_unit_context meth_decl = + let open Clang_ast_t in + match meth_decl with + | FunctionDecl (decl_info, name_info, _, fdi) -> + let name = CAst_utils.get_qualified_name name_info in + let function_info = Some (decl_info, fdi) in + mk_c_function translation_unit_context name function_info + | CXXMethodDecl (_, name_info, _, fdi, mdi) + | CXXConstructorDecl (_, name_info, _, fdi, mdi) + | CXXConversionDecl (_, name_info, _, fdi, mdi) + | CXXDestructorDecl (_, name_info, _, fdi, mdi) -> + let mangled = get_mangled_method_name fdi mdi in + let method_name = CAst_utils.get_unqualified_name name_info in + let class_name = CAst_utils.get_class_name_from_member name_info in + mk_cpp_method class_name method_name ~meth_decl mangled + | ObjCMethodDecl (_, name_info, mdi) -> + let class_name = CAst_utils.get_class_name_from_member name_info in + let method_name = name_info.Clang_ast_t.ni_name in + let is_instance = mdi.Clang_ast_t.omdi_is_instance_method in + let method_kind = Procname.objc_method_kind_of_bool is_instance in + NoAstDecl.objc_method_of_string_kind class_name method_name method_kind + | BlockDecl _ -> + let name = Config.anonymous_block_prefix ^ Config.anonymous_block_num_sep ^ + (string_of_int (get_fresh_block_index ())) in + Procname.mangled_objc_block name + | _ -> assert false diff --git a/infer/src/clang/CProcname.mli b/infer/src/clang/CProcname.mli new file mode 100644 index 000000000..4d170904f --- /dev/null +++ b/infer/src/clang/CProcname.mli @@ -0,0 +1,35 @@ +(* + * Copyright (c) 2017 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + *) + +open! IStd + +(** Given decl, return its procname. This function should be used for all procedures + present in original AST *) +val from_decl : CFrontend_config.translation_unit_context -> Clang_ast_t.decl -> Procname.t + + +(** WARNING: functions from this module should not be used if full decl is available in AST *) +module NoAstDecl : sig + val c_function_of_string : CFrontend_config.translation_unit_context -> string -> Procname.t + + val cpp_method_of_string : string -> string -> Procname.t + + val objc_method_of_string_kind : string -> string -> Procname.objc_cpp_method_kind -> Procname.t + +end + +(** Makes a fresh name for a block defined inside the defining procedure. + It updates the global block_counter *) +val mk_fresh_block_procname : Procname.t -> Procname.t + +(** Returns the next fresh name for a block defined inside the defining procedure + It does not update the global block_counter *) +val get_next_block_pvar : Procname.t -> Pvar.t + +val reset_block_counter : unit -> unit diff --git a/infer/src/clang/cFrontend.ml b/infer/src/clang/cFrontend.ml index 8a819952d..bafe424e7 100644 --- a/infer/src/clang/cFrontend.ml +++ b/infer/src/clang/cFrontend.ml @@ -35,7 +35,7 @@ let compute_icfg trans_unit_ctx tenv ast = let init_global_state_capture () = Ident.NameGenerator.reset (); CFrontend_config.global_translation_unit_decls := []; - CGeneral_utils.reset_block_counter () + CProcname.reset_block_counter () let do_source_file translation_unit_context ast = let tenv = Tenv.create () in diff --git a/infer/src/clang/cFrontend_decl.ml b/infer/src/clang/cFrontend_decl.ml index 63ef53b25..8f8aa2ac9 100644 --- a/infer/src/clang/cFrontend_decl.ml +++ b/infer/src/clang/cFrontend_decl.ml @@ -106,7 +106,7 @@ struct Some (ProcAttributes.Objc_getter field_name) else Some (ProcAttributes.Objc_setter field_name) in - let procname = CGeneral_utils.procname_of_decl trans_unit_ctx d in + let procname = CProcname.from_decl trans_unit_ctx d in let attrs = { (ProcAttributes.default procname Config.Clang) with loc = loc; objc_accessor = property_accessor; } in diff --git a/infer/src/clang/cFrontend_errors.ml b/infer/src/clang/cFrontend_errors.ml index f5c05b751..4fc1b5e61 100644 --- a/infer/src/clang/cFrontend_errors.ml +++ b/infer/src/clang/cFrontend_errors.ml @@ -175,7 +175,7 @@ let expand_checkers checkers = let get_err_log translation_unit_context method_decl_opt = let procname = match method_decl_opt with - | Some method_decl -> CGeneral_utils.procname_of_decl translation_unit_context method_decl + | Some method_decl -> CProcname.from_decl translation_unit_context method_decl | None -> Procname.Linters_dummy_method in LintIssues.get_err_log procname diff --git a/infer/src/clang/cGeneral_utils.ml b/infer/src/clang/cGeneral_utils.ml index 960209ab7..f6871fa34 100644 --- a/infer/src/clang/cGeneral_utils.ml +++ b/infer/src/clang/cGeneral_utils.ml @@ -86,36 +86,6 @@ let is_static_var var_decl_info = | Some sc -> String.equal sc CFrontend_config.static | _ -> false -let block_procname_with_index defining_proc i = - Config.anonymous_block_prefix ^ - (Procname.to_string defining_proc) ^ - Config.anonymous_block_num_sep ^ - (string_of_int i) - -(* Global counter for anonymous block*) -let block_counter = ref 0 - -(* Returns a fresh index for a new anonymous block *) -let get_fresh_block_index () = - block_counter := !block_counter +1; - !block_counter - -(* Makes a fresh name for a block defined inside the defining procedure.*) -(* It updates the global block_counter *) -let mk_fresh_block_procname defining_proc = - let name = block_procname_with_index defining_proc (get_fresh_block_index ()) in - Procname.mangled_objc_block name - -(* Returns the next fresh name for a block defined inside the defining procedure *) -(* It does not update the global block_counter *) -let get_next_block_pvar defining_proc = - let name = block_procname_with_index defining_proc (!block_counter +1) in - Pvar.mk_tmp name defining_proc - -(* Reset block counter *) -let reset_block_counter () = - block_counter := 0 - let rec zip xs ys = match xs, ys with | [], _ @@ -144,93 +114,6 @@ let is_objc_extension translation_unit_context = CFrontend_config.equal_clang_lang lang CFrontend_config.ObjC || CFrontend_config.equal_clang_lang lang CFrontend_config.ObjCPP -let rec get_mangled_method_name function_decl_info method_decl_info = - (* For virtual methods return mangled name of the method from most base class - Go recursively until there is no method in any parent class. All names - of the same method need to be the same, otherwise dynamic dispatch won't - work. *) - let open Clang_ast_t in - match method_decl_info.xmdi_overriden_methods with - | [] -> function_decl_info.fdi_mangled_name - | base1_dr :: _ -> - (let base1 = match CAst_utils.get_decl base1_dr.dr_decl_pointer with - | Some b -> b - | _ -> assert false in - match base1 with - | CXXMethodDecl (_, _, _, fdi, mdi) - | CXXConstructorDecl (_, _, _, fdi, mdi) - | CXXConversionDecl (_, _, _, fdi, mdi) - | CXXDestructorDecl (_, _, _, fdi, mdi) -> - get_mangled_method_name fdi mdi - | _ -> assert false) - -let mk_procname_from_function translation_unit_context name function_decl_info_opt = - let file = - match function_decl_info_opt with - | Some (decl_info, function_decl_info) -> - (match function_decl_info.Clang_ast_t.fdi_storage_class with - | Some "static" -> - let file_opt = (fst decl_info.Clang_ast_t.di_source_range).Clang_ast_t.sl_file in - Option.value_map ~f:SourceFile.to_string ~default:"" file_opt - | _ -> "") - | None -> "" in - let mangled_opt = match function_decl_info_opt with - | Some (_, function_decl_info) -> function_decl_info.Clang_ast_t.fdi_mangled_name - | _ -> None in - let mangled_name = - match mangled_opt with - | Some m when is_cpp_translation translation_unit_context -> m - | _ -> "" in - let mangled = (Utils.string_crc_hex32 file) ^ mangled_name in - if String.is_empty file && String.is_empty mangled_name then - Procname.from_string_c_fun name - else - Procname.C (Procname.c name mangled) - -let mk_procname_from_objc_method class_name method_name method_kind = - Procname.ObjC_Cpp - (Procname.objc_cpp class_name method_name method_kind) - -let mk_procname_from_cpp_method class_name method_name ?meth_decl mangled = - let method_kind = match meth_decl with - | Some (Clang_ast_t.CXXConstructorDecl (_, _, _, _, {xmdi_is_constexpr})) -> - Procname.CPPConstructor (mangled, xmdi_is_constexpr) - | _ -> - Procname.CPPMethod mangled in - Procname.ObjC_Cpp - (Procname.objc_cpp class_name method_name method_kind) - -let get_objc_method_name name_info mdi class_name = - let method_name = name_info.Clang_ast_t.ni_name in - let is_instance = mdi.Clang_ast_t.omdi_is_instance_method in - let method_kind = Procname.objc_method_kind_of_bool is_instance in - mk_procname_from_objc_method class_name method_name method_kind - -let procname_of_decl translation_unit_context meth_decl = - let open Clang_ast_t in - match meth_decl with - | FunctionDecl (decl_info, name_info, _, fdi) -> - let name = CAst_utils.get_qualified_name name_info in - let function_info = Some (decl_info, fdi) in - mk_procname_from_function translation_unit_context name function_info - | CXXMethodDecl (_, name_info, _, fdi, mdi) - | CXXConstructorDecl (_, name_info, _, fdi, mdi) - | CXXConversionDecl (_, name_info, _, fdi, mdi) - | CXXDestructorDecl (_, name_info, _, fdi, mdi) -> - let mangled = get_mangled_method_name fdi mdi in - let method_name = CAst_utils.get_unqualified_name name_info in - let class_name = CAst_utils.get_class_name_from_member name_info in - mk_procname_from_cpp_method class_name method_name ~meth_decl mangled - | ObjCMethodDecl (_, name_info, mdi) -> - let class_name = CAst_utils.get_class_name_from_member name_info in - get_objc_method_name name_info mdi class_name - | BlockDecl _ -> - let name = Config.anonymous_block_prefix ^ Config.anonymous_block_num_sep ^ - (string_of_int (get_fresh_block_index ())) in - Procname.mangled_objc_block name - | _ -> assert false - - let get_var_name_mangled name_info var_decl_info = let clang_name = CAst_utils.get_qualified_name name_info in let param_idx_opt = var_decl_info.Clang_ast_t.vdi_parm_index_in_function in diff --git a/infer/src/clang/cGeneral_utils.mli b/infer/src/clang/cGeneral_utils.mli index 0aa774988..c4b8431a8 100644 --- a/infer/src/clang/cGeneral_utils.mli +++ b/infer/src/clang/cGeneral_utils.mli @@ -37,31 +37,12 @@ val swap_elements_list : 'a list -> 'a list val is_static_var : Clang_ast_t.var_decl_info -> bool -val mk_fresh_block_procname : Procname.t -> Procname.t - -val get_next_block_pvar : Procname.t -> Pvar.t - -val reset_block_counter : unit -> unit - val zip: 'a list -> 'b list -> ('a * 'b) list val list_range: int -> int -> int list val replicate: int -> 'a -> 'a list -val mk_procname_from_objc_method : string -> string -> Procname.objc_cpp_method_kind -> Procname.t - -val mk_procname_from_function : CFrontend_config.translation_unit_context -> string - -> (Clang_ast_t.decl_info * Clang_ast_t.function_decl_info) option -> Procname.t - -val get_mangled_method_name : Clang_ast_t.function_decl_info -> - Clang_ast_t.cxx_method_decl_info -> string option - -val mk_procname_from_cpp_method : - string -> string -> ?meth_decl:Clang_ast_t.decl -> string option -> Procname.t - -val procname_of_decl : CFrontend_config.translation_unit_context -> Clang_ast_t.decl -> Procname.t - val mk_class_field_name : Clang_ast_t.named_decl_info -> Ident.fieldname val get_var_name_mangled : Clang_ast_t.named_decl_info -> Clang_ast_t.var_decl_info -> diff --git a/infer/src/clang/cMethod_trans.ml b/infer/src/clang/cMethod_trans.ml index d50b7b0be..5a15168a4 100644 --- a/infer/src/clang/cMethod_trans.ml +++ b/infer/src/clang/cMethod_trans.ml @@ -162,7 +162,7 @@ let method_signature_of_decl trans_unit_ctx tenv meth_decl block_data_opt = match meth_decl, block_data_opt with | FunctionDecl (decl_info, _, qt, fdi), _ -> let func_decl = Func_decl_info (fdi, qt.Clang_ast_t.qt_type_ptr) in - let procname = CGeneral_utils.procname_of_decl trans_unit_ctx meth_decl in + let procname = CProcname.from_decl trans_unit_ctx meth_decl in let ms = build_method_signature trans_unit_ctx tenv decl_info procname func_decl None None in let extra_instrs = get_assume_not_null_calls fdi.Clang_ast_t.fdi_parameters in ms, fdi.Clang_ast_t.fdi_body, extra_instrs @@ -170,7 +170,7 @@ let method_signature_of_decl trans_unit_ctx tenv meth_decl block_data_opt = | CXXConstructorDecl (decl_info, _, qt, fdi, mdi), _ | CXXConversionDecl (decl_info, _, qt, fdi, mdi), _ | CXXDestructorDecl (decl_info, _, qt, fdi, mdi), _ -> - let procname = CGeneral_utils.procname_of_decl trans_unit_ctx meth_decl in + let procname = CProcname.from_decl trans_unit_ctx meth_decl in let parent_ptr = Option.value_exn decl_info.di_parent_pointer in let method_decl = Cpp_Meth_decl_info (fdi, mdi, parent_ptr, qt.Clang_ast_t.qt_type_ptr) in let parent_pointer = decl_info.Clang_ast_t.di_parent_pointer in @@ -180,7 +180,7 @@ let method_signature_of_decl trans_unit_ctx tenv meth_decl block_data_opt = let init_list_instrs = get_init_list_instrs mdi in (* it will be empty for methods *) ms, fdi.Clang_ast_t.fdi_body, (init_list_instrs @ non_null_instrs) | ObjCMethodDecl (decl_info, _, mdi), _ -> - let procname = CGeneral_utils.procname_of_decl trans_unit_ctx meth_decl in + let procname = CProcname.from_decl trans_unit_ctx meth_decl in let parent_ptr = Option.value_exn decl_info.di_parent_pointer in let method_decl = ObjC_Meth_decl_info (mdi, parent_ptr) in let parent_pointer = decl_info.Clang_ast_t.di_parent_pointer in @@ -468,9 +468,9 @@ let create_procdesc_with_pointer context pointer class_name_opt name = let callee_name = match class_name_opt with | Some class_name -> - CGeneral_utils.mk_procname_from_cpp_method class_name name None + CProcname.NoAstDecl.cpp_method_of_string class_name name | None -> - CGeneral_utils.mk_procname_from_function context.translation_unit_context name None in + CProcname.NoAstDecl.c_function_of_string context.translation_unit_context name in create_external_procdesc context.cfg callee_name false None; callee_name diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index ae2238596..34207cf42 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -43,12 +43,12 @@ struct | None -> (* fall back to our method resolution if clang's fails *) let class_name = CMethod_trans.get_class_name_method_call_from_receiver_kind context obj_c_message_expr_info act_params in - CGeneral_utils.mk_procname_from_objc_method class_name selector method_kind in + CProcname.NoAstDecl.objc_method_of_string_kind class_name selector method_kind in let predefined_ms_opt = match proc_name with | Procname.ObjC_Cpp objc_cpp -> let class_name = Procname.objc_cpp_get_class_name objc_cpp in CTrans_models.get_predefined_model_method_signature class_name selector - CGeneral_utils.mk_procname_from_objc_method CFrontend_config.ObjC + CProcname.NoAstDecl.objc_method_of_string_kind CFrontend_config.ObjC | _ -> None in match predefined_ms_opt, ms_opt with @@ -1120,7 +1120,7 @@ struct and block_enumeration_trans trans_state stmt_info stmt_list ei = Logging.out_debug "\n Call to a block enumeration function treated as special case...\n@."; let procname = Procdesc.get_proc_name trans_state.context.CContext.procdesc in - let pvar = CGeneral_utils.get_next_block_pvar procname in + let pvar = CProcname.get_next_block_pvar procname in let transformed_stmt, _ = Ast_expressions.translate_block_enumerate (Pvar.to_string pvar) stmt_info stmt_list ei in instruction trans_state transformed_stmt @@ -2061,7 +2061,7 @@ struct | Clang_ast_t.BlockDecl (_, block_decl_info) -> let open CContext in let type_ptr = expr_info.Clang_ast_t.ei_type_ptr in - let block_pname = CGeneral_utils.mk_fresh_block_procname procname in + let block_pname = CProcname.mk_fresh_block_procname procname in let typ = CType_decl.type_ptr_to_sil_type context.tenv type_ptr in (* We need to set the explicit dependency between the newly created block and the *) (* defining procedure. We add an edge in the call graph.*) diff --git a/infer/src/clang/cTrans_utils.ml b/infer/src/clang/cTrans_utils.ml index 7d75b0513..c971e97c0 100644 --- a/infer/src/clang/cTrans_utils.ml +++ b/infer/src/clang/cTrans_utils.ml @@ -335,7 +335,7 @@ let objc_new_trans trans_state loc stmt_info cls_name function_type = let is_instance = true in let call_flags = { CallFlags.default with CallFlags.cf_virtual = is_instance; } in let pname = - CGeneral_utils.mk_procname_from_objc_method + CProcname.NoAstDecl.objc_method_of_string_kind cls_name CFrontend_config.init Procname.ObjCInstanceMethod in CMethod_trans.create_external_procdesc trans_state.context.CContext.cfg pname is_instance None; let args = [(alloc_ret_exp, alloc_ret_type)] in