@ -13,7 +13,8 @@ module L = Logging
(* * backward analysis for computing set of maybe-live variables at each program point *)
(* * backward analysis for computing set of maybe-live variables at each program point *)
module Domain = AbstractDomain . FiniteSet ( Var )
module VarSet = AbstractDomain . FiniteSet ( Var )
module Domain = VarSet
(* compilers 101-style backward transfer functions for liveness analysis. gen a variable when it is
(* compilers 101-style backward transfer functions for liveness analysis. gen a variable when it is
read , kill the variable when it is assigned * )
read , kill the variable when it is assigned * )
@ -95,11 +96,32 @@ let matcher_scope_guard =
| > QualifiedCppName . Match . of_fuzzy_qual_names
| > QualifiedCppName . Match . of_fuzzy_qual_names
module CapturedByRefTransferFunctions ( CFG : ProcCfg . S ) = struct
module CFG = CFG
module Domain = VarSet
type extras = ProcData . no_extras
let exec_instr astate _ _ instr =
List . fold ( Sil . instr_get_exps instr )
~ f : ( fun acc exp ->
Exp . fold_captured ~ f : ( fun acc pvar -> Domain . add ( Var . of_pvar pvar ) acc ) exp acc )
~ init : astate
end
module CapturedByRefAnalyzer =
AbstractInterpreter . Make ( ProcCfg . Exceptional ) ( CapturedByRefTransferFunctions )
let get_captured_by_ref_invariant_map proc_desc proc_data =
let cfg = ProcCfg . Exceptional . from_pdesc proc_desc in
CapturedByRefAnalyzer . exec_cfg cfg proc_data ~ initial : VarSet . empty ~ debug : false
let checker { Callbacks . tenv ; summary ; proc_desc } : Specs . summary =
let checker { Callbacks . tenv ; summary ; proc_desc } : Specs . summary =
let proc_data = ProcData . make_default proc_desc tenv in
let captured_by_ref_invariant_map = get_captured_by_ref_invariant_map proc_desc proc_data in
let cfg = CFG . from_pdesc proc_desc in
let cfg = CFG . from_pdesc proc_desc in
let invariant_map =
let invariant_map = Analyzer . exec_cfg cfg proc_data ~ initial : Domain . empty ~ debug : false in
Analyzer . exec_cfg cfg ( ProcData . make_default proc_desc tenv ) ~ initial : Domain . empty ~ debug : true
in
(* we don't want to report in harmless cases like int i = 0; if ( ... ) { i = ... } else { i = ... }
(* we don't want to report in harmless cases like int i = 0; if ( ... ) { i = ... } else { i = ... }
that create an intentional dead store as an attempt to imitate default value semantics .
that create an intentional dead store as an attempt to imitate default value semantics .
use dead stores to a " sentinel " value as a heuristic for ignoring this case * )
use dead stores to a " sentinel " value as a heuristic for ignoring this case * )
@ -119,9 +141,10 @@ let checker {Callbacks.tenv; summary; proc_desc} : Specs.summary =
| _ ->
| _ ->
false
false
in
in
let should_report pvar typ live_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
( Pvar . is_frontend_tmp pvar | | Pvar . is_return pvar | | Pvar . is_global pvar
| | VarSet . mem ( Var . of_pvar pvar ) captured_by_ref_vars
| | Domain . mem ( Var . of_pvar pvar ) live_vars | | Procdesc . is_captured_var proc_desc pvar
| | Domain . mem ( Var . of_pvar pvar ) live_vars | | Procdesc . is_captured_var proc_desc pvar
| | is_scope_guard typ | | Procdesc . has_modify_in_block_attr proc_desc pvar )
| | is_scope_guard typ | | Procdesc . has_modify_in_block_attr proc_desc pvar )
in
in
@ -134,24 +157,36 @@ let checker {Callbacks.tenv; summary; proc_desc} : Specs.summary =
let exn = Exceptions . Checkers ( IssueType . dead_store , Localise . verbatim_desc message ) in
let exn = Exceptions . Checkers ( IssueType . dead_store , Localise . verbatim_desc message ) in
Reporting . log_error summary ~ loc ~ ltr exn
Reporting . log_error summary ~ loc ~ ltr exn
in
in
let report_dead_store live_vars = function
let report_dead_store live_vars captured_by_ref_vars = function
| Sil . Store ( Lvar pvar , typ , rhs_exp , loc )
| Sil . Store ( Lvar pvar , typ , rhs_exp , loc )
when should_report pvar typ live_vars && not ( is_sentinel_exp rhs_exp ) ->
when should_report pvar typ live_vars captured_by_ref_vars && not ( is_sentinel_exp rhs_exp ) ->
log_report pvar typ loc
log_report pvar typ loc
| Sil . Call
| Sil . Call
( None , Exp . Const Cfun ( Typ . Procname . ObjC_Cpp _ as pname ) , ( Exp . Lvar pvar , typ ) :: _ , loc , _ )
( None , Exp . Const Cfun ( Typ . Procname . ObjC_Cpp _ as pname ) , ( Exp . Lvar pvar , typ ) :: _ , loc , _ )
when Typ . Procname . is_constructor pname && should_report pvar typ live_vars ->
when Typ . Procname . is_constructor pname
&& should_report pvar typ live_vars captured_by_ref_vars ->
log_report pvar typ loc
log_report pvar typ loc
| _ ->
| _ ->
()
()
in
in
let report_on_node node =
let report_on_node node =
let captured_by_ref_vars =
match
CapturedByRefAnalyzer . extract_post
( ProcCfg . Exceptional . id ( CFG . underlying_node node ) )
captured_by_ref_invariant_map
with
| Some post ->
post
| None ->
VarSet . empty
in
List . iter ( CFG . instr_ids node ) ~ f : ( fun ( instr , node_id_opt ) ->
List . iter ( CFG . instr_ids node ) ~ f : ( fun ( instr , node_id_opt ) ->
match node_id_opt with
match node_id_opt with
| Some node_id -> (
| Some node_id -> (
match Analyzer . extract_pre node_id invariant_map with
match Analyzer . extract_pre node_id invariant_map with
| Some live_vars ->
| Some live_vars ->
report_dead_store live_vars instr
report_dead_store live_vars captured_by_ref_vars instr
| None ->
| None ->
() )
() )
| None ->
| None ->