diff --git a/infer/src/clang/cFrontend_config.ml b/infer/src/clang/cFrontend_config.ml index f04bcdfb7..c35f03555 100644 --- a/infer/src/clang/cFrontend_config.ml +++ b/infer/src/clang/cFrontend_config.ml @@ -74,6 +74,13 @@ let lang_from_string lang_string = else assert false in language := lang +let lang_to_string lang = + match lang with + | C -> "c" + | OBJC -> "objective-c" + | CPP -> "c++" + | OBJCPP -> "objective-c++" + let emtpy_name_category ="EMPTY_NAME_CATEGORY_FOR_" let objc_object = "objc_object" diff --git a/infer/src/clang/cFrontend_config.mli b/infer/src/clang/cFrontend_config.mli index 741ee7a64..9cf56fb99 100644 --- a/infer/src/clang/cFrontend_config.mli +++ b/infer/src/clang/cFrontend_config.mli @@ -29,6 +29,8 @@ type lang = val lang_from_string : string -> unit +val lang_to_string : lang -> string + val language : lang ref val ast_file : string option ref diff --git a/infer/src/clang/cFrontend_utils.ml b/infer/src/clang/cFrontend_utils.ml index ffd6a1877..b31e05cdc 100644 --- a/infer/src/clang/cFrontend_utils.ml +++ b/infer/src/clang/cFrontend_utils.ml @@ -446,7 +446,7 @@ struct let prefix = Ast_utils.get_qualifier_string field_qual_name in Ident.create_fieldname (Mangled.mangled field_name prefix) 0 - let mk_procname_from_function name function_decl_info_opt tp = + let mk_procname_from_function name function_decl_info_opt tp language = let file = match function_decl_info_opt with | Some (decl_info, function_decl_info) -> @@ -458,7 +458,7 @@ struct | _ -> "") | None -> "" in let type_string = - match !CFrontend_config.language with + match language with | CFrontend_config.CPP | CFrontend_config.OBJCPP -> Ast_utils.string_of_type_ptr tp | _ -> "" in diff --git a/infer/src/clang/cFrontend_utils.mli b/infer/src/clang/cFrontend_utils.mli index a58660f66..416320b02 100644 --- a/infer/src/clang/cFrontend_utils.mli +++ b/infer/src/clang/cFrontend_utils.mli @@ -141,7 +141,7 @@ sig val mk_procname_from_objc_method : string -> string -> Procname.objc_method_kind -> Procname.t val mk_procname_from_function : string -> (Clang_ast_t.decl_info * Clang_ast_t.function_decl_info) - option -> Clang_ast_t.type_ptr -> Procname.t + option -> Clang_ast_t.type_ptr -> CFrontend_config.lang -> Procname.t val mk_procname_from_cpp_method : string -> string -> Clang_ast_t.type_ptr -> Procname.t diff --git a/infer/src/clang/cMethod_signature.ml b/infer/src/clang/cMethod_signature.ml index d6641bc39..d0219cfd5 100644 --- a/infer/src/clang/cMethod_signature.ml +++ b/infer/src/clang/cMethod_signature.ml @@ -18,6 +18,7 @@ type method_signature = { _loc : Clang_ast_t.source_range; _is_instance : bool; _is_generated : bool; + _language : CFrontend_config.lang; } let ms_get_name ms = @@ -41,7 +42,10 @@ let ms_is_instance ms = let ms_is_generated ms = ms._is_generated -let make_ms procname args ret_type attributes loc is_instance is_generated = +let ms_get_lang ms = + ms._language + +let make_ms procname args ret_type attributes loc is_instance is_generated lang = let meth_signature = { _name = procname; _args = args; @@ -49,7 +53,9 @@ let make_ms procname args ret_type attributes loc is_instance is_generated = _attributes = attributes; _loc = loc; _is_instance = is_instance; - _is_generated = is_generated} in + _is_generated = is_generated; + _language = lang; + } in meth_signature let replace_name_ms ms name = diff --git a/infer/src/clang/cMethod_signature.mli b/infer/src/clang/cMethod_signature.mli index f398ccd62..c49b706cf 100644 --- a/infer/src/clang/cMethod_signature.mli +++ b/infer/src/clang/cMethod_signature.mli @@ -25,9 +25,11 @@ val ms_get_loc : method_signature -> Clang_ast_t.source_range val ms_is_instance : method_signature -> bool +val ms_get_lang : method_signature -> CFrontend_config.lang + val make_ms : Procname.t -> (string * Clang_ast_t.type_ptr * Clang_ast_t.stmt option) list -> - Clang_ast_t.type_ptr -> Clang_ast_t.attribute list -> Clang_ast_t.source_range -> bool -> bool -> - method_signature + Clang_ast_t.type_ptr -> Clang_ast_t.attribute list -> Clang_ast_t.source_range -> bool -> + bool -> CFrontend_config.lang -> method_signature val replace_name_ms : method_signature -> Procname.t -> method_signature diff --git a/infer/src/clang/cMethod_trans.ml b/infer/src/clang/cMethod_trans.ml index 09ff6c062..80a3a2595 100644 --- a/infer/src/clang/cMethod_trans.ml +++ b/infer/src/clang/cMethod_trans.ml @@ -28,14 +28,14 @@ type method_call_type = | MCStatic type function_method_decl_info = - | Func_decl_info of Clang_ast_t.function_decl_info * Clang_ast_t.type_ptr + | Func_decl_info of Clang_ast_t.function_decl_info * Clang_ast_t.type_ptr * CFrontend_config.lang | Cpp_Meth_decl_info of Clang_ast_t.function_decl_info * string * Clang_ast_t.type_ptr | ObjC_Meth_decl_info of Clang_ast_t.obj_c_method_decl_info * string | Block_decl_info of Clang_ast_t.block_decl_info * Clang_ast_t.type_ptr let is_instance_method function_method_decl_info is_instance = match function_method_decl_info with - | Func_decl_info (function_decl_info, _) -> false + | Func_decl_info (function_decl_info, _, _) -> false | Cpp_Meth_decl_info _ -> true | ObjC_Meth_decl_info (method_decl_info, _) -> method_decl_info.Clang_ast_t.omdi_is_instance_method @@ -55,11 +55,18 @@ let get_class_param function_method_decl_info = let get_param_decls function_method_decl_info = match function_method_decl_info with - | Func_decl_info (function_decl_info, _) + | Func_decl_info (function_decl_info, _, _) | Cpp_Meth_decl_info (function_decl_info, _, _) -> function_decl_info.Clang_ast_t.fdi_parameters | ObjC_Meth_decl_info (method_decl_info, _) -> method_decl_info.Clang_ast_t.omdi_parameters | Block_decl_info (block_decl_info, _) -> block_decl_info.Clang_ast_t.bdi_parameters +let get_language function_method_decl_info = + match function_method_decl_info with + | Func_decl_info (_, _, language) -> language + | Cpp_Meth_decl_info _ -> CFrontend_config.CPP + | ObjC_Meth_decl_info _ -> CFrontend_config.OBJC + | Block_decl_info _ -> CFrontend_config.OBJC + let get_parameters function_method_decl_info = let par_to_ms_par par = match par with @@ -73,7 +80,7 @@ let get_parameters function_method_decl_info = let get_return_type function_method_decl_info = match function_method_decl_info with - | Func_decl_info (_, typ) + | Func_decl_info (_, typ, _) | Cpp_Meth_decl_info (_, _, typ) | Block_decl_info (_, typ) -> Ast_expressions.create_type_ptr_with_just_pointer (CTypes.return_type_of_function_type typ) @@ -85,16 +92,19 @@ let build_method_signature decl_info procname function_method_decl_info is_insta let is_instance_method = is_instance_method function_method_decl_info is_instance in let parameters = get_parameters function_method_decl_info in let attributes = decl_info.Clang_ast_t.di_attributes in - CMethod_signature.make_ms procname parameters tp attributes source_range is_instance_method is_generated + let lang = get_language function_method_decl_info in + CMethod_signature.make_ms procname parameters tp attributes source_range is_instance_method + is_generated lang let method_signature_of_decl class_name_opt meth_decl block_data_opt = let open Clang_ast_t in match meth_decl, block_data_opt, class_name_opt with | FunctionDecl (decl_info, name_info, tp, fdi), _, _ -> let name = name_info.ni_name in - let func_decl = Func_decl_info (fdi, tp) in + let language = !CFrontend_config.language in + let func_decl = Func_decl_info (fdi, tp, language) in let function_info = Some (decl_info, fdi) in - let procname = General_utils.mk_procname_from_function name function_info tp in + let procname = General_utils.mk_procname_from_function name function_info tp language in let ms = build_method_signature decl_info procname func_decl false false false in ms, fdi.Clang_ast_t.fdi_body, fdi.Clang_ast_t.fdi_parameters | CXXMethodDecl (decl_info, name_info, tp, fdi), _, Some class_name -> @@ -173,8 +183,9 @@ let get_formal_parameters tenv ms = match pl with | [] -> [] | (name, raw_type, _):: pl' -> - let tp = - if (name = CFrontend_config.self && CMethod_signature.ms_is_instance ms) then + let is_objc_self = name = CFrontend_config.self && + CMethod_signature.ms_get_lang ms = CFrontend_config.OBJC in + let tp = if is_objc_self && CMethod_signature.ms_is_instance ms then (Ast_expressions.create_pointer_type raw_type) else raw_type in let typ = CTypes_decl.type_ptr_to_sil_type tenv tp in @@ -324,7 +335,7 @@ let create_procdesc_with_pointer context pointer class_name_opt name tp = | Some class_name -> General_utils.mk_procname_from_cpp_method class_name name tp | None -> - General_utils.mk_procname_from_function name None tp in + General_utils.mk_procname_from_function name None tp !CFrontend_config.language 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 c272f6581..380e9840f 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -42,7 +42,7 @@ struct let callee_pn = General_utils.mk_procname_from_objc_method class_name method_name method_kind in let open CContext in match CTrans_models.get_predefined_model_method_signature class_name method_name - General_utils.mk_procname_from_objc_method with + General_utils.mk_procname_from_objc_method CFrontend_config.OBJC with | Some ms -> ignore (CMethod_trans.create_local_procdesc context.cfg context.tenv ms [] [] is_instance); CMethod_signature.ms_get_name ms, CMethod_trans.MCNoVirtual diff --git a/infer/src/clang/cTrans_models.ml b/infer/src/clang/cTrans_models.ml index bcce15463..8cf5282b3 100644 --- a/infer/src/clang/cTrans_models.ml +++ b/infer/src/clang/cTrans_models.ml @@ -119,7 +119,7 @@ let is_assert_log sil_fe = let is_objc_memory_model_controlled o = Core_foundation_model.is_objc_memory_model_controlled o -let get_predefined_ms_method condition class_name method_name method_kind mk_procname +let get_predefined_ms_method condition class_name method_name method_kind mk_procname lang arguments return_type attributes builtin = if condition then let procname = @@ -127,54 +127,57 @@ let get_predefined_ms_method condition class_name method_name method_kind mk_pro | Some procname -> procname | None -> mk_procname class_name method_name method_kind in let ms = CMethod_signature.make_ms procname arguments return_type attributes - (Ast_expressions.dummy_source_range ()) false false in + (Ast_expressions.dummy_source_range ()) false false lang in Some ms else None -let get_predefined_ms_stringWithUTF8String class_name method_name mk_procname = +let get_predefined_ms_stringWithUTF8String class_name method_name mk_procname lang = let condition = class_name = CFrontend_config.nsstring_cl && method_name = CFrontend_config.string_with_utf8_m in let id_type = Ast_expressions.create_id_type in get_predefined_ms_method condition class_name method_name Procname.Class_objc_method - mk_procname [("x", Ast_expressions.create_char_star_type, None)] id_type [] None + mk_procname lang [("x", Ast_expressions.create_char_star_type, None)] id_type [] None -let get_predefined_ms_retain_release class_name method_name mk_procname = +let get_predefined_ms_retain_release class_name method_name mk_procname lang = let condition = is_retain_or_release method_name in let return_type = if is_retain_method method_name || is_autorelease_method method_name then Ast_expressions.create_id_type else Ast_expressions.create_void_type in - get_predefined_ms_method condition CFrontend_config.nsobject_cl method_name Procname.Instance_objc_method - mk_procname [(CFrontend_config.self, class_name, None)] return_type [] (get_builtinname method_name) + let class_name = CFrontend_config.nsobject_cl in + let args = [(CFrontend_config.self, class_name, None)] in + get_predefined_ms_method condition class_name method_name Procname.Instance_objc_method + mk_procname lang args return_type [] (get_builtinname method_name) -let get_predefined_ms_autoreleasepool_init class_name method_name mk_procname = +let get_predefined_ms_autoreleasepool_init class_name method_name mk_procname lang = let condition = method_name = CFrontend_config.init && class_name = CFrontend_config.nsautorelease_pool_cl in let class_type = Ast_expressions.create_class_type class_name in get_predefined_ms_method condition class_name method_name Procname.Instance_objc_method - mk_procname [(CFrontend_config.self, class_type, None)] Ast_expressions.create_void_type [] None + mk_procname lang [(CFrontend_config.self, class_type, None)] + Ast_expressions.create_void_type [] None -let get_predefined_ms_nsautoreleasepool_release class_name method_name mk_procname = +let get_predefined_ms_nsautoreleasepool_release class_name method_name mk_procname lang = let condition = (method_name = CFrontend_config.release || method_name = CFrontend_config.drain) && class_name = CFrontend_config.nsautorelease_pool_cl in let class_type = Ast_expressions.create_class_type class_name in get_predefined_ms_method condition class_name method_name Procname.Instance_objc_method - mk_procname [(CFrontend_config.self, class_type, None)] - Ast_expressions.create_void_type [] (Some SymExec.ModelBuiltins.__objc_release_autorelease_pool) + mk_procname lang [(CFrontend_config.self, class_type, None)] Ast_expressions.create_void_type + [] (Some SymExec.ModelBuiltins.__objc_release_autorelease_pool) -let get_predefined_model_method_signature class_name method_name mk_procname = - match get_predefined_ms_nsautoreleasepool_release class_name method_name mk_procname with +let get_predefined_model_method_signature class_name method_name mk_procname lang = + match get_predefined_ms_nsautoreleasepool_release class_name method_name mk_procname lang with | Some ms -> Some ms | None -> let class_type = Ast_expressions.create_class_type class_name in - match get_predefined_ms_retain_release class_type method_name mk_procname with + match get_predefined_ms_retain_release class_type method_name mk_procname lang with | Some ms -> Some ms | None -> - match get_predefined_ms_stringWithUTF8String class_name method_name mk_procname with + match get_predefined_ms_stringWithUTF8String class_name method_name mk_procname lang with | Some ms -> Some ms - | None -> get_predefined_ms_autoreleasepool_init class_name method_name mk_procname + | None -> get_predefined_ms_autoreleasepool_init class_name method_name mk_procname lang let dispatch_functions = [ ("_dispatch_once", 1); diff --git a/infer/src/clang/cTrans_models.mli b/infer/src/clang/cTrans_models.mli index 9105e2755..12a354e43 100644 --- a/infer/src/clang/cTrans_models.mli +++ b/infer/src/clang/cTrans_models.mli @@ -27,6 +27,6 @@ val is_toll_free_bridging : Procname.t option -> bool val get_predefined_model_method_signature : string -> string -> (string -> string -> Procname.objc_method_kind -> Procname.t) -> - CMethod_signature.method_signature option + CFrontend_config.lang -> CMethod_signature.method_signature option val is_dispatch_function_name : string -> (string * int) option diff --git a/infer/tests/codetoanalyze/cpp/frontend/keywords/self_parameter.cpp b/infer/tests/codetoanalyze/cpp/frontend/keywords/self_parameter.cpp new file mode 100644 index 000000000..6a6976dcc --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/keywords/self_parameter.cpp @@ -0,0 +1,23 @@ +/* +* Copyright (c) 2015 - 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. +*/ + +class A { +public: + int meth_with_self(int self, int b) { + return self + b; + } +}; + +int fun_with_self(int self) { + return self; +} + +int test(A *a) { + return a->meth_with_self(1, 2) + fun_with_self(10); +} diff --git a/infer/tests/codetoanalyze/cpp/frontend/keywords/self_parameter.dot b/infer/tests/codetoanalyze/cpp/frontend/keywords/self_parameter.dot new file mode 100644 index 000000000..75d46b07f --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/keywords/self_parameter.dot @@ -0,0 +1,35 @@ +digraph iCFG { +9 [label="9: Return Stmt \n n$0=*&a:class A * [line 22]\n n$1=_fun_A_meth_with_self(n$0:class A ,1:int ,2:int ) [line 22]\n n$2=_fun_fun_with_self(10:int ) [line 22]\n *&return:int =(n$1 + n$2) [line 22]\n REMOVE_TEMPS(n$0,n$1,n$2); [line 22]\n NULLIFY(&a,false); [line 22]\n APPLY_ABSTRACTION; [line 22]\n " shape="box"] + + + 9 -> 8 ; +8 [label="8: Exit test \n " color=yellow style=filled] + + +7 [label="7: Start test\nFormals: a:class A *\nLocals: \n DECLARE_LOCALS(&return); [line 21]\n " color=yellow style=filled] + + + 7 -> 9 ; +6 [label="6: Return Stmt \n n$0=*&self:int [line 18]\n *&return:int =n$0 [line 18]\n REMOVE_TEMPS(n$0); [line 18]\n NULLIFY(&self,false); [line 18]\n APPLY_ABSTRACTION; [line 18]\n " shape="box"] + + + 6 -> 5 ; +5 [label="5: Exit fun_with_self \n " color=yellow style=filled] + + +4 [label="4: Start fun_with_self\nFormals: self:int \nLocals: \n DECLARE_LOCALS(&return); [line 17]\n " color=yellow style=filled] + + + 4 -> 6 ; +3 [label="3: Return Stmt \n n$0=*&self:int [line 13]\n n$1=*&b:int [line 13]\n *&return:int =(n$0 + n$1) [line 13]\n REMOVE_TEMPS(n$0,n$1); [line 13]\n NULLIFY(&b,false); [line 13]\n NULLIFY(&self,false); [line 13]\n APPLY_ABSTRACTION; [line 13]\n " shape="box"] + + + 3 -> 2 ; +2 [label="2: Exit A_meth_with_self \n " color=yellow style=filled] + + +1 [label="1: Start A_meth_with_self\nFormals: this:class A self:int b:int \nLocals: \n DECLARE_LOCALS(&return); [line 12]\n NULLIFY(&this,false); [line 12]\n " color=yellow style=filled] + + + 1 -> 3 ; +} diff --git a/infer/tests/frontend/cpp/SelfParameterTest.java b/infer/tests/frontend/cpp/SelfParameterTest.java new file mode 100644 index 000000000..3b311ab87 --- /dev/null +++ b/infer/tests/frontend/cpp/SelfParameterTest.java @@ -0,0 +1,51 @@ +/* +* Copyright (c) 2013 - 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. +*/ + +package frontend.cpp; + +import static org.hamcrest.MatcherAssert.assertThat; +import static utils.matchers.DotFilesEqual.dotFileEqualTo; + +import com.google.common.collect.ImmutableList; + +import org.junit.Rule; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; + +import utils.DebuggableTemporaryFolder; +import utils.InferException; +import utils.InferRunner; + +public class SelfParameterTest { + + @Rule + public DebuggableTemporaryFolder folder = new DebuggableTemporaryFolder(); + + @Test + public void whenCaptureRunCommaThenDotFilesAreTheSame() + throws InterruptedException, IOException, InferException { + String src = + "infer/tests/codetoanalyze/cpp/frontend/keywords/self_parameter.cpp"; + + String dotty = + "infer/tests/codetoanalyze/cpp/frontend/keywords/self_parameter.dot"; + + ImmutableList inferCmd = + InferRunner.createCPPInferCommandFrontend( + folder, + src); + File newDotFile = InferRunner.runInferFrontend(inferCmd); + assertThat( + "In the capture of " + src + + " the dotty files should be the same.", + newDotFile, dotFileEqualTo(dotty)); + } +}