Handle C++ static methods

Summary: public
Add support for translation and calling c++ static methods.
They can be called in two ways:
  ClassName::method()
  classInstance.method()

Both of them have the same meaning, but AST produced is different

Reviewed By: dulmarod

Differential Revision: D2636489

fb-gh-sync-id: 9294a3f
master
Andrzej Kotulski 9 years ago committed by facebook-github-bot-7
parent 2e01d3402f
commit 9748502a1a

@ -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 *)

@ -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

@ -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);
}

@ -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 ;
}

@ -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<String> 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
)
);
}
}

@ -53,4 +53,9 @@ public class MethodsTest {
frontendTest("dereference_this.cpp");
}
@Test
public void testStaticDotFilesMatch()
throws InterruptedException, IOException, InferException {
frontendTest("static.cpp");
}
}

Loading…
Cancel
Save