diff --git a/infer/src/checkers/NullabilityCheck.ml b/infer/src/checkers/NullabilityCheck.ml index 5b8b383c7..da4c83e0c 100644 --- a/infer/src/checkers/NullabilityCheck.ml +++ b/infer/src/checkers/NullabilityCheck.ml @@ -21,7 +21,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct type extras = Specs.summary - let is_instance_method callee_pname = + let is_non_objc_instance_method callee_pname = if Typ.Procname.is_java callee_pname then not (Typ.Procname.java_is_static callee_pname) else Option.exists @@ -29,6 +29,12 @@ module TransferFunctions (CFG : ProcCfg.S) = struct (Specs.proc_resolve_attributes callee_pname) + let is_objc_instance_method callee_pname = + Option.exists + ~f:(fun attributes -> attributes.ProcAttributes.is_objc_instance_method) + (Specs.proc_resolve_attributes callee_pname) + + let is_blacklisted_method : Typ.Procname.t -> bool = let blacklist = ["URLWithString:"] in fun proc_name -> @@ -156,16 +162,16 @@ module TransferFunctions (CFG : ProcCfg.S) = struct assume_pnames_notnull ap astate - let rec check_nil_in_nsarray proc_data loc args astate = + let rec check_nil_in_objc_container proc_data loc args astate = match args with | [] -> astate | [arg] when HilExp.is_null_literal arg -> astate | (HilExp.AccessPath ap) :: other_args -> - check_nil_in_nsarray proc_data loc other_args (check_ap proc_data loc ap astate) + check_nil_in_objc_container proc_data loc other_args (check_ap proc_data loc ap astate) | _ :: other_args -> - check_nil_in_nsarray proc_data loc other_args astate + check_nil_in_objc_container proc_data loc other_args astate let exec_instr ((_, checked_pnames) as astate) proc_data _ (instr: HilInstr.t) : Domain.astate = @@ -192,10 +198,18 @@ module TransferFunctions (CFG : ProcCfg.S) = struct let call_site = CallSite.make callee_pname loc in add_nullable_ap (ret_var, []) (CallSites.singleton call_site) astate | Call (_, Direct callee_pname, (HilExp.AccessPath receiver) :: _, _, loc) - when is_instance_method callee_pname -> + when is_non_objc_instance_method callee_pname -> check_ap proc_data loc receiver astate | Call (_, Direct callee_pname, args, _, loc) when is_objc_container_add_method callee_pname -> - check_nil_in_nsarray proc_data loc args astate + check_nil_in_objc_container proc_data loc args astate + | Call (Some ret_var, Direct callee_pname, (HilExp.AccessPath receiver) :: _, _, _) + when is_objc_instance_method callee_pname -> ( + match longest_nullable_prefix receiver astate with + | None -> + astate + | Some (_, call_sites) -> + (* Objective C method will return nil when called on a nil receiver *) + add_nullable_ap (ret_var, []) call_sites astate ) | Call (Some ret_var, _, _, _, _) -> remove_nullable_ap (ret_var, []) astate | Assign (lhs, rhs, loc) diff --git a/infer/tests/codetoanalyze/objc/checkers/Nullable.m b/infer/tests/codetoanalyze/objc/checkers/Nullable.m index e8427aaf7..c0d20c17c 100644 --- a/infer/tests/codetoanalyze/objc/checkers/Nullable.m +++ b/infer/tests/codetoanalyze/objc/checkers/Nullable.m @@ -185,4 +185,12 @@ int* __nullable returnsNull(); return dict; } +- (NSDictionary*)indirectNullableKeyInNSDictionaryBad { + NSObject* nullableKey = [self nullableMethod]; + NSString* nullableKeyString = [nullableKey description]; + NSDictionary* dict = [[NSDictionary alloc] + initWithObjectsAndKeys:nullableKeyString, @"value", nil]; // reports here + return dict; +} + @end diff --git a/infer/tests/codetoanalyze/objc/checkers/issues.exp b/infer/tests/codetoanalyze/objc/checkers/issues.exp index 3e2d41cfd..16e844dca 100644 --- a/infer/tests/codetoanalyze/objc/checkers/issues.exp +++ b/infer/tests/codetoanalyze/objc/checkers/issues.exp @@ -9,6 +9,8 @@ codetoanalyze/objc/checkers/Nullable.m, T_dereferenceNullableFunctionBad, 2, NUL codetoanalyze/objc/checkers/Nullable.m, T_dereferenceNullableFunctionBad, 2, NULL_DEREFERENCE, [start of procedure dereferenceNullableFunctionBad,Skipping returnsNull(): function or method not found] codetoanalyze/objc/checkers/Nullable.m, T_dereferenceUnnanotatedFieldAfterTestForNullBad, 1, FIELD_SHOULD_BE_NULLABLE, [Field unnanotatedField is compared to null here] codetoanalyze/objc/checkers/Nullable.m, T_dereferenceUnnanotatedFieldAfterTestForNullBad, 2, NULL_DEREFERENCE, [start of procedure dereferenceUnnanotatedFieldAfterTestForNullBad,Condition is true] +codetoanalyze/objc/checkers/Nullable.m, T_indirectNullableKeyInNSDictionaryBad, 3, NULLABLE_DEREFERENCE, [dereference of &nullableKeyString,assignment of the nullable value,definition of nullableMethod] +codetoanalyze/objc/checkers/Nullable.m, T_indirectNullableKeyInNSDictionaryBad, 3, PREMATURE_NIL_TERMINATION_ARGUMENT, [start of procedure indirectNullableKeyInNSDictionaryBad,start of procedure nullableMethod,return from a call to T_nullableMethod,Message description with receiver nil returns nil.] codetoanalyze/objc/checkers/Nullable.m, T_nullableKeyInNSDictionaryBad, 2, NULLABLE_DEREFERENCE, [dereference of &nullableKey,assignment of the nullable value,definition of nullableMethod] codetoanalyze/objc/checkers/Nullable.m, T_nullableKeyInNSDictionaryBad, 2, PREMATURE_NIL_TERMINATION_ARGUMENT, [start of procedure nullableKeyInNSDictionaryBad,start of procedure nullableMethod,return from a call to T_nullableMethod] codetoanalyze/objc/checkers/Nullable.m, T_nullableKeyInNSDictionaryInitBad, 2, NULLABLE_DEREFERENCE, [dereference of &nullableKey,assignment of the nullable value,definition of nullableMethod]