[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
master
Daiva Naudziuniene 4 years ago committed by Facebook GitHub Bot
parent 59592a60e4
commit 35de604422

@ -29,12 +29,13 @@ let pp_objc_accessor_type fmt objc_accessor_type =
annots 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] [@@deriving compare]
let pp_var_data fmt {name; typ; modify_in_block} = let pp_var_data fmt {name; typ; modify_in_block; is_declared_unused} =
F.fprintf fmt "@[<h>{ name=@ %a;@ typ=@ %a;@ modify_in_block=@ %b@ }@]" Mangled.pp name F.fprintf fmt "@[<h>{ name=@ %a;@ typ=@ %a;@ modify_in_block=@ %b;@ is_declared_unused=@ %b@ }@]"
(Typ.pp_full Pp.text) typ modify_in_block Mangled.pp name (Typ.pp_full Pp.text) typ modify_in_block is_declared_unused
type t = type t =

@ -17,7 +17,8 @@ type var_data =
; modify_in_block: bool ; modify_in_block: bool
(** __block attribute of Objective-C variables, means that it will be modified inside a (** __block attribute of Objective-C variables, means that it will be modified inside a
block *) block *)
; is_constexpr: bool } ; is_constexpr: bool
; is_declared_unused: bool (** variable declared with attribute [unused] *) }
type t = type t =
{ access: PredSymb.access (** visibility access *) { access: PredSymb.access (** visibility access *)

@ -236,14 +236,16 @@ let checker {IntraproceduralAnalysis.proc_desc; err_log} =
false false
in in
let locals = Procdesc.get_locals proc_desc 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 -> List.find locals ~f:(fun local_data ->
Mangled.equal (Pvar.get_name pvar) local_data.ProcAttributes.name ) 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 in
let should_report pvar typ live_vars captured_by_ref_vars = let should_report pvar typ live_vars captured_by_ref_vars =
not 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 || VarSet.mem (Var.of_pvar pvar) captured_by_ref_vars
|| Domain.mem (Var.of_pvar pvar) live_vars || Domain.mem (Var.of_pvar pvar) live_vars
|| Procdesc.is_captured_pvar proc_desc pvar || Procdesc.is_captured_pvar proc_desc pvar

@ -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 CVar_decl.mk_temp_sil_var_for_expr context ~name:var_name ~clang_pointer expr_info
in in
let var_data = 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 in
Procdesc.append_locals procdesc [var_data] ; Procdesc.append_locals procdesc [var_data] ;
(Exp.Lvar pvar, typ) (Exp.Lvar pvar, typ)
@ -245,7 +250,8 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
{ name= Pvar.get_name pvar { name= Pvar.get_name pvar
; typ= return_type ; typ= return_type
; modify_in_block= false ; modify_in_block= false
; is_constexpr= false } ; is_constexpr= false
; is_declared_unused= false }
in in
Procdesc.append_locals procdesc [var_data] ; Procdesc.append_locals procdesc [var_data] ;
Exp.Lvar pvar 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 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 class_type = CType_decl.get_type_from_expr_info ei context.CContext.tenv in
let var_data : ProcAttributes.var_data = 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 in
Procdesc.append_locals procdesc [var_data] ; Procdesc.append_locals procdesc [var_data] ;
(Exp.Lvar pvar, class_type) (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 pvar = CVar_decl.mk_temp_sil_var procdesc ~name:"SIL_temp_conditional___" in
let var_data = let var_data =
ProcAttributes. 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 in
Procdesc.append_locals procdesc [var_data] ; Procdesc.append_locals procdesc [var_data] ;
let continuation' = mk_cond_continuation trans_state.continuation in 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 |> mk_trans_result ret_exp_typ
and init_expr_trans ?(is_var_unused = false) ?(is_initListExpr_builtin = false) trans_state and init_expr_trans ?(is_initListExpr_builtin = false) trans_state var_exp_typ ?qual_type
var_exp_typ ?qual_type var_stmt_info init_expr_opt = var_stmt_info init_expr_opt =
match init_expr_opt with match init_expr_opt with
| None -> ( | None -> (
match Option.bind qual_type ~f:(fun qt -> CAst_utils.get_type qt.Clang_ast_t.qt_type_ptr) with 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 in
let assign_trans_control_opt = let assign_trans_control_opt =
if 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 List.exists ~f:(Exp.equal var_exp) res_trans_ie.control.initd_exps
then None then None
else else
@ -2501,21 +2514,18 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
let context = trans_state.context in let context = trans_state.context in
let procdesc = context.CContext.procdesc in let procdesc = context.CContext.procdesc in
let procname = Procdesc.get_proc_name 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 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 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 ; CVar_decl.add_var_to_locals procdesc var_decl typ pvar ;
let trans_state' = {trans_state with succ_nodes= next_node} in 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 vdi.Clang_ast_t.vdi_init_expr
in in
let has_unused_attr attributes =
List.exists attributes ~f:(function `UnusedAttr _ -> true | _ -> false)
in
let rec aux : decl list -> trans_result option = function let rec aux : decl list -> trans_result option = function
| [] -> | [] ->
None 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 *) (* Var are defined when procdesc is created, here we only take care of initialization *)
let res_trans_tl = aux var_decls' in let res_trans_tl = aux var_decls' in
let root_nodes_tl, instrs_tl, initd_exps_tl = 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}} -> | Some {control= {root_nodes; instrs; initd_exps}} ->
(root_nodes, instrs, initd_exps) (root_nodes, instrs, initd_exps)
in in
let is_var_unused = has_unused_attr di_attributes in let res_trans_tmp = do_var_dec var_decl qt vdi root_nodes_tl in
let res_trans_tmp = do_var_dec ~is_var_unused var_decl qt vdi root_nodes_tl in
(* keep the last return and leaf_nodes from the list *) (* keep the last return and leaf_nodes from the list *)
let return, leaf_nodes = let return, leaf_nodes =
match res_trans_tl with 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 [ { ProcAttributes.name= Pvar.get_name temp
; typ= array_typ ; typ= array_typ
; modify_in_block= false ; modify_in_block= false
; is_constexpr= false } ] ; ; is_constexpr= false
; is_declared_unused= false } ] ;
(* 2. Translate array elements *) (* 2. Translate array elements *)
let res_trans_elems = List.mapi ~f:(exec_instruction_with_trans_state trans_state None) stmts in let res_trans_elems = List.mapi ~f:(exec_instruction_with_trans_state trans_state None) stmts in
(* 3. Add array initialization (elements assignments) *) (* 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 res_trans = init_expr_trans trans_state var_exp_typ stmt_info (Some temp_exp) in
let _, typ = res_trans.return in let _, typ = res_trans.return in
let var_data = 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 in
Procdesc.append_locals procdesc [var_data] ; Procdesc.append_locals procdesc [var_data] ;
res_trans res_trans

@ -70,8 +70,11 @@ let add_var_to_locals procdesc var_decl typ pvar =
vdi.Clang_ast_t.vdi_is_const_expr vdi.Clang_ast_t.vdi_is_const_expr
|| (Typ.is_const typ.Typ.quals && vdi.Clang_ast_t.vdi_is_init_expr_cxx11_constant) || (Typ.is_const typ.Typ.quals && vdi.Clang_ast_t.vdi_is_init_expr_cxx11_constant)
in in
let is_declared_unused =
List.exists decl_info.di_attributes ~f:(function `UnusedAttr _ -> true | _ -> false)
in
let var_data : ProcAttributes.var_data = 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 in
Procdesc.append_locals procdesc [var_data] Procdesc.append_locals procdesc [var_data]
| _ -> | _ ->

@ -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_ = translate_locals program tenv formals bytecode jbir_code in
let locals = let locals =
List.map locals_ ~f:(fun (name, typ) -> 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 in
let method_annotation = JAnnotation.translate_method cm.Javalib.cm_annotations in let method_annotation = JAnnotation.translate_method cm.Javalib.cm_annotations in
let proc_attributes = let proc_attributes =

@ -344,3 +344,8 @@ void use_uninit_in_expr_bad() {
int x; int x;
int y = x + 2; int y = x + 2;
} }
int unused_attribute_ok() {
int __attribute__((unused)) x = 42;
return x;
}

@ -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_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" ; "super_example_main.e3ebe95e6c5ae811733f235c29fbbf6d_3" -> "super_example_main.e3ebe95e6c5ae811733f235c29fbbf6d_2" ;

Loading…
Cancel
Save