[Frontend][C++] Add support for default params

Summary:
@public
Add support for default function arguments.
As a side change - always create cmethod_signature for a function

Test Plan:
1. Call function with default parameter and confirm that it gets parsed and reports null dereference (B5 but still). It didn't before.
2. Created a test case
master
Andrzej Kotulski 10 years ago
parent d15e60ffbf
commit 9f9bc53361

@ -50,7 +50,7 @@ struct
| ParmVarDecl(decl_info, name, qtype, var_decl_info) -> | ParmVarDecl(decl_info, name, qtype, var_decl_info) ->
Printing.log_out "Adding param '%s' " name; Printing.log_out "Adding param '%s' " name;
Printing.log_out "with pointer %s@." decl_info.Clang_ast_t.di_pointer; Printing.log_out "with pointer %s@." decl_info.Clang_ast_t.di_pointer;
(name, CTypes.get_type qtype) (name, CTypes.get_type qtype, var_decl_info.vdi_init_expr)
| _ -> assert false in | _ -> assert false in
match function_method_decl_info with match function_method_decl_info with
| Func_decl_info (function_decl_info, _) -> | Func_decl_info (function_decl_info, _) ->
@ -58,7 +58,7 @@ struct
| Meth_decl_info (method_decl_info, class_name) -> | Meth_decl_info (method_decl_info, class_name) ->
let pars = list_map par_to_ms_par method_decl_info.Clang_ast_t.omdi_parameters in let pars = list_map par_to_ms_par method_decl_info.Clang_ast_t.omdi_parameters in
if (is_instance_method function_method_decl_info false false) then if (is_instance_method function_method_decl_info false false) then
("self", class_name):: pars ("self", class_name, None):: pars
else pars else pars
let get_return_type function_method_decl_info = let get_return_type function_method_decl_info =
@ -154,6 +154,7 @@ struct
let is_objc_method = is_anonym_block in let is_objc_method = is_anonym_block in
let curr_class = if is_anonym_block then curr_class else CContext.ContextNoCls in let curr_class = if is_anonym_block then curr_class else CContext.ContextNoCls in
let attributes = CMethod_signature.ms_get_attributes ms in let attributes = CMethod_signature.ms_get_attributes ms in
CMethod_signature.add ms;
add_method tenv cg cfg curr_class procname namespace [body] is_objc_method is_instance add_method tenv cg cfg curr_class procname namespace [body] is_objc_method is_instance
captured_vars is_anonym_block fdecl_info.Clang_ast_t.fdi_parameters attributes captured_vars is_anonym_block fdecl_info.Clang_ast_t.fdi_parameters attributes
| None, ms -> | None, ms ->
@ -166,6 +167,7 @@ struct
let method_decl = Meth_decl_info (method_decl_info, class_name) in let method_decl = Meth_decl_info (method_decl_info, class_name) in
let ms = build_method_signature decl_info procname method_decl false false in let ms = build_method_signature decl_info procname method_decl false false in
Printing.log_out " ....Processing implementation for method '%s'\n" (Procname.to_string procname); Printing.log_out " ....Processing implementation for method '%s'\n" (Procname.to_string procname);
CMethod_signature.add ms;
(match method_body_to_translate decl_info ms method_decl_info.Clang_ast_t.omdi_body with (match method_body_to_translate decl_info ms method_decl_info.Clang_ast_t.omdi_body with
| Some body -> | Some body ->
let is_instance = CMethod_signature.ms_is_instance ms in let is_instance = CMethod_signature.ms_is_instance ms in
@ -173,8 +175,7 @@ struct
CMethod_trans.create_local_procdesc cfg tenv ms [body] [] is_instance; CMethod_trans.create_local_procdesc cfg tenv ms [body] [] is_instance;
add_method tenv cg cfg curr_class procname namespace [body] true is_instance [] false add_method tenv cg cfg curr_class procname namespace [body] true is_instance [] false
method_decl_info.Clang_ast_t.omdi_parameters attributes method_decl_info.Clang_ast_t.omdi_parameters attributes
| None -> | None -> ())
CMethod_signature.add ms)
let rec process_one_method_decl tenv cg cfg curr_class namespace dec = let rec process_one_method_decl tenv cg cfg curr_class namespace dec =
match dec with match dec with

@ -8,7 +8,7 @@
type method_signature = { type method_signature = {
_name : Procname.t; _name : Procname.t;
_args : (string * string) list; (* (name, type) *) _args : (string * string * Clang_ast_t.stmt option) list; (* (name, type, default value) *)
_ret_type : string; _ret_type : string;
_attributes : Clang_ast_t.attribute list; _attributes : Clang_ast_t.attribute list;
_loc : Clang_ast_t.source_range; _loc : Clang_ast_t.source_range;
@ -52,7 +52,7 @@ let replace_name_ms ms name =
let ms_to_string ms = let ms_to_string ms =
"Method "^(Procname.to_string ms._name)^" "^ "Method "^(Procname.to_string ms._name)^" "^
(Utils.list_to_string (fun (s1, s2) -> s1^", "^s2) ms._args)^"->"^ms._ret_type^" "^ (Utils.list_to_string (fun (s1, s2, _) -> s1^", "^s2) ms._args)^"->"^ms._ret_type^" "^
Clang_ast_j.string_of_source_range ms._loc Clang_ast_j.string_of_source_range ms._loc
let find ms = let find ms =

@ -16,7 +16,7 @@ val reset_map : unit -> unit
val ms_get_name : method_signature -> Procname.t val ms_get_name : method_signature -> Procname.t
val ms_get_args : method_signature -> (string * string) list val ms_get_args : method_signature -> (string * string * Clang_ast_t.stmt option) list
val ms_get_ret_type : method_signature -> string val ms_get_ret_type : method_signature -> string
@ -26,7 +26,7 @@ val ms_get_loc : method_signature -> Clang_ast_t.source_range
val ms_is_instance : method_signature -> bool val ms_is_instance : method_signature -> bool
val make_ms : Procname.t -> (string * string) list -> string -> Clang_ast_t.attribute list -> val make_ms : Procname.t -> (string * string * Clang_ast_t.stmt option) list -> string -> Clang_ast_t.attribute list ->
Clang_ast_t.source_range -> bool -> method_signature Clang_ast_t.source_range -> bool -> method_signature
val replace_name_ms : method_signature -> Procname.t -> method_signature val replace_name_ms : method_signature -> Procname.t -> method_signature

@ -87,7 +87,7 @@ let get_formal_parameters tenv ms =
let rec defined_parameters pl = let rec defined_parameters pl =
match pl with match pl with
| [] -> [] | [] -> []
| (name, raw_type):: pl' -> | (name, raw_type, _):: pl' ->
let qt = let qt =
if (name = CFrontend_config.self && CMethod_signature.ms_is_instance ms) then if (name = CFrontend_config.self && CMethod_signature.ms_is_instance ms) then
(Ast_expressions.create_pointer_type raw_type) (Ast_expressions.create_pointer_type raw_type)

@ -539,7 +539,28 @@ struct
(* verbatim from a call to a different function, and they might be side-effecting *) (* verbatim from a call to a different function, and they might be side-effecting *)
(Procname.to_string pn) <> CFrontend_config.builtin_object_size (Procname.to_string pn) <> CFrontend_config.builtin_object_size
| _ -> true in | _ -> true in
let params_stmt = if should_translate_args then params_stmt else [] in
let assign_default_params params_stmt =
match callee_pname_opt with
| None -> params_stmt
| Some callee_pname ->
try
let callee_ms = CMethod_signature.find callee_pname in
let args = CMethod_signature.ms_get_args callee_ms in
let params_args = list_combine params_stmt args in
let replace_default_arg param =
match param with
| CXXDefaultArgExpr(_, _, _), (_, _, Some default_instr) -> default_instr
| instr, _ -> instr in
list_map replace_default_arg params_args
with
| Invalid_argument _ ->
(* list_combine failed because of different list lengths *)
Printing.log_err "Param count doesn't match %s\n" (Procname.to_string callee_pname);
params_stmt
| Not_found -> params_stmt in
let params_stmt = if should_translate_args then assign_default_params params_stmt else [] in
let res_trans_par = let res_trans_par =
let l = list_map (fun i -> exec_with_self_exception instruction trans_state_param i) params_stmt in let l = list_map (fun i -> exec_with_self_exception instruction trans_state_param i) params_stmt in
let rt = collect_res_trans (res_trans_callee :: l) in let rt = collect_res_trans (res_trans_callee :: l) in

@ -130,7 +130,7 @@ let get_predefined_ms_method condition class_name method_name mk_procname
let get_predefined_ms_stringWithUTF8String class_name method_name mk_procname = let get_predefined_ms_stringWithUTF8String class_name method_name mk_procname =
let condition = class_name = nsstring_cl && method_name = string_with_utf8_m in let condition = class_name = nsstring_cl && method_name = string_with_utf8_m in
get_predefined_ms_method condition class_name method_name mk_procname [("x", "char *")] get_predefined_ms_method condition class_name method_name mk_procname [("x", "char *", None)]
id_cl [] None id_cl [] 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 =
@ -139,17 +139,17 @@ let get_predefined_ms_retain_release class_name method_name mk_procname =
if is_retain_method method_name || is_autorelease_method method_name if is_retain_method method_name || is_autorelease_method method_name
then id_cl else void in then id_cl else void in
get_predefined_ms_method condition nsobject_cl method_name mk_procname get_predefined_ms_method condition nsobject_cl method_name mk_procname
[(self, class_name)] return_type [] (get_builtinname method_name) [(self, class_name, None)] 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 =
let condition = (method_name = init) && (class_name = nsautorelease_pool_cl) in let condition = (method_name = init) && (class_name = nsautorelease_pool_cl) in
get_predefined_ms_method condition class_name method_name mk_procname get_predefined_ms_method condition class_name method_name mk_procname
[(self, class_name)] void [] None [(self, class_name, None)] void [] 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 =
let condition = (method_name = release || method_name = drain) && let condition = (method_name = release || method_name = drain) &&
(class_name = nsautorelease_pool_cl) in (class_name = nsautorelease_pool_cl) in
get_predefined_ms_method condition class_name method_name mk_procname [(self, class_name)] get_predefined_ms_method condition class_name method_name mk_procname [(self, class_name, None)]
void [] (Some SymExec.ModelBuiltins.__objc_release_autorelease_pool) void [] (Some SymExec.ModelBuiltins.__objc_release_autorelease_pool)
let get_predefined_model_method_signature class_name method_name mk_procname = let get_predefined_model_method_signature class_name method_name mk_procname =

@ -0,0 +1,18 @@
int fun_default(int a = 3, int b = 5) {
return a + b;
}
int fun_default_decl(int a, int b = 5);
// note that b is default param, but function was declared earlier
int fun_default_decl(int a, int b) {
return a + b;
}
void test() {
fun_default(1, 2);
fun_default(1);
fun_default();
fun_default_decl(6);
fun_default_decl(6,6);
}

@ -0,0 +1,51 @@
digraph iCFG {
13 [label="13: Call _fun_fun_default \n n$8=_fun_fun_default(1:int ,2:int ) [line 12]\n REMOVE_TEMPS(n$8); [line 12]\n " shape="box"]
13 -> 12 ;
12 [label="12: Call _fun_fun_default \n n$7=_fun_fun_default(1:int ,5:int ) [line 13]\n REMOVE_TEMPS(n$7); [line 13]\n " shape="box"]
12 -> 11 ;
11 [label="11: Call _fun_fun_default \n n$6=_fun_fun_default(3:int ,5:int ) [line 14]\n REMOVE_TEMPS(n$6); [line 14]\n " shape="box"]
11 -> 10 ;
10 [label="10: Call _fun_fun_default_decl \n n$5=_fun_fun_default_decl(6:int ,5:int ) [line 16]\n REMOVE_TEMPS(n$5); [line 16]\n " shape="box"]
10 -> 9 ;
9 [label="9: Call _fun_fun_default_decl \n n$4=_fun_fun_default_decl(6:int ,6:int ) [line 17]\n REMOVE_TEMPS(n$4); [line 17]\n APPLY_ABSTRACTION; [line 17]\n " shape="box"]
9 -> 8 ;
8 [label="8: Exit test \n " color=yellow style=filled]
7 [label="7: Start test\nFormals: \nLocals: \n DECLARE_LOCALS(&return); [line 11]\n " color=yellow style=filled]
7 -> 13 ;
6 [label="6: Return Stmt \n n$2=*&a:int [line 8]\n n$3=*&b:int [line 8]\n *&return:int =(n$2 + n$3) [line 8]\n REMOVE_TEMPS(n$2,n$3); [line 8]\n NULLIFY(&a,false); [line 8]\n NULLIFY(&b,false); [line 8]\n APPLY_ABSTRACTION; [line 8]\n " shape="box"]
6 -> 5 ;
5 [label="5: Exit fun_default_decl \n " color=yellow style=filled]
4 [label="4: Start fun_default_decl\nFormals: a:int b:int \nLocals: \n DECLARE_LOCALS(&return); [line 7]\n " color=yellow style=filled]
4 -> 6 ;
3 [label="3: Return Stmt \n n$0=*&a:int [line 2]\n n$1=*&b:int [line 2]\n *&return:int =(n$0 + n$1) [line 2]\n REMOVE_TEMPS(n$0,n$1); [line 2]\n NULLIFY(&a,false); [line 2]\n NULLIFY(&b,false); [line 2]\n APPLY_ABSTRACTION; [line 2]\n " shape="box"]
3 -> 2 ;
2 [label="2: Exit fun_default \n " color=yellow style=filled]
1 [label="1: Start fun_default\nFormals: a:int b:int \nLocals: \n DECLARE_LOCALS(&return); [line 1]\n " color=yellow style=filled]
1 -> 3 ;
}

@ -0,0 +1,47 @@
/*
* Copyright (c) 2013- Facebook.
* All rights reserved.
*/
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 FunctionsTest {
@Rule
public DebuggableTemporaryFolder folder = new DebuggableTemporaryFolder();
@Test
public void whenCaptureRunCommaThenDotFilesAreTheSame()
throws InterruptedException, IOException, InferException {
String literal_src =
"infer/tests/codetoanalyze/cpp/frontend/types/functions.cpp";
String literal_dotty =
"infer/tests/codetoanalyze/cpp/frontend/types/functions.dot";
ImmutableList<String> inferCmd =
InferRunner.createCPPInferCommandFrontend(
folder,
literal_src);
File newDotFile = InferRunner.runInferFrontend(inferCmd);
assertThat(
"In the capture of " + literal_src +
" the dotty files should be the same.",
newDotFile, dotFileEqualTo(literal_dotty));
}
}
Loading…
Cancel
Save