From 4d52e874fc79774b719d7b1199693d1fe85f9d85 Mon Sep 17 00:00:00 2001 From: Mitya Lyubarskiy Date: Mon, 21 Oct 2019 04:40:31 -0700 Subject: [PATCH] [nullsafe] Introduce DeclaredNonnull Summary: This is an intermediate nullability type powering future Strict mode. See the next diff. Reviewed By: artempyanykh Differential Revision: D17977909 fbshipit-source-id: 2d5ab66d4 --- infer/src/nullsafe/AnnotatedNullability.ml | 40 +++++++++-------- infer/src/nullsafe/AnnotatedNullability.mli | 12 ++++-- infer/src/nullsafe/AssignmentRule.ml | 9 +++- infer/src/nullsafe/DereferenceRule.ml | 2 +- infer/src/nullsafe/InferredNullability.ml | 14 +++++- infer/src/nullsafe/InferredNullability.mli | 2 + infer/src/nullsafe/InheritanceRule.ml | 16 ++++++- infer/src/nullsafe/Nullability.ml | 21 ++++++++- infer/src/nullsafe/Nullability.mli | 5 +++ infer/src/nullsafe/OverAnnotatedRule.ml | 12 +++++- infer/src/nullsafe/eradicateChecks.ml | 15 ++++--- infer/src/nullsafe/typeCheck.ml | 43 ++++++++++--------- .../java/nullsafe-default/issues.exp | 20 ++++----- 13 files changed, 146 insertions(+), 65 deletions(-) diff --git a/infer/src/nullsafe/AnnotatedNullability.ml b/infer/src/nullsafe/AnnotatedNullability.ml index 36238b09c..5da5c1a14 100644 --- a/infer/src/nullsafe/AnnotatedNullability.ml +++ b/infer/src/nullsafe/AnnotatedNullability.ml @@ -16,8 +16,11 @@ module F = Format this information is inferred according to flow-sensitive inferrence rule. *) -(* TODO(T52947663) add notion of unknown nullability *) -type t = Nullable of nullable_origin | Nonnull of nonnull_origin [@@deriving compare] +type t = + | Nullable of nullable_origin + | DeclaredNonnull of declared_nonnull_origin (** See {Nullability.t} for explanation *) + | Nonnull of nonnull_origin +[@@deriving compare] and nullable_origin = | AnnotatedNullable (** The type is expicitly annotated with @Nullable in the code *) @@ -28,17 +31,21 @@ and nullable_origin = | ModelledNullable (** nullsafe knows it is nullable via its internal models *) [@@deriving compare] -and nonnull_origin = +and declared_nonnull_origin = | AnnotatedNonnull (** The type is explicitly annotated as non nullable via one of nonnull annotations Nullsafe recognizes *) - | NotAnnotatedHenceNullableMode + | ImplicitlyNonnull (** Infer was run in mode where all not annotated (non local) types are treated as non nullable *) + +and nonnull_origin = | ModelledNonnull (** nullsafe knows it is non-nullable via its internal models *) [@@deriving compare] let get_nullability = function | Nullable _ -> Nullability.Nullable + | DeclaredNonnull _ -> + Nullability.DeclaredNonnull | Nonnull _ -> Nullability.Nonnull @@ -55,20 +62,19 @@ let pp fmt t = | ModelledNullable -> "model" in + let string_of_declared_nonnull_origin origin = + match origin with AnnotatedNonnull -> "@" | ImplicitlyNonnull -> "implicit" + in let string_of_nonnull_origin nonnull_origin = - match nonnull_origin with - | AnnotatedNonnull -> - "@" - | NotAnnotatedHenceNullableMode -> - "default" - | ModelledNonnull -> - "model" + match nonnull_origin with ModelledNonnull -> "model" in match t with - | Nullable nullable_origin -> - F.fprintf fmt "Nullable[%s]" (string_of_nullable_origin nullable_origin) - | Nonnull nonnull_origin -> - F.fprintf fmt "Nonnull[%s]" (string_of_nonnull_origin nonnull_origin) + | Nullable origin -> + F.fprintf fmt "Nullable[%s]" (string_of_nullable_origin origin) + | DeclaredNonnull origin -> + F.fprintf fmt "DeclaredNonnull[%s]" (string_of_declared_nonnull_origin origin) + | Nonnull origin -> + F.fprintf fmt "Nonnull[%s]" (string_of_nonnull_origin origin) let of_annot_item ia = @@ -78,6 +84,6 @@ let of_annot_item ia = else AnnotatedNullable in Nullable nullable_origin - else if Annotations.ia_is_nonnull ia then Nonnull AnnotatedNonnull + else if Annotations.ia_is_nonnull ia then DeclaredNonnull AnnotatedNonnull (* Currently, we treat not annotated types as nonnull *) - else Nonnull NotAnnotatedHenceNullableMode + else DeclaredNonnull ImplicitlyNonnull diff --git a/infer/src/nullsafe/AnnotatedNullability.mli b/infer/src/nullsafe/AnnotatedNullability.mli index 22a9fb6c3..55b1e42e7 100644 --- a/infer/src/nullsafe/AnnotatedNullability.mli +++ b/infer/src/nullsafe/AnnotatedNullability.mli @@ -19,7 +19,11 @@ open! IStd annotated nullability applies only for types declared at methods and field level. *) -type t = Nullable of nullable_origin | Nonnull of nonnull_origin [@@deriving compare] +type t = + | Nullable of nullable_origin + | DeclaredNonnull of declared_nonnull_origin (** See {Nullability.t} for explanation *) + | Nonnull of nonnull_origin +[@@deriving compare] and nullable_origin = | AnnotatedNullable (** The type is expicitly annotated with @Nullable in the code *) @@ -30,11 +34,13 @@ and nullable_origin = | ModelledNullable (** nullsafe knows it is nullable via its internal models *) [@@deriving compare] -and nonnull_origin = +and declared_nonnull_origin = | AnnotatedNonnull (** The type is explicitly annotated as non nullable via one of nonnull annotations Nullsafe recognizes *) - | NotAnnotatedHenceNullableMode + | ImplicitlyNonnull (** Infer was run in mode where all not annotated (non local) types are treated as non nullable *) + +and nonnull_origin = | ModelledNonnull (** nullsafe knows it is non-nullable via its internal models *) [@@deriving compare] diff --git a/infer/src/nullsafe/AssignmentRule.ml b/infer/src/nullsafe/AssignmentRule.ml index 21e9b5dcc..41abeb714 100644 --- a/infer/src/nullsafe/AssignmentRule.ml +++ b/infer/src/nullsafe/AssignmentRule.ml @@ -17,8 +17,15 @@ type assignment_type = | ReturningFromFunction of Typ.Procname.t [@@deriving compare] +let is_whitelisted_assignment ~lhs ~rhs = + match (lhs, rhs) with Nullability.Nonnull, Nullability.DeclaredNonnull -> true | _ -> false + + let check ~lhs ~rhs = - Result.ok_if_true (Nullability.is_subtype ~subtype:rhs ~supertype:lhs) ~error:{lhs; rhs} + let is_allowed_assignment = + Nullability.is_subtype ~subtype:rhs ~supertype:lhs || is_whitelisted_assignment ~lhs ~rhs + in + Result.ok_if_true is_allowed_assignment ~error:{lhs; rhs} let violation_description _ assignment_type ~rhs_origin_descr = diff --git a/infer/src/nullsafe/DereferenceRule.ml b/infer/src/nullsafe/DereferenceRule.ml index 6ce188b0f..28f628574 100644 --- a/infer/src/nullsafe/DereferenceRule.ml +++ b/infer/src/nullsafe/DereferenceRule.ml @@ -18,7 +18,7 @@ type dereference_type = let check = function | Nullability.Nullable as nullability -> Error nullability - | Nullability.Nonnull -> + | Nullability.DeclaredNonnull | Nullability.Nonnull -> Ok () diff --git a/infer/src/nullsafe/InferredNullability.ml b/infer/src/nullsafe/InferredNullability.ml index bd6eedfc7..fa79be027 100644 --- a/infer/src/nullsafe/InferredNullability.ml +++ b/infer/src/nullsafe/InferredNullability.ml @@ -11,9 +11,17 @@ type t = {nullability: Nullability.t; origin: TypeOrigin.t} [@@deriving compare] let get_nullability {nullability} = nullability -let is_nonnull {nullability} = match nullability with Nullable -> false | Nonnull -> true +let is_nonnull_or_declared_nonnull {nullability} = + match nullability with Nullable -> false | DeclaredNonnull -> true | Nonnull -> true -let set_nonnull t = {t with nullability= Nullability.Nonnull} + +let is_nonnull {nullability} = + match nullability with Nullable -> false | DeclaredNonnull -> false | Nonnull -> true + + +let set_nullability nullability t = {t with nullability} + +let set_nonnull = set_nullability Nullability.Nonnull let descr_origin t = let descr_opt = TypeOrigin.get_description t.origin in @@ -71,5 +79,7 @@ let of_annotated_nullability annotated_nullability origin = match annotated_nullability with | AnnotatedNullability.Nullable _ -> {origin; nullability= Nullability.Nullable} + | AnnotatedNullability.DeclaredNonnull _ -> + {origin; nullability= Nullability.DeclaredNonnull} | AnnotatedNullability.Nonnull _ -> {origin; nullability= Nullability.Nonnull} diff --git a/infer/src/nullsafe/InferredNullability.mli b/infer/src/nullsafe/InferredNullability.mli index 3a84f15ec..609ab0b3e 100644 --- a/infer/src/nullsafe/InferredNullability.mli +++ b/infer/src/nullsafe/InferredNullability.mli @@ -25,6 +25,8 @@ val create_nullable : TypeOrigin.t -> t val create_nonnull : TypeOrigin.t -> t +val is_nonnull_or_declared_nonnull : t -> bool + val is_nonnull : t -> bool val set_nonnull : t -> t diff --git a/infer/src/nullsafe/InheritanceRule.ml b/infer/src/nullsafe/InheritanceRule.ml index 13ebfbcc3..36cd43a52 100644 --- a/infer/src/nullsafe/InheritanceRule.ml +++ b/infer/src/nullsafe/InheritanceRule.ml @@ -10,6 +10,18 @@ type violation = {base: Nullability.t; overridden: Nullability.t} [@@deriving co type type_role = Param | Ret +let is_whitelisted_violation ~subtype ~supertype = + match (subtype, supertype) with + | Nullability.DeclaredNonnull, Nullability.Nonnull -> + (* It is a violation that we are currently willing to ignore because + it is hard to obey in practice. + It might lead to unsoundness issues, so this might be reconsidered. + *) + true + | _ -> + false + + let check type_role ~base ~overridden = let subtype, supertype = match type_role with @@ -20,7 +32,9 @@ let check type_role ~base ~overridden = (* contravariance for param *) (base, overridden) in - Result.ok_if_true (Nullability.is_subtype ~subtype ~supertype) ~error:{base; overridden} + Result.ok_if_true + (Nullability.is_subtype ~subtype ~supertype || is_whitelisted_violation ~subtype ~supertype) + ~error:{base; overridden} type violation_type = diff --git a/infer/src/nullsafe/Nullability.ml b/infer/src/nullsafe/Nullability.ml index 8837d8776..f1a992d27 100644 --- a/infer/src/nullsafe/Nullability.ml +++ b/infer/src/nullsafe/Nullability.ml @@ -9,6 +9,11 @@ open! IStd type t = | Nullable (** No guarantees on the nullability *) + | DeclaredNonnull + (** The type comes from a signature that is annotated (explicitly or implicitly according to conventions) + as non-nullable. Hovewer, it might still contain null since the truthfullness of the declaration was + not checked. + *) | Nonnull (** We believe that this value can not be null. If it is not the case, this is an unsoundness issue for Nullsafe, and we aim to minimize number of such issues @@ -18,7 +23,13 @@ type t = let top = Nullable let join x y = - match (x, y) with Nullable, _ | _, Nullable -> Nullable | Nonnull, Nonnull -> Nonnull + match (x, y) with + | Nullable, _ | _, Nullable -> + Nullable + | DeclaredNonnull, _ | _, DeclaredNonnull -> + DeclaredNonnull + | Nonnull, Nonnull -> + Nonnull let is_subtype ~subtype ~supertype = equal (join subtype supertype) supertype @@ -27,4 +38,10 @@ let is_strict_subtype ~subtype ~supertype = is_subtype ~subtype ~supertype && not (equal subtype supertype) -let to_string = function Nullable -> "Nullable" | Nonnull -> "Nonnull" +let to_string = function + | Nullable -> + "Nullable" + | DeclaredNonnull -> + "DeclaredNonnull" + | Nonnull -> + "Nonnull" diff --git a/infer/src/nullsafe/Nullability.mli b/infer/src/nullsafe/Nullability.mli index f647b5d61..03a278936 100644 --- a/infer/src/nullsafe/Nullability.mli +++ b/infer/src/nullsafe/Nullability.mli @@ -18,6 +18,11 @@ open! IStd type t = | Nullable (** No guarantees on the nullability *) + | DeclaredNonnull + (** The type comes from a signature that is annotated (explicitly or implicitly according to conventions) + as non-nullable. Hovewer, it might still contain null since the truthfullness of the declaration was + not checked. + *) | Nonnull (** We believe that this value can not be null. If it is not the case, this is an unsoundness issue for Nullsafe, and we aim to minimize number of such issues diff --git a/infer/src/nullsafe/OverAnnotatedRule.ml b/infer/src/nullsafe/OverAnnotatedRule.ml index 0c1b7feaa..a76e6b563 100644 --- a/infer/src/nullsafe/OverAnnotatedRule.ml +++ b/infer/src/nullsafe/OverAnnotatedRule.ml @@ -9,8 +9,16 @@ open! IStd type violation = {lhs: Nullability.t; rhs_upper_bound: Nullability.t} [@@deriving compare] let check ~lhs ~rhs_upper_bound = - if Nullability.is_strict_subtype ~subtype:rhs_upper_bound ~supertype:lhs then - Error {lhs; rhs_upper_bound} + if + Nullability.is_strict_subtype ~subtype:rhs_upper_bound ~supertype:lhs + && (* Suppress violations for anything apart from Nullable since such + issues are not very actionable and/or clear for the user. + E.g. we technically can suggest changing [DeclaredNonnull] to [Nonnull], + but in practice that requires strictification the code, which is a + separate effort. + *) + Nullability.equal lhs Nullable + then Error {lhs; rhs_upper_bound} else Ok () diff --git a/infer/src/nullsafe/eradicateChecks.ml b/infer/src/nullsafe/eradicateChecks.ml index 8f2bd488c..26c67d557 100644 --- a/infer/src/nullsafe/eradicateChecks.ml +++ b/infer/src/nullsafe/eradicateChecks.ml @@ -91,7 +91,8 @@ let check_condition tenv case_zero find_canonical_duplicate curr_pdesc node e ty in let is_temp = Idenv.exp_is_temp idenv e in let should_report = - InferredNullability.is_nonnull inferred_nullability + (* TODO: This condition should be extracted into a dedicated rule *) + InferredNullability.is_nonnull_or_declared_nonnull inferred_nullability && Config.eradicate_condition_redundant && true_branch && (not is_temp) && PatternMatch.type_is_class typ && (not (from_try_with_resources ())) @@ -166,20 +167,22 @@ let check_field_assignment tenv find_canonical_duplicate curr_pdesc node instr_r (Some instr_ref) loc curr_pdesc ) -let is_nonnull AnnotatedField.{annotated_type} = +let is_declared_nonnull AnnotatedField.{annotated_type} = match annotated_type.nullability with | AnnotatedNullability.Nullable _ -> false + | AnnotatedNullability.DeclaredNonnull _ -> + true | AnnotatedNullability.Nonnull _ -> true -(* Do we have evidence that the field is annotated as non-nullable? *) -let is_field_annotated_as_nonnull annotated_field_opt = +(* Is field declared as non-nullable (implicitly or explicitly)? *) +let is_field_declared_as_nonnull annotated_field_opt = (* If the field is not present, we optimistically assume it is not nullable. TODO(T54687014) investigate if this leads to unsoundness issues in practice *) - Option.exists annotated_field_opt ~f:is_nonnull + Option.exists annotated_field_opt ~f:is_declared_nonnull let lookup_field_in_typestate pname field_name typestate = @@ -282,7 +285,7 @@ let check_constructor_initialization tenv find_canonical_duplicate curr_construc if should_check_field_initialization then ( (* Check if non-null field is not initialized. *) if - is_field_annotated_as_nonnull annotated_field + is_field_declared_as_nonnull annotated_field && not is_initialized_in_either_constructor_or_initializer then report_error tenv find_canonical_duplicate diff --git a/infer/src/nullsafe/typeCheck.ml b/infer/src/nullsafe/typeCheck.ml index 9609c3fc1..14077bf7f 100644 --- a/infer/src/nullsafe/typeCheck.ml +++ b/infer/src/nullsafe/typeCheck.ml @@ -538,7 +538,8 @@ let typecheck_instr tenv calls_this checks (node : Procdesc.Node.t) idenv curr_p | Some (t, nullability) -> let should_report = Config.eradicate_condition_redundant - && InferredNullability.is_nonnull nullability + (* TODO: This condition should be extracted into a dedicated rule *) + && InferredNullability.is_nonnull_or_declared_nonnull nullability && not (InferredNullability.origin_is_fun_library nullability) in ( if checks.eradicate && should_report then @@ -693,29 +694,31 @@ let typecheck_instr tenv calls_this checks (node : Procdesc.Node.t) idenv curr_p in EradicateChecks.{num; formal= formal_param; actual; is_formal_propagates_nullable} in - (* If the function has @PropagatesNullable params, and inferred type for each of - corresponding actual param is non-nullable, inferred type for the whole result - can be strengthened to non-nullable as well. + (* If the function has @PropagatesNullable params the nullability of result is determined by + nullability of actual values of these params. *) let clarify_ret_by_propagates_nullable ret (resolved_params : EradicateChecks.resolved_param list) = - let propagates_nullable_params = - List.filter resolved_params ~f:(fun (param : EradicateChecks.resolved_param) -> - param.is_formal_propagates_nullable ) + (* Nullability of actual values of params that are marked as propagating nullables *) + let nullability_of_propagates_nullable_params = + List.filter_map resolved_params + ~f:(fun EradicateChecks. + {is_formal_propagates_nullable; actual= _, inferred_nullability} + -> Option.some_if is_formal_propagates_nullable inferred_nullability ) in - if List.is_empty propagates_nullable_params then ret - else if - List.for_all propagates_nullable_params - ~f:(fun EradicateChecks.{actual= _, inferred_nullability_actual} -> - InferredNullability.is_nonnull inferred_nullability_actual ) - then - (* All params' inferred types are non-nullable. - Strengten the result to be non-nullable as well! *) - let ret_type_annotation, ret_typ = ret in - (InferredNullability.set_nonnull ret_type_annotation, ret_typ) - else - (* At least one param's inferred type is nullable, can not strengthen the result *) - ret + match nullability_of_propagates_nullable_params with + | [] -> + ret + | head :: tail -> + (* We got non-empty list of params that propagate null. + It means that nullability of the return value will be determined by actual (inferred) nullability of them. + Joining their nullability will give us the least upper bound of nullability of the result *) + let upper_bound_nullability = + List.fold tail ~init:head ~f:(fun acc nullability -> + InferredNullability.join acc nullability ) + in + let _, ret_typ = ret in + (upper_bound_nullability, ret_typ) in (* Infer nullability of function call result based on its signature *) let preliminary_resolved_ret = diff --git a/infer/tests/codetoanalyze/java/nullsafe-default/issues.exp b/infer/tests/codetoanalyze/java/nullsafe-default/issues.exp index a6c3186f3..e12329132 100644 --- a/infer/tests/codetoanalyze/java/nullsafe-default/issues.exp +++ b/infer/tests/codetoanalyze/java/nullsafe-default/issues.exp @@ -34,23 +34,23 @@ codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.n codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$Test.testModelledTrueOnNull(java.lang.String):void, 7, ERADICATE_CONDITION_REDUNDANT, no_bucket, WARNING, [The condition s is always false according to the existing annotations.] codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$Test.testTrueOnNull(java.lang.CharSequence):void, 8, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `toString()`. (Origin: method parameter s)] codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$Test.testTrueOnNull(java.lang.CharSequence):void, 12, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `toString()`. (Origin: method parameter s)] -codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestBothParams.testBothParamsShouldBeNonnull(java.lang.String,java.lang.String):void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`propagatesNullable(...)` is nullable and is not locally checked for null when calling `length()`. (Origin: call to propagatesNullable(...) at line 194)] -codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestBothParams.testBothParamsShouldBeNonnull(java.lang.String,java.lang.String):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`propagatesNullable(...)` is nullable and is not locally checked for null when calling `length()`. (Origin: call to propagatesNullable(...) at line 195)] -codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestBothParams.testBothParamsShouldBeNonnull(java.lang.String,java.lang.String):void, 3, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`propagatesNullable(...)` is nullable and is not locally checked for null when calling `length()`. (Origin: call to propagatesNullable(...) at line 196)] -codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestOneParameter.test(java.lang.String,java.lang.String):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`propagatesNullable(...)` is nullable and is not locally checked for null when calling `length()`. (Origin: call to propagatesNullable(...) at line 120)] +codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestBothParams.testBothParamsShouldBeNonnull(java.lang.String,java.lang.String):void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`propagatesNullable(...)` is nullable and is not locally checked for null when calling `length()`. (Origin: method parameter sNullable)] +codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestBothParams.testBothParamsShouldBeNonnull(java.lang.String,java.lang.String):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`propagatesNullable(...)` is nullable and is not locally checked for null when calling `length()`. (Origin: method parameter sNullable)] +codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestBothParams.testBothParamsShouldBeNonnull(java.lang.String,java.lang.String):void, 3, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`propagatesNullable(...)` is nullable and is not locally checked for null when calling `length()`. (Origin: method parameter sNullable)] +codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestOneParameter.test(java.lang.String,java.lang.String):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`propagatesNullable(...)` is nullable and is not locally checked for null when calling `length()`. (Origin: null constant at line 120)] codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestOneParameter.test(java.lang.String,java.lang.String):void, 3, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`nullable(...)` is nullable and is not locally checked for null when calling `length()`. (Origin: call to nullable(...) at line 121)] -codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestOneParameter.test(java.lang.String,java.lang.String):void, 6, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`propagatesNullable(...)` is nullable and is not locally checked for null when calling `length()`. (Origin: call to propagatesNullable(...) at line 124)] +codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestOneParameter.test(java.lang.String,java.lang.String):void, 6, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`propagatesNullable(...)` is nullable and is not locally checked for null when calling `length()`. (Origin: method parameter sNullable)] codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestOneParameter.test(java.lang.String,java.lang.String):void, 7, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`nullable(...)` is nullable and is not locally checked for null when calling `length()`. (Origin: call to nullable(...) at line 125)] codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestOneParameter.test(java.lang.String,java.lang.String):void, 11, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`nullable(...)` is nullable and is not locally checked for null when calling `length()`. (Origin: call to nullable(...) at line 129)] codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestOneParameter.test(java.lang.String,java.lang.String):void, 15, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`nullable(...)` is nullable and is not locally checked for null when calling `length()`. (Origin: call to nullable(...) at line 133)] -codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestOneParameter.test(java.lang.String,java.lang.String):void, 21, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`propagatesNullable(...)` is nullable and is not locally checked for null when calling `length()`. (Origin: call to propagatesNullable(...) at line 124)] +codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestOneParameter.test(java.lang.String,java.lang.String):void, 21, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`propagatesNullable(...)` is nullable and is not locally checked for null when calling `length()`. (Origin: method parameter sNullable)] codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestOneParameter.test(java.lang.String,java.lang.String):void, 22, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`nullable(...)` is nullable and is not locally checked for null when calling `length()`. (Origin: call to nullable(...) at line 125)] -codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestReturnValueAnnotationIsAutomaticallyInferred.annotated_dereferencingAfterPassingNullableIsBAD(java.lang.String):void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`annotatedReturn(...)` is nullable and is not locally checked for null when calling `toString()`. (Origin: call to annotatedReturn(...) at line 230)] -codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestReturnValueAnnotationIsAutomaticallyInferred.notAnnotated_dereferencingAfterPassingNullableIsBAD(java.lang.String):void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`notAnnotatedReturn(...)` is nullable and is not locally checked for null when calling `toString()`. (Origin: call to notAnnotatedReturn(...) at line 234)] +codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestReturnValueAnnotationIsAutomaticallyInferred.annotated_dereferencingAfterPassingNullableIsBAD(java.lang.String):void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`annotatedReturn(...)` is nullable and is not locally checked for null when calling `toString()`. (Origin: method parameter s)] +codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestReturnValueAnnotationIsAutomaticallyInferred.notAnnotated_dereferencingAfterPassingNullableIsBAD(java.lang.String):void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`notAnnotatedReturn(...)` is nullable and is not locally checked for null when calling `toString()`. (Origin: method parameter s)] codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestReturnValueAnnotationIsAutomaticallyInferred.notAnnotatingReturnWhenThereAreNoPropagatesNullableIsBAD(java.lang.String):java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [Method `notAnnotatingReturnWhenThereAreNoPropagatesNullableIsBAD(...)` may return null but it is not annotated with `@Nullable`. (Origin: null constant at line 213)] -codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestSecondParameter.test(java.lang.String,java.lang.String):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`propagatesNullable(...)` is nullable and is not locally checked for null when calling `length()`. (Origin: call to propagatesNullable(...) at line 169)] +codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestSecondParameter.test(java.lang.String,java.lang.String):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`propagatesNullable(...)` is nullable and is not locally checked for null when calling `length()`. (Origin: method parameter sNullable)] codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestSecondParameter.test(java.lang.String,java.lang.String):void, 3, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`nullable(...)` is nullable and is not locally checked for null when calling `length()`. (Origin: call to nullable(...) at line 170)] -codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestSecondParameter.test(java.lang.String,java.lang.String):void, 6, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`propagatesNullable(...)` is nullable and is not locally checked for null when calling `length()`. (Origin: call to propagatesNullable(...) at line 173)] +codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestSecondParameter.test(java.lang.String,java.lang.String):void, 6, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`propagatesNullable(...)` is nullable and is not locally checked for null when calling `length()`. (Origin: method parameter sNullable)] codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestSecondParameter.test(java.lang.String,java.lang.String):void, 7, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`nullable(...)` is nullable and is not locally checked for null when calling `length()`. (Origin: call to nullable(...) at line 174)] codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestSecondParameter.test(java.lang.String,java.lang.String):void, 11, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`nullable(...)` is nullable and is not locally checked for null when calling `length()`. (Origin: call to nullable(...) at line 178)] codetoanalyze/java/nullsafe-default/CustomAnnotations.java, codetoanalyze.java.nullsafe_default.CustomAnnotations$TestPropagatesNullable$TestSecondParameter.test(java.lang.String,java.lang.String):void, 15, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`nullable(...)` is nullable and is not locally checked for null when calling `length()`. (Origin: call to nullable(...) at line 182)]