diff --git a/Makefile b/Makefile index 698d07f47..dbf28927e 100644 --- a/Makefile +++ b/Makefile @@ -66,7 +66,6 @@ DIRECT_TESTS += \ cpp_linters \ cpp_linters-for-test-only \ cpp_liveness \ - cpp_nullable \ cpp_pulse \ cpp_quandary cpp_quandaryBO \ cpp_racerd \ @@ -115,7 +114,6 @@ DIRECT_TESTS += \ objc_linters-def-folder \ objc_linters-for-test-only \ objc_liveness \ - objc_nullable \ objc_performance \ objc_pulse \ objc_quandary \ @@ -126,7 +124,6 @@ DIRECT_TESTS += \ objcpp_linters \ objcpp_linters-for-test-only \ objcpp_liveness \ - objcpp_nullable \ objcpp_pulse \ objcpp_racerd \ objcpp_retain-cycles \ diff --git a/infer/man/man1/infer-analyze.txt b/infer/man/man1/infer-analyze.txt index 3037a73b3..97ca0d75e 100644 --- a/infer/man/man1/infer-analyze.txt +++ b/infer/man/man1/infer-analyze.txt @@ -201,8 +201,8 @@ OPTIONS (Conversely: --no-loop-hoisting-only) --nullsafe - Activates: [EXPERIMENTAL] Nullable type checker (incomplete: use - --eradicate for now) (Conversely: --no-nullsafe) + Activates: [RESERVED] Reserved for nullsafe typechecker, use + --eradicate for now (Conversely: --no-nullsafe) --nullsafe-only Activates: Enable --nullsafe and disable all other checkers diff --git a/infer/man/man1/infer-full.txt b/infer/man/man1/infer-full.txt index f11c29f4f..2f0577a39 100644 --- a/infer/man/man1/infer-full.txt +++ b/infer/man/man1/infer-full.txt @@ -470,8 +470,6 @@ OPTIONS MUTABLE_LOCAL_VARIABLE_IN_COMPONENT_FILE (enabled by default), Missing_fld (enabled 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_TEST_AFTER_DEREFERENCE (disabled by default), PARAMETER_NOT_NULL_CHECKED (enabled by default), @@ -801,8 +799,8 @@ OPTIONS See also infer-analyze(1). --nullsafe - Activates: [EXPERIMENTAL] Nullable type checker (incomplete: use - --eradicate for now) (Conversely: --no-nullsafe) See also infer-analyze(1). + Activates: [RESERVED] Reserved for nullsafe typechecker, use + --eradicate for now (Conversely: --no-nullsafe) See also infer-analyze(1). --nullsafe-only Activates: Enable --nullsafe and disable all other checkers diff --git a/infer/man/man1/infer-report.txt b/infer/man/man1/infer-report.txt index 6075959e2..3fc369d9d 100644 --- a/infer/man/man1/infer-report.txt +++ b/infer/man/man1/infer-report.txt @@ -192,8 +192,6 @@ OPTIONS MUTABLE_LOCAL_VARIABLE_IN_COMPONENT_FILE (enabled by default), Missing_fld (enabled 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_TEST_AFTER_DEREFERENCE (disabled by default), PARAMETER_NOT_NULL_CHECKED (enabled by default), diff --git a/infer/man/man1/infer.txt b/infer/man/man1/infer.txt index 0f31dc402..76848c533 100644 --- a/infer/man/man1/infer.txt +++ b/infer/man/man1/infer.txt @@ -470,8 +470,6 @@ OPTIONS MUTABLE_LOCAL_VARIABLE_IN_COMPONENT_FILE (enabled by default), Missing_fld (enabled 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_TEST_AFTER_DEREFERENCE (disabled by default), PARAMETER_NOT_NULL_CHECKED (enabled by default), @@ -801,8 +799,8 @@ OPTIONS See also infer-analyze(1). --nullsafe - Activates: [EXPERIMENTAL] Nullable type checker (incomplete: use - --eradicate for now) (Conversely: --no-nullsafe) See also infer-analyze(1). + Activates: [RESERVED] Reserved for nullsafe typechecker, use + --eradicate for now (Conversely: --no-nullsafe) See also infer-analyze(1). --nullsafe-only Activates: Enable --nullsafe and disable all other checkers diff --git a/infer/src/IR/AccessPath.ml b/infer/src/IR/AccessPath.ml index a5b66d6fa..cdf273158 100644 --- a/infer/src/IR/AccessPath.ml +++ b/infer/src/IR/AccessPath.ml @@ -55,14 +55,6 @@ module Raw = struct 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 = Tenv.lookup tenv in Struct.get_field_type_and_annotation ~lookup field_name base_typ @@ -76,16 +68,6 @@ module Raw = struct 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. * 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. @@ -109,16 +91,6 @@ module Raw = struct 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 = match last_access_info ap tenv with | (Some _ as typ), None -> diff --git a/infer/src/IR/AccessPath.mli b/infer/src/IR/AccessPath.mli index 7a49a3d1e..0116ebd9f 100644 --- a/infer/src/IR/AccessPath.mli +++ b/infer/src/IR/AccessPath.mli @@ -20,17 +20,6 @@ type access = as (x, [f; g]) *) 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 (** 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) *) diff --git a/infer/src/IR/Fieldname.ml b/infer/src/IR/Fieldname.ml index f795cb63d..d1d3c6595 100644 --- a/infer/src/IR/Fieldname.ml +++ b/infer/src/IR/Fieldname.ml @@ -51,10 +51,6 @@ let to_full_string fld = 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) = is_java field && diff --git a/infer/src/IR/Fieldname.mli b/infer/src/IR/Fieldname.mli index 6efc69457..830c36911 100644 --- a/infer/src/IR/Fieldname.mli +++ b/infer/src/IR/Fieldname.mli @@ -26,9 +26,6 @@ module Set : Caml.Set.S with type elt = t module Map : Caml.Map.S with type key = t (** Map for fieldnames *) -val is_java_captured_parameter : t -> bool -(** Check if field is a captured parameter *) - 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 instance. *) diff --git a/infer/src/IR/Localise.mli b/infer/src/IR/Localise.mli index 561883115..49aec5014 100644 --- a/infer/src/IR/Localise.mli +++ b/infer/src/IR/Localise.mli @@ -94,9 +94,6 @@ type access = | Initialized_automatically | Returned_from_call of int -val nullable_annotation_name : Procname.t -> string -(** Name of the nullable annotation *) - val dereference_string : Procname.t -> deref_str -> string -> access option -> Location.t -> error_desc diff --git a/infer/src/base/Config.ml b/infer/src/base/Config.ml index a2dbd4e25..cff58d479 100644 --- a/infer/src/base/Config.ml +++ b/infer/src/base/Config.ml @@ -32,7 +32,6 @@ type checkers = ; litho_required_props: bool ref ; liveness: bool ref ; loop_hoisting: bool ref - ; nullsafe: bool ref ; self_in_block: bool ref ; printf_args: bool ref ; pulse: bool ref @@ -631,7 +630,6 @@ and { annotation_reachability ; litho_required_props ; liveness ; loop_hoisting - ; nullsafe ; self_in_block ; printf_args ; pulse @@ -643,9 +641,11 @@ and { annotation_reachability ; siof ; starvation ; uninit } = - let mk_checker ?(default = false) ?(deprecated = []) ~long doc = + let mk_checker ?(default = false) ?(deprecated = []) ~long ?f doc = 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 all_checkers := (var, long, doc, default) :: !all_checkers ; var @@ -681,10 +681,14 @@ and { annotation_reachability and liveness = 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 nullsafe = + and _nullsafe = + (* TODO make this to be activate nullsafe typechecker when old usages are gone *) 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"] - "[EXPERIMENTAL] Nullable type checker (incomplete: use --eradicate for now)" + "[RESERVED] Reserved for nullsafe typechecker, use --eradicate for now" and printf_args = mk_checker ~long:"printf-args" ~default:false "the detection of mismatch between the Java printf format strings and the argument types \ @@ -758,7 +762,6 @@ and { annotation_reachability ; litho_required_props ; liveness ; loop_hoisting - ; nullsafe ; printf_args ; pulse ; purity @@ -2810,8 +2813,6 @@ and censor_report = and changed_files_index = !changed_files_index -and nullsafe = !nullsafe - and check_version = !check_version and clang_biniou_file = !clang_biniou_file diff --git a/infer/src/base/Config.mli b/infer/src/base/Config.mli index a60d40318..33f6a2285 100644 --- a/infer/src/base/Config.mli +++ b/infer/src/base/Config.mli @@ -489,8 +489,6 @@ val no_translate_libs : bool val nullable_annotation : string option -val nullsafe : bool - val nullsafe_disable_field_not_initialized_in_nonstrict_classes : bool val nullsafe_optimistic_third_party_params_in_non_strict : bool diff --git a/infer/src/base/IssueType.ml b/infer/src/base/IssueType.ml index 6728456d5..e31cb318d 100644 --- a/infer/src/base/IssueType.ml +++ b/infer/src/base/IssueType.ml @@ -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 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 pointer_size_mismatch = register_from_string "POINTER_SIZE_MISMATCH" diff --git a/infer/src/base/IssueType.mli b/infer/src/base/IssueType.mli index e68f6df04..7d001ae6c 100644 --- a/infer/src/base/IssueType.mli +++ b/infer/src/base/IssueType.mli @@ -250,10 +250,6 @@ val null_test_after_dereference : t val nullptr_dereference : t -val nullsafe_field_not_nullable : t - -val nullsafe_nullable_dereference : t - val parameter_not_null_checked : t val pointer_size_mismatch : t diff --git a/infer/src/checkers/registerCheckers.ml b/infer/src/checkers/registerCheckers.ml index bb19c3e48..6aa75ce8c 100644 --- a/infer/src/checkers/registerCheckers.ml +++ b/infer/src/checkers/registerCheckers.ml @@ -35,11 +35,6 @@ let all_checkers = ; callbacks= [ (Procedure AnnotationReachability.checker, Language.Java) ; (Procedure AnnotationReachability.checker, Language.Clang) ] } - ; { name= "nullable checks" - ; active= Config.nullsafe - ; callbacks= - [ (Procedure NullabilityCheck.checker, Language.Clang) - ; (Procedure NullabilityCheck.checker, Language.Java) ] } ; { name= "biabduction" ; active= Config.biabduction ; callbacks= @@ -76,11 +71,6 @@ let all_checkers = ; { name= "printf args" ; active= Config.printf_args ; 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" ; active= Config.impurity ; callbacks= diff --git a/infer/src/nullsafe/NullabilityCheck.ml b/infer/src/nullsafe/NullabilityCheck.ml deleted file mode 100644 index 4a7b1ac35..000000000 --- a/infer/src/nullsafe/NullabilityCheck.ml +++ /dev/null @@ -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 diff --git a/infer/src/nullsafe/NullabilityCheck.mli b/infer/src/nullsafe/NullabilityCheck.mli deleted file mode 100644 index 489676eb8..000000000 --- a/infer/src/nullsafe/NullabilityCheck.mli +++ /dev/null @@ -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 diff --git a/infer/src/nullsafe/NullabilitySuggest.ml b/infer/src/nullsafe/NullabilitySuggest.ml deleted file mode 100644 index e2fb64a5b..000000000 --- a/infer/src/nullsafe/NullabilitySuggest.ml +++ /dev/null @@ -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 diff --git a/infer/src/nullsafe/NullabilitySuggest.mli b/infer/src/nullsafe/NullabilitySuggest.mli deleted file mode 100644 index e49449258..000000000 --- a/infer/src/nullsafe/NullabilitySuggest.mli +++ /dev/null @@ -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 diff --git a/infer/tests/codetoanalyze/cpp/conflicts/Makefile b/infer/tests/codetoanalyze/cpp/conflicts/Makefile index 665dcbd58..3a9659583 100644 --- a/infer/tests/codetoanalyze/cpp/conflicts/Makefile +++ b/infer/tests/codetoanalyze/cpp/conflicts/Makefile @@ -7,7 +7,7 @@ TESTS_DIR = ../../.. 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 SOURCES = $(wildcard *.cpp) diff --git a/infer/tests/codetoanalyze/cpp/conflicts/issues.exp b/infer/tests/codetoanalyze/cpp/conflicts/issues.exp index 27b7536fa..f2a1c89f2 100644 --- a/infer/tests/codetoanalyze/cpp/conflicts/issues.exp +++ b/infer/tests/codetoanalyze/cpp/conflicts/issues.exp @@ -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, 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] diff --git a/infer/tests/codetoanalyze/cpp/nullable/Makefile b/infer/tests/codetoanalyze/cpp/nullable/Makefile deleted file mode 100644 index 1cb682130..000000000 --- a/infer/tests/codetoanalyze/cpp/nullable/Makefile +++ /dev/null @@ -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) diff --git a/infer/tests/codetoanalyze/cpp/nullable/example.cpp b/infer/tests/codetoanalyze/cpp/nullable/example.cpp deleted file mode 100644 index abf5457d5..000000000 --- a/infer/tests/codetoanalyze/cpp/nullable/example.cpp +++ /dev/null @@ -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; - } - } -}; diff --git a/infer/tests/codetoanalyze/cpp/nullable/issues.exp b/infer/tests/codetoanalyze/cpp/nullable/issues.exp deleted file mode 100644 index 996383ee2..000000000 --- a/infer/tests/codetoanalyze/cpp/nullable/issues.exp +++ /dev/null @@ -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] diff --git a/infer/tests/codetoanalyze/cpp/nullable/method.cpp b/infer/tests/codetoanalyze/cpp/nullable/method.cpp deleted file mode 100644 index 9f794de3c..000000000 --- a/infer/tests/codetoanalyze/cpp/nullable/method.cpp +++ /dev/null @@ -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(); - } -} diff --git a/infer/tests/codetoanalyze/java/checkers/Makefile b/infer/tests/codetoanalyze/java/checkers/Makefile index 0ce68d834..43d7754b3 100644 --- a/infer/tests/codetoanalyze/java/checkers/Makefile +++ b/infer/tests/codetoanalyze/java/checkers/Makefile @@ -8,7 +8,7 @@ TESTS_DIR = ../../.. INFER_OPTIONS = \ --debug-exceptions --no-default-checkers \ --fragment-retains-view --immutable-cast --printf-args --quandary \ - --nullsafe --racerd \ + --racerd \ INFERPRINT_OPTIONS = --issues-tests SOURCES = $(wildcard *.java) $(wildcard $(TESTS_DIR)/external/library/*.java) diff --git a/infer/tests/codetoanalyze/java/checkers/issues.exp b/infer/tests/codetoanalyze/java/checkers/issues.exp index 3b47e747d..e82e114b0 100644 --- a/infer/tests/codetoanalyze/java/checkers/issues.exp +++ b/infer/tests/codetoanalyze/java/checkers/issues.exp @@ -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/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/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.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, [] diff --git a/infer/tests/codetoanalyze/objc/nullable/Examples.m b/infer/tests/codetoanalyze/objc/nullable/Examples.m deleted file mode 100644 index dcee1b3ee..000000000 --- a/infer/tests/codetoanalyze/objc/nullable/Examples.m +++ /dev/null @@ -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 -#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

pObject) { - NSObject* nullableObject = [pObject nullableMethod]; - return @{@"key" : nullableObject}; -} diff --git a/infer/tests/codetoanalyze/objc/nullable/Library.h b/infer/tests/codetoanalyze/objc/nullable/Library.h deleted file mode 100644 index 7f2c6a7ee..000000000 --- a/infer/tests/codetoanalyze/objc/nullable/Library.h +++ /dev/null @@ -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 - -@interface L : NSObject -- (NSObject*)libraryMethod; -- (NSObject* _Nullable)nullableLibraryMethod; -@end diff --git a/infer/tests/codetoanalyze/objc/nullable/Makefile b/infer/tests/codetoanalyze/objc/nullable/Makefile deleted file mode 100644 index e735f80af..000000000 --- a/infer/tests/codetoanalyze/objc/nullable/Makefile +++ /dev/null @@ -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) diff --git a/infer/tests/codetoanalyze/objc/nullable/issues.exp b/infer/tests/codetoanalyze/objc/nullable/issues.exp deleted file mode 100644 index a59a7b62b..000000000 --- a/infer/tests/codetoanalyze/objc/nullable/issues.exp +++ /dev/null @@ -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] diff --git a/infer/tests/codetoanalyze/objcpp/nullable/Examples.mm b/infer/tests/codetoanalyze/objcpp/nullable/Examples.mm deleted file mode 100644 index a2bf63f22..000000000 --- a/infer/tests/codetoanalyze/objcpp/nullable/Examples.mm +++ /dev/null @@ -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 -#import - -@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; -} diff --git a/infer/tests/codetoanalyze/objcpp/nullable/Makefile b/infer/tests/codetoanalyze/objcpp/nullable/Makefile deleted file mode 100644 index 62aceb639..000000000 --- a/infer/tests/codetoanalyze/objcpp/nullable/Makefile +++ /dev/null @@ -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) diff --git a/infer/tests/codetoanalyze/objcpp/nullable/issues.exp b/infer/tests/codetoanalyze/objcpp/nullable/issues.exp deleted file mode 100644 index 2a8aedb56..000000000 --- a/infer/tests/codetoanalyze/objcpp/nullable/issues.exp +++ /dev/null @@ -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]