[dead store objc] Do not report dead store in a captured variable of an objc block

Reviewed By: sblackshear

Differential Revision: D6136733

fbshipit-source-id: 568c36d
master
Dulma Churchill 7 years ago committed by Facebook Github Bot
parent b95c4e34d6
commit 3799dfa4bb

@ -722,6 +722,12 @@ module Procname = struct
"Linters_dummy_method" "Linters_dummy_method"
(** Return whether the procname is a block procname. *)
let is_objc_block = function Block _ -> true | _ -> false
(** Return whether the procname is a cpp lambda. *)
let is_cpp_lambda procname = String.is_substring ~substring:"operator()" (get_method procname)
(** Return the language of the procedure. *) (** Return the language of the procedure. *)
let get_language = function let get_language = function
| ObjC_Cpp _ -> | ObjC_Cpp _ ->

@ -332,6 +332,12 @@ module Procname : sig
val get_method : t -> string val get_method : t -> string
(** Return the method/function of a procname. *) (** Return the method/function of a procname. *)
val is_objc_block : t -> bool
(** Return whether the procname is a block procname. *)
val is_cpp_lambda : t -> bool
(** Return whether the procname is a cpp lambda. *)
val hash_pname : t -> int val hash_pname : t -> int
(** Hash function for procname. *) (** Hash function for procname. *)

@ -61,6 +61,25 @@ end
module CFG = ProcCfg.OneInstrPerNode (ProcCfg.Backward (ProcCfg.Exceptional)) module CFG = ProcCfg.OneInstrPerNode (ProcCfg.Backward (ProcCfg.Exceptional))
module Analyzer = AbstractInterpreter.Make (CFG) (TransferFunctions) module Analyzer = AbstractInterpreter.Make (CFG) (TransferFunctions)
let is_captured_var procdesc pvar =
let procname = Procdesc.get_proc_name procdesc in
let pvar_name = Pvar.get_name pvar in
let pvar_matches (name, _) = Mangled.equal name pvar_name in
let is_captured_var_cpp_lambda =
(* var is captured if the procedure is a lambda and the var is not in the locals or formals *)
Typ.Procname.is_cpp_lambda procname
&& not
( List.exists ~f:pvar_matches (Procdesc.get_locals procdesc)
|| List.exists ~f:pvar_matches (Procdesc.get_formals procdesc) )
in
let is_captured_var_objc_block =
(* var is captured if the procedure is a objc block and the var is in the captured *)
Typ.Procname.is_objc_block procname
&& List.exists ~f:pvar_matches (Procdesc.get_captured procdesc)
in
is_captured_var_cpp_lambda || is_captured_var_objc_block
let is_whitelisted_var var = let is_whitelisted_var var =
let whitelisted_vars = ["__assert_file__"; "__assert_fn__"] in let whitelisted_vars = ["__assert_file__"; "__assert_fn__"] in
List.exists List.exists
@ -73,24 +92,11 @@ let checker {Callbacks.tenv; summary; proc_desc} : Specs.summary =
let invariant_map = let invariant_map =
Analyzer.exec_cfg cfg (ProcData.make_default proc_desc tenv) ~initial:Domain.empty ~debug:true Analyzer.exec_cfg cfg (ProcData.make_default proc_desc tenv) ~initial:Domain.empty ~debug:true
in in
let procname = Procdesc.get_proc_name proc_desc in
let is_cpp_lambda =
String.is_substring ~substring:"operator()" (Typ.Procname.get_method procname)
in
let is_captured_var pvar =
let pvar_name = Pvar.get_name pvar in
let pvar_matches (name, _) = Mangled.equal name pvar_name in
(* var is captured if the procedure is a lambda and the var is not in the locals or formals *)
is_cpp_lambda
&& not
( List.exists ~f:pvar_matches (Procdesc.get_locals proc_desc)
|| List.exists ~f:pvar_matches (Procdesc.get_formals proc_desc) )
in
let report_dead_store live_vars = function let report_dead_store live_vars = function
| Sil.Store (Lvar pvar, _, _, loc) | Sil.Store (Lvar pvar, _, _, loc)
when not when 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
|| Domain.mem (Var.of_pvar pvar) live_vars || is_captured_var pvar || Domain.mem (Var.of_pvar pvar) live_vars || is_captured_var proc_desc pvar
|| is_whitelisted_var pvar ) -> || is_whitelisted_var pvar ) ->
let issue_id = IssueType.dead_store.unique_id in let issue_id = IssueType.dead_store.unique_id in
let message = F.asprintf "The value written to %a is never used" (Pvar.pp Pp.text) pvar in let message = F.asprintf "The value written to %a is never used" (Pvar.pp Pp.text) pvar in
@ -114,3 +120,4 @@ let checker {Callbacks.tenv; summary; proc_desc} : Specs.summary =
in in
List.iter (CFG.nodes cfg) ~f:report_on_node ; List.iter (CFG.nodes cfg) ~f:report_on_node ;
summary summary

@ -0,0 +1,32 @@
/*
* Copyright (c) 2017 - 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.
*/
#import <Foundation/Foundation.h>
@interface A : NSObject
@end
typedef void (^MyBlock)();
@interface C : NSObject
+ (void)use_block:(MyBlock)block;
@end
@implementation A
+ (A*)g {
__block A* a;
[C use_block:^() {
a = [A new];
}];
return a;
};
@end

@ -20,11 +20,10 @@
__assert_fn__ = __assert_fn__ ? __assert_fn__ : @"<Unknown Function>"; __assert_fn__ = __assert_fn__ ? __assert_fn__ : @"<Unknown Function>";
NSString* __assert_file__ = [NSString stringWithUTF8String:"A.m"]; NSString* __assert_file__ = [NSString stringWithUTF8String:"A.m"];
__assert_file__ = __assert_file__ ? __assert_file__ : @"<Unknown File>"; __assert_file__ = __assert_file__ ? __assert_file__ : @"<Unknown File>";
[[NSAssertionHandler currentHandler] [[NSAssertionHandler currentHandler] handleFailureInFunction:__assert_fn__
handleFailureInFunction:__assert_fn__ file:__assert_file__
file:__assert_file__ lineNumber:23
lineNumber:23 description:(@"Hello")];
description:(@"Hello")];
} }
} }

Loading…
Cancel
Save