diff --git a/infer/src/IR/Typ.ml b/infer/src/IR/Typ.ml index 2d7e84163..783791ecd 100644 --- a/infer/src/IR/Typ.ml +++ b/infer/src/IR/Typ.ml @@ -722,6 +722,12 @@ module Procname = struct "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. *) let get_language = function | ObjC_Cpp _ -> diff --git a/infer/src/IR/Typ.mli b/infer/src/IR/Typ.mli index 91e688a1d..5be448fcb 100644 --- a/infer/src/IR/Typ.mli +++ b/infer/src/IR/Typ.mli @@ -332,6 +332,12 @@ module Procname : sig val get_method : t -> string (** 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 (** Hash function for procname. *) diff --git a/infer/src/checkers/liveness.ml b/infer/src/checkers/liveness.ml index f3b70edf5..fe1dfeaf2 100644 --- a/infer/src/checkers/liveness.ml +++ b/infer/src/checkers/liveness.ml @@ -61,6 +61,25 @@ end module CFG = ProcCfg.OneInstrPerNode (ProcCfg.Backward (ProcCfg.Exceptional)) 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 whitelisted_vars = ["__assert_file__"; "__assert_fn__"] in List.exists @@ -73,24 +92,11 @@ let checker {Callbacks.tenv; summary; proc_desc} : Specs.summary = let invariant_map = Analyzer.exec_cfg cfg (ProcData.make_default proc_desc tenv) ~initial:Domain.empty ~debug:true 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 | Sil.Store (Lvar pvar, _, _, loc) when not ( 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 ) -> 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 @@ -114,3 +120,4 @@ let checker {Callbacks.tenv; summary; proc_desc} : Specs.summary = in List.iter (CFG.nodes cfg) ~f:report_on_node ; summary + diff --git a/infer/tests/codetoanalyze/objc/liveness/CapturedVariableExample.m b/infer/tests/codetoanalyze/objc/liveness/CapturedVariableExample.m new file mode 100644 index 000000000..deb6f4d82 --- /dev/null +++ b/infer/tests/codetoanalyze/objc/liveness/CapturedVariableExample.m @@ -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 + +@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 diff --git a/infer/tests/codetoanalyze/objc/liveness/NSParameterAssertExample.m b/infer/tests/codetoanalyze/objc/liveness/NSParameterAssertExample.m index 1b2f1cda9..1c42a0aaf 100644 --- a/infer/tests/codetoanalyze/objc/liveness/NSParameterAssertExample.m +++ b/infer/tests/codetoanalyze/objc/liveness/NSParameterAssertExample.m @@ -20,11 +20,10 @@ __assert_fn__ = __assert_fn__ ? __assert_fn__ : @""; NSString* __assert_file__ = [NSString stringWithUTF8String:"A.m"]; __assert_file__ = __assert_file__ ? __assert_file__ : @""; - [[NSAssertionHandler currentHandler] - handleFailureInFunction:__assert_fn__ - file:__assert_file__ - lineNumber:23 - description:(@"Hello")]; + [[NSAssertionHandler currentHandler] handleFailureInFunction:__assert_fn__ + file:__assert_file__ + lineNumber:23 + description:(@"Hello")]; } }