diff --git a/infer/src/backend/Attribute.ml b/infer/src/backend/Attribute.ml index 3a81df1f8..500c7cd7a 100644 --- a/infer/src/backend/Attribute.ml +++ b/infer/src/backend/Attribute.ml @@ -194,17 +194,30 @@ let rec nullify_exp_with_objc_null tenv prop exp = | _ -> prop -(** mark Exp.Var's or Exp.Lvar's as undefined *) -let mark_vars_as_undefined tenv prop vars_to_mark callee_pname ret_annots loc path_pos = - let att_undef = PredSymb.Aundef (callee_pname, ret_annots, loc, path_pos) in - let mark_var_as_undefined exp prop = +(** mark Exp.Var's or Exp.Lvar's as undefined +The annotations of the return type of the method get propagated to the return id, +with the exception of when the return type is a struct, and we translate it as passing a reference +to the method. *) +let mark_vars_as_undefined tenv prop ~ret_exp_opt ~undefined_actuals_by_ref callee_pname ret_annots + loc path_pos = + let mark_var_as_undefined ~annot exp prop = match exp with | Exp.Var _ | Lvar _ - -> add_or_replace tenv prop (Apred (att_undef, [exp])) + -> let att_undef = PredSymb.Aundef (callee_pname, annot, loc, path_pos) in + add_or_replace tenv prop (Apred (att_undef, [exp])) | _ -> prop in - List.fold ~f:(fun prop id -> mark_var_as_undefined id prop) ~init:prop vars_to_mark + let prop_with_ret_attr = + match ret_exp_opt with + | Some ret_exp + -> mark_var_as_undefined ~annot:ret_annots ret_exp prop + | None + -> prop + in + List.fold + ~f:(fun prop id -> mark_var_as_undefined ~annot:[] id prop) + ~init:prop_with_ret_attr undefined_actuals_by_ref (** type for arithmetic problems *) type arith_problem = diff --git a/infer/src/backend/Attribute.mli b/infer/src/backend/Attribute.mli index 7ba3933f1..b9c7d24ad 100644 --- a/infer/src/backend/Attribute.mli +++ b/infer/src/backend/Attribute.mli @@ -92,8 +92,8 @@ val nullify_exp_with_objc_null : Tenv.t -> Prop.normal Prop.t -> Exp.t -> Prop.n remove the attribute and conjoin an equality to zero. *) val mark_vars_as_undefined : - Tenv.t -> Prop.normal Prop.t -> Exp.t list -> Typ.Procname.t -> Annot.Item.t -> Location.t - -> PredSymb.path_pos -> Prop.normal Prop.t + Tenv.t -> Prop.normal Prop.t -> ret_exp_opt:Exp.t option -> undefined_actuals_by_ref:Exp.t list + -> Typ.Procname.t -> Annot.Item.t -> Location.t -> PredSymb.path_pos -> Prop.normal Prop.t (** mark Exp.Var's or Exp.Lvar's as undefined *) (** type for arithmetic problems *) diff --git a/infer/src/backend/symExec.ml b/infer/src/backend/symExec.ml index 510d6b9f6..a84a332f0 100644 --- a/infer/src/backend/symExec.ml +++ b/infer/src/backend/symExec.ml @@ -1578,16 +1578,12 @@ and unknown_or_scan_call ~is_scan ret_type_option ret_annots [(Tabulation.remove_constant_string_class tenv pre_final, path)] else (* otherwise, add undefined attribute to retvals and actuals passed by ref *) - let exps_to_mark = - let ret_exps = Option.value_map ~f:(fun (id, _) -> [Exp.Var id]) ~default:[] ret_id in - List.fold - ~f:(fun exps_to_mark (exp, _, _) -> exp :: exps_to_mark) - ~init:ret_exps actuals_by_ref - in + let undefined_actuals_by_ref = List.map ~f:(fun (exp, _, _) -> exp) actuals_by_ref in + let ret_exp_opt = Option.map ~f:(fun (id, _) -> Exp.Var id) ret_id in let prop_with_undef_attr = let path_pos = State.get_path_pos () in - Attribute.mark_vars_as_undefined tenv pre_final exps_to_mark callee_pname ret_annots loc - path_pos + Attribute.mark_vars_as_undefined tenv pre_final ~ret_exp_opt ~undefined_actuals_by_ref + callee_pname ret_annots loc path_pos in let reason = "function or method not found" in let skip_path = Paths.Path.add_skipped_call path callee_pname reason in diff --git a/infer/tests/codetoanalyze/objc/errors/Makefile b/infer/tests/codetoanalyze/objc/errors/Makefile index ec97c1900..bbd91ef2e 100644 --- a/infer/tests/codetoanalyze/objc/errors/Makefile +++ b/infer/tests/codetoanalyze/objc/errors/Makefile @@ -97,6 +97,7 @@ SOURCES_ARC = \ npe/nil_in_dictionary_literal.m \ npe/npe_conditional.m \ npe/npe_self.m \ + npe/Npe_self_annotation.m \ npe/nullable.m \ property/ExplicitIvarName.m \ shared/block/dispatch_examples.m \ diff --git a/infer/tests/codetoanalyze/objc/errors/npe/Npe_self_annotation.m b/infer/tests/codetoanalyze/objc/errors/npe/Npe_self_annotation.m new file mode 100644 index 000000000..e1868f945 --- /dev/null +++ b/infer/tests/codetoanalyze/objc/errors/npe/Npe_self_annotation.m @@ -0,0 +1,29 @@ +/* + * 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 B : NSObject + +@end + +@interface A + ++ (nullable NSData*)foo:(B*)b; + +@end + +@implementation B { + B* _decodedMetadata; +} + +- (B*)decodedMetadata { + NSData* metadata = [A foo:self]; + return _decodedMetadata; // No NPE here +} +@end