[tech debt] Remove experimental nullability checkers

Summary:
These were not used (and were actually activated byt the same config
param). They both are in experimental stage that never reached maturity.

Since the team does not have immediate plans to work on ObjC nullability
checker; and since "eradicate" (now known as nullsafe) is the main
solution for Java, removing it is sensible.

Reviewed By: jvillard

Differential Revision: D20279866

fbshipit-source-id: 79e64992b
master
Mitya Lyubarskiy 5 years ago committed by Facebook Github Bot
parent fa4f9ef5f3
commit bd83813b3e

@ -66,7 +66,6 @@ DIRECT_TESTS += \
cpp_linters \ cpp_linters \
cpp_linters-for-test-only \ cpp_linters-for-test-only \
cpp_liveness \ cpp_liveness \
cpp_nullable \
cpp_pulse \ cpp_pulse \
cpp_quandary cpp_quandaryBO \ cpp_quandary cpp_quandaryBO \
cpp_racerd \ cpp_racerd \
@ -115,7 +114,6 @@ DIRECT_TESTS += \
objc_linters-def-folder \ objc_linters-def-folder \
objc_linters-for-test-only \ objc_linters-for-test-only \
objc_liveness \ objc_liveness \
objc_nullable \
objc_performance \ objc_performance \
objc_pulse \ objc_pulse \
objc_quandary \ objc_quandary \
@ -126,7 +124,6 @@ DIRECT_TESTS += \
objcpp_linters \ objcpp_linters \
objcpp_linters-for-test-only \ objcpp_linters-for-test-only \
objcpp_liveness \ objcpp_liveness \
objcpp_nullable \
objcpp_pulse \ objcpp_pulse \
objcpp_racerd \ objcpp_racerd \
objcpp_retain-cycles \ objcpp_retain-cycles \

@ -201,8 +201,8 @@ OPTIONS
(Conversely: --no-loop-hoisting-only) (Conversely: --no-loop-hoisting-only)
--nullsafe --nullsafe
Activates: [EXPERIMENTAL] Nullable type checker (incomplete: use Activates: [RESERVED] Reserved for nullsafe typechecker, use
--eradicate for now) (Conversely: --no-nullsafe) --eradicate for now (Conversely: --no-nullsafe)
--nullsafe-only --nullsafe-only
Activates: Enable --nullsafe and disable all other checkers Activates: Enable --nullsafe and disable all other checkers

@ -470,8 +470,6 @@ OPTIONS
MUTABLE_LOCAL_VARIABLE_IN_COMPONENT_FILE (enabled by default), MUTABLE_LOCAL_VARIABLE_IN_COMPONENT_FILE (enabled by default),
Missing_fld (enabled by default), Missing_fld (enabled by default),
NULLPTR_DEREFERENCE (disabled by default), NULLPTR_DEREFERENCE (disabled by default),
NULLSAFE_FIELD_NOT_NULLABLE (enabled by default),
NULLSAFE_NULLABLE_DEREFERENCE (enabled by default),
NULL_DEREFERENCE (enabled by default), NULL_DEREFERENCE (enabled by default),
NULL_TEST_AFTER_DEREFERENCE (disabled by default), NULL_TEST_AFTER_DEREFERENCE (disabled by default),
PARAMETER_NOT_NULL_CHECKED (enabled by default), PARAMETER_NOT_NULL_CHECKED (enabled by default),
@ -801,8 +799,8 @@ OPTIONS
See also infer-analyze(1). See also infer-analyze(1).
--nullsafe --nullsafe
Activates: [EXPERIMENTAL] Nullable type checker (incomplete: use Activates: [RESERVED] Reserved for nullsafe typechecker, use
--eradicate for now) (Conversely: --no-nullsafe) See also infer-analyze(1). --eradicate for now (Conversely: --no-nullsafe) See also infer-analyze(1).
--nullsafe-only --nullsafe-only
Activates: Enable --nullsafe and disable all other checkers Activates: Enable --nullsafe and disable all other checkers

@ -192,8 +192,6 @@ OPTIONS
MUTABLE_LOCAL_VARIABLE_IN_COMPONENT_FILE (enabled by default), MUTABLE_LOCAL_VARIABLE_IN_COMPONENT_FILE (enabled by default),
Missing_fld (enabled by default), Missing_fld (enabled by default),
NULLPTR_DEREFERENCE (disabled by default), NULLPTR_DEREFERENCE (disabled by default),
NULLSAFE_FIELD_NOT_NULLABLE (enabled by default),
NULLSAFE_NULLABLE_DEREFERENCE (enabled by default),
NULL_DEREFERENCE (enabled by default), NULL_DEREFERENCE (enabled by default),
NULL_TEST_AFTER_DEREFERENCE (disabled by default), NULL_TEST_AFTER_DEREFERENCE (disabled by default),
PARAMETER_NOT_NULL_CHECKED (enabled by default), PARAMETER_NOT_NULL_CHECKED (enabled by default),

@ -470,8 +470,6 @@ OPTIONS
MUTABLE_LOCAL_VARIABLE_IN_COMPONENT_FILE (enabled by default), MUTABLE_LOCAL_VARIABLE_IN_COMPONENT_FILE (enabled by default),
Missing_fld (enabled by default), Missing_fld (enabled by default),
NULLPTR_DEREFERENCE (disabled by default), NULLPTR_DEREFERENCE (disabled by default),
NULLSAFE_FIELD_NOT_NULLABLE (enabled by default),
NULLSAFE_NULLABLE_DEREFERENCE (enabled by default),
NULL_DEREFERENCE (enabled by default), NULL_DEREFERENCE (enabled by default),
NULL_TEST_AFTER_DEREFERENCE (disabled by default), NULL_TEST_AFTER_DEREFERENCE (disabled by default),
PARAMETER_NOT_NULL_CHECKED (enabled by default), PARAMETER_NOT_NULL_CHECKED (enabled by default),
@ -801,8 +799,8 @@ OPTIONS
See also infer-analyze(1). See also infer-analyze(1).
--nullsafe --nullsafe
Activates: [EXPERIMENTAL] Nullable type checker (incomplete: use Activates: [RESERVED] Reserved for nullsafe typechecker, use
--eradicate for now) (Conversely: --no-nullsafe) See also infer-analyze(1). --eradicate for now (Conversely: --no-nullsafe) See also infer-analyze(1).
--nullsafe-only --nullsafe-only
Activates: Enable --nullsafe and disable all other checkers Activates: Enable --nullsafe and disable all other checkers

@ -55,14 +55,6 @@ module Raw = struct
let equal = [%compare.equal: t] let equal = [%compare.equal: t]
let truncate ((base, accesses) as t) =
match List.rev accesses with
| [] ->
(t, None)
| last_access :: accesses ->
((base, List.rev accesses), Some last_access)
let lookup_field_type_annot tenv base_typ field_name = let lookup_field_type_annot tenv base_typ field_name =
let lookup = Tenv.lookup tenv in let lookup = Tenv.lookup tenv in
Struct.get_field_type_and_annotation ~lookup field_name base_typ Struct.get_field_type_and_annotation ~lookup field_name base_typ
@ -76,16 +68,6 @@ module Raw = struct
Some array_typ Some array_typ
(* For field access, get the field name and the annotation associated with it
* Return None if given an array access, or if the info cannot be obtained *)
let get_access_field_annot tenv base_typ = function
| FieldAccess field_name ->
Option.map (lookup_field_type_annot tenv base_typ field_name) ~f:(fun (_, annot) ->
(field_name, annot) )
| ArrayAccess _ ->
None
(* Extract the last access of the given access path together with its base type. (* Extract the last access of the given access path together with its base type.
* Here the base type is defined to be the declaring class of the last accessed field, * Here the base type is defined to be the declaring class of the last accessed field,
* or the type of base if the access list is empty. * or the type of base if the access list is empty.
@ -109,16 +91,6 @@ module Raw = struct
last_access_info_impl tenv base_typ accesses last_access_info_impl tenv base_typ accesses
let get_last_access (_, accesses) = List.last accesses
let get_field_and_annotation ap tenv =
match last_access_info ap tenv with
| Some base_typ, Some access ->
get_access_field_annot tenv base_typ access
| _ ->
None
let get_typ ap tenv = let get_typ ap tenv =
match last_access_info ap tenv with match last_access_info ap tenv with
| (Some _ as typ), None -> | (Some _ as typ), None ->

@ -20,17 +20,6 @@ type access =
as (x, [f; g]) *) as (x, [f; g]) *)
and t = base * access list [@@deriving compare] and t = base * access list [@@deriving compare]
val truncate : t -> t * access option
(** remove and return the last access of the access path if the access list is non-empty. returns
the original access path * None if the access list is empty *)
val get_last_access : t -> access option
(** get the last access in the list. returns None if the list is empty *)
val get_field_and_annotation : t -> Tenv.t -> (Fieldname.t * Annot.Item.t) option
(** get the field name and the annotation of the last access in the list of accesses if the list is
non-empty and the last access is a field access *)
val get_typ : t -> Tenv.t -> Typ.t option val get_typ : t -> Tenv.t -> Typ.t option
(** get the typ of the last access in the list of accesses if the list is non-empty, or the base if (** get the typ of the last access in the list of accesses if the list is non-empty, or the base if
the list is empty. that is, for x.f.g, return typ(g), and for x, return typ(x) *) the list is empty. that is, for x.f.g, return typ(g), and for x, return typ(x) *)

@ -51,10 +51,6 @@ let to_full_string fld =
let pp f fld = F.pp_print_string f fld.field_name let pp f fld = F.pp_print_string f fld.field_name
let is_java_captured_parameter ({field_name} as field) =
is_java field && String.is_prefix ~prefix:"val$" field_name
let is_java_outer_instance ({field_name} as field) = let is_java_outer_instance ({field_name} as field) =
is_java field is_java field
&& &&

@ -26,9 +26,6 @@ module Set : Caml.Set.S with type elt = t
module Map : Caml.Map.S with type key = t module Map : Caml.Map.S with type key = t
(** Map for fieldnames *) (** Map for fieldnames *)
val is_java_captured_parameter : t -> bool
(** Check if field is a captured parameter *)
val is_java_outer_instance : t -> bool val is_java_outer_instance : t -> bool
(** Check if the field is the synthetic this$n of a nested class, used to access the n-th outer (** Check if the field is the synthetic this$n of a nested class, used to access the n-th outer
instance. *) instance. *)

@ -94,9 +94,6 @@ type access =
| Initialized_automatically | Initialized_automatically
| Returned_from_call of int | Returned_from_call of int
val nullable_annotation_name : Procname.t -> string
(** Name of the nullable annotation *)
val dereference_string : val dereference_string :
Procname.t -> deref_str -> string -> access option -> Location.t -> error_desc Procname.t -> deref_str -> string -> access option -> Location.t -> error_desc

@ -32,7 +32,6 @@ type checkers =
; litho_required_props: bool ref ; litho_required_props: bool ref
; liveness: bool ref ; liveness: bool ref
; loop_hoisting: bool ref ; loop_hoisting: bool ref
; nullsafe: bool ref
; self_in_block: bool ref ; self_in_block: bool ref
; printf_args: bool ref ; printf_args: bool ref
; pulse: bool ref ; pulse: bool ref
@ -631,7 +630,6 @@ and { annotation_reachability
; litho_required_props ; litho_required_props
; liveness ; liveness
; loop_hoisting ; loop_hoisting
; nullsafe
; self_in_block ; self_in_block
; printf_args ; printf_args
; pulse ; pulse
@ -643,9 +641,11 @@ and { annotation_reachability
; siof ; siof
; starvation ; starvation
; uninit } = ; uninit } =
let mk_checker ?(default = false) ?(deprecated = []) ~long doc = let mk_checker ?(default = false) ?(deprecated = []) ~long ?f doc =
let var = let var =
CLOpt.mk_bool ~long ~in_help:InferCommand.[(Analyze, manual_generic)] ~default ~deprecated doc CLOpt.mk_bool ?f ~long
~in_help:InferCommand.[(Analyze, manual_generic)]
~default ~deprecated doc
in in
all_checkers := (var, long, doc, default) :: !all_checkers ; all_checkers := (var, long, doc, default) :: !all_checkers ;
var var
@ -681,10 +681,14 @@ and { annotation_reachability
and liveness = and liveness =
mk_checker ~long:"liveness" ~default:true "the detection of dead stores and unused variables" mk_checker ~long:"liveness" ~default:true "the detection of dead stores and unused variables"
and loop_hoisting = mk_checker ~long:"loop-hoisting" ~default:false "checker for loop-hoisting" and loop_hoisting = mk_checker ~long:"loop-hoisting" ~default:false "checker for loop-hoisting"
and nullsafe = and _nullsafe =
(* TODO make this to be activate nullsafe typechecker when old usages are gone *)
mk_checker ~long:"nullsafe" mk_checker ~long:"nullsafe"
~f:(fun b ->
CLOpt.warnf "nullsafe is is a reserved (no-op) checker; use --eradicate\n" ;
b )
~deprecated:["-check-nullable"; "-suggest-nullable"] ~deprecated:["-check-nullable"; "-suggest-nullable"]
"[EXPERIMENTAL] Nullable type checker (incomplete: use --eradicate for now)" "[RESERVED] Reserved for nullsafe typechecker, use --eradicate for now"
and printf_args = and printf_args =
mk_checker ~long:"printf-args" ~default:false mk_checker ~long:"printf-args" ~default:false
"the detection of mismatch between the Java printf format strings and the argument types \ "the detection of mismatch between the Java printf format strings and the argument types \
@ -758,7 +762,6 @@ and { annotation_reachability
; litho_required_props ; litho_required_props
; liveness ; liveness
; loop_hoisting ; loop_hoisting
; nullsafe
; printf_args ; printf_args
; pulse ; pulse
; purity ; purity
@ -2810,8 +2813,6 @@ and censor_report =
and changed_files_index = !changed_files_index and changed_files_index = !changed_files_index
and nullsafe = !nullsafe
and check_version = !check_version and check_version = !check_version
and clang_biniou_file = !clang_biniou_file and clang_biniou_file = !clang_biniou_file

@ -489,8 +489,6 @@ val no_translate_libs : bool
val nullable_annotation : string option val nullable_annotation : string option
val nullsafe : bool
val nullsafe_disable_field_not_initialized_in_nonstrict_classes : bool val nullsafe_disable_field_not_initialized_in_nonstrict_classes : bool
val nullsafe_optimistic_third_party_params_in_non_strict : bool val nullsafe_optimistic_third_party_params_in_non_strict : bool

@ -378,14 +378,6 @@ let null_test_after_dereference = register_from_string ~enabled:false "NULL_TEST
let nullptr_dereference = register_from_string ~enabled:false "NULLPTR_DEREFERENCE" let nullptr_dereference = register_from_string ~enabled:false "NULLPTR_DEREFERENCE"
let nullsafe_field_not_nullable =
register_from_string "NULLSAFE_FIELD_NOT_NULLABLE" ~hum:"Field Not Nullable"
let nullsafe_nullable_dereference =
register_from_string "NULLSAFE_NULLABLE_DEREFERENCE" ~hum:"Nullable Dereference"
let parameter_not_null_checked = register_from_string "PARAMETER_NOT_NULL_CHECKED" let parameter_not_null_checked = register_from_string "PARAMETER_NOT_NULL_CHECKED"
let pointer_size_mismatch = register_from_string "POINTER_SIZE_MISMATCH" let pointer_size_mismatch = register_from_string "POINTER_SIZE_MISMATCH"

@ -250,10 +250,6 @@ val null_test_after_dereference : t
val nullptr_dereference : t val nullptr_dereference : t
val nullsafe_field_not_nullable : t
val nullsafe_nullable_dereference : t
val parameter_not_null_checked : t val parameter_not_null_checked : t
val pointer_size_mismatch : t val pointer_size_mismatch : t

@ -35,11 +35,6 @@ let all_checkers =
; callbacks= ; callbacks=
[ (Procedure AnnotationReachability.checker, Language.Java) [ (Procedure AnnotationReachability.checker, Language.Java)
; (Procedure AnnotationReachability.checker, Language.Clang) ] } ; (Procedure AnnotationReachability.checker, Language.Clang) ] }
; { name= "nullable checks"
; active= Config.nullsafe
; callbacks=
[ (Procedure NullabilityCheck.checker, Language.Clang)
; (Procedure NullabilityCheck.checker, Language.Java) ] }
; { name= "biabduction" ; { name= "biabduction"
; active= Config.biabduction ; active= Config.biabduction
; callbacks= ; callbacks=
@ -76,11 +71,6 @@ let all_checkers =
; { name= "printf args" ; { name= "printf args"
; active= Config.printf_args ; active= Config.printf_args
; callbacks= [(Procedure PrintfArgs.callback_printf_args, Language.Java)] } ; callbacks= [(Procedure PrintfArgs.callback_printf_args, Language.Java)] }
; { name= "nullable suggestion"
; active= Config.nullsafe
; callbacks=
[ (Procedure NullabilitySuggest.checker, Language.Java)
; (Procedure NullabilitySuggest.checker, Language.Clang) ] }
; { name= "impurity" ; { name= "impurity"
; active= Config.impurity ; active= Config.impurity
; callbacks= ; callbacks=

@ -1,339 +0,0 @@
(*
* 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 L = Logging
module MF = MarkupFormatter
module CallSites = AbstractDomain.FiniteSet (CallSite)
module NullableAP = AbstractDomain.Map (AccessPath) (CallSites)
module NullCheckedPname = AbstractDomain.InvertedSet (Procname)
module Domain = AbstractDomain.Pair (NullableAP) (NullCheckedPname)
module TransferFunctions (CFG : ProcCfg.S) = struct
module CFG = CFG
module Domain = Domain
type extras = unit
let rec is_pointer_subtype tenv typ1 typ2 =
match (typ1.Typ.desc, typ2.Typ.desc) with
| Typ.Tptr (t1, _), Typ.Tptr (t2, _) -> (
match (t1.Typ.desc, t2.Typ.desc) with
| Typ.Tstruct n1, Typ.Tstruct n2 ->
Subtype.is_known_subtype tenv n1 n2
| _ ->
Typ.equal t1 t2 || is_pointer_subtype tenv t1 t2 )
| _ ->
false
let is_non_objc_instance_method callee_pname =
match callee_pname with
| Procname.Java java_pname ->
not (Procname.Java.is_static java_pname)
| _ ->
Option.exists
~f:(fun attributes ->
ClangMethodKind.equal attributes.ProcAttributes.clang_method_kind
ClangMethodKind.CPP_INSTANCE )
(Summary.OnDisk.proc_resolve_attributes callee_pname)
let is_objc_instance_method callee_pname =
Option.exists
~f:(fun attributes ->
ClangMethodKind.equal attributes.ProcAttributes.clang_method_kind
ClangMethodKind.OBJC_INSTANCE )
(Summary.OnDisk.proc_resolve_attributes callee_pname)
let is_blacklisted_method : Procname.t -> bool =
let blacklist = ["URLWithString:"; "objectForKeyedSubscript:"] in
fun proc_name ->
let simplified_callee_pname = Procname.to_simplified_string proc_name in
List.exists ~f:(String.equal simplified_callee_pname) blacklist
let container_method_regex =
Str.regexp @@ "^\\(NS.*::\\(arrayByAddingObject\\|arrayWithObjects\\|"
^ "dictionaryWithObjects\\|dictionaryWithObjectsAndKeys\\|initWithObjectsAndKeys\\|"
^ "addObject\\|insertObject\\|setObject\\|"
^ "stringWithUTF8String\\|stringWithString\\|initWithFormat\\|stringByAppendingString\\):\\|"
^ "std::basic_string\\).*"
let is_objc_container_add_method proc_name =
let callee_pname = Procname.to_string proc_name in
Str.string_match container_method_regex callee_pname 0
let is_conflicting_report summary report_location =
(* FIXME(T54950303) replace use of filtering with deduplicate *)
if not Config.filtering then false
else
Errlog.fold
(fun {Errlog.err_name; err_desc} {Errlog.loc} found_confict ->
found_confict
|| IssueType.equal err_name IssueType.null_dereference
&& Location.equal loc report_location
&& Localise.error_desc_is_reportable_bucket err_desc )
(Summary.get_err_log summary) false
(* On Clang languages, the annotations like _Nullabe can be found on the declaration
or on the implementation without the two being necessarily consistent.
Here, we explicitely want to lookup the annotations locally: either form
the implementation when defined locally, or from the included headers *)
let lookup_local_attributes = function
| Procname.Java _ as pname ->
(* Looking up the attribute according to the classpath *)
Summary.OnDisk.proc_resolve_attributes pname
| pname ->
(* Looking up the attributes locally, i.e. either from the file of from the includes *)
Option.map ~f:Procdesc.get_attributes (Ondemand.get_proc_desc pname)
let report_nullable_dereference ap call_sites {ProcData.summary} loc =
if is_conflicting_report summary loc then ()
else
let pname = Summary.get_proc_name summary in
let annotation = Localise.nullable_annotation_name pname in
let call_site =
try CallSites.min_elt call_sites
with Caml.Not_found ->
L.(die InternalError)
"Expecting a least one element in the set of call sites when analyzing %a" Procname.pp
pname
in
let simplified_pname =
Procname.to_simplified_string ~withclass:true (CallSite.pname call_site)
in
let is_direct_dereference =
match ap with
| (Var.LogicalVar _, _), _ ->
true
| (Var.ProgramVar pvar, _), _ ->
Pvar.is_frontend_tmp pvar
in
let message =
if is_direct_dereference then
(* direct dereference without intermediate variable *)
F.asprintf
"The return value of %s is annotated with %a and is dereferenced without being checked \
for null at %a"
(MF.monospaced_to_string simplified_pname)
MF.pp_monospaced annotation Location.pp loc
else
(* dereference with intermediate variable *)
F.asprintf
"Variable %a is indirectly annotated with %a (source %a) and is dereferenced without \
being checked for null at %a"
(MF.wrap_monospaced AccessPath.pp)
ap MF.pp_monospaced annotation (MF.wrap_monospaced CallSite.pp) call_site Location.pp
loc
in
let trace =
let with_origin_site =
let callee_pname = CallSite.pname call_site in
match lookup_local_attributes callee_pname with
| None ->
[]
| Some attributes ->
let description = F.asprintf "definition of %s" (Procname.get_method callee_pname) in
let trace_element =
Errlog.make_trace_element 1 attributes.ProcAttributes.loc description []
in
[trace_element]
in
let with_assignment_site =
let call_site_loc = CallSite.loc call_site in
if Location.equal call_site_loc loc then with_origin_site
else
let trace_element =
Errlog.make_trace_element 0 call_site_loc "assignment of the nullable value" []
in
trace_element :: with_origin_site
in
let dereference_site =
let description =
if is_direct_dereference then
F.asprintf "dereferencing the return of %s" simplified_pname
else F.asprintf "dereference of %a" AccessPath.pp ap
in
Errlog.make_trace_element 0 loc description []
in
dereference_site :: with_assignment_site
in
Reporting.log_error summary ~loc ~ltr:trace IssueType.nullsafe_nullable_dereference message
let add_nullable_ap ap call_sites (aps, pnames) = (NullableAP.add ap call_sites aps, pnames)
let remove_nullable_ap ap (aps, pnames) = (NullableAP.remove ap aps, pnames)
let find_nullable_ap ap (aps, _) = NullableAP.find ap aps
let assume_pnames_notnull ap (aps, checked_pnames) : Domain.t =
let remove_call_sites ap aps =
let add_diff (to_remove : CallSites.t) ap call_sites map =
let remaining_call_sites = CallSites.diff call_sites to_remove in
if CallSites.is_empty remaining_call_sites then map
else NullableAP.add ap remaining_call_sites map
in
match NullableAP.find_opt ap aps with
| None ->
aps
| Some call_sites ->
let updated_aps = NullableAP.fold (add_diff call_sites) aps NullableAP.empty in
updated_aps
in
let updated_pnames =
try
let call_sites = NullableAP.find ap aps in
CallSites.fold
(fun call_site s -> NullCheckedPname.add (CallSite.pname call_site) s)
call_sites checked_pnames
with Caml.Not_found -> checked_pnames
in
(remove_call_sites ap aps, updated_pnames)
let rec longest_nullable_prefix ap ((nullable_aps, _) as astate) =
try Some (ap, NullableAP.find ap nullable_aps)
with Caml.Not_found -> (
match ap with
| _, [] ->
None
| p ->
longest_nullable_prefix (fst (AccessPath.truncate p)) astate )
let check_ap proc_data loc ap astate =
match longest_nullable_prefix ap astate with
| None ->
astate
| Some (nullable_ap, call_sites) ->
report_nullable_dereference nullable_ap call_sites proc_data loc ;
assume_pnames_notnull ap 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.AccessExpression access_expr :: other_args ->
let ap = HilExp.AccessExpression.to_access_path access_expr in
check_nil_in_objc_container proc_data loc other_args (check_ap proc_data loc ap astate)
| _ :: other_args ->
check_nil_in_objc_container proc_data loc other_args astate
let exec_instr ((_, checked_pnames) as astate) proc_data _ (instr : HilInstr.t) : Domain.t =
let is_pointer_assignment tenv lhs rhs =
(* the rhs has type int when assigning the lhs to null *)
if HilExp.is_null_literal rhs then true
(* the lhs and rhs have the same type in the case of pointer assignment
but the types are different when assigning the pointee *)
else
match (AccessPath.get_typ lhs tenv, HilExp.get_typ tenv rhs) with
(* defensive assumption when the types are not known *)
| None, _ | _, None ->
true
(* the rhs can be a subtype of the lhs *)
| Some lhs_typ, Some rhs_typ ->
is_pointer_subtype tenv rhs_typ lhs_typ
in
match instr with
| Call (ret_var, Direct callee_pname, _, _, _)
when NullCheckedPname.mem callee_pname checked_pnames ->
(* Do not report nullable when the method has already been checked for null *)
remove_nullable_ap (ret_var, []) astate
| Call (_, Direct callee_pname, HilExp.AccessExpression receiver :: _, _, _)
when Models.is_check_not_null callee_pname ->
assume_pnames_notnull (HilExp.AccessExpression.to_access_path receiver) astate
| Call (_, Direct callee_pname, _, _, _) when is_blacklisted_method callee_pname ->
astate
| Call (ret_var, Direct callee_pname, _, _, loc)
when Annotations.pname_has_return_annot callee_pname ~attrs_of_pname:lookup_local_attributes
Annotations.ia_is_nullable ->
let call_site = CallSite.make callee_pname loc in
add_nullable_ap (ret_var, []) (CallSites.singleton call_site) astate
| Call (_, Direct callee_pname, args, _, loc) when is_objc_container_add_method callee_pname ->
check_nil_in_objc_container proc_data loc args astate
| Call (_, Direct callee_pname, HilExp.AccessExpression receiver :: _, _, loc)
when is_non_objc_instance_method callee_pname ->
check_ap proc_data loc (HilExp.AccessExpression.to_access_path receiver) astate
| Call
(((_, ret_typ) as ret_var), Direct callee_pname, HilExp.AccessExpression receiver :: _, _, _)
when Typ.is_pointer ret_typ && is_objc_instance_method callee_pname -> (
match longest_nullable_prefix (HilExp.AccessExpression.to_access_path 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 (ret_var, _, _, _, _) ->
remove_nullable_ap (ret_var, []) astate
| Assign (lhs_access_expr, rhs, loc) -> (
let lhs = HilExp.AccessExpression.to_access_path lhs_access_expr in
Option.iter
~f:(fun (nullable_ap, call_sites) ->
if not (is_pointer_assignment proc_data.ProcData.tenv nullable_ap rhs) then
(* TODO (T22426288): Undertand why the pointer derference and the pointer
assignment have the same HIL representation *)
report_nullable_dereference nullable_ap call_sites proc_data loc )
(longest_nullable_prefix lhs astate) ;
match rhs with
| HilExp.AccessExpression access_expr -> (
try
(* Add the lhs to the list of nullable values if the rhs is nullable *)
let ap = HilExp.AccessExpression.to_access_path access_expr in
add_nullable_ap lhs (find_nullable_ap ap astate) astate
with Caml.Not_found ->
(* Remove the lhs from the list of nullable values if the rhs is not nullable *)
remove_nullable_ap lhs astate )
| _ ->
(* Remove the lhs from the list of nullable values if the rhs is not an access path *)
remove_nullable_ap lhs astate )
| Assume (HilExp.AccessExpression access_expr, _, _, _) ->
assume_pnames_notnull (HilExp.AccessExpression.to_access_path access_expr) astate
| Assume
( ( HilExp.BinaryOperator (Binop.Ne, HilExp.AccessExpression access_expr, exp)
| HilExp.BinaryOperator (Binop.Ne, exp, HilExp.AccessExpression access_expr) )
, _
, _
, _ )
| Assume
( HilExp.UnaryOperator
( Unop.LNot
, ( HilExp.BinaryOperator (Binop.Eq, HilExp.AccessExpression access_expr, exp)
| HilExp.BinaryOperator (Binop.Eq, exp, HilExp.AccessExpression access_expr) )
, _ )
, _
, _
, _ ) ->
if HilExp.is_null_literal exp then
assume_pnames_notnull (HilExp.AccessExpression.to_access_path access_expr) astate
else astate
| _ ->
astate
let pp_session_name _node fmt = F.pp_print_string fmt "nullability check"
end
module Analyzer = LowerHil.MakeAbstractInterpreter (TransferFunctions (ProcCfg.Exceptional))
let checker {Callbacks.summary; exe_env} =
let initial = (NullableAP.empty, NullCheckedPname.empty) in
let tenv = Exe_env.get_tenv exe_env (Summary.get_proc_name summary) in
let proc_data = ProcData.make summary tenv () in
ignore (Analyzer.compute_post proc_data ~initial) ;
summary

@ -1,10 +0,0 @@
(*
* 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

@ -1,236 +0,0 @@
(*
* 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 L = Logging
module MF = MarkupFormatter
module UseDefChain = struct
type t =
| DependsOn of (Location.t * AccessPath.t)
| NullDefCompare of (Location.t * AccessPath.t)
| NullDefAssign of (Location.t * AccessPath.t)
[@@deriving compare]
let leq ~lhs ~rhs = compare lhs rhs <= 0
(* Keep only one chain in join/widen as we are going to report only one
* trace to the user eventually. *)
let join lhs rhs = if leq ~lhs ~rhs then rhs else lhs
let widen ~prev ~next ~num_iters:_ = join prev next
let pp fmt = function
| NullDefAssign (loc, ap) ->
F.fprintf fmt "NullDefAssign(%a, %a)" Location.pp loc AccessPath.pp ap
| NullDefCompare (loc, ap) ->
F.fprintf fmt "NullDefCompare(%a, %a)" Location.pp loc AccessPath.pp ap
| DependsOn (loc, ap) ->
F.fprintf fmt "DependsOn(%a, %a)" Location.pp loc AccessPath.pp ap
module Set = Caml.Set.Make (struct
type nonrec t = t
let compare = compare
end)
end
module Domain = AbstractDomain.Map (AccessPath) (UseDefChain)
type extras = ProcData.no_extras
module TransferFunctions (CFG : ProcCfg.S) = struct
module CFG = CFG
module Domain = Domain
type nonrec extras = extras
let is_access_nullable ap proc_data =
match AccessPath.get_field_and_annotation ap proc_data.ProcData.tenv with
| Some (_, annot_item) ->
Annotations.ia_is_nullable annot_item
| _ ->
false
let rec nullable_usedef_chain_of exp lhs astate loc =
match exp with
| HilExp.Cast (_, e) ->
nullable_usedef_chain_of e lhs astate loc
| HilExp.Constant (Cint n) when IntLit.isnull n ->
Some (UseDefChain.NullDefAssign (loc, lhs))
| HilExp.AccessExpression access_expr -> (
try
let ap = HilExp.AccessExpression.to_access_path access_expr in
match Domain.find ap astate with
| UseDefChain.NullDefCompare _ ->
(* Stop NullDefCompare from propagating here because we want to prevent
* the checker from suggesting @Nullable on y in the following case:
* if (x == null) ... else { y = x; } *)
None
| _ ->
Some (UseDefChain.DependsOn (loc, ap))
with Caml.Not_found -> None )
| _ ->
None
let rec extract_null_compare_expr = function
| HilExp.Cast (_, e) ->
extract_null_compare_expr e
| HilExp.BinaryOperator ((Eq | Ne), HilExp.AccessExpression access_expr, exp)
| HilExp.BinaryOperator ((Eq | Ne), exp, HilExp.AccessExpression access_expr) ->
Option.some_if (HilExp.is_null_literal exp)
(HilExp.AccessExpression.to_access_path access_expr)
| _ ->
None
let exec_instr (astate : Domain.t) proc_data _ (instr : HilInstr.t) =
match instr with
| Assume (expr, _, _, loc) -> (
match extract_null_compare_expr expr with
| Some ap when not (is_access_nullable ap proc_data) ->
let udchain = UseDefChain.NullDefCompare (loc, ap) in
Domain.add ap udchain astate
| _ ->
astate )
| Call _ ->
(* For now we just assume the callee always return non-null *)
astate
| Assign (lhs_access_expr, rhs, loc) ->
let lhs = HilExp.AccessExpression.to_access_path lhs_access_expr in
if not (is_access_nullable lhs proc_data) then
match nullable_usedef_chain_of rhs lhs astate loc with
| Some udchain ->
Domain.add lhs udchain astate
| None ->
astate
else astate
| Metadata _ ->
astate
let pp_session_name _node fmt = F.pp_print_string fmt "nullability suggest"
end
module Analyzer = LowerHil.MakeAbstractInterpreter (TransferFunctions (ProcCfg.Exceptional))
let make_error_trace astate ap ud =
let name_of ap =
match AccessPath.get_last_access ap with
| Some (AccessPath.FieldAccess field_name) ->
"Field " ^ Fieldname.get_field_name field_name
| Some (AccessPath.ArrayAccess _) ->
"Some array element"
| None ->
"Variable"
in
let open UseDefChain in
let rec error_trace_impl seen depth ap = function
| NullDefAssign (loc, src) ->
let msg = F.sprintf "%s is assigned null here" (name_of src) in
let ltr = [Errlog.make_trace_element depth loc msg []] in
Some (loc, ltr)
| NullDefCompare (loc, src) ->
let msg = F.sprintf "%s is compared to null here" (name_of src) in
let ltr = [Errlog.make_trace_element depth loc msg []] in
Some (loc, ltr)
| DependsOn (loc, dep) -> (
match Domain.find dep astate with
| exception Caml.Not_found ->
None
| ud' when Set.mem ud' seen ->
None
| ud' ->
let msg = F.sprintf "%s could be assigned here" (name_of ap) in
let trace_elem = Errlog.make_trace_element depth loc msg [] in
let seen' = Set.add ud' seen in
Option.map
(error_trace_impl seen' (depth + 1) dep ud')
~f:(fun (_, trace) -> (loc, trace_elem :: trace)) )
in
error_trace_impl Set.empty 0 ap ud
let pretty_field_name proc_data field_name =
match Summary.get_proc_name proc_data.ProcData.summary with
| Procname.Java jproc_name ->
let proc_class_name = Procname.Java.get_class_name jproc_name in
let field_class_name = Fieldname.get_class_name field_name |> Typ.Name.name in
if String.equal proc_class_name field_class_name then Fieldname.get_field_name field_name
else Fieldname.to_simplified_string field_name
| _ ->
(* This format is subject to change once this checker gets to run on C/Cpp/ObjC *)
Fieldname.to_string field_name
(* Checks if a field name stems from a class outside the domain of what is analyzed by Infer *)
let is_outside_codebase proc_name field_name =
match proc_name with
| Procname.Java _ ->
Typ.Name.Java.is_external (Fieldname.get_class_name field_name)
| _ ->
false
let checker {Callbacks.summary; exe_env} =
let proc_desc = Summary.get_proc_desc summary in
let proc_name = Procdesc.get_proc_name proc_desc in
let tenv = Exe_env.get_tenv exe_env proc_name in
let annotation = Localise.nullable_annotation_name proc_name in
let report astate (proc_data : extras ProcData.t) =
let report_access_path ap udchain =
match AccessPath.get_field_and_annotation ap proc_data.tenv with
| Some (field_name, _) when is_outside_codebase proc_name field_name ->
(* Skip reporting when the field is outside the analyzed codebase.
Note that we do similar filtering on high level which is common
for all checkers.
But this one is different: here we look NOT at the function
to be reported (the one that is using the field), but the root
cause (the field with the wrong annotation itself).
NOTE: Ideally we'd like to support such filtering in the way that
is agnostic to particular checker, but it is not trivial to
do, so let's do it in ad hoc way.
*)
()
| Some (field_name, _) when Fieldname.is_java_captured_parameter field_name ->
(* Skip reporting when field comes from generated code *)
()
| Some (field_name, _) ->
let message =
F.asprintf "Field %a should be annotated with %a" MF.pp_monospaced
(pretty_field_name proc_data field_name)
MF.pp_monospaced annotation
in
let loc, ltr =
match make_error_trace astate ap udchain with
| Some (loc, ltr) ->
(loc, Some ltr)
| None ->
(Procdesc.get_loc proc_desc, None)
in
Reporting.log_warning summary ~loc ?ltr IssueType.nullsafe_field_not_nullable message
| _ ->
()
in
Domain.iter report_access_path astate
in
if AndroidFramework.is_destroy_method proc_name then
(* Skip the fields nullified in Fragment onDestroy and onDestroyView *)
summary
else
(* Assume all fields are not null in the beginning *)
let initial = Domain.empty in
let proc_data = ProcData.make_default summary tenv in
( match Analyzer.compute_post proc_data ~initial with
| Some post ->
report post proc_data
| None ->
L.internal_error "Analyzer failed to compute post for %a@." Procname.pp proc_name ) ;
summary

@ -1,11 +0,0 @@
(*
* 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.
*)
(* Module that suggest adding nullability annotations *)
open! IStd
val checker : Callbacks.proc_callback_t

@ -7,7 +7,7 @@
TESTS_DIR = ../../.. TESTS_DIR = ../../..
CLANG_OPTIONS = -c -x c++ -std=c++14 CLANG_OPTIONS = -c -x c++ -std=c++14
INFER_OPTIONS = --no-default-checkers --biabduction --nullsafe --project-root $(TESTS_DIR) INFER_OPTIONS = --no-default-checkers --biabduction --project-root $(TESTS_DIR)
INFERPRINT_OPTIONS = --issues-tests INFERPRINT_OPTIONS = --issues-tests
SOURCES = $(wildcard *.cpp) SOURCES = $(wildcard *.cpp)

@ -1,3 +1,2 @@
codetoanalyze/cpp/conflicts/test.cpp, test1_bad, 2, NULL_DEREFERENCE, no_bucket, ERROR, [start of procedure test1_bad(),Skipping nullableMethod(): method has no implementation] codetoanalyze/cpp/conflicts/test.cpp, test1_bad, 2, NULL_DEREFERENCE, no_bucket, ERROR, [start of procedure test1_bad(),Skipping nullableMethod(): method has no implementation]
codetoanalyze/cpp/conflicts/test.cpp, test2_bad, 1, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereferencing the return of nullableMethod(),definition of nullableMethod]
codetoanalyze/cpp/conflicts/test.cpp, test3_bad, 5, NULL_DEREFERENCE, no_bucket, ERROR, [start of procedure test3_bad(),Skipping nullableMethod(): method has no implementation,Loop condition is true. Entering loop body,Skipping nullableMethod(): method has no implementation,Loop condition is false. Leaving loop] codetoanalyze/cpp/conflicts/test.cpp, test3_bad, 5, NULL_DEREFERENCE, no_bucket, ERROR, [start of procedure test3_bad(),Skipping nullableMethod(): method has no implementation,Loop condition is true. Entering loop body,Skipping nullableMethod(): method has no implementation,Loop condition is false. Leaving loop]

@ -1,17 +0,0 @@
# 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 = ../../..
# see explanations in cpp/errors/Makefile for the custom isystem
CLANG_OPTIONS = -x c++ -std=c++11 -nostdinc++ -isystem$(ROOT_DIR) -isystem$(CLANG_INCLUDES)/c++/v1/ -c
INFER_OPTIONS = --biabduction --nullsafe --debug-exceptions --project-root $(TESTS_DIR)
INFERPRINT_OPTIONS = --issues-tests
SOURCES = $(wildcard *.cpp)
include $(TESTS_DIR)/clang.make
infer-out/report.json: $(MAKEFILE_LIST)

@ -1,62 +0,0 @@
/*
* 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.
*/
class T {
private:
int* unnanotated_field;
int* _Nullable nullable_field;
int* _Nonnull nonnull_field;
public:
void assign_nullable_field_to_null_okay() { nullable_field = nullptr; }
public:
void assign_unnanotated_field_to_null_bad() { unnanotated_field = nullptr; }
public:
void assign_nonnull_field_to_null_bad() { nonnull_field = nullptr; }
public:
void test_nullable_field_for_null_okay() {
if (nullable_field == nullptr) {
}
}
public:
void test_unnanotated_field_for_null_bad() {
if (unnanotated_field == nullptr) {
}
}
public:
void test_nonnull_field_for_null_bad() {
if (nonnull_field == nullptr) {
}
}
public:
void dereference_unnanotated_field_okay() { *unnanotated_field = 42; }
public:
void dereference_nonnull_field_okay() { *nonnull_field = 42; }
public:
void dereference_nullable_field_bad() { *nullable_field = 42; }
public:
void dereference_unnanotated_field_after_test_for_null_bad() {
if (unnanotated_field == nullptr) {
*unnanotated_field = 42;
}
}
public:
void FP_dereference_nonnull_field_after_test_for_null_okay() {
if (nonnull_field == nullptr) {
*nonnull_field = 42;
}
}
};

@ -1,53 +0,0 @@
codetoanalyze/cpp/nullable/example.cpp, T::FP_dereference_nonnull_field_after_test_for_null_okay, 1, NULLSAFE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [Field nonnull_field is compared to null here]
codetoanalyze/cpp/nullable/example.cpp, T::FP_dereference_nonnull_field_after_test_for_null_okay, 2, NULL_DEREFERENCE, B5, ERROR, [start of procedure FP_dereference_nonnull_field_after_test_for_null_okay,Taking true branch]
codetoanalyze/cpp/nullable/example.cpp, T::assign_nonnull_field_to_null_bad, 0, NULLSAFE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [Field nonnull_field is assigned null here]
codetoanalyze/cpp/nullable/example.cpp, T::assign_unnanotated_field_to_null_bad, 0, NULLSAFE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [Field unnanotated_field is assigned null here]
codetoanalyze/cpp/nullable/example.cpp, T::dereference_nullable_field_bad, 0, NULL_DEREFERENCE, B1, ERROR, [start of procedure dereference_nullable_field_bad]
codetoanalyze/cpp/nullable/example.cpp, T::dereference_unnanotated_field_after_test_for_null_bad, 1, NULLSAFE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [Field unnanotated_field is compared to null here]
codetoanalyze/cpp/nullable/example.cpp, T::dereference_unnanotated_field_after_test_for_null_bad, 2, NULL_DEREFERENCE, B5, ERROR, [start of procedure dereference_unnanotated_field_after_test_for_null_bad,Taking true branch]
codetoanalyze/cpp/nullable/example.cpp, T::test_nonnull_field_for_null_bad, 1, NULLSAFE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [Field nonnull_field is compared to null here]
codetoanalyze/cpp/nullable/example.cpp, T::test_unnanotated_field_for_null_bad, 1, NULLSAFE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [Field unnanotated_field is compared to null here]
codetoanalyze/cpp/nullable/method.cpp, assignNullableValueBad, 2, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereference of p,assignment of the nullable value,definition of mayReturnNullPointer]
codetoanalyze/cpp/nullable/method.cpp, assignNullableValueBad, 2, NULL_DEREFERENCE, B2, ERROR, [start of procedure assignNullableValueBad(),start of procedure mayReturnNullPointer,Taking true branch,return from a call to T::mayReturnNullPointer]
codetoanalyze/cpp/nullable/method.cpp, avoidDoubleReportingBad, 2, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereference of p,assignment of the nullable value,definition of mayReturnNullObject]
codetoanalyze/cpp/nullable/method.cpp, avoidDoubleReportingBad, 2, NULL_DEREFERENCE, B2, ERROR, [start of procedure avoidDoubleReportingBad(),start of procedure mayReturnNullObject,Taking true branch,return from a call to T::mayReturnNullObject]
codetoanalyze/cpp/nullable/method.cpp, callMethodOnNullableObjectBad, 1, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereferencing the return of T::mayReturnNullObject,definition of mayReturnNullObject]
codetoanalyze/cpp/nullable/method.cpp, callMethodOnNullableObjectBad, 1, NULL_DEREFERENCE, B5, ERROR, [start of procedure callMethodOnNullableObjectBad(),start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject]
codetoanalyze/cpp/nullable/method.cpp, callMethodOnNullableObjectOkay, 2, NULL_TEST_AFTER_DEREFERENCE, no_bucket, WARNING, [start of procedure callMethodOnNullableObjectOkay(),start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject,Taking false branch]
codetoanalyze/cpp/nullable/method.cpp, dereferenceFieldOfNullableObjectBad, 2, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereference of p,assignment of the nullable value,definition of mayReturnNullObject]
codetoanalyze/cpp/nullable/method.cpp, dereferenceFieldOfNullableObjectBad, 2, NULL_DEREFERENCE, B2, ERROR, [start of procedure dereferenceFieldOfNullableObjectBad(),start of procedure mayReturnNullObject,Taking true branch,return from a call to T::mayReturnNullObject]
codetoanalyze/cpp/nullable/method.cpp, dereferenceOfAliasesCheckedForNullOkay, 3, NULL_TEST_AFTER_DEREFERENCE, no_bucket, WARNING, [start of procedure dereferenceOfAliasesCheckedForNullOkay(),start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject,Taking false branch]
codetoanalyze/cpp/nullable/method.cpp, methodAlwaysCheckedForNullOkay, 1, NULL_TEST_AFTER_DEREFERENCE, no_bucket, WARNING, [start of procedure methodAlwaysCheckedForNullOkay(),Taking true branch,start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject,Taking false branch]
codetoanalyze/cpp/nullable/method.cpp, methodAlwaysCheckedForNullOkay, 2, NULL_DEREFERENCE, B5, ERROR, [start of procedure methodAlwaysCheckedForNullOkay(),Taking true branch,start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject,Taking true branch,start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject]
codetoanalyze/cpp/nullable/method.cpp, methodCallOnFieldOfNullableObjectBad, 2, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereference of p,assignment of the nullable value,definition of mayReturnNullObject]
codetoanalyze/cpp/nullable/method.cpp, methodCallOnFieldOfNullableObjectBad, 2, NULL_DEREFERENCE, B2, ERROR, [start of procedure methodCallOnFieldOfNullableObjectBad(),start of procedure mayReturnNullObject,Taking true branch,return from a call to T::mayReturnNullObject]
codetoanalyze/cpp/nullable/method.cpp, methodCheckedForNullAndReturnOkay, 1, NULL_TEST_AFTER_DEREFERENCE, no_bucket, WARNING, [start of procedure methodCheckedForNullAndReturnOkay(),start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject,Taking false branch]
codetoanalyze/cpp/nullable/method.cpp, methodCheckedForNullAndReturnOkay, 4, NULL_DEREFERENCE, B5, ERROR, [start of procedure methodCheckedForNullAndReturnOkay(),start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject,Taking false branch,start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject]
codetoanalyze/cpp/nullable/method.cpp, methodCheckedForNullOkay, 1, NULL_TEST_AFTER_DEREFERENCE, no_bucket, WARNING, [start of procedure methodCheckedForNullOkay(),start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject,Taking false branch]
codetoanalyze/cpp/nullable/method.cpp, methodCheckedForNullOkay, 2, NULL_DEREFERENCE, B5, ERROR, [start of procedure methodCheckedForNullOkay(),start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject,Taking true branch,start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject]
codetoanalyze/cpp/nullable/method.cpp, methodNotAlwaysCheckedForNullBad, 1, NULL_TEST_AFTER_DEREFERENCE, no_bucket, WARNING, [start of procedure methodNotAlwaysCheckedForNullBad(),Taking false branch,start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject,Taking false branch]
codetoanalyze/cpp/nullable/method.cpp, methodNotAlwaysCheckedForNullBad, 2, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereferencing the return of T::mayReturnNullObject,definition of mayReturnNullObject]
codetoanalyze/cpp/nullable/method.cpp, methodNotAlwaysCheckedForNullBad, 2, NULL_DEREFERENCE, B5, ERROR, [start of procedure methodNotAlwaysCheckedForNullBad(),Taking false branch,start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject,Taking true branch,start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject]
codetoanalyze/cpp/nullable/method.cpp, methodTestedForNullOkay, 1, NULL_TEST_AFTER_DEREFERENCE, no_bucket, WARNING, [start of procedure methodTestedForNullOkay(),start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject,Taking false branch]
codetoanalyze/cpp/nullable/method.cpp, methodTestedForNullOkay, 2, NULL_DEREFERENCE, B5, ERROR, [start of procedure methodTestedForNullOkay(),start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject,Taking true branch,start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject]
codetoanalyze/cpp/nullable/method.cpp, nullableAssignmentInOneBranchBad, 7, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereference of p,assignment of the nullable value,definition of mayReturnNullObject]
codetoanalyze/cpp/nullable/method.cpp, nullableAssignmentInOneBranchBad, 7, NULL_DEREFERENCE, B2, ERROR, [start of procedure nullableAssignmentInOneBranchBad(),Taking true branch,start of procedure mayReturnNullObject,Taking true branch,return from a call to T::mayReturnNullObject]
codetoanalyze/cpp/nullable/method.cpp, onlyReportOnceBad, 1, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereferencing the return of T::mayReturnNullObject,definition of mayReturnNullObject]
codetoanalyze/cpp/nullable/method.cpp, onlyReportOnceBad, 1, NULL_DEREFERENCE, B5, ERROR, [start of procedure onlyReportOnceBad(),start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject]
codetoanalyze/cpp/nullable/method.cpp, onlyReportOnceBad, 3, NULL_DEREFERENCE, B5, ERROR, [start of procedure onlyReportOnceBad(),start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject,start of procedure doSomething,return from a call to T::doSomething,start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject]
codetoanalyze/cpp/nullable/method.cpp, pointerTestBad, 2, NULL_TEST_AFTER_DEREFERENCE, no_bucket, WARNING, [start of procedure pointerTestBad(),start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject,Taking false branch]
codetoanalyze/cpp/nullable/method.cpp, pointerTestBad, 5, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereference of p,assignment of the nullable value,definition of mayReturnNullObject]
codetoanalyze/cpp/nullable/method.cpp, pointerTestBad, 5, NULL_DEREFERENCE, B2, ERROR, [start of procedure pointerTestBad(),start of procedure mayReturnNullObject,Taking true branch,return from a call to T::mayReturnNullObject,Taking false branch]
codetoanalyze/cpp/nullable/method.cpp, pointerTestOkay, 2, NULL_TEST_AFTER_DEREFERENCE, no_bucket, WARNING, [start of procedure pointerTestOkay(),start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject,Taking false branch]
codetoanalyze/cpp/nullable/method.cpp, reassigningNullablePointerOkay, 1, DEAD_STORE, no_bucket, ERROR, [Write of unused value]
codetoanalyze/cpp/nullable/method.cpp, reassigningNullablePointerToNullOkay, 1, DEAD_STORE, no_bucket, ERROR, [Write of unused value]
codetoanalyze/cpp/nullable/method.cpp, reportsViolationInNotNullElseBranchBad, 1, NULL_TEST_AFTER_DEREFERENCE, no_bucket, WARNING, [start of procedure reportsViolationInNotNullElseBranchBad(),start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject,Taking false branch]
codetoanalyze/cpp/nullable/method.cpp, reportsViolationInNotNullElseBranchBad, 3, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereferencing the return of T::mayReturnNullObject,definition of mayReturnNullObject]
codetoanalyze/cpp/nullable/method.cpp, reportsViolationInNotNullElseBranchBad, 3, NULL_DEREFERENCE, B5, ERROR, [start of procedure reportsViolationInNotNullElseBranchBad(),start of procedure mayReturnNullObject,Taking true branch,return from a call to T::mayReturnNullObject,Taking false branch,start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject]
codetoanalyze/cpp/nullable/method.cpp, reportsViolationInNullBranchBad, 1, NULL_TEST_AFTER_DEREFERENCE, no_bucket, WARNING, [start of procedure reportsViolationInNullBranchBad(),start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject,Taking false branch]
codetoanalyze/cpp/nullable/method.cpp, reportsViolationInNullBranchBad, 2, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereferencing the return of T::mayReturnNullObject,definition of mayReturnNullObject]
codetoanalyze/cpp/nullable/method.cpp, reportsViolationInNullBranchBad, 2, NULL_DEREFERENCE, B5, ERROR, [start of procedure reportsViolationInNullBranchBad(),start of procedure mayReturnNullObject,Taking true branch,return from a call to T::mayReturnNullObject,Taking true branch,start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject]
codetoanalyze/cpp/nullable/method.cpp, reportsViolationOutsideOfNullCheckBad, 1, NULL_TEST_AFTER_DEREFERENCE, no_bucket, WARNING, [start of procedure reportsViolationOutsideOfNullCheckBad(),start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject,Taking false branch]
codetoanalyze/cpp/nullable/method.cpp, reportsViolationOutsideOfNullCheckBad, 2, NULL_DEREFERENCE, B5, ERROR, [start of procedure reportsViolationOutsideOfNullCheckBad(),start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject,Taking true branch,start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject]
codetoanalyze/cpp/nullable/method.cpp, reportsViolationOutsideOfNullCheckBad, 4, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereferencing the return of T::mayReturnNullObject,definition of mayReturnNullObject]
codetoanalyze/cpp/nullable/method.cpp, reportsViolationOutsideOfNullCheckBad, 4, NULL_DEREFERENCE, B5, ERROR, [start of procedure reportsViolationOutsideOfNullCheckBad(),start of procedure mayReturnNullObject,Taking true branch,return from a call to T::mayReturnNullObject,Taking false branch,start of procedure mayReturnNullObject,Taking false branch,return from a call to T::mayReturnNullObject]

@ -1,170 +0,0 @@
/*
* 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.
*/
bool star();
class T {
public:
int x;
T* field;
public:
int* _Nullable mayReturnNullPointer() {
if (star()) {
return nullptr;
} else {
return new int;
}
}
public:
T* _Nullable mayReturnNullObject() {
if (star()) {
return nullptr;
} else {
return this;
}
}
public:
T* doesNotReturnNullObject() { return new T(); }
public:
void doSomething() {}
};
void assignNullableValueBad(T* t) {
int* p = t->mayReturnNullPointer();
*p = 42;
}
void reassigningNullablePointerOkay(T* t) {
int* p = t->mayReturnNullPointer();
p = new int; // does not report here
*p = 42; // does not report here
}
void reassigningNullablePointerToNullOkay(T* t) {
int* p = t->mayReturnNullPointer();
p = nullptr; // does not report here
}
void callMethodOnNullableObjectBad(T* t) {
t->mayReturnNullObject()->doSomething();
}
void callMethodOnNullableObjectOkay(T* t) {
T* p = t->mayReturnNullObject();
if (p != nullptr) {
p->doSomething();
}
}
void dereferenceFieldOfNullableObjectBad(T* t) {
T* p = t->mayReturnNullObject();
p->x = 42;
}
void methodCallOnFieldOfNullableObjectBad(T* t) {
T* p = t->mayReturnNullObject();
p->field->doSomething();
}
void avoidDoubleReportingBad(T* t) {
T* p = t->mayReturnNullObject();
p->doSomething(); // reports here
p->doSomething(); // does not report here
}
void nullableAssignmentInOneBranchBad(T* t) {
T* p;
if (star()) {
p = t->mayReturnNullObject();
} else {
p = t->doesNotReturnNullObject();
}
p->doSomething(); // reports here
}
void methodCheckedForNullOkay(T* t) {
if (t->mayReturnNullObject() != nullptr) {
t->mayReturnNullObject()->doSomething(); // does not report here
}
}
void methodCheckedForNullAndReturnOkay(T* t) {
if (t->mayReturnNullObject() == nullptr) {
return;
}
t->mayReturnNullObject()->doSomething(); // does not report here
}
void reportsViolationOutsideOfNullCheckBad(T* t) {
if (t->mayReturnNullObject() != nullptr) {
t->mayReturnNullObject()->doSomething(); // does not report here
}
t->mayReturnNullObject()->doSomething(); // reports here
}
void reportsViolationInNullBranchBad(T* t) {
if (t->mayReturnNullObject() == nullptr) {
t->mayReturnNullObject()->doSomething(); // reports here
}
}
void reportsViolationInNotNullElseBranchBad(T* t) {
if (t->mayReturnNullObject() != nullptr) {
} else {
t->mayReturnNullObject()->doSomething(); // reports here
}
}
void methodAlwaysCheckedForNullOkay(T* t) {
if (star() && t->mayReturnNullObject() != nullptr) {
t->mayReturnNullObject()->doSomething(); // does not report here
}
}
void methodNotAlwaysCheckedForNullBad(T* t) {
if (star() || t->mayReturnNullObject() != nullptr) {
t->mayReturnNullObject()->doSomething(); // reports here
}
}
void onlyReportOnceBad(T* t) {
t->mayReturnNullObject()->doSomething(); // reports here
// ...
t->mayReturnNullObject()->doSomething(); // does not report here
}
void dereferenceOfAliasesCheckedForNullOkay(T* t) {
T* s = t->mayReturnNullObject();
T* r = s;
if (r != nullptr) {
s->doSomething();
}
}
void pointerTestOkay(T* t) {
T* p = t->mayReturnNullObject();
if (p) {
p->doSomething();
}
}
void pointerTestBad(T* t) {
T* p = t->mayReturnNullObject();
if (p) {
// ...
}
p->doSomething();
}
void methodTestedForNullOkay(T* t) {
if (t->mayReturnNullObject()) {
t->mayReturnNullObject()->doSomething();
}
}

@ -8,7 +8,7 @@ TESTS_DIR = ../../..
INFER_OPTIONS = \ INFER_OPTIONS = \
--debug-exceptions --no-default-checkers \ --debug-exceptions --no-default-checkers \
--fragment-retains-view --immutable-cast --printf-args --quandary \ --fragment-retains-view --immutable-cast --printf-args --quandary \
--nullsafe --racerd \ --racerd \
INFERPRINT_OPTIONS = --issues-tests INFERPRINT_OPTIONS = --issues-tests
SOURCES = $(wildcard *.java) $(wildcard $(TESTS_DIR)/external/library/*.java) SOURCES = $(wildcard *.java) $(wildcard $(TESTS_DIR)/external/library/*.java)

@ -3,17 +3,6 @@ codetoanalyze/java/checkers/FragmentRetainsViewExample.java, codetoanalyze.java.
codetoanalyze/java/checkers/FragmentRetainsViewExample.java, codetoanalyze.java.checkers.FragmentRetainsViewExample.onDestroyView():void, 0, CHECKERS_FRAGMENT_RETAINS_VIEW, no_bucket, WARNING, [] codetoanalyze/java/checkers/FragmentRetainsViewExample.java, codetoanalyze.java.checkers.FragmentRetainsViewExample.onDestroyView():void, 0, CHECKERS_FRAGMENT_RETAINS_VIEW, no_bucket, WARNING, []
codetoanalyze/java/checkers/ImmutableCast.java, codetoanalyze.java.checkers.ImmutableCast.badCast(com.google.common.collect.ImmutableList):java.util.List, 0, CHECKERS_IMMUTABLE_CAST, no_bucket, WARNING, [Method badCast(...) returns class com.google.common.collect.ImmutableList but the return type is class java.util.List. Make sure that users of this method do not try to modify the collection.] codetoanalyze/java/checkers/ImmutableCast.java, codetoanalyze.java.checkers.ImmutableCast.badCast(com.google.common.collect.ImmutableList):java.util.List, 0, CHECKERS_IMMUTABLE_CAST, no_bucket, WARNING, [Method badCast(...) returns class com.google.common.collect.ImmutableList but the return type is class java.util.List. Make sure that users of this method do not try to modify the collection.]
codetoanalyze/java/checkers/ImmutableCast.java, codetoanalyze.java.checkers.ImmutableCast.badCastFromField():java.util.List, 0, CHECKERS_IMMUTABLE_CAST, no_bucket, WARNING, [Method badCastFromField() returns class com.google.common.collect.ImmutableList but the return type is class java.util.List. Make sure that users of this method do not try to modify the collection.] codetoanalyze/java/checkers/ImmutableCast.java, codetoanalyze.java.checkers.ImmutableCast.badCastFromField():java.util.List, 0, CHECKERS_IMMUTABLE_CAST, no_bucket, WARNING, [Method badCastFromField() returns class com.google.common.collect.ImmutableList but the return type is class java.util.List. Make sure that users of this method do not try to modify the collection.]
codetoanalyze/java/checkers/NullableSuggest.java, codetoanalyze.java.checkers.NullableSuggest.assignNullBad():void, 0, NULLSAFE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [Field obj0 is assigned null here]
codetoanalyze/java/checkers/NullableSuggest.java, codetoanalyze.java.checkers.NullableSuggest.assignNullToFieldInOtherClassBad():void, 1, NULLSAFE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [Field obj2 is assigned null here]
codetoanalyze/java/checkers/NullableSuggest.java, codetoanalyze.java.checkers.NullableSuggest.assignNullToFieldTransitiveBad(boolean):void, 2, NULLSAFE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [Field obj0 could be assigned here,Variable could be assigned here,Variable could be assigned here,Variable is assigned null here]
codetoanalyze/java/checkers/NullableSuggest.java, codetoanalyze.java.checkers.NullableSuggest.assignNullToFieldTransitiveLoopBad(int):void, 6, NULLSAFE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [Field obj0 could be assigned here,Some array element could be assigned here,Variable is assigned null here]
codetoanalyze/java/checkers/NullableSuggest.java, codetoanalyze.java.checkers.NullableSuggest.compareNullToFieldBad():void, 1, NULLSAFE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [Field obj0 is compared to null here]
codetoanalyze/java/checkers/NullableSuggest.java, codetoanalyze.java.checkers.NullableSuggest.multipleChainsAlwaysSelectShortestBad(boolean):void, 5, NULLSAFE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [Field obj0 could be assigned here,Variable is assigned null here]
codetoanalyze/java/checkers/NullableViolation.java, codetoanalyze.java.checkers.NullableViolation.dereferenceNullableMethodBad():void, 0, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereferencing the return of NullableViolation.returnsNullable(),definition of returnsNullable]
codetoanalyze/java/checkers/NullableViolation.java, codetoanalyze.java.checkers.NullableViolation.dereferenceNullableMethodInElseBranchBad():void, 2, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereferencing the return of NullableViolation.returnsNullable(),definition of returnsNullable]
codetoanalyze/java/checkers/NullableViolation.java, codetoanalyze.java.checkers.NullableViolation.dereferenceNullableMethodIncorrectlyCheckedForNullBad():void, 1, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereferencing the return of NullableViolation.returnsNullable(),definition of returnsNullable]
codetoanalyze/java/checkers/NullableViolation.java, codetoanalyze.java.checkers.NullableViolation.dereferenceNullableMethodNotAlwaysCheckedForNullBad():void, 1, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereferencing the return of NullableViolation.returnsNullable(),definition of returnsNullable]
codetoanalyze/java/checkers/NullableViolation.java, codetoanalyze.java.checkers.NullableViolation.dereferenceNullableReturnValueBad():void, 1, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereference of t,assignment of the nullable value,definition of returnsNullable]
codetoanalyze/java/checkers/PrintfArgsChecker.java, codetoanalyze.java.checkers.PrintfArgsChecker.formatStringIsNotLiteral(java.io.PrintStream):void, 1, CHECKERS_PRINTF_ARGS, no_bucket, WARNING, [] codetoanalyze/java/checkers/PrintfArgsChecker.java, codetoanalyze.java.checkers.PrintfArgsChecker.formatStringIsNotLiteral(java.io.PrintStream):void, 1, CHECKERS_PRINTF_ARGS, no_bucket, WARNING, []
codetoanalyze/java/checkers/PrintfArgsChecker.java, codetoanalyze.java.checkers.PrintfArgsChecker.stringInsteadOfInteger(java.io.PrintStream):void, 0, CHECKERS_PRINTF_ARGS, no_bucket, ERROR, [] codetoanalyze/java/checkers/PrintfArgsChecker.java, codetoanalyze.java.checkers.PrintfArgsChecker.stringInsteadOfInteger(java.io.PrintStream):void, 0, CHECKERS_PRINTF_ARGS, no_bucket, ERROR, []
codetoanalyze/java/checkers/PrintfArgsChecker.java, codetoanalyze.java.checkers.PrintfArgsChecker.wrongNumberOfArguments(java.io.PrintStream):void, 0, CHECKERS_PRINTF_ARGS, no_bucket, ERROR, [] codetoanalyze/java/checkers/PrintfArgsChecker.java, codetoanalyze.java.checkers.PrintfArgsChecker.wrongNumberOfArguments(java.io.PrintStream):void, 0, CHECKERS_PRINTF_ARGS, no_bucket, ERROR, []

@ -1,290 +0,0 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
#import "Library.h"
int* __nullable returnsNull();
typedef struct s_ {
int x;
} S;
@interface T : NSObject
- (NSObject* _Nullable)nullableMethod;
- (T* _Nullable)nullableT;
@property(nonatomic) S structProperty;
@property(nonatomic) S* pointerProperty;
@property(nonatomic, nullable) NSObject* nullableProperty;
@end
@implementation T {
int* unnanotatedField;
int* __nullable nullableField;
int* nonnullField;
}
- (void)assignNullableFieldToNullOkay {
nullableField = nil;
}
- (void)assignUnnanotatedFieldToNullBad {
unnanotatedField = nil;
}
- (void)assignNonnullFieldToNullBad {
nonnullField = nil;
}
- (void)testNullableFieldForNullOkay {
if (nullableField == nil) {
}
}
- (void)testUnnanotatedFieldForNullBad {
if (unnanotatedField == nil) {
}
}
- (int)DeadStoreFP_testUnnanotatedFieldInClosureBad {
int (^testField)(int defaultValue);
testField = ^(int defaultValue) {
if (unnanotatedField != nil) {
return *unnanotatedField;
} else {
return defaultValue;
}
};
return testField(42);
}
- (void)testNonnullFieldForNullBad {
if (nonnullField == nil) {
}
}
- (void)dereferenceUnnanotatedFieldOkay {
*unnanotatedField = 42;
}
- (void)dereferenceNonnullFieldOkay {
*nonnullField = 42;
}
- (void)dereferenceNullableFieldBad {
*nullableField = 42;
}
- (void)dereferenceUnnanotatedFieldAfterTestForNullBad {
if (unnanotatedField == nil) {
*unnanotatedField = 42;
}
}
- (void)FP_dereferenceNonnullFieldAfterTestForNullOkay {
if (nonnullField == nil) {
*nonnullField = 42;
}
}
- (void)dereferenceNullableFunctionBad {
int* p = returnsNull();
*p = 42;
}
- (void)dereferenceNullableFunction1Ok {
int* p = returnsNull();
if (p) {
*p = 42;
}
}
- (void)dereferenceNullableFunction2Ok {
int* p = returnsNull();
if (p != nil) {
*p = 42;
}
}
- (NSObject* _Nullable)nullableMethod {
return nil;
}
- (NSString*)dereferenceNullableMethodOkay {
NSObject* nullableObject = [self nullableMethod];
return [nullableObject description]; // does not report here
}
- (void)reassigningNullableObjectOkay {
NSObject* nullableObject = [self nullableMethod];
nullableObject = nil; // does not report here
}
- (NSArray*)nullableObjectInNSArrayBad {
NSObject* nullableObject = [self nullableMethod];
NSArray* array = @[ nullableObject ]; // reports here
return array;
}
- (NSArray*)nullablePropertyInNSArrayBad {
return @[ self.nullableMethod ]; // reports here
}
- (NSArray*)nullableMethodCheckedForNullAndReturnOkay {
NSObject* nullableObject = [self nullableMethod];
NSArray* array;
if (nullableObject == nil) {
return array;
}
array = @[ nullableObject ]; // does not report here
return array;
}
- (NSArray*)secondElementNullableObjectInNSArrayBad {
NSObject* allocatedObject = [NSObject alloc];
NSObject* nullableObject = [self nullableMethod];
NSArray* array = @[ allocatedObject, nullableObject ]; // reports here
return array;
}
- (NSArray*)nullableObjectInNSArrayOkay {
NSObject* nullableObject = [self nullableMethod];
NSArray* array;
if (nullableObject) {
array = @[ nullableObject ]; // reports here
} else {
array = @[ @"String" ];
}
return array;
}
- (NSArray*)URLWithStringOkay {
NSURL* url = [NSURL URLWithString:@"some/url/string"];
NSArray* array = @[ url ]; // reports here
}
- (NSDictionary*)nullableValueInNSDictionaryBad {
NSObject* nullableValue = [self nullableMethod];
NSMutableDictionary* dict = [NSMutableDictionary
dictionaryWithObjectsAndKeys:@"key", nullableValue, nil]; // reports here
return dict;
}
- (NSDictionary*)nullableKeyInNSDictionaryBad {
NSObject* nullableKey = [self nullableMethod];
NSMutableDictionary* dict = [NSMutableDictionary
dictionaryWithObjectsAndKeys:nullableKey, @"value", nil]; // reports here
return dict;
}
- (NSDictionary*)nullableKeyInNSDictionaryInitBad {
NSObject* nullableKey = [self nullableMethod];
NSDictionary* dict = [[NSDictionary alloc]
initWithObjectsAndKeys:nullableKey, @"value", nil]; // reports here
return dict;
}
- (NSDictionary*)nullableValueInNSDictionaryInitBad {
NSObject* nullableValue = [self nullableMethod];
NSDictionary* dict = [[NSDictionary alloc]
initWithObjectsAndKeys:@"key", nullableValue, nil]; // reports here
return dict;
}
- (NSDictionary*)nullableKeyInNSDictionaryInitLiteralBad {
NSObject* nullableKey = [self nullableMethod];
NSDictionary* dict = @{nullableKey : @"value"}; // reports here
return dict;
}
- (NSDictionary*)nullableValueInNSDictionaryInitLiteralBad {
NSObject* nullableValue = [self nullableMethod];
NSDictionary* dict = @{@"key" : nullableValue}; // reports here
return dict;
}
- (NSDictionary*)indirectNullableKeyInNSDictionaryBad {
NSObject* nullableKey = [self nullableMethod];
NSString* nullableKeyString = [nullableKey description];
NSDictionary* dict = [[NSDictionary alloc]
initWithObjectsAndKeys:nullableKeyString, @"value", nil]; // reports here
return dict;
}
- (NSArray*)createArrayByAddingNilBad {
NSArray* array = @[ [NSObject alloc] ];
return [array arrayByAddingObject:[self nullableMethod]];
}
- (NSDictionary*)setNullableObjectInDictionaryBad {
NSMutableDictionary* mutableDict = [NSMutableDictionary dictionary];
[mutableDict setObject:[self nullableMethod] forKey:@"key"]; // reports here
return mutableDict;
}
- (NSArray*)addNullableObjectInMutableArrayBad {
NSMutableArray* mutableArray = [[NSMutableArray alloc] init];
[mutableArray addObject:[self nullableMethod]]; // reports here
return mutableArray;
}
- (NSArray*)insertNullableObjectInMutableArrayBad {
NSMutableArray* mutableArray = [[NSMutableArray alloc] init];
[mutableArray insertObject:[self nullableMethod] atIndex:0]; // reports here
return mutableArray;
}
- (NSArray*)propagateNullabilityOnMethodCallBad {
NSObject* nullableObject = [self nullableMethod];
NSString* nullableString =
[nullableObject description]; // returns nil if nullableObject is nil
return @[ nullableString ]; // reports here
}
- (S*)shouldPropagateNullabilityOnPointerTypeBad {
T* nullableT = [self nullableT];
S* s = nullableT.pointerProperty; // returns nil when nullableT is nil
s->x = 42; // reports here
return s;
}
- (S)shouldNotPropagateNullabilityOnNonPointerTypeGood {
T* nullableT = [self nullableT];
S s =
nullableT.structProperty; // returns an empty struct when nullabeT is nil
s.x = 42; // does not report here
return s;
}
- (NSArray*)pointerAssignmentWithSubtypeOkay:(NSString*)string {
NSObject* nullableObject = [self nullableMethod];
nullableObject = string;
NSArray* array = @[ nullableObject ]; // does not report here
return array;
}
- (NSArray*)dereferenceLibraryMethodOk:(L*)object {
NSObject* nullableObject = [object libraryMethod];
NSArray* array = @[ nullableObject ]; // does not report here
return array;
}
- (NSArray*)dereferenceNullableLibraryMethodBad:(L*)object {
NSObject* nullableObject = [object nullableLibraryMethod];
NSArray* array = @[ nullableObject ]; // reports here
return array;
}
@end
@protocol P
- (NSObject* _Nullable)nullableMethod;
@end
NSDictionary* callNullableMethodFromProtocolBad(id<P> pObject) {
NSObject* nullableObject = [pObject nullableMethod];
return @{@"key" : nullableObject};
}

@ -1,13 +0,0 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
@interface L : NSObject
- (NSObject*)libraryMethod;
- (NSObject* _Nullable)nullableLibraryMethod;
@end

@ -1,17 +0,0 @@
# 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)
INFER_OPTIONS = --debug-exceptions --no-default-checkers --nullsafe --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)

@ -1,26 +0,0 @@
codetoanalyze/objc/nullable/Examples.m, T::FP_dereferenceNonnullFieldAfterTestForNullOkay, 1, NULLSAFE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [Field nonnullField is compared to null here]
codetoanalyze/objc/nullable/Examples.m, T::addNullableObjectInMutableArrayBad, 2, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereferencing the return of T::nullableMethod,assignment of the nullable value,definition of nullableMethod]
codetoanalyze/objc/nullable/Examples.m, T::assignNonnullFieldToNullBad, 1, NULLSAFE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [Field nonnullField is assigned null here]
codetoanalyze/objc/nullable/Examples.m, T::assignUnnanotatedFieldToNullBad, 1, NULLSAFE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [Field unnanotatedField is assigned null here]
codetoanalyze/objc/nullable/Examples.m, T::createArrayByAddingNilBad, 2, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereferencing the return of T::nullableMethod,assignment of the nullable value,definition of nullableMethod]
codetoanalyze/objc/nullable/Examples.m, T::dereferenceNullableFunctionBad, 2, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereference of p,assignment of the nullable value,definition of returnsNull]
codetoanalyze/objc/nullable/Examples.m, T::dereferenceNullableLibraryMethodBad:, 2, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereference of nullableObject,assignment of the nullable value,definition of nullableLibraryMethod]
codetoanalyze/objc/nullable/Examples.m, T::dereferenceUnnanotatedFieldAfterTestForNullBad, 1, NULLSAFE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [Field unnanotatedField is compared to null here]
codetoanalyze/objc/nullable/Examples.m, T::indirectNullableKeyInNSDictionaryBad, 3, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereference of nullableKeyString,assignment of the nullable value,definition of nullableMethod]
codetoanalyze/objc/nullable/Examples.m, T::insertNullableObjectInMutableArrayBad, 2, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereferencing the return of T::nullableMethod,assignment of the nullable value,definition of nullableMethod]
codetoanalyze/objc/nullable/Examples.m, T::nullableKeyInNSDictionaryBad, 2, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereference of nullableKey,assignment of the nullable value,definition of nullableMethod]
codetoanalyze/objc/nullable/Examples.m, T::nullableKeyInNSDictionaryInitBad, 2, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereference of nullableKey,assignment of the nullable value,definition of nullableMethod]
codetoanalyze/objc/nullable/Examples.m, T::nullableKeyInNSDictionaryInitLiteralBad, 2, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereference of nullableKey,assignment of the nullable value,definition of nullableMethod]
codetoanalyze/objc/nullable/Examples.m, T::nullableObjectInNSArrayBad, 2, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereference of nullableObject,assignment of the nullable value,definition of nullableMethod]
codetoanalyze/objc/nullable/Examples.m, T::nullablePropertyInNSArrayBad, 1, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereferencing the return of T::nullableMethod,assignment of the nullable value,definition of nullableMethod]
codetoanalyze/objc/nullable/Examples.m, T::nullableValueInNSDictionaryBad, 2, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereference of nullableValue,assignment of the nullable value,definition of nullableMethod]
codetoanalyze/objc/nullable/Examples.m, T::nullableValueInNSDictionaryInitBad, 2, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereference of nullableValue,assignment of the nullable value,definition of nullableMethod]
codetoanalyze/objc/nullable/Examples.m, T::nullableValueInNSDictionaryInitLiteralBad, 2, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereference of nullableValue,assignment of the nullable value,definition of nullableMethod]
codetoanalyze/objc/nullable/Examples.m, T::propagateNullabilityOnMethodCallBad, 4, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereference of nullableString,assignment of the nullable value,definition of nullableMethod]
codetoanalyze/objc/nullable/Examples.m, T::secondElementNullableObjectInNSArrayBad, 3, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereference of nullableObject,assignment of the nullable value,definition of nullableMethod]
codetoanalyze/objc/nullable/Examples.m, T::setNullableObjectInDictionaryBad, 2, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereferencing the return of T::nullableMethod,assignment of the nullable value,definition of nullableMethod]
codetoanalyze/objc/nullable/Examples.m, T::shouldPropagateNullabilityOnPointerTypeBad, 3, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereference of s,assignment of the nullable value,definition of nullableT]
codetoanalyze/objc/nullable/Examples.m, T::testNonnullFieldForNullBad, 1, NULLSAFE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [Field nonnullField is compared to null here]
codetoanalyze/objc/nullable/Examples.m, T::testUnnanotatedFieldForNullBad, 1, NULLSAFE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [Field unnanotatedField is compared to null here]
codetoanalyze/objc/nullable/Examples.m, callNullableMethodFromProtocolBad, 2, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereference of nullableObject,assignment of the nullable value,definition of nullableMethod]
codetoanalyze/objc/nullable/Examples.m, objc_blockT::DeadStoreFP_testUnnanotatedFieldInClosureBad_1, 1, NULLSAFE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [Field unnanotatedField is compared to null here]

@ -1,27 +0,0 @@
/*
* 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.
*/
#import <Foundation/NSObject.h>
#import <string>
@interface A : NSObject {
}
- (char _Nullable*)toString;
@end
std::string stdStringOK(A* a) {
const char* s1 = [a toString];
if (s1) {
std::string s2 = std::string(s1);
return s2;
} else
return "";
}
std::string stdStringBad(A* a) {
std::string s2 = std::string([a toString]);
return s2;
}

@ -1,17 +0,0 @@
# 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 $(OBJCPP_CLANG_OPTIONS)
INFER_OPTIONS = --debug-exceptions --no-default-checkers --nullsafe --project-root $(TESTS_DIR)
INFERPRINT_OPTIONS = --issues-tests
SOURCES = $(wildcard *.mm)
include $(TESTS_DIR)/clang.make
include $(TESTS_DIR)/objc.make
infer-out/report.json: $(MAKEFILE_LIST)

@ -1 +0,0 @@
codetoanalyze/objcpp/nullable/Examples.mm, stdStringBad, 1, NULLSAFE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [dereferencing the return of A::toString,assignment of the nullable value,definition of toString]
Loading…
Cancel
Save