From de1a6273356dbb1ecb3e4a84ef74cfbfbbe94568 Mon Sep 17 00:00:00 2001 From: Dulma Rodriguez Date: Thu, 12 Nov 2015 04:17:25 -0800 Subject: [PATCH] Translate enums as ints and not add them to the tenv Summary: public This diff changes the way we treat enums in Infer. 1. The semantics of the translation is now correct, it was a bit incorrect before. 2. We don't add the enum types to the tenv anymore, which saves a lot of disk space and avoids errors in the backend dealing with the enum type. Reviewed By: akotulski Differential Revision: D2641903 fb-gh-sync-id: 6295e5f --- infer/src/clang/cArithmetic_trans.ml | 6 + infer/src/clang/cArithmetic_trans.mli | 2 + infer/src/clang/cEnum_decl.ml | 80 +++++------ infer/src/clang/cEnum_decl.mli | 4 +- infer/src/clang/cFrontend.ml | 9 +- infer/src/clang/cFrontend_config.ml | 3 + infer/src/clang/cFrontend_config.mli | 3 + infer/src/clang/cFrontend_utils.ml | 17 +++ infer/src/clang/cFrontend_utils.mli | 6 + infer/src/clang/cTrans.ml | 124 ++++++++++-------- infer/src/clang/cTrans.mli | 4 - infer/src/clang/cType_to_sil_type.ml | 21 ++- infer/src/clang/cTypes_decl.ml | 1 + .../c/frontend/enumeration/enum.c | 12 +- .../c/frontend/enumeration/enum.c.dot | 12 +- .../c/frontend/enumeration/enum_bitmask.c | 18 +++ .../c/frontend/enumeration/enum_bitmask.c.dot | 17 +++ .../c/frontend/enumeration/other_enum.c | 28 ++++ .../c/frontend/enumeration/other_enum.c.dot | 77 +++++++++++ infer/tests/endtoend/c/EnumTest.java | 62 +++++++++ infer/tests/frontend/c/EnumerationTest.java | 14 ++ 21 files changed, 384 insertions(+), 136 deletions(-) create mode 100644 infer/tests/codetoanalyze/c/frontend/enumeration/enum_bitmask.c create mode 100644 infer/tests/codetoanalyze/c/frontend/enumeration/enum_bitmask.c.dot create mode 100644 infer/tests/codetoanalyze/c/frontend/enumeration/other_enum.c create mode 100644 infer/tests/codetoanalyze/c/frontend/enumeration/other_enum.c.dot create mode 100644 infer/tests/endtoend/c/EnumTest.java diff --git a/infer/src/clang/cArithmetic_trans.ml b/infer/src/clang/cArithmetic_trans.ml index ed31970ce..a174a0233 100644 --- a/infer/src/clang/cArithmetic_trans.ml +++ b/infer/src/clang/cArithmetic_trans.ml @@ -217,3 +217,9 @@ let bin_op_to_string boi = | `XorAssign -> "XorAssign" | `OrAssign -> "OrAssign" | `Comma -> "Comma" + +let sil_const_plus_one const = + match const with + | Sil.Const (Sil.Cint n) -> + Sil.Const (Sil.Cint (Sil.Int.add n Sil.Int.one)) + | _ -> Sil.BinOp (Sil.PlusA, const, Sil.Const (Sil.Cint (Sil.Int.one))) diff --git a/infer/src/clang/cArithmetic_trans.mli b/infer/src/clang/cArithmetic_trans.mli index 13afea2e6..3c9aac48c 100644 --- a/infer/src/clang/cArithmetic_trans.mli +++ b/infer/src/clang/cArithmetic_trans.mli @@ -25,3 +25,5 @@ val compound_assignment_binary_operation_instruction : Clang_ast_t.binary_operat val assignment_arc_mode : CContext.t -> Sil.exp -> Sil.typ -> Sil.exp -> Location.t -> bool -> bool -> Sil.exp * Sil.instr list * Ident.t list + +val sil_const_plus_one : Sil.exp -> Sil.exp diff --git a/infer/src/clang/cEnum_decl.ml b/infer/src/clang/cEnum_decl.ml index 8bab57c39..2d2b2da89 100644 --- a/infer/src/clang/cEnum_decl.ml +++ b/infer/src/clang/cEnum_decl.ml @@ -13,52 +13,40 @@ open Utils open CFrontend_utils -let create_empty_procdesc () = - let proc_name = Procname.from_string_c_fun "__INFER_$GLOBAL_VAR_env" in - let proc_attributes = ProcAttributes.default proc_name Config.C_CPP in - Cfg.Procdesc.create { - Cfg.Procdesc.cfg = Cfg.Node.create_cfg (); - proc_attributes = proc_attributes; - } +(*Check if the constant is in the map, in which case that means that all the *) +(* contants of this enum are in the map, by invariant. Otherwise, add the constant *) +(* to the map. *) +let add_enum_constant_to_map_if_needed decl_pointer pred_decl_opt = + try + ignore (Ast_utils.get_enum_constant_exp decl_pointer); + true + with Not_found -> + Ast_utils.add_enum_constant decl_pointer pred_decl_opt; + false -(* We will use global_procdesc for storing global variables. *) -(* Globals will be stored as locals in global_procdesc and they are added*) -(* when traversing the AST. *) -let global_procdesc = ref (create_empty_procdesc ()) +(* Add the constants of this enum to the map if they are not in the map yet *) +let enum_decl decl = + let open Clang_ast_t in + let get_constant_decl_ptr decl = + match decl with + | EnumConstantDecl (decl_info, _, _, _) -> decl_info.di_pointer + | _ -> assert false in + let rec add_enum_constants_to_map decl_list = + match decl_list with + | decl :: pred_decl :: rest -> + let decl_pointer = get_constant_decl_ptr decl in + let pred_decl_pointer = get_constant_decl_ptr pred_decl in + if not (add_enum_constant_to_map_if_needed decl_pointer (Some pred_decl_pointer)) then + add_enum_constants_to_map (pred_decl::rest) + | [decl] -> + let decl_pointer = get_constant_decl_ptr decl in + ignore (add_enum_constant_to_map_if_needed decl_pointer None) + | _ -> () in + match decl with + | EnumDecl (decl_info, _, _, type_ptr, decl_list, _, _) -> + add_enum_constants_to_map (IList.rev decl_list); + let sil_type = Sil.Tint Sil.IInt in + Ast_utils.update_sil_types_map type_ptr sil_type; + sil_type -let rec get_enum_constants context decl_list v = - match decl_list with - | [] -> [] - | Clang_ast_t.EnumConstantDecl (decl_info, name_info, type_ptr, enum_constant_decl_info) :: decl_list' -> - let name = name_info.Clang_ast_t.ni_name in - (match enum_constant_decl_info.Clang_ast_t.ecdi_init_expr with - | None -> Printing.log_out "%s" (" ...Defining Enum Constant ("^name^", "^(string_of_int v)); - (Mangled.from_string name, Sil.Cint (Sil.Int.of_int v)) - :: get_enum_constants context decl_list' (v + 1) - | Some stmt -> - let e = CGen_trans.CTransImpl.expression_trans context stmt - "WARNING: Expression in Enumeration constant not found\n" in - let const = (match Prop.exp_normalize_prop Prop.prop_emp e with - | Sil.Const c -> c - | _ -> (* This is a hack to avoid failing in some strange definition of Enum *) - Sil.Cint Sil.Int.zero) in - Printing.log_out " ...Defining Enum Constant ('%s', " name; - Printing.log_out "'%s')\n" (Sil.exp_to_string (Sil.Const const)); - (Mangled.from_string name, const) :: get_enum_constants context decl_list' v) | _ -> assert false - -let enum_decl name tenv cfg cg namespace type_ptr decl_list opt_type = - Printing.log_out "ADDING: EnumDecl '%s'\n" name; - let context' = - CContext.create_context tenv cg cfg !global_procdesc namespace CContext.ContextNoCls - false None in - let enum_constants = get_enum_constants context' decl_list 0 in - let name = (match opt_type with (* If the type is defined it's of the kind "enum name" and we take that.*) - | `Type s -> s - | `NoType -> assert false) in - (* Here we could give "enum "^name but I want to check that this the type is always defined *) - let typename = CTypes.mk_enumname name in - let typ = Sil.Tenum enum_constants in - Ast_utils.update_sil_types_map type_ptr typ; - Printing.log_out " TN_typename('%s')\n" (Sil.typename_to_string typename); - Sil.tenv_add tenv typename typ diff --git a/infer/src/clang/cEnum_decl.mli b/infer/src/clang/cEnum_decl.mli index 9cf37ec43..9c07ab849 100644 --- a/infer/src/clang/cEnum_decl.mli +++ b/infer/src/clang/cEnum_decl.mli @@ -9,6 +9,6 @@ (** Translate an enumeration declaration by adding it to the tenv and *) (** translating the code and adding it to a fake procdesc *) +open CFrontend_utils -val enum_decl : string -> Sil.tenv -> Cfg.cfg -> Cg.t -> string option -> Clang_ast_t.type_ptr -> - Clang_ast_t.decl list -> Clang_ast_t.opt_type -> unit +val enum_decl : Clang_ast_t.decl -> Sil.typ diff --git a/infer/src/clang/cFrontend.ml b/infer/src/clang/cFrontend.ml index 1b8b0019a..efaf91401 100644 --- a/infer/src/clang/cFrontend.ml +++ b/infer/src/clang/cFrontend.ml @@ -29,7 +29,6 @@ let rec translate_one_declaration tenv cg cfg namespace parent_dec dec = CLocation.update_curr_file info; let source_range = info.Clang_ast_t.di_source_range in let should_translate_decl = CLocation.should_translate_lib source_range in - let should_translate_enum = CLocation.should_translate_enum source_range in let open Clang_ast_t in match dec with | FunctionDecl(di, name_info, tp, fdecl_info) when should_translate_decl -> @@ -99,10 +98,7 @@ let rec translate_one_declaration tenv cg cfg namespace parent_dec dec = | Some dec -> Printing.log_stats "Methods of %s skipped\n" (Ast_utils.string_of_decl dec) | None -> ()) - | EnumDecl(decl_info, name_info, opt_type, pointer, decl_list, decl_context_info, enum_decl_info) - when should_translate_enum -> - let name = name_info.Clang_ast_t.ni_name in - CEnum_decl.enum_decl name tenv cfg cg namespace pointer decl_list opt_type + | EnumDecl _ -> ignore (CEnum_decl.enum_decl dec) | LinkageSpecDecl(decl_info, decl_list, decl_context_info) -> Printing.log_out "ADDING: LinkageSpecDecl decl list\n"; @@ -112,8 +108,7 @@ let rec translate_one_declaration tenv cg cfg namespace parent_dec dec = IList.iter (translate_one_declaration tenv cg cfg (Some name) dec) decl_list | EmptyDecl _ -> Printing.log_out "Passing from EmptyDecl. Treated as skip\n"; - | dec -> - Printing.log_stats "\nWARNING: found Declaration %s skipped\n" (Ast_utils.string_of_decl dec) + | dec -> () (* Translates a file by translating the ast into a cfg. *) let compute_icfg tenv source_file ast = diff --git a/infer/src/clang/cFrontend_config.ml b/infer/src/clang/cFrontend_config.ml index 700cb8d30..eaee22d3d 100644 --- a/infer/src/clang/cFrontend_config.ml +++ b/infer/src/clang/cFrontend_config.ml @@ -156,6 +156,9 @@ let pointer_type_index = ref Clang_ast_main.PointerMap.empty (* Map from type pointers or declaration pointers to sil types *) let sil_types_map = ref Clang_ast_types.TypePointerMap.empty +(* Map from enum constants pointers to their predecesor and their sil value *) +let enum_map = ref Clang_ast_main.PointerMap.empty + let type_pointer_prefix = "internal_type" let nsarray_cl = "NSArray" diff --git a/infer/src/clang/cFrontend_config.mli b/infer/src/clang/cFrontend_config.mli index ca3d5c032..9ae2bae49 100644 --- a/infer/src/clang/cFrontend_config.mli +++ b/infer/src/clang/cFrontend_config.mli @@ -151,6 +151,9 @@ val pointer_type_index : Clang_ast_t.c_type Clang_ast_main.PointerMap.t ref Populated during frontend execution when new type is found *) val sil_types_map : (Sil.typ Clang_ast_types.TypePointerMap.t) ref +(** Map from enum constants pointers to their predecesor and their sil value *) +val enum_map : (Clang_ast_t.pointer option * Sil.exp option) Clang_ast_main.PointerMap.t ref + val type_pointer_prefix : string val nsarray_cl : string diff --git a/infer/src/clang/cFrontend_utils.ml b/infer/src/clang/cFrontend_utils.ml index 2c2138020..30f142815 100644 --- a/infer/src/clang/cFrontend_utils.ml +++ b/infer/src/clang/cFrontend_utils.ml @@ -286,6 +286,23 @@ struct CFrontend_config.sil_types_map := Clang_ast_types.TypePointerMap.add type_ptr sil_type !CFrontend_config.sil_types_map + let update_enum_map enum_constant_pointer sil_exp = + let open Clang_ast_main in + let (predecessor_pointer_opt, sil_exp_opt) = + try PointerMap.find enum_constant_pointer !CFrontend_config.enum_map + with Not_found -> assert false in + let enum_map_value = (predecessor_pointer_opt, Some sil_exp) in + CFrontend_config.enum_map := + PointerMap.add enum_constant_pointer enum_map_value !CFrontend_config.enum_map + + let add_enum_constant enum_constant_pointer predecessor_pointer_opt = + let enum_map_value = (predecessor_pointer_opt, None) in + CFrontend_config.enum_map := + Clang_ast_main.PointerMap.add enum_constant_pointer enum_map_value !CFrontend_config.enum_map + + let get_enum_constant_exp enum_constant_pointer = + Clang_ast_main.PointerMap.find enum_constant_pointer !CFrontend_config.enum_map + let get_type type_ptr = try (* There is chance for success only if type_ptr is in fact clang pointer *) diff --git a/infer/src/clang/cFrontend_utils.mli b/infer/src/clang/cFrontend_utils.mli index 7f571e768..33f294449 100644 --- a/infer/src/clang/cFrontend_utils.mli +++ b/infer/src/clang/cFrontend_utils.mli @@ -81,6 +81,12 @@ sig val update_sil_types_map : Clang_ast_t.type_ptr -> Sil.typ -> unit + val update_enum_map : Clang_ast_t.pointer -> Sil.exp -> unit + + val add_enum_constant : Clang_ast_t.pointer -> Clang_ast_t.pointer option -> unit + + val get_enum_constant_exp : Clang_ast_t.pointer -> Clang_ast_t.pointer option * Sil.exp option + (** creates a string to append to a name from a list of qualifiers to a name *) val get_qualifier_string : Clang_ast_t.named_decl_info -> string diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index e287cd2b8..20069c4ec 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -25,10 +25,6 @@ module type CTrans = sig val instructions_trans : CContext.t -> Clang_ast_t.stmt list -> CModule_type.instr_type list -> Cfg.Node.t -> Cfg.Node.t list - (** It receives the context and a statement and a warning string and returns the translated sil expression *) - (** that represents the translation of the stmts into sil. *) - val expression_trans : CContext.t -> Clang_ast_t.stmt -> string -> Sil.exp - end module CTrans_funct(M: CModule_type.CMethod_declaration) : CTrans = @@ -125,7 +121,7 @@ struct if pred_exit = [] then [Sil.Nullify(block_var, loc, true)] else (IList.iter (fun n -> let loc = Cfg.Node.get_loc n in - Cfg.Node.append_instrs_temps n [Sil.Nullify(block_var, loc, true)] []) pred_exit; + Cfg.Node.append_instrs_temps n [Sil.Nullify(block_var, loc, true)] []) pred_exit; []) in let set_instr = Sil.Set(Sil.Lvar block_var, block_type, Sil.Var id_block, loc) in let ids, captured_instrs = IList.split (IList.map (fun (vname, typ, _) -> @@ -302,24 +298,6 @@ struct let root_node' = GotoLabel.find_goto_label trans_state.context label_name sil_loc in { empty_res_trans with root_nodes = [root_node']; leaf_nodes = trans_state.succ_nodes } - let enum_constant_trans trans_state decl_ref = - let open CContext in - let context = trans_state.context in - let name_info, _ , type_ptr = get_info_from_decl_ref decl_ref in - let name = name_info.Clang_ast_t.ni_name in - let typ = CTypes_decl.type_ptr_to_sil_type context.tenv type_ptr in - let const_exp = match CTypes.search_enum_type_by_name context.tenv name with - | Some v -> - let ce = Sil.Const v in - Printing.log_out " ....Found enum constant '%s', " name; - Printing.log_out "replacing with integer '%s' \n" (Sil.exp_to_string ce); ce - | None -> - Printing.log_out - "WARNING: Found enum constant '%s', but its value was not found in the tenv.\n" - name; - (Sil.Const(Sil.Cint Sil.Int.zero)) in - { root_nodes = []; leaf_nodes = []; ids = []; instrs = []; exps = [(const_exp, typ)]} - let function_deref_trans trans_state decl_ref = let open CContext in let context = trans_state.context in @@ -413,7 +391,34 @@ struct (* TODO for static methods we shouldn't return (obj_sil, class_typ) *) { pre_trans_result with exps = [method_exp; (obj_sil, class_typ)] } - let decl_ref_trans trans_state pre_trans_result stmt_info expr_info decl_ref = + let cxxThisExpr_trans trans_state stmt_info expr_info = + let context = trans_state.context in + let pln = trans_state.parent_line_number in + let sil_loc = CLocation.get_sil_location stmt_info pln context in + let tp = expr_info.Clang_ast_t.ei_type_ptr in + let procname = Cfg.Procdesc.get_proc_name context.CContext.procdesc in + let name = CFrontend_config.this in + let pvar = Sil.mk_pvar (Mangled.from_string name) procname in + let exp = Sil.Lvar pvar in + let typ = CTypes_decl.type_ptr_to_sil_type context.CContext.tenv tp in + let exps = [(exp, typ)] in + (* there is no cast operation in AST, but backend needs it *) + dereference_value_from_result sil_loc { empty_res_trans with exps = exps } + + let rec labelStmt_trans trans_state stmt_info stmt_list label_name = + (* go ahead with the translation *) + let res_trans = match stmt_list with + | [stmt] -> + instruction trans_state stmt + | _ -> assert false (* expected a stmt or at most a compoundstmt *) in + (* create the label root node into the hashtbl *) + let sil_loc = + CLocation.get_sil_location stmt_info trans_state.parent_line_number trans_state.context in + let root_node' = GotoLabel.find_goto_label trans_state.context label_name sil_loc in + Cfg.Node.set_succs_exn root_node' res_trans.root_nodes []; + { empty_res_trans with root_nodes = [root_node']; leaf_nodes = trans_state.succ_nodes } + + and decl_ref_trans trans_state pre_trans_result stmt_info expr_info decl_ref = let open CContext in Printing.log_out " priority node free = '%s'\n@." (string_of_bool (PriorityNode.is_priority_free trans_state)); @@ -432,7 +437,7 @@ struct decl_ref.Clang_ast_t.dr_decl_pointer in print_error decl_kind; assert false - let declRefExpr_trans trans_state stmt_info expr_info decl_ref_expr_info e = + and declRefExpr_trans trans_state stmt_info expr_info decl_ref_expr_info e = let open CContext in Printing.log_out " priority node free = '%s'\n@." (string_of_bool (PriorityNode.is_priority_free trans_state)); @@ -441,31 +446,42 @@ struct | None -> assert false in decl_ref_trans trans_state empty_res_trans stmt_info expr_info decl_ref - let cxxThisExpr_trans trans_state stmt_info expr_info = - let context = trans_state.context in - let pln = trans_state.parent_line_number in - let sil_loc = CLocation.get_sil_location stmt_info pln context in - let tp = expr_info.Clang_ast_t.ei_type_ptr in - let procname = Cfg.Procdesc.get_proc_name context.CContext.procdesc in - let name = CFrontend_config.this in - let pvar = Sil.mk_pvar (Mangled.from_string name) procname in - let exp = Sil.Lvar pvar in - let typ = CTypes_decl.type_ptr_to_sil_type context.CContext.tenv tp in - let exps = [(exp, typ)] in - (* there is no cast operation in AST, but backend needs it *) - dereference_value_from_result sil_loc { empty_res_trans with exps = exps } + (* evaluates an enum constant *) + and enum_const_eval context enum_constant_pointer prev_enum_constant_opt zero = + match Ast_utils.get_decl enum_constant_pointer with + | Some Clang_ast_t.EnumConstantDecl (_, _, _, enum_constant_decl_info) -> + (match enum_constant_decl_info.Clang_ast_t.ecdi_init_expr with + | Some stmt -> + expression_trans context stmt + "WARNING: Expression in Enumeration constant not found\n" + | None -> + match prev_enum_constant_opt with + | Some prev_constant_pointer -> + let previous_exp = get_enum_constant_expr context prev_constant_pointer in + CArithmetic_trans.sil_const_plus_one previous_exp + | None -> zero) + | _ -> zero + + (* get the sil value of the enum constant from the map or by evaluating it *) + and get_enum_constant_expr context enum_constant_pointer = + let zero = Sil.Const (Sil.Cint Sil.Int.zero) in + try + let (prev_enum_constant_opt, sil_exp_opt) = + Ast_utils.get_enum_constant_exp enum_constant_pointer in + match sil_exp_opt with + | Some exp -> exp + | None -> + let exp = enum_const_eval context enum_constant_pointer prev_enum_constant_opt zero in + Ast_utils.update_enum_map enum_constant_pointer exp; + exp + with Not_found -> zero - let rec labelStmt_trans trans_state stmt_info stmt_list label_name = - (* go ahead with the translation *) - let res_trans = match stmt_list with - | [stmt] -> - instruction trans_state stmt - | _ -> assert false (* expected a stmt or at most a compoundstmt *) in - (* create the label root node into the hashtbl *) - let sil_loc = CLocation.get_sil_location stmt_info trans_state.parent_line_number trans_state.context in - let root_node' = GotoLabel.find_goto_label trans_state.context label_name sil_loc in - Cfg.Node.set_succs_exn root_node' res_trans.root_nodes []; - { empty_res_trans with root_nodes = [root_node']; leaf_nodes = trans_state.succ_nodes } + and enum_constant_trans trans_state decl_ref = + let context = trans_state.context in + let _, _, type_ptr = get_info_from_decl_ref decl_ref in + let typ = CTypes_decl.type_ptr_to_sil_type context.CContext.tenv type_ptr in + let const_exp = get_enum_constant_expr context decl_ref.Clang_ast_t.dr_decl_pointer in + { empty_res_trans with exps = [(const_exp, typ)] } and arraySubscriptExpr_trans trans_state stmt_info expr_info stmt_list = let typ = CTypes_decl.get_type_from_expr_info expr_info trans_state.context.CContext.tenv in @@ -553,9 +569,9 @@ struct let instrs_after_assign, assign_ids, exp_to_parent = if (is_binary_assign_op binary_operator_info) - (* assignment operator result is lvalue in CPP, rvalue in C, hence the difference *) - && (not (General_utils.is_cpp_translation !CFrontend_config.language)) - && ((not creating_node) || (is_return_temp trans_state.continuation)) then ( + (* assignment operator result is lvalue in CPP, rvalue in C, hence the difference *) + && (not (General_utils.is_cpp_translation !CFrontend_config.language)) + && ((not creating_node) || (is_return_temp trans_state.continuation)) then ( (* We are in this case when an assignment is inside *) (* another operator that creates a node. Eg. another *) (* assignment. *) @@ -613,7 +629,7 @@ struct Printing.log_out " |nodes_e2|=%s .\n" (string_of_int (IList.length res_trans_e2.root_nodes)); IList.iter (fun id -> Printing.log_out " ... '%s'\n" - (Ident.to_string id)) ids_to_ancestor; + (Ident.to_string id)) ids_to_ancestor; { root_nodes = root_nodes_to_ancestor; leaf_nodes = leaf_nodes_to_ancestor; ids = ids_to_ancestor; @@ -2157,7 +2173,7 @@ struct and instructions trans_state stmt_list = exec_trans_instrs trans_state (get_clang_stmt_trans stmt_list) - let expression_trans context stmt warning = + and expression_trans context stmt warning = let trans_state = { context = context; succ_nodes =[]; continuation = None; parent_line_number = -1; priority = Free } in let res_trans_stmt = instruction trans_state stmt in fst (CTrans_utils.extract_exp_from_list res_trans_stmt.exps warning) diff --git a/infer/src/clang/cTrans.mli b/infer/src/clang/cTrans.mli index 3a2e2c003..b4e99a582 100644 --- a/infer/src/clang/cTrans.mli +++ b/infer/src/clang/cTrans.mli @@ -16,10 +16,6 @@ module type CTrans = sig val instructions_trans : CContext.t -> Clang_ast_t.stmt list -> CModule_type.instr_type list -> Cfg.Node.t -> Cfg.Node.t list - (** It receives the context and a statement and a warning string and returns the translated sil expression *) - (** that represents the translation of the stmts into sil. *) - val expression_trans : CContext.t -> Clang_ast_t.stmt -> string -> Sil.exp - end diff --git a/infer/src/clang/cType_to_sil_type.ml b/infer/src/clang/cType_to_sil_type.ml index bad957b54..41a4d8486 100644 --- a/infer/src/clang/cType_to_sil_type.ml +++ b/infer/src/clang/cType_to_sil_type.ml @@ -136,17 +136,16 @@ and decl_ptr_to_sil_type translate_decl tenv decl_ptr = | Some (ObjCImplementationDecl _ as d) | Some (ObjCProtocolDecl _ as d) | Some (ObjCCategoryDecl _ as d) - | Some (ObjCCategoryImplDecl _ as d) -> translate_decl tenv None d - | Some (EnumDecl(_, name_info, _, _, _, _, _) ) -> - Sil.Tvar (CTypes.mk_enumname name_info.Clang_ast_t.ni_name) - | Some _ -> - Printing.log_err "Warning: Wrong decl found for pointer %s " - (Clang_ast_j.string_of_pointer decl_ptr); - Sil.Tvoid - | None -> - Printing.log_err "Warning: Decl pointer %s not found." - (Clang_ast_j.string_of_pointer decl_ptr); - Sil.Tvoid + | Some (ObjCCategoryImplDecl _ as d) + | Some (EnumDecl _ as d) -> translate_decl tenv None d + | Some _ -> + Printing.log_err "Warning: Wrong decl found for pointer %s " + (Clang_ast_j.string_of_pointer decl_ptr); + Sil.Tvoid + | None -> + Printing.log_err "Warning: Decl pointer %s not found." + (Clang_ast_j.string_of_pointer decl_ptr); + Sil.Tvoid and clang_type_ptr_to_sil_type translate_decl tenv type_ptr = try diff --git a/infer/src/clang/cTypes_decl.ml b/infer/src/clang/cTypes_decl.ml index a2a2f4d97..fd1dacd4b 100644 --- a/infer/src/clang/cTypes_decl.ml +++ b/infer/src/clang/cTypes_decl.ml @@ -202,6 +202,7 @@ and add_types_from_decl_to_tenv tenv namespace decl = | ObjCProtocolDecl _ -> ObjcProtocol_decl.protocol_decl type_ptr_to_sil_type tenv decl | ObjCCategoryDecl _ -> ObjcCategory_decl.category_decl type_ptr_to_sil_type tenv decl | ObjCCategoryImplDecl _ -> ObjcCategory_decl.category_impl_decl type_ptr_to_sil_type tenv decl + | EnumDecl _ -> CEnum_decl.enum_decl decl | _ -> assert false and type_ptr_to_sil_type tenv tp = diff --git a/infer/tests/codetoanalyze/c/frontend/enumeration/enum.c b/infer/tests/codetoanalyze/c/frontend/enumeration/enum.c index 46e7ba74e..c835dd76d 100644 --- a/infer/tests/codetoanalyze/c/frontend/enumeration/enum.c +++ b/infer/tests/codetoanalyze/c/frontend/enumeration/enum.c @@ -7,14 +7,14 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -enum week{ sunday, monday, tuesday, wednesday=0, thursday, friday, saturday}; +enum week{ sunday, monday, tuesday, wednesday = 0, thursday, friday, saturday}; int main(){ enum week today; - today=wednesday; - today=monday; - today=today+4; - today=(enum week) tuesday+1; - int i = tuesday+(friday-sunday); + today = wednesday; + today = monday; + today = today + 4; + today = (enum week) tuesday + 1; + int i = tuesday + (friday - sunday); return 0; } diff --git a/infer/tests/codetoanalyze/c/frontend/enumeration/enum.c.dot b/infer/tests/codetoanalyze/c/frontend/enumeration/enum.c.dot index c0aeed424..e8288e7af 100644 --- a/infer/tests/codetoanalyze/c/frontend/enumeration/enum.c.dot +++ b/infer/tests/codetoanalyze/c/frontend/enumeration/enum.c.dot @@ -1,21 +1,21 @@ digraph iCFG { -8 [label="8: BinaryOperatorStmt: Assign \n *&today:enum { (sunday, 0) (monday, 1) (tuesday, 2) (wednesday, 0) (thursday, 3) (friday, 4) (saturday, 5) }=0 [line 14]\n NULLIFY(&today,false); [line 14]\n " shape="box"] +8 [label="8: BinaryOperatorStmt: Assign \n *&today:int =0 [line 14]\n NULLIFY(&today,false); [line 14]\n " shape="box"] 8 -> 7 ; -7 [label="7: BinaryOperatorStmt: Assign \n *&today:enum { (sunday, 0) (monday, 1) (tuesday, 2) (wednesday, 0) (thursday, 3) (friday, 4) (saturday, 5) }=1 [line 15]\n " shape="box"] +7 [label="7: BinaryOperatorStmt: Assign \n *&today:int =1 [line 15]\n " shape="box"] 7 -> 6 ; -6 [label="6: BinaryOperatorStmt: Assign \n n$0=*&today:enum { (sunday, 0) (monday, 1) (tuesday, 2) (wednesday, 0) (thursday, 3) (friday, 4) (saturday, 5) } [line 16]\n *&today:enum { (sunday, 0) (monday, 1) (tuesday, 2) (wednesday, 0) (thursday, 3) (friday, 4) (saturday, 5) }=(n$0 + 4) [line 16]\n REMOVE_TEMPS(n$0); [line 16]\n NULLIFY(&today,false); [line 16]\n " shape="box"] +6 [label="6: BinaryOperatorStmt: Assign \n n$0=*&today:int [line 16]\n *&today:int =(n$0 + 4) [line 16]\n REMOVE_TEMPS(n$0); [line 16]\n NULLIFY(&today,false); [line 16]\n " shape="box"] 6 -> 5 ; -5 [label="5: BinaryOperatorStmt: Assign \n *&today:enum { (sunday, 0) (monday, 1) (tuesday, 2) (wednesday, 0) (thursday, 3) (friday, 4) (saturday, 5) }=(2 + 1) [line 17]\n NULLIFY(&today,false); [line 17]\n " shape="box"] +5 [label="5: BinaryOperatorStmt: Assign \n *&today:int =(2 + 1) [line 17]\n NULLIFY(&today,false); [line 17]\n " shape="box"] 5 -> 4 ; -4 [label="4: DeclStmt \n *&i:int =(2 + (4 - 0)) [line 18]\n NULLIFY(&i,false); [line 18]\n " shape="box"] +4 [label="4: DeclStmt \n *&i:int =(2 + (2 - 0)) [line 18]\n NULLIFY(&i,false); [line 18]\n " shape="box"] 4 -> 3 ; @@ -26,7 +26,7 @@ digraph iCFG { 2 [label="2: Exit main \n " color=yellow style=filled] -1 [label="1: Start main\nFormals: \nLocals: i:int today:enum { (sunday, 0) (monday, 1) (tuesday, 2) (wednesday, 0) (thursday, 3) (friday, 4) (saturday, 5) } \n DECLARE_LOCALS(&return,&i,&today); [line 12]\n NULLIFY(&i,false); [line 12]\n NULLIFY(&today,false); [line 12]\n " color=yellow style=filled] +1 [label="1: Start main\nFormals: \nLocals: i:int today:int \n DECLARE_LOCALS(&return,&i,&today); [line 12]\n NULLIFY(&i,false); [line 12]\n NULLIFY(&today,false); [line 12]\n " color=yellow style=filled] 1 -> 8 ; diff --git a/infer/tests/codetoanalyze/c/frontend/enumeration/enum_bitmask.c b/infer/tests/codetoanalyze/c/frontend/enumeration/enum_bitmask.c new file mode 100644 index 000000000..e93f9e24d --- /dev/null +++ b/infer/tests/codetoanalyze/c/frontend/enumeration/enum_bitmask.c @@ -0,0 +1,18 @@ +/* +* 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. +*/ + +typedef enum MyOption { + MyOption1 = 1 << 0, + MyOption2 = 1 << 1, +}; + +int main() { + MyOption option1 = MyOption1; + MyOption option2 = MyOption2; +} diff --git a/infer/tests/codetoanalyze/c/frontend/enumeration/enum_bitmask.c.dot b/infer/tests/codetoanalyze/c/frontend/enumeration/enum_bitmask.c.dot new file mode 100644 index 000000000..e300c49ab --- /dev/null +++ b/infer/tests/codetoanalyze/c/frontend/enumeration/enum_bitmask.c.dot @@ -0,0 +1,17 @@ +digraph iCFG { +4 [label="4: DeclStmt \n *&option1:int =(1 << 0) [line 16]\n NULLIFY(&option1,false); [line 16]\n " shape="box"] + + + 4 -> 3 ; +3 [label="3: DeclStmt \n *&option2:int =(1 << 1) [line 17]\n NULLIFY(&option2,false); [line 17]\n APPLY_ABSTRACTION; [line 17]\n " shape="box"] + + + 3 -> 2 ; +2 [label="2: Exit main \n " color=yellow style=filled] + + +1 [label="1: Start main\nFormals: \nLocals: option2:int option1:int \n DECLARE_LOCALS(&return,&option2,&option1); [line 15]\n NULLIFY(&option1,false); [line 15]\n NULLIFY(&option2,false); [line 15]\n " color=yellow style=filled] + + + 1 -> 4 ; +} diff --git a/infer/tests/codetoanalyze/c/frontend/enumeration/other_enum.c b/infer/tests/codetoanalyze/c/frontend/enumeration/other_enum.c new file mode 100644 index 000000000..94bed6069 --- /dev/null +++ b/infer/tests/codetoanalyze/c/frontend/enumeration/other_enum.c @@ -0,0 +1,28 @@ +/* +* 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. +*/ + +enum Foo { A, B, C = 10, D, E = 1, F, G = F + C }; + +int main() { + enum Foo foo_a = A; + enum Foo foo_b = B; + enum Foo foo_c = C; + enum Foo foo_d = D; + enum Foo foo_e = E; + enum Foo foo_f = F; + enum Foo foo_g = G; +} + +int test() { + enum Foo foo_g = G; + enum Foo foo_a = A; + if (foo_g == 12) + return foo_g/ foo_a; + else return 0; +} diff --git a/infer/tests/codetoanalyze/c/frontend/enumeration/other_enum.c.dot b/infer/tests/codetoanalyze/c/frontend/enumeration/other_enum.c.dot new file mode 100644 index 000000000..980b9f947 --- /dev/null +++ b/infer/tests/codetoanalyze/c/frontend/enumeration/other_enum.c.dot @@ -0,0 +1,77 @@ +digraph iCFG { +19 [label="19: DeclStmt \n *&foo_g:int =(2 + 10) [line 23]\n " shape="box"] + + + 19 -> 18 ; +18 [label="18: DeclStmt \n *&foo_a:int =0 [line 24]\n " shape="box"] + + + 18 -> 13 ; +17 [label="17: Return Stmt \n NULLIFY(&foo_a,false); [line 27]\n NULLIFY(&foo_g,false); [line 27]\n *&return:int =0 [line 27]\n APPLY_ABSTRACTION; [line 27]\n " shape="box"] + + + 17 -> 11 ; +16 [label="16: Return Stmt \n n$1=*&foo_g:int [line 26]\n n$2=*&foo_a:int [line 26]\n *&return:int =(n$1 / n$2) [line 26]\n REMOVE_TEMPS(n$1,n$2); [line 26]\n NULLIFY(&foo_a,false); [line 26]\n NULLIFY(&foo_g,false); [line 26]\n APPLY_ABSTRACTION; [line 26]\n " shape="box"] + + + 16 -> 11 ; +15 [label="15: Prune (false branch) \n PRUNE(((n$0 == 12) == 0), false); [line 25]\n REMOVE_TEMPS(n$0); [line 25]\n " shape="invhouse"] + + + 15 -> 17 ; +14 [label="14: Prune (true branch) \n PRUNE(((n$0 == 12) != 0), true); [line 25]\n REMOVE_TEMPS(n$0); [line 25]\n " shape="invhouse"] + + + 14 -> 16 ; +13 [label="13: BinaryOperatorStmt: EQ \n n$0=*&foo_g:int [line 25]\n " shape="box"] + + + 13 -> 14 ; + 13 -> 15 ; +12 [label="12: + \n NULLIFY(&foo_a,false); [line 25]\n NULLIFY(&foo_g,false); [line 25]\n " ] + + + 12 -> 11 ; +11 [label="11: Exit test \n " color=yellow style=filled] + + +10 [label="10: Start test\nFormals: \nLocals: foo_a:int foo_g:int \n DECLARE_LOCALS(&return,&foo_a,&foo_g); [line 22]\n NULLIFY(&foo_a,false); [line 22]\n NULLIFY(&foo_g,false); [line 22]\n " color=yellow style=filled] + + + 10 -> 19 ; +9 [label="9: DeclStmt \n *&foo_a:int =0 [line 13]\n NULLIFY(&foo_a,false); [line 13]\n " shape="box"] + + + 9 -> 8 ; +8 [label="8: DeclStmt \n *&foo_b:int =1 [line 14]\n NULLIFY(&foo_b,false); [line 14]\n " shape="box"] + + + 8 -> 7 ; +7 [label="7: DeclStmt \n *&foo_c:int =10 [line 15]\n NULLIFY(&foo_c,false); [line 15]\n " shape="box"] + + + 7 -> 6 ; +6 [label="6: DeclStmt \n *&foo_d:int =11 [line 16]\n NULLIFY(&foo_d,false); [line 16]\n " shape="box"] + + + 6 -> 5 ; +5 [label="5: DeclStmt \n *&foo_e:int =1 [line 17]\n NULLIFY(&foo_e,false); [line 17]\n " shape="box"] + + + 5 -> 4 ; +4 [label="4: DeclStmt \n *&foo_f:int =2 [line 18]\n NULLIFY(&foo_f,false); [line 18]\n " shape="box"] + + + 4 -> 3 ; +3 [label="3: DeclStmt \n *&foo_g:int =(2 + 10) [line 19]\n NULLIFY(&foo_g,false); [line 19]\n APPLY_ABSTRACTION; [line 19]\n " shape="box"] + + + 3 -> 2 ; +2 [label="2: Exit main \n " color=yellow style=filled] + + +1 [label="1: Start main\nFormals: \nLocals: foo_g:int foo_f:int foo_e:int foo_d:int foo_c:int foo_b:int foo_a:int \n DECLARE_LOCALS(&return,&foo_g,&foo_f,&foo_e,&foo_d,&foo_c,&foo_b,&foo_a); [line 12]\n NULLIFY(&foo_a,false); [line 12]\n NULLIFY(&foo_b,false); [line 12]\n NULLIFY(&foo_c,false); [line 12]\n NULLIFY(&foo_d,false); [line 12]\n NULLIFY(&foo_e,false); [line 12]\n NULLIFY(&foo_f,false); [line 12]\n NULLIFY(&foo_g,false); [line 12]\n " color=yellow style=filled] + + + 1 -> 9 ; +} diff --git a/infer/tests/endtoend/c/EnumTest.java b/infer/tests/endtoend/c/EnumTest.java new file mode 100644 index 000000000..f66b73274 --- /dev/null +++ b/infer/tests/endtoend/c/EnumTest.java @@ -0,0 +1,62 @@ +/* +* 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 endtoend.c; + +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 EnumTest { + + public static final String SOURCE_FILE = + "infer/tests/codetoanalyze/c/frontend/enumeration/other_enum.c"; + + public static final String DIVIDE_BY_ZERO = "DIVIDE_BY_ZERO"; + private static ImmutableList inferCmd; + + @ClassRule + public static DebuggableTemporaryFolder folder = + new DebuggableTemporaryFolder(); + + @BeforeClass + public static void runInfer() throws InterruptedException, IOException { + inferCmd = InferRunner.createCInferCommand(folder, SOURCE_FILE); + } + + @Test + public void whenInferRunsOnDivideByZeroThenDivideByZeroIsFound() + throws InterruptedException, IOException, InferException { + InferResults inferResults = InferRunner.runInferC(inferCmd); + String[] procedures = {"test"}; + assertThat( + "Results should contain divide by zero error", + inferResults, + containsExactly( + DIVIDE_BY_ZERO, + SOURCE_FILE, + procedures + ) + ); + } + + +} diff --git a/infer/tests/frontend/c/EnumerationTest.java b/infer/tests/frontend/c/EnumerationTest.java index d61fd5e31..ca6fd1a62 100644 --- a/infer/tests/frontend/c/EnumerationTest.java +++ b/infer/tests/frontend/c/EnumerationTest.java @@ -31,4 +31,18 @@ public class EnumerationTest { ClangFrontendUtils.createAndCompareCDotFiles(folder, src); } + @Test + public void whenCaptureRunOnOtherEnumThenDotFilesAreTheSame() + throws InterruptedException, IOException, InferException { + String src = "infer/tests/codetoanalyze/c/frontend/enumeration/other_enum.c"; + ClangFrontendUtils.createAndCompareCDotFiles(folder, src); + } + + @Test + public void whenCaptureRunOnEnumBitmaskThenDotFilesAreTheSame() + throws InterruptedException, IOException, InferException { + String src = "infer/tests/codetoanalyze/c/frontend/enumeration/enum_bitmask.c"; + ClangFrontendUtils.createAndCompareCDotFiles(folder, src); + } + }