[Infer] Dedup reports of strongSelf Not checked

Summary: When we discovered that a strongSelf var was not checked for null, we then report in each occurrence which is spammy. Now we report only the first occurrence. To achieve that, we store a `reported` flag in the domain that gets set to true after we report once, and we only report if it's false.

Reviewed By: jvillard

Differential Revision: D19877218

fbshipit-source-id: c44109ae9
master
Dulma Churchill 5 years ago committed by Facebook Github Bot
parent 22601846b2
commit f6341977fb

@ -90,16 +90,20 @@ module PPPVar = struct
end end
module CheckedForNull = struct module CheckedForNull = struct
type t = {checked: bool; loc: Location.t} type t = {checked: bool; loc: Location.t; reported: bool}
let pp fmt {checked} = let pp fmt {checked; reported} =
let s = match checked with true -> "Checked" | false -> "NotChecked" in let s = match checked with true -> "Checked" | false -> "NotChecked" in
F.fprintf fmt "%s" s let s' = match reported with true -> "Reported" | false -> "NotReported" in
F.fprintf fmt "%s, %s" s s'
let join {checked= elem1; loc= loc1} {checked= elem2; loc= loc2} = let join {checked= elem1; loc= loc1; reported= reported1}
let loc = match (elem1, elem2) with true, false -> loc2 | false, _ | true, true -> loc1 in {checked= elem2; loc= loc2; reported= reported2} =
{checked= AbstractDomain.BooleanAnd.join elem1 elem2; loc} let loc, reported =
match (elem1, elem2) with true, false -> (loc2, reported2) | _ -> (loc1, reported1)
in
{checked= elem1 && elem2; loc; reported}
let widen ~prev ~next ~num_iters:_ = join prev next let widen ~prev ~next ~num_iters:_ = join prev next
@ -165,13 +169,19 @@ module TransferFunctions = struct
attributes.ProcAttributes.captured attributes.ProcAttributes.captured
let find_strong_var (domain : Domain.t) id =
match Vars.find_opt id domain.vars with
| Some elem when StrongEqualToWeakCapturedVars.mem elem.pvar domain.strongVars ->
Some (elem, elem.pvar, StrongEqualToWeakCapturedVars.find elem.pvar domain.strongVars)
| _ ->
None
let exec_null_check_id (astate : Domain.t) id = let exec_null_check_id (astate : Domain.t) id =
try match find_strong_var astate id with
let elem = Vars.find id astate.vars in | Some (elem, pvar, strongVarElem) ->
if StrongEqualToWeakCapturedVars.mem elem.pvar astate.strongVars then
let strongVarElem = StrongEqualToWeakCapturedVars.find elem.pvar astate.strongVars in
let strongVars = let strongVars =
StrongEqualToWeakCapturedVars.add elem.pvar StrongEqualToWeakCapturedVars.add pvar
{strongVarElem with checked= true} {strongVarElem with checked= true}
astate.strongVars astate.strongVars
in in
@ -181,8 +191,8 @@ module TransferFunctions = struct
Vars.add id {elem with kind= CHECKED_STRONG_SELF} astate.vars Vars.add id {elem with kind= CHECKED_STRONG_SELF} astate.vars
in in
{Domain.vars; strongVars} {Domain.vars; strongVars}
else astate | None ->
with Caml.Not_found -> astate astate
let make_trace_unchecked_strongself (domain : Domain.t) = let make_trace_unchecked_strongself (domain : Domain.t) =
@ -214,9 +224,9 @@ module TransferFunctions = struct
let report_unchecked_strongself_issues summary (domain : Domain.t) var_use var = let report_unchecked_strongself_issues summary (domain : Domain.t) var_use var =
try match find_strong_var domain var with
let {DomainData.pvar; loc; kind} = Vars.find var domain.vars in | Some ({DomainData.pvar; loc; kind}, _, strongVarElem)
if DomainData.is_unchecked_strong_self kind then when DomainData.is_unchecked_strong_self kind && not strongVarElem.reported ->
let message = let message =
F.asprintf F.asprintf
"The variable `%a`, equal to a weak pointer to `self`, is %s without a check for null \ "The variable `%a`, equal to a weak pointer to `self`, is %s without a check for null \
@ -224,20 +234,27 @@ module TransferFunctions = struct
(Pvar.pp Pp.text) pvar var_use Location.pp loc (Pvar.pp Pp.text) pvar var_use Location.pp loc
in in
let ltr = make_trace_unchecked_strongself domain in let ltr = make_trace_unchecked_strongself domain in
Reporting.log_error summary ~ltr ~loc IssueType.strong_self_not_checked message Reporting.log_error summary ~ltr ~loc IssueType.strong_self_not_checked message ;
with Caml.Not_found -> () let strongVars =
StrongEqualToWeakCapturedVars.add pvar
{strongVarElem with reported= true}
domain.strongVars
in
{domain with strongVars}
| _ ->
domain
let report_unchecked_strongself_issues_on_exps (domain : Domain.t) summary (instr : Sil.instr) = let report_unchecked_strongself_issues_on_exps (domain : Domain.t) summary (instr : Sil.instr) =
let report_unchecked_strongself_issues_on_exp (exp : Exp.t) = let report_unchecked_strongself_issues_on_exp strongVars (exp : Exp.t) =
match exp with match exp with
| Lfield (Var var, _, _) -> | Lfield (Var var, _, _) ->
report_unchecked_strongself_issues summary domain "dereferenced" var report_unchecked_strongself_issues summary domain "dereferenced" var
| _ -> | _ ->
() strongVars
in in
let exps = Sil.exps_of_instr instr in let exps = Sil.exps_of_instr instr in
List.iter ~f:report_unchecked_strongself_issues_on_exp exps List.fold ~f:report_unchecked_strongself_issues_on_exp exps ~init:domain
(* The translation of closures includes a load instruction for the captured variable, (* The translation of closures includes a load instruction for the captured variable,
@ -288,20 +305,22 @@ module TransferFunctions = struct
(F.sprintf "passed to `%s`" (Procname.to_simplified_string pname)) (F.sprintf "passed to `%s`" (Procname.to_simplified_string pname))
var var
in in
let rec report_on_non_nullable_arg ?annotations args = let rec report_on_non_nullable_arg ?annotations domain args =
match (annotations, args) with match (annotations, args) with
| Some (annot :: annot_rest), (arg, _) :: rest -> | Some (annot :: annot_rest), (arg, _) :: rest ->
( match arg with let domain =
match arg with
| Exp.Var var when not (Annotations.ia_is_nullable annot) -> | Exp.Var var when not (Annotations.ia_is_nullable annot) ->
report_issue var report_issue var
| _ -> | _ ->
() ) ; domain
report_on_non_nullable_arg ~annotations:annot_rest rest in
report_on_non_nullable_arg ~annotations:annot_rest domain rest
| None, (arg, _) :: rest -> | None, (arg, _) :: rest ->
(match arg with Exp.Var var -> report_issue var | _ -> ()) ; let domain = match arg with Exp.Var var -> report_issue var | _ -> domain in
report_on_non_nullable_arg rest report_on_non_nullable_arg domain rest
| _ -> | _ ->
() domain
in in
let proc_desc_opt = Ondemand.get_proc_desc pname in let proc_desc_opt = Ondemand.get_proc_desc pname in
let annotations = get_annotations proc_desc_opt in let annotations = get_annotations proc_desc_opt in
@ -314,12 +333,12 @@ module TransferFunctions = struct
match annotations with Some (_ :: rest) -> Some rest | _ -> annotations match annotations with Some (_ :: rest) -> Some rest | _ -> annotations
else annotations else annotations
in in
report_on_non_nullable_arg ?annotations args report_on_non_nullable_arg ?annotations domain args
let exec_instr (astate : Domain.t) {ProcData.summary} _cfg_node (instr : Sil.instr) = let exec_instr (astate : Domain.t) {ProcData.summary} _cfg_node (instr : Sil.instr) =
let attributes = Summary.get_attributes summary in let attributes = Summary.get_attributes summary in
report_unchecked_strongself_issues_on_exps astate summary instr ; let astate = report_unchecked_strongself_issues_on_exps astate summary instr in
let astate = remove_ids_in_closures_from_domain astate instr in let astate = remove_ids_in_closures_from_domain astate instr in
match instr with match instr with
| Load {id; e= Lvar pvar; loc; typ} -> | Load {id; e= Lvar pvar; loc; typ} ->
@ -344,7 +363,10 @@ module TransferFunctions = struct
try try
let {DomainData.pvar= binding_for_id} = Vars.find id astate.vars in let {DomainData.pvar= binding_for_id} = Vars.find id astate.vars in
if is_captured_weak_self attributes binding_for_id && Typ.is_strong_pointer pvar_typ if is_captured_weak_self attributes binding_for_id && Typ.is_strong_pointer pvar_typ
then StrongEqualToWeakCapturedVars.add pvar {checked= false; loc} astate.strongVars then
StrongEqualToWeakCapturedVars.add pvar
{checked= false; loc; reported= false}
astate.strongVars
else astate.strongVars else astate.strongVars
with Caml.Not_found -> astate.strongVars with Caml.Not_found -> astate.strongVars
in in
@ -357,7 +379,7 @@ module TransferFunctions = struct
| Prune (UnOp (LNot, BinOp (Binop.Eq, Var id, e), _), _, _, _) -> | Prune (UnOp (LNot, BinOp (Binop.Eq, Var id, e), _), _, _, _) ->
if Exp.is_null_literal e then exec_null_check_id astate id else astate if Exp.is_null_literal e then exec_null_check_id astate id else astate
| Call (_, Exp.Const (Const.Cfun callee_pn), args, _, _) -> | Call (_, Exp.Const (Const.Cfun callee_pn), args, _, _) ->
report_unchecked_strongself_issues_on_args astate summary callee_pn args ; let astate = report_unchecked_strongself_issues_on_args astate summary callee_pn args in
astate astate
| _ -> | _ ->
astate astate

@ -98,23 +98,36 @@ void m2(_Nullable SelfInBlockTest* obj) {}
} else { } else {
strongSelf->x; // bug here strongSelf->x; // bug here
[strongSelf foo]; // no bug here [strongSelf foo]; // no bug here
m(strongSelf); // bug here m(strongSelf); // no bug here because of dedup
m2(strongSelf); // no bug here because of _Nullable annotation
[strongSelf.user use_self_in_block_test:strongSelf]; // bug here
[strongSelf.user
use_self_in_block_test_nullable:1
and:strongSelf]; // no bug here because of
// _Nullable annotation
} }
[strongSelf foo]; return 0;
if (strongSelf != nil) { };
}
- (void)strongSelfCheck2_bad {
__weak __typeof(self) weakSelf = self;
int (^my_block)(BOOL) = ^(BOOL isTapped) {
__strong __typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf foo]; [strongSelf foo];
int x = strongSelf->x; int x = strongSelf->x;
} else {
m(strongSelf); // bug here
int x = strongSelf->x; // no bug here because of dedup
} }
return 0; return 0;
}; };
} }
- (void)strongSelfCheck6_good {
__weak __typeof(self) weakSelf = self;
int (^my_block)(BOOL) = ^(BOOL isTapped) {
__strong __typeof(weakSelf) strongSelf = weakSelf;
m2(strongSelf); // no bug here because of _Nullable annotation
return 0;
};
}
- (void)strongSelfCheck2_good { - (void)strongSelfCheck2_good {
__weak __typeof(self) weakSelf = self; __weak __typeof(self) weakSelf = self;
int (^my_block)(BOOL) = ^(BOOL isTapped) { int (^my_block)(BOOL) = ^(BOOL isTapped) {
@ -140,13 +153,23 @@ void m2(_Nullable SelfInBlockTest* obj) {}
}; };
} }
- (void)strongSelfCheck4_good { - (void)strongSelfCheck4_bad {
__weak __typeof(self) weakSelf = self; __weak __typeof(self) weakSelf = self;
int (^my_block)() = ^() { int (^my_block)() = ^() {
__strong __typeof(weakSelf) strongSelf = weakSelf; __strong __typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf) { [strongSelf.user use_self_in_block_test:strongSelf]; // bug here
} else { return 0;
} };
}
- (void)strongSelfCheck5_good {
__weak __typeof(self) weakSelf = self;
int (^my_block)() = ^() {
__strong __typeof(weakSelf) strongSelf = weakSelf;
[strongSelf.user
use_self_in_block_test_nullable:1
and:strongSelf]; // no bug here because of
// _Nullable annotation
return 0; return 0;
}; };
} }
@ -162,7 +185,7 @@ void m2(_Nullable SelfInBlockTest* obj) {}
}; };
} }
- (void)strongSelfCheck6_good { - (void)strongSelfCheck7_good {
__weak __typeof(self) weakSelf = self; __weak __typeof(self) weakSelf = self;
int (^my_block)() = ^() { int (^my_block)() = ^() {
__strong __typeof(weakSelf) strongSelf = weakSelf; __strong __typeof(weakSelf) strongSelf = weakSelf;

@ -1,9 +1,9 @@
codetoanalyze/objc/self-in-block/NoescapeBlock.m, objc_blockA::weak_in_noescape_block_bad:_1, 1, WEAK_SELF_IN_NO_ESCAPE_BLOCK, no_bucket, ERROR, [Using &weakSelf] codetoanalyze/objc/self-in-block/NoescapeBlock.m, objc_blockA::weak_in_noescape_block_bad:_1, 1, WEAK_SELF_IN_NO_ESCAPE_BLOCK, no_bucket, ERROR, [Using &weakSelf]
codetoanalyze/objc/self-in-block/StrongSelf.m, objc_blockSelfInBlockTest::mixSelfWeakSelf_bad_1, 4, MIXED_SELF_WEAKSELF, no_bucket, ERROR, [Using &weakSelf,Using &self,Using &self] codetoanalyze/objc/self-in-block/StrongSelf.m, objc_blockSelfInBlockTest::mixSelfWeakSelf_bad_1, 4, MIXED_SELF_WEAKSELF, no_bucket, ERROR, [Using &weakSelf,Using &self,Using &self]
codetoanalyze/objc/self-in-block/StrongSelf.m, objc_blockSelfInBlockTest::strongSelfCheck3_bad_7, 2, STRONG_SELF_NOT_CHECKED, no_bucket, ERROR, [&strongSelf assigned here,Using &strongSelf not checked for null] codetoanalyze/objc/self-in-block/StrongSelf.m, objc_blockSelfInBlockTest::strongSelfCheck2_bad_6, 6, STRONG_SELF_NOT_CHECKED, no_bucket, ERROR, [&strongSelf assigned here,Using &strongSelf not checked for null,Using &strongSelf not checked for null]
codetoanalyze/objc/self-in-block/StrongSelf.m, objc_blockSelfInBlockTest::strongSelfCheck3_bad_9, 2, STRONG_SELF_NOT_CHECKED, no_bucket, ERROR, [&strongSelf assigned here,Using &strongSelf not checked for null]
codetoanalyze/objc/self-in-block/StrongSelf.m, objc_blockSelfInBlockTest::strongSelfCheck4_bad_10, 2, STRONG_SELF_NOT_CHECKED, no_bucket, ERROR, [&strongSelf assigned here,Using &strongSelf not checked for null,Using &strongSelf not checked for null]
codetoanalyze/objc/self-in-block/StrongSelf.m, objc_blockSelfInBlockTest::strongSelfCheckOnce_bad_5, 6, STRONG_SELF_NOT_CHECKED, no_bucket, ERROR, [&strongSelf assigned here,Using &strongSelf not checked for null,Using &strongSelf not checked for null] codetoanalyze/objc/self-in-block/StrongSelf.m, objc_blockSelfInBlockTest::strongSelfCheckOnce_bad_5, 6, STRONG_SELF_NOT_CHECKED, no_bucket, ERROR, [&strongSelf assigned here,Using &strongSelf not checked for null,Using &strongSelf not checked for null]
codetoanalyze/objc/self-in-block/StrongSelf.m, objc_blockSelfInBlockTest::strongSelfCheckOnce_bad_5, 8, STRONG_SELF_NOT_CHECKED, no_bucket, ERROR, [&strongSelf assigned here,Using &strongSelf not checked for null,Using &strongSelf not checked for null,Using &strongSelf not checked for null,Using &strongSelf not checked for null]
codetoanalyze/objc/self-in-block/StrongSelf.m, objc_blockSelfInBlockTest::strongSelfCheckOnce_bad_5, 10, STRONG_SELF_NOT_CHECKED, no_bucket, ERROR, [&strongSelf assigned here,Using &strongSelf not checked for null,Using &strongSelf not checked for null,Using &strongSelf not checked for null,Using &strongSelf not checked for null,Using &strongSelf not checked for null,Using &strongSelf not checked for null,Using &strongSelf not checked for null]
codetoanalyze/objc/self-in-block/StrongSelf.m, objc_blockSelfInBlockTest::strongSelfNoCheckNotWeakSelf_good_2, 1, CAPTURED_STRONG_SELF, no_bucket, ERROR, [Using captured &weakSelf] codetoanalyze/objc/self-in-block/StrongSelf.m, objc_blockSelfInBlockTest::strongSelfNoCheckNotWeakSelf_good_2, 1, CAPTURED_STRONG_SELF, no_bucket, ERROR, [Using captured &weakSelf]
codetoanalyze/objc/self-in-block/StrongSelf.m, objc_blockSelfInBlockTest::weakSelfMultiple_bad_11, 1, MULTIPLE_WEAKSELF, no_bucket, ERROR, [Using &weakSelf,Using &weakSelf] codetoanalyze/objc/self-in-block/StrongSelf.m, objc_blockSelfInBlockTest::weakSelfMultiple_bad_14, 1, MULTIPLE_WEAKSELF, no_bucket, ERROR, [Using &weakSelf,Using &weakSelf]
codetoanalyze/objc/self-in-block/StrongSelf.m, objc_blockobjc_blockSelfInBlockTest::capturedStrongSelf_bad_12_13, 1, CAPTURED_STRONG_SELF, no_bucket, ERROR, [Using captured &strongSelf] codetoanalyze/objc/self-in-block/StrongSelf.m, objc_blockobjc_blockSelfInBlockTest::capturedStrongSelf_bad_15_16, 1, CAPTURED_STRONG_SELF, no_bucket, ERROR, [Using captured &strongSelf]

Loading…
Cancel
Save