diff --git a/infer/src/clang/cMethod_trans.ml b/infer/src/clang/cMethod_trans.ml index c4ce2f9d9..795c2aeaa 100644 --- a/infer/src/clang/cMethod_trans.ml +++ b/infer/src/clang/cMethod_trans.ml @@ -29,21 +29,22 @@ type method_call_type = type function_method_decl_info = | 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 + | Cpp_Meth_decl_info of Clang_ast_t.function_decl_info * Clang_ast_t.cxx_method_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 * CContext.t let is_instance_method function_method_decl_info = match function_method_decl_info with | Func_decl_info _ | Block_decl_info _ -> false - | Cpp_Meth_decl_info _ -> true + | Cpp_Meth_decl_info (_, method_decl_info, _, _) -> + not method_decl_info.Clang_ast_t.xmdi_is_static | ObjC_Meth_decl_info (method_decl_info, _) -> method_decl_info.Clang_ast_t.omdi_is_instance_method let get_class_param function_method_decl_info = if (is_instance_method function_method_decl_info) then match function_method_decl_info with - | Cpp_Meth_decl_info (_, class_name, _) -> + | Cpp_Meth_decl_info (_, _, class_name, _) -> let class_type = Ast_expressions.create_class_type class_name in [(CFrontend_config.this, class_type)] | ObjC_Meth_decl_info (_, class_name) -> @@ -55,7 +56,8 @@ 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, _, _) - | Cpp_Meth_decl_info (function_decl_info, _, _) -> function_decl_info.Clang_ast_t.fdi_parameters + | 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 @@ -80,7 +82,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, _) - | Cpp_Meth_decl_info (_, _, typ) + | Cpp_Meth_decl_info (_, _, _, typ) | Block_decl_info (_, typ, _) -> CTypes.return_type_of_function_type typ | ObjC_Meth_decl_info (method_decl_info, _) -> method_decl_info.Clang_ast_t.omdi_result_type @@ -125,7 +127,7 @@ let method_signature_of_decl meth_decl block_data_opt = let method_name = name_info.Clang_ast_t.ni_name in let class_name = Ast_utils.get_class_name_from_member name_info in let procname = General_utils.mk_procname_from_cpp_method class_name method_name tp in - let method_decl = Cpp_Meth_decl_info (fdi, class_name, tp) in + let method_decl = Cpp_Meth_decl_info (fdi, mdi, class_name, tp) in let ms = build_method_signature decl_info procname method_decl false false in let non_null_instrs = get_assume_not_null_calls ms fdi.Clang_ast_t.fdi_parameters in let init_list_instrs = get_init_list_instrs mdi in (* it will be empty for methods *) diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index e8af22640..00d7bece7 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -380,18 +380,25 @@ struct let class_name = Ast_utils.get_class_name_from_member name_info in Printing.log_out "!!!!! Dealing with method '%s' @." method_name; let method_typ = CTypes_decl.type_ptr_to_sil_type context.tenv type_ptr in - (* we don't handle C++ static methods yet - when they are called, this might cause a crash *) - assert (IList.length pre_trans_result.exps = 1); - let (obj_sil, class_typ) = extract_exp_from_list pre_trans_result.exps - "WARNING: in Method call we expect to know the object\n" in + let is_instance_method = + match CMethod_trans.method_signature_of_pointer decl_ptr with + | Some ms -> CMethod_signature.ms_is_instance ms + | _ -> assert false in (* will happen for generated methods, shouldn't happen right now *) + let extra_exps = if is_instance_method then + (assert (IList.length pre_trans_result.exps = 1); + let (obj_sil, class_typ) = extract_exp_from_list pre_trans_result.exps + "WARNING: in Method call we expect to know the object\n" in + [(obj_sil, class_typ)]) + else + (* don't add (obj_sil, class_typ) for static methods *) + [] in (* consider using context.CContext.is_callee_expression to deal with pointers to methods? *) (* unlike field access, for method calls there is no need to expand class type *) let pname = CMethod_trans.create_procdesc_with_pointer context decl_ptr (Some class_name) method_name type_ptr in let method_exp = (Sil.Const (Sil.Cfun pname), method_typ) in Cfg.set_procname_priority context.CContext.cfg pname; - (* TODO for static methods we shouldn't return (obj_sil, class_typ) *) - { pre_trans_result with exps = [method_exp; (obj_sil, class_typ)] } + { pre_trans_result with exps = [method_exp] @ extra_exps } let cxxThisExpr_trans trans_state stmt_info expr_info = let context = trans_state.context in diff --git a/infer/tests/codetoanalyze/cpp/frontend/methods/static.cpp b/infer/tests/codetoanalyze/cpp/frontend/methods/static.cpp new file mode 100644 index 000000000..86b9457c4 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/methods/static.cpp @@ -0,0 +1,24 @@ +/* +* 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: + static int fun(int); +}; + +int A::fun(int a) { return 1 / a;} + +void div0_class() { + A::fun(0); +} + +void div0_instance(A *a) { + /* this will call static method as well */ + a->fun(0); +} diff --git a/infer/tests/codetoanalyze/cpp/frontend/methods/static.cpp.dot b/infer/tests/codetoanalyze/cpp/frontend/methods/static.cpp.dot new file mode 100644 index 000000000..e2f092732 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/methods/static.cpp.dot @@ -0,0 +1,35 @@ +digraph iCFG { +9 [label="9: Call _fun_A_fun \n n$0=*&a:class A * [line 23]\n n$1=_fun_A_fun(0:int ) [line 23]\n REMOVE_TEMPS(n$0,n$1); [line 23]\n NULLIFY(&a,false); [line 23]\n APPLY_ABSTRACTION; [line 23]\n " shape="box"] + + + 9 -> 8 ; +8 [label="8: Exit div0_instance \n " color=yellow style=filled] + + +7 [label="7: Start div0_instance\nFormals: a:class A *\nLocals: \n DECLARE_LOCALS(&return); [line 21]\n " color=yellow style=filled] + + + 7 -> 9 ; +6 [label="6: Call _fun_A_fun \n n$0=_fun_A_fun(0:int ) [line 18]\n REMOVE_TEMPS(n$0); [line 18]\n APPLY_ABSTRACTION; [line 18]\n " shape="box"] + + + 6 -> 5 ; +5 [label="5: Exit div0_class \n " color=yellow style=filled] + + +4 [label="4: Start div0_class\nFormals: \nLocals: \n DECLARE_LOCALS(&return); [line 17]\n " color=yellow style=filled] + + + 4 -> 6 ; +3 [label="3: Return Stmt \n n$0=*&a:int [line 15]\n *&return:int =(1 / n$0) [line 15]\n REMOVE_TEMPS(n$0); [line 15]\n NULLIFY(&a,false); [line 15]\n APPLY_ABSTRACTION; [line 15]\n " shape="box"] + + + 3 -> 2 ; +2 [label="2: Exit A_fun \n " color=yellow style=filled] + + +1 [label="1: Start A_fun\nFormals: a:int \nLocals: \n DECLARE_LOCALS(&return); [line 15]\n " color=yellow style=filled] + + + 1 -> 3 ; +} diff --git a/infer/tests/endtoend/cpp/StaticMethodTest.java b/infer/tests/endtoend/cpp/StaticMethodTest.java new file mode 100644 index 000000000..252db4d62 --- /dev/null +++ b/infer/tests/endtoend/cpp/StaticMethodTest.java @@ -0,0 +1,64 @@ +/* +* 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. +*/ + +package endtoend.cpp; + +import static org.hamcrest.MatcherAssert.assertThat; +import static utils.matchers.ResultContainsExactly.containsExactly; + +import com.google.common.collect.ImmutableList; + +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; + +import java.io.IOException; + +import utils.DebuggableTemporaryFolder; +import utils.InferException; +import utils.InferResults; +import utils.InferRunner; + +public class StaticMethodTest { + + public static final String FILE = + "infer/tests/codetoanalyze/cpp/frontend/methods/static.cpp"; + + private static ImmutableList inferCmd; + + public static final String DIVIDE_BY_ZERO = "DIVIDE_BY_ZERO"; + + @ClassRule + public static DebuggableTemporaryFolder folder = + new DebuggableTemporaryFolder(); + + @BeforeClass + public static void runInfer() throws InterruptedException, IOException { + inferCmd = InferRunner.createCPPInferCommand(folder, FILE); + } + + @Test + public void whenInferRunsOnDiv0MethodsErrorIsFound() + throws InterruptedException, IOException, InferException { + InferResults inferResults = InferRunner.runInferCPP(inferCmd); + String[] procedures = { + "div0_class", + "div0_instance", + }; + assertThat( + "Results should contain the expected divide by zero", + inferResults, + containsExactly( + DIVIDE_BY_ZERO, + FILE, + procedures + ) + ); + } +} diff --git a/infer/tests/frontend/cpp/MethodsTest.java b/infer/tests/frontend/cpp/MethodsTest.java index 2f1fb07b8..a3222543b 100644 --- a/infer/tests/frontend/cpp/MethodsTest.java +++ b/infer/tests/frontend/cpp/MethodsTest.java @@ -53,4 +53,9 @@ public class MethodsTest { frontendTest("dereference_this.cpp"); } + @Test + public void testStaticDotFilesMatch() + throws InterruptedException, IOException, InferException { + frontendTest("static.cpp"); + } }