Reviewed By: skcho Differential Revision: D18245267 fbshipit-source-id: 6e3f1a7f7master
parent
4d708befd1
commit
43823266ec
@ -0,0 +1,101 @@
|
||||
(*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*)
|
||||
open! IStd
|
||||
module F = Format
|
||||
|
||||
module DomainData = struct
|
||||
type self_pointer_kind = SELF | WEAK_SELF [@@deriving compare]
|
||||
|
||||
let is_self kind = match kind with SELF -> true | WEAK_SELF -> false
|
||||
|
||||
let is_weak_self kind = match kind with SELF -> false | WEAK_SELF -> true
|
||||
|
||||
let pp_self_pointer_kind fmt kind =
|
||||
let s = match kind with SELF -> "SELF" | WEAK_SELF -> "WEAK_SELF" in
|
||||
F.fprintf fmt "%s" s
|
||||
|
||||
|
||||
type t = {pvar: Pvar.t; typ: Typ.t; loc: Location.t; kind: self_pointer_kind}
|
||||
[@@deriving compare]
|
||||
|
||||
let pp fmt {pvar; typ; loc; kind} =
|
||||
F.fprintf fmt "%a:%a, at %a (%a)" (Pvar.pp Pp.text) pvar (Typ.pp Pp.text) typ Location.pp loc
|
||||
pp_self_pointer_kind kind
|
||||
end
|
||||
|
||||
module TransferFunctions = struct
|
||||
module Domain = AbstractDomain.FiniteSet (DomainData)
|
||||
module CFG = ProcCfg.Normal
|
||||
|
||||
type extras = unit
|
||||
|
||||
let pp_session_name _node fmt = F.pp_print_string fmt "SelfCapturedInBlock"
|
||||
|
||||
let is_captured_strong_self attributes pvar =
|
||||
List.exists
|
||||
~f:(fun (captured, typ) ->
|
||||
Mangled.equal captured (Pvar.get_name pvar)
|
||||
&& Pvar.is_self pvar && Typ.is_strong_pointer typ )
|
||||
attributes.ProcAttributes.captured
|
||||
|
||||
|
||||
let is_captured_weak_self attributes pvar =
|
||||
List.exists
|
||||
~f:(fun (captured, typ) ->
|
||||
Mangled.equal captured (Pvar.get_name pvar)
|
||||
&& String.is_substring ~substring:"self" (String.lowercase (Mangled.to_string captured))
|
||||
&& Typ.is_weak_pointer typ )
|
||||
attributes.ProcAttributes.captured
|
||||
|
||||
|
||||
let exec_instr (astate : Domain.t) {ProcData.summary} _cfg_node (instr : Sil.instr) =
|
||||
let attributes = Summary.get_attributes summary in
|
||||
match instr with
|
||||
| Load {e= Lvar pvar; loc; typ} ->
|
||||
if is_captured_strong_self attributes pvar then
|
||||
Domain.add {pvar; typ; loc; kind= SELF} astate
|
||||
else if is_captured_weak_self attributes pvar then
|
||||
Domain.add {pvar; typ; loc; kind= WEAK_SELF} astate
|
||||
else astate
|
||||
| _ ->
|
||||
astate
|
||||
end
|
||||
|
||||
let report_issues summary domain =
|
||||
let weakSelf_opt =
|
||||
TransferFunctions.Domain.find_first_opt (fun {kind} -> DomainData.is_weak_self kind) domain
|
||||
in
|
||||
let self_opt =
|
||||
TransferFunctions.Domain.find_first_opt (fun {kind} -> DomainData.is_self kind) domain
|
||||
in
|
||||
match (weakSelf_opt, self_opt) with
|
||||
| Some {pvar= weakSelf; loc= weakLoc}, Some {pvar= self; loc= selfLoc} ->
|
||||
let message =
|
||||
F.asprintf
|
||||
"This block uses both %a (%a) and %a (%a). This could lead to retain cycles or \
|
||||
unexpected behavior."
|
||||
(Pvar.pp Pp.text) weakSelf Location.pp weakLoc (Pvar.pp Pp.text) self Location.pp selfLoc
|
||||
in
|
||||
Reporting.log_error summary ~loc:selfLoc IssueType.mixed_self_weakself message
|
||||
| _ ->
|
||||
()
|
||||
|
||||
|
||||
module Analyzer = AbstractInterpreter.MakeWTO (TransferFunctions)
|
||||
|
||||
let checker {Callbacks.exe_env; summary} =
|
||||
let initial = TransferFunctions.Domain.empty in
|
||||
let procname = Summary.get_proc_name summary in
|
||||
let tenv = Exe_env.get_tenv exe_env procname in
|
||||
let proc_data = ProcData.make summary tenv () in
|
||||
( if Typ.Procname.is_objc_block procname then
|
||||
match Analyzer.compute_post proc_data ~initial with
|
||||
| Some domain ->
|
||||
report_issues summary domain
|
||||
| None ->
|
||||
() ) ;
|
||||
summary
|
@ -0,0 +1,10 @@
|
||||
(*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*)
|
||||
|
||||
open! IStd
|
||||
|
||||
val checker : Callbacks.proc_callback_t
|
@ -0,0 +1,17 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
TESTS_DIR = ../../..
|
||||
|
||||
CLANG_OPTIONS = -c $(OBJC_CLANG_OPTIONS) -fobjc-arc
|
||||
INFER_OPTIONS = --self_in_block-only --debug-exceptions --project-root $(TESTS_DIR)
|
||||
INFERPRINT_OPTIONS = --issues-tests
|
||||
|
||||
SOURCES = $(wildcard *.m)
|
||||
|
||||
include $(TESTS_DIR)/clang.make
|
||||
include $(TESTS_DIR)/objc.make
|
||||
|
||||
infer-out/report.json: $(MAKEFILE_LIST)
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include <Foundation/NSObject.h>
|
||||
|
||||
@interface SelfInBlockTest : NSObject
|
||||
|
||||
- (void)foo;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SelfInBlockTest {
|
||||
int x;
|
||||
}
|
||||
|
||||
- (void)foo {
|
||||
}
|
||||
|
||||
- (void)mixSelfWeakSelf_bad {
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
int (^my_block)() = ^() {
|
||||
__strong __typeof(weakSelf) strongSelf = weakSelf;
|
||||
if (strongSelf) {
|
||||
[strongSelf foo];
|
||||
int x = self->x; // bug here
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
@end
|
@ -0,0 +1 @@
|
||||
codetoanalyze/objc/self-in-block/StrongSelf.m, objc_blockSelfInBlockTest::mixSelfWeakSelf_bad_1, 4, MIXED_SELF_WEAKSELF, no_bucket, ERROR, []
|
Loading…
Reference in new issue