From 35de60442203d2ad893a6fdac75732bc05c119a1 Mon Sep 17 00:00:00 2001 From: Daiva Naudziuniene Date: Mon, 10 Aug 2020 05:46:28 -0700 Subject: [PATCH] [frontend] Record attribute unused in local variable data Summary: To avoid dead store false positives we skip initialization of a variable that has an `unused` attribute. However, this causes uninitialized value false positives when the variable is later used in macros. To fix this, instead of skipping initialization we record the information about `unused` attribute in local variable data that we can later use for filtering out dead store issues. Reviewed By: jvillard Differential Revision: D22868050 fbshipit-source-id: 4a2d0e680 --- infer/src/IR/ProcAttributes.ml | 9 ++-- infer/src/IR/ProcAttributes.mli | 3 +- infer/src/checkers/liveness.ml | 8 +-- infer/src/clang/cTrans.ml | 51 ++++++++++++------- infer/src/clang/cVar_decl.ml | 5 +- infer/src/java/jTrans.ml | 3 +- .../tests/codetoanalyze/cpp/uninit/uninit.cpp | 5 ++ .../field_superclass/SuperExample.m.dot | 2 +- 8 files changed, 57 insertions(+), 29 deletions(-) diff --git a/infer/src/IR/ProcAttributes.ml b/infer/src/IR/ProcAttributes.ml index 7a666290b..627191d5a 100644 --- a/infer/src/IR/ProcAttributes.ml +++ b/infer/src/IR/ProcAttributes.ml @@ -29,12 +29,13 @@ let pp_objc_accessor_type fmt objc_accessor_type = annots -type var_data = {name: Mangled.t; typ: Typ.t; modify_in_block: bool; is_constexpr: bool} +type var_data = + {name: Mangled.t; typ: Typ.t; modify_in_block: bool; is_constexpr: bool; is_declared_unused: bool} [@@deriving compare] -let pp_var_data fmt {name; typ; modify_in_block} = - F.fprintf fmt "@[{ name=@ %a;@ typ=@ %a;@ modify_in_block=@ %b@ }@]" Mangled.pp name - (Typ.pp_full Pp.text) typ modify_in_block +let pp_var_data fmt {name; typ; modify_in_block; is_declared_unused} = + F.fprintf fmt "@[{ name=@ %a;@ typ=@ %a;@ modify_in_block=@ %b;@ is_declared_unused=@ %b@ }@]" + Mangled.pp name (Typ.pp_full Pp.text) typ modify_in_block is_declared_unused type t = diff --git a/infer/src/IR/ProcAttributes.mli b/infer/src/IR/ProcAttributes.mli index 20296fd85..1118539a3 100644 --- a/infer/src/IR/ProcAttributes.mli +++ b/infer/src/IR/ProcAttributes.mli @@ -17,7 +17,8 @@ type var_data = ; modify_in_block: bool (** __block attribute of Objective-C variables, means that it will be modified inside a block *) - ; is_constexpr: bool } + ; is_constexpr: bool + ; is_declared_unused: bool (** variable declared with attribute [unused] *) } type t = { access: PredSymb.access (** visibility access *) diff --git a/infer/src/checkers/liveness.ml b/infer/src/checkers/liveness.ml index fb92a0c1e..03115d4c7 100644 --- a/infer/src/checkers/liveness.ml +++ b/infer/src/checkers/liveness.ml @@ -236,14 +236,16 @@ let checker {IntraproceduralAnalysis.proc_desc; err_log} = false in let locals = Procdesc.get_locals proc_desc in - let is_constexpr pvar = + let is_constexpr_or_unused pvar = List.find locals ~f:(fun local_data -> Mangled.equal (Pvar.get_name pvar) local_data.ProcAttributes.name ) - |> Option.exists ~f:(fun local -> local.ProcAttributes.is_constexpr) + |> Option.exists ~f:(fun local -> + local.ProcAttributes.is_constexpr || local.ProcAttributes.is_declared_unused ) in let should_report pvar typ live_vars captured_by_ref_vars = not - ( Pvar.is_frontend_tmp pvar || Pvar.is_return pvar || Pvar.is_global pvar || is_constexpr pvar + ( Pvar.is_frontend_tmp pvar || Pvar.is_return pvar || Pvar.is_global pvar + || is_constexpr_or_unused pvar || VarSet.mem (Var.of_pvar pvar) captured_by_ref_vars || Domain.mem (Var.of_pvar pvar) live_vars || Procdesc.is_captured_pvar proc_desc pvar diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index 90c3ee6df..fad27c469 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -205,7 +205,12 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s CVar_decl.mk_temp_sil_var_for_expr context ~name:var_name ~clang_pointer expr_info in let var_data = - ProcAttributes.{name= Pvar.get_name pvar; typ; modify_in_block= false; is_constexpr= false} + ProcAttributes. + { name= Pvar.get_name pvar + ; typ + ; modify_in_block= false + ; is_constexpr= false + ; is_declared_unused= false } in Procdesc.append_locals procdesc [var_data] ; (Exp.Lvar pvar, typ) @@ -245,7 +250,8 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s { name= Pvar.get_name pvar ; typ= return_type ; modify_in_block= false - ; is_constexpr= false } + ; is_constexpr= false + ; is_declared_unused= false } in Procdesc.append_locals procdesc [var_data] ; Exp.Lvar pvar @@ -1201,7 +1207,11 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s let pvar = Pvar.mk_tmp "__temp_construct_" (Procdesc.get_proc_name procdesc) in let class_type = CType_decl.get_type_from_expr_info ei context.CContext.tenv in let var_data : ProcAttributes.var_data = - {name= Pvar.get_name pvar; typ= class_type; modify_in_block= false; is_constexpr= false} + { name= Pvar.get_name pvar + ; typ= class_type + ; modify_in_block= false + ; is_constexpr= false + ; is_declared_unused= false } in Procdesc.append_locals procdesc [var_data] ; (Exp.Lvar pvar, class_type) @@ -1615,7 +1625,11 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s let pvar = CVar_decl.mk_temp_sil_var procdesc ~name:"SIL_temp_conditional___" in let var_data = ProcAttributes. - {name= Pvar.get_name pvar; typ= var_typ; modify_in_block= false; is_constexpr= false} + { name= Pvar.get_name pvar + ; typ= var_typ + ; modify_in_block= false + ; is_constexpr= false + ; is_declared_unused= false } in Procdesc.append_locals procdesc [var_data] ; let continuation' = mk_cond_continuation trans_state.continuation in @@ -2435,8 +2449,8 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s |> mk_trans_result ret_exp_typ - and init_expr_trans ?(is_var_unused = false) ?(is_initListExpr_builtin = false) trans_state - var_exp_typ ?qual_type var_stmt_info init_expr_opt = + and init_expr_trans ?(is_initListExpr_builtin = false) trans_state var_exp_typ ?qual_type + var_stmt_info init_expr_opt = match init_expr_opt with | None -> ( match Option.bind qual_type ~f:(fun qt -> CAst_utils.get_type qt.Clang_ast_t.qt_type_ptr) with @@ -2464,8 +2478,7 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s in let assign_trans_control_opt = if - is_var_unused - || (* variable might be initialized already - do nothing in that case*) + (* variable might be initialized already - do nothing in that case*) List.exists ~f:(Exp.equal var_exp) res_trans_ie.control.initd_exps then None else @@ -2501,21 +2514,18 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s let context = trans_state.context in let procdesc = context.CContext.procdesc in let procname = Procdesc.get_proc_name procdesc in - let do_var_dec ~is_var_unused var_decl qual_type vdi next_node = + let do_var_dec var_decl qual_type vdi next_node = let pvar = CVar_decl.sil_var_of_decl context var_decl procname in let typ = CType_decl.qual_type_to_sil_type context.CContext.tenv qual_type in CVar_decl.add_var_to_locals procdesc var_decl typ pvar ; let trans_state' = {trans_state with succ_nodes= next_node} in - init_expr_trans ~is_var_unused trans_state' (Exp.Lvar pvar, typ) ~qual_type stmt_info + init_expr_trans trans_state' (Exp.Lvar pvar, typ) ~qual_type stmt_info vdi.Clang_ast_t.vdi_init_expr in - let has_unused_attr attributes = - List.exists attributes ~f:(function `UnusedAttr _ -> true | _ -> false) - in let rec aux : decl list -> trans_result option = function | [] -> None - | (VarDecl ({di_attributes}, _, qt, vdi) as var_decl) :: var_decls' -> + | (VarDecl (_, _, qt, vdi) as var_decl) :: var_decls' -> (* Var are defined when procdesc is created, here we only take care of initialization *) let res_trans_tl = aux var_decls' in let root_nodes_tl, instrs_tl, initd_exps_tl = @@ -2525,8 +2535,7 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s | Some {control= {root_nodes; instrs; initd_exps}} -> (root_nodes, instrs, initd_exps) in - let is_var_unused = has_unused_attr di_attributes in - let res_trans_tmp = do_var_dec ~is_var_unused var_decl qt vdi root_nodes_tl in + let res_trans_tmp = do_var_dec var_decl qt vdi root_nodes_tl in (* keep the last return and leaf_nodes from the list *) let return, leaf_nodes = match res_trans_tl with @@ -2895,7 +2904,8 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s [ { ProcAttributes.name= Pvar.get_name temp ; typ= array_typ ; modify_in_block= false - ; is_constexpr= false } ] ; + ; is_constexpr= false + ; is_declared_unused= false } ] ; (* 2. Translate array elements *) let res_trans_elems = List.mapi ~f:(exec_instruction_with_trans_state trans_state None) stmts in (* 3. Add array initialization (elements assignments) *) @@ -3240,7 +3250,12 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s let res_trans = init_expr_trans trans_state var_exp_typ stmt_info (Some temp_exp) in let _, typ = res_trans.return in let var_data = - ProcAttributes.{name= Pvar.get_name pvar; typ; modify_in_block= false; is_constexpr= false} + ProcAttributes. + { name= Pvar.get_name pvar + ; typ + ; modify_in_block= false + ; is_constexpr= false + ; is_declared_unused= false } in Procdesc.append_locals procdesc [var_data] ; res_trans diff --git a/infer/src/clang/cVar_decl.ml b/infer/src/clang/cVar_decl.ml index 3740d8a22..5060f9bc9 100644 --- a/infer/src/clang/cVar_decl.ml +++ b/infer/src/clang/cVar_decl.ml @@ -70,8 +70,11 @@ let add_var_to_locals procdesc var_decl typ pvar = vdi.Clang_ast_t.vdi_is_const_expr || (Typ.is_const typ.Typ.quals && vdi.Clang_ast_t.vdi_is_init_expr_cxx11_constant) in + let is_declared_unused = + List.exists decl_info.di_attributes ~f:(function `UnusedAttr _ -> true | _ -> false) + in let var_data : ProcAttributes.var_data = - {name= Pvar.get_name pvar; typ; modify_in_block; is_constexpr} + {name= Pvar.get_name pvar; typ; modify_in_block; is_constexpr; is_declared_unused} in Procdesc.append_locals procdesc [var_data] | _ -> diff --git a/infer/src/java/jTrans.ml b/infer/src/java/jTrans.ml index ae9c77a7d..d89d645bf 100644 --- a/infer/src/java/jTrans.ml +++ b/infer/src/java/jTrans.ml @@ -437,7 +437,8 @@ let create_cm_procdesc source_file program icfg cm proc_name = let locals_ = translate_locals program tenv formals bytecode jbir_code in let locals = List.map locals_ ~f:(fun (name, typ) -> - ({name; typ; modify_in_block= false; is_constexpr= false} : ProcAttributes.var_data) ) + ( {name; typ; modify_in_block= false; is_constexpr= false; is_declared_unused= false} + : ProcAttributes.var_data ) ) in let method_annotation = JAnnotation.translate_method cm.Javalib.cm_annotations in let proc_attributes = diff --git a/infer/tests/codetoanalyze/cpp/uninit/uninit.cpp b/infer/tests/codetoanalyze/cpp/uninit/uninit.cpp index 2e11e9f85..be8e76e87 100644 --- a/infer/tests/codetoanalyze/cpp/uninit/uninit.cpp +++ b/infer/tests/codetoanalyze/cpp/uninit/uninit.cpp @@ -344,3 +344,8 @@ void use_uninit_in_expr_bad() { int x; int y = x + 2; } + +int unused_attribute_ok() { + int __attribute__((unused)) x = 42; + return x; +} diff --git a/infer/tests/codetoanalyze/objc/shared/field_superclass/SuperExample.m.dot b/infer/tests/codetoanalyze/objc/shared/field_superclass/SuperExample.m.dot index 3d77d40cb..34ac884e2 100644 --- a/infer/tests/codetoanalyze/objc/shared/field_superclass/SuperExample.m.dot +++ b/infer/tests/codetoanalyze/objc/shared/field_superclass/SuperExample.m.dot @@ -7,7 +7,7 @@ digraph cfg { "super_example_main.e3ebe95e6c5ae811733f235c29fbbf6d_2" [label="2: Exit super_example_main \n " color=yellow style=filled] -"super_example_main.e3ebe95e6c5ae811733f235c29fbbf6d_3" [label="3: DeclStmt \n VARIABLE_DECLARED(a:objc_object*); [line 40, column 5]\n n$0=_fun___objc_alloc_no_fail(sizeof(t=ASuper):unsigned long) [line 40, column 21]\n n$1=_fun_NSObject.init(n$0:ASuper*) virtual [line 40, column 21]\n " shape="box"] +"super_example_main.e3ebe95e6c5ae811733f235c29fbbf6d_3" [label="3: DeclStmt \n VARIABLE_DECLARED(a:objc_object*); [line 40, column 5]\n n$0=_fun___objc_alloc_no_fail(sizeof(t=ASuper):unsigned long) [line 40, column 21]\n n$1=_fun_NSObject.init(n$0:ASuper*) virtual [line 40, column 21]\n *&a:objc_object*=n$1 [line 40, column 5]\n " shape="box"] "super_example_main.e3ebe95e6c5ae811733f235c29fbbf6d_3" -> "super_example_main.e3ebe95e6c5ae811733f235c29fbbf6d_2" ;