diff --git a/infer/src/nullsafe/AssignmentRule.ml b/infer/src/nullsafe/AssignmentRule.ml index 7747d2817..097e8c8c6 100644 --- a/infer/src/nullsafe/AssignmentRule.ml +++ b/infer/src/nullsafe/AssignmentRule.ml @@ -6,222 +6,225 @@ *) open! IStd -type violation = {nullsafe_mode: NullsafeMode.t; lhs: Nullability.t; rhs: Nullability.t} -[@@deriving compare] - -type assignment_type = - | PassingParamToFunction of function_info - | AssigningToField of Fieldname.t - | ReturningFromFunction of Procname.t -[@@deriving compare] - -and function_info = - { param_signature: AnnotatedSignature.param_signature - ; model_source: AnnotatedSignature.model_source option - ; actual_param_expression: string - ; param_position: int - ; function_procname: Procname.t } - -let check ~(nullsafe_mode : NullsafeMode.t) ~lhs ~rhs = - let falls_under_optimistic_third_party = - match nullsafe_mode with - | NullsafeMode.Default when Config.nullsafe_optimistic_third_party_params_in_non_strict -> ( - match lhs with Nullability.ThirdPartyNonnull -> true | _ -> false ) +type violation = {lhs: Nullability.t; rhs: Nullability.t} [@@deriving compare] + +module ReportableViolation = struct + type t = {nullsafe_mode: NullsafeMode.t; violation: violation} + + type assignment_type = + | PassingParamToFunction of function_info + | AssigningToField of Fieldname.t + | ReturningFromFunction of Procname.t + [@@deriving compare] + + and function_info = + { param_signature: AnnotatedSignature.param_signature + ; model_source: AnnotatedSignature.model_source option + ; actual_param_expression: string + ; param_position: int + ; function_procname: Procname.t } + + let get_severity {nullsafe_mode} = NullsafeMode.severity nullsafe_mode + + let get_origin_opt assignment_type origin = + let should_show_origin = + match assignment_type with + | PassingParamToFunction {actual_param_expression} -> + not + (ErrorRenderingUtils.is_object_nullability_self_explanatory + ~object_expression:actual_param_expression origin) + | AssigningToField _ | ReturningFromFunction _ -> + true + in + if should_show_origin then Some origin else None + + + let pp_param_name fmt mangled = + let name = Mangled.to_string mangled in + if String.is_substring name ~substring:"_arg_" then + (* The real name was not fetched for whatever reason, this is an autogenerated name *) + Format.fprintf fmt "" + else Format.fprintf fmt "(%a)" MarkupFormatter.pp_monospaced name + + + let mk_description_for_bad_param_passed + {model_source; param_signature; actual_param_expression; param_position; function_procname} + ~param_nullability ~nullability_evidence = + let nullability_evidence_as_suffix = + Option.value_map nullability_evidence ~f:(fun evidence -> ": " ^ evidence) ~default:"" + in + let annotated_param_nullability = param_signature.param_annotated_type.nullability in + let module MF = MarkupFormatter in + let argument_description = + if String.equal actual_param_expression "null" then "is `null`" + else + let nullability_descr = + match param_nullability with + | Nullability.Null -> + "`null`" + | Nullability.Nullable -> + "nullable" + | other -> + Logging.die InternalError + "mk_description_for_bad_param:: invariant violation: unexpected nullability %a" + Nullability.pp other + in + Format.asprintf "%a is %s" MF.pp_monospaced actual_param_expression nullability_descr + in + match AnnotatedNullability.get_nullability annotated_param_nullability with + | Nullability.Null -> + Logging.die Logging.InternalError "Unexpected param nullability: Null" + | Nullability.Nullable -> + Logging.die Logging.InternalError "Passing anything to a nullable param should be allowed" + | Nullability.ThirdPartyNonnull -> + (* This is a special case. While for FB codebase we can assume "not annotated hence not nullable" rule for all_whitelisted signatures, + This is not the case for third party functions, which can have different conventions, + So we can not just say "param is declared as non-nullable" like we say for FB-internal or modelled case: + param can be nullable according to API but it was just not annotated. + So we phrase it differently to remain truthful, but as specific as possible. + *) + let suggested_third_party_sig_file = + ThirdPartyAnnotationInfo.lookup_related_sig_file_for_proc + (ThirdPartyAnnotationGlobalRepo.get_repo ()) + function_procname + in + let where_to_add_signature = + Option.value_map suggested_third_party_sig_file + ~f:(fun sig_file_name -> + ThirdPartyAnnotationGlobalRepo.get_user_friendly_third_party_sig_file_name + ~filename:sig_file_name ) + (* this can happen when third party is registered in a deprecated way (not in third party repository) *) + ~default:"the third party signature storage" + in + let procname_str = Procname.to_simplified_string ~withclass:true function_procname in + Format.asprintf + "Third-party %a is missing a signature that would allow passing a nullable to param \ + #%d%a. Actual argument %s%s. Consider adding the correct signature of %a to %s." + MF.pp_monospaced procname_str param_position pp_param_name param_signature.mangled + argument_description nullability_evidence_as_suffix MF.pp_monospaced procname_str + where_to_add_signature + | Nullability.LocallyCheckedNonnull | Nullability.UncheckedNonnull | Nullability.StrictNonnull + -> + let nonnull_evidence = + match model_source with + | None -> + "" + | Some InternalModel -> + " (according to nullsafe internal models)" + | Some (ThirdPartyRepo {filename; line_number}) -> + Format.sprintf " (see %s at line %d)" + (ThirdPartyAnnotationGlobalRepo.get_user_friendly_third_party_sig_file_name + ~filename) + line_number + in + Format.asprintf "%a: parameter #%d%a is declared non-nullable%s but the argument %s%s." + MF.pp_monospaced + (Procname.to_simplified_string ~withclass:true function_procname) + param_position pp_param_name param_signature.mangled nonnull_evidence argument_description + nullability_evidence_as_suffix + + + let get_issue_type = function + | PassingParamToFunction _ -> + IssueType.eradicate_parameter_not_nullable + | AssigningToField _ -> + IssueType.eradicate_field_not_nullable + | ReturningFromFunction _ -> + IssueType.eradicate_return_not_nullable + + + let get_description ~assignment_location assignment_type ~rhs_origin + {nullsafe_mode; violation= {rhs}} = + let special_message = + if not (NullsafeMode.equal NullsafeMode.Default nullsafe_mode) then + ErrorRenderingUtils.mk_special_nullsafe_issue ~nullsafe_mode ~bad_nullability:rhs + ~bad_usage_location:assignment_location rhs_origin + else None + in + match special_message with + | Some desc -> + desc | _ -> - false - in - let is_allowed_assignment = - Nullability.is_subtype ~supertype:lhs ~subtype:rhs - || falls_under_optimistic_third_party - (* For better adoption we allow certain conversions. Otherwise using code checked under - different nullsafe modes becomes a pain because of extra warnings. *) - (* TODO(T62521386): consider using caller context when determining nullability to get rid of - white-lists. *) - || Nullability.is_considered_nonnull ~nullsafe_mode rhs - in - Result.ok_if_true is_allowed_assignment ~error:{nullsafe_mode; lhs; rhs} - - -let get_origin_opt assignment_type origin = - let should_show_origin = - match assignment_type with - | PassingParamToFunction {actual_param_expression} -> - not - (ErrorRenderingUtils.is_object_nullability_self_explanatory - ~object_expression:actual_param_expression origin) - | AssigningToField _ | ReturningFromFunction _ -> - true - in - if should_show_origin then Some origin else None - - -let pp_param_name fmt mangled = - let name = Mangled.to_string mangled in - if String.is_substring name ~substring:"_arg_" then - (* The real name was not fetched for whatever reason, this is an autogenerated name *) - Format.fprintf fmt "" - else Format.fprintf fmt "(%a)" MarkupFormatter.pp_monospaced name - - -let mk_description_for_bad_param_passed - {model_source; param_signature; actual_param_expression; param_position; function_procname} - ~param_nullability ~nullability_evidence = - let nullability_evidence_as_suffix = - Option.value_map nullability_evidence ~f:(fun evidence -> ": " ^ evidence) ~default:"" - in - let annotated_param_nullability = param_signature.param_annotated_type.nullability in - let module MF = MarkupFormatter in - let argument_description = - if String.equal actual_param_expression "null" then "is `null`" - else - let nullability_descr = - match param_nullability with - | Nullability.Null -> - "`null`" - | Nullability.Nullable -> - "nullable" - | other -> - Logging.die InternalError - "mk_description_for_bad_param:: invariant violation: unexpected nullability %a" - Nullability.pp other - in - Format.asprintf "%a is %s" MF.pp_monospaced actual_param_expression nullability_descr + let nullability_evidence = + get_origin_opt assignment_type rhs_origin + |> Option.bind ~f:(fun origin -> TypeOrigin.get_description origin) + in + let nullability_evidence_as_suffix = + Option.value_map nullability_evidence ~f:(fun evidence -> ": " ^ evidence) ~default:"" + in + let module MF = MarkupFormatter in + let alternative_method_description = + ErrorRenderingUtils.find_alternative_nonnull_method_description rhs_origin + in + let alternative_recommendation = + Option.value_map alternative_method_description + ~f:(fun descr -> + Format.asprintf " If you don't expect null, use %a instead." MF.pp_monospaced descr ) + ~default:"" + in + let error_message = + match assignment_type with + | PassingParamToFunction function_info -> + Format.sprintf "%s%s" + (mk_description_for_bad_param_passed function_info ~nullability_evidence + ~param_nullability:rhs) + alternative_recommendation + | AssigningToField field_name -> + let rhs_description = + Nullability.( + match rhs with + | Null -> + "`null`" + | Nullable -> + "a nullable" + | other -> + Logging.die InternalError + "violation_description(assign_field):: invariant violation: unexpected \ + nullability %a" + Nullability.pp other) + in + Format.asprintf "%a is declared non-nullable but is assigned %s%s.%s" MF.pp_monospaced + (Fieldname.get_field_name field_name) + rhs_description nullability_evidence_as_suffix alternative_recommendation + | ReturningFromFunction function_proc_name -> + let return_description = + Nullability.( + match rhs with + | Null -> + (* Return `null` in all_whitelisted branches *) + "`null`" + | Nullable -> + "a nullable value" + | other -> + Logging.die InternalError + "violation_description(ret_fun):: invariant violation: unexpected \ + nullability %a" + Nullability.pp other) + in + Format.asprintf + "%a: return type is declared non-nullable but the method returns %s%s.%s" + MF.pp_monospaced + (Procname.to_simplified_string ~withclass:false function_proc_name) + return_description nullability_evidence_as_suffix alternative_recommendation + in + let issue_type = get_issue_type assignment_type in + (error_message, issue_type, assignment_location) +end + +let check ~lhs ~rhs = + let is_subtype = Nullability.is_subtype ~supertype:lhs ~subtype:rhs in + Result.ok_if_true is_subtype ~error:{lhs; rhs} + + +let to_reportable_violation nullsafe_mode ({lhs; rhs} as violation) = + let falls_under_optimistic_third_party = + Config.nullsafe_optimistic_third_party_params_in_non_strict + && NullsafeMode.equal nullsafe_mode Default + && Nullability.equal lhs ThirdPartyNonnull in - match AnnotatedNullability.get_nullability annotated_param_nullability with - | Nullability.Null -> - Logging.die Logging.InternalError "Unexpected param nullability: Null" - | Nullability.Nullable -> - Logging.die Logging.InternalError "Passing anything to a nullable param should be allowed" - | Nullability.ThirdPartyNonnull -> - (* This is a special case. While for FB codebase we can assume "not annotated hence not nullable" rule for all_whitelisted signatures, - This is not the case for third party functions, which can have different conventions, - So we can not just say "param is declared as non-nullable" like we say for FB-internal or modelled case: - param can be nullable according to API but it was just not annotated. - So we phrase it differently to remain truthful, but as specific as possible. - *) - let suggested_third_party_sig_file = - ThirdPartyAnnotationInfo.lookup_related_sig_file_for_proc - (ThirdPartyAnnotationGlobalRepo.get_repo ()) - function_procname - in - let where_to_add_signature = - Option.value_map suggested_third_party_sig_file - ~f:(fun sig_file_name -> - ThirdPartyAnnotationGlobalRepo.get_user_friendly_third_party_sig_file_name - ~filename:sig_file_name ) - (* this can happen when third party is registered in a deprecated way (not in third party repository) *) - ~default:"the third party signature storage" - in - let procname_str = Procname.to_simplified_string ~withclass:true function_procname in - Format.asprintf - "Third-party %a is missing a signature that would allow passing a nullable to param #%d%a. \ - Actual argument %s%s. Consider adding the correct signature of %a to %s." - MF.pp_monospaced procname_str param_position pp_param_name param_signature.mangled - argument_description nullability_evidence_as_suffix MF.pp_monospaced procname_str - where_to_add_signature - | Nullability.LocallyCheckedNonnull | Nullability.UncheckedNonnull | Nullability.StrictNonnull -> - let nonnull_evidence = - match model_source with - | None -> - "" - | Some InternalModel -> - " (according to nullsafe internal models)" - | Some (ThirdPartyRepo {filename; line_number}) -> - Format.sprintf " (see %s at line %d)" - (ThirdPartyAnnotationGlobalRepo.get_user_friendly_third_party_sig_file_name ~filename) - line_number - in - Format.asprintf "%a: parameter #%d%a is declared non-nullable%s but the argument %s%s." - MF.pp_monospaced - (Procname.to_simplified_string ~withclass:true function_procname) - param_position pp_param_name param_signature.mangled nonnull_evidence argument_description - nullability_evidence_as_suffix - - -let get_issue_type = function - | PassingParamToFunction _ -> - IssueType.eradicate_parameter_not_nullable - | AssigningToField _ -> - IssueType.eradicate_field_not_nullable - | ReturningFromFunction _ -> - IssueType.eradicate_return_not_nullable - - -let violation_description {nullsafe_mode; rhs} ~assignment_location assignment_type ~rhs_origin = - let special_message = - if not (NullsafeMode.equal NullsafeMode.Default nullsafe_mode) then - ErrorRenderingUtils.mk_special_nullsafe_issue ~nullsafe_mode ~bad_nullability:rhs - ~bad_usage_location:assignment_location rhs_origin - else None + let is_non_reportable = + falls_under_optimistic_third_party + || (* In certain modes, we trust rhs to be non-nullable and don't report violation *) + Nullability.is_considered_nonnull ~nullsafe_mode rhs in - match special_message with - | Some desc -> - desc - | _ -> - let nullability_evidence = - get_origin_opt assignment_type rhs_origin - |> Option.bind ~f:(fun origin -> TypeOrigin.get_description origin) - in - let nullability_evidence_as_suffix = - Option.value_map nullability_evidence ~f:(fun evidence -> ": " ^ evidence) ~default:"" - in - let module MF = MarkupFormatter in - let alternative_method_description = - ErrorRenderingUtils.find_alternative_nonnull_method_description rhs_origin - in - let alternative_recommendation = - Option.value_map alternative_method_description - ~f:(fun descr -> - Format.asprintf " If you don't expect null, use %a instead." MF.pp_monospaced descr ) - ~default:"" - in - let error_message = - match assignment_type with - | PassingParamToFunction function_info -> - Format.sprintf "%s%s" - (mk_description_for_bad_param_passed function_info ~nullability_evidence - ~param_nullability:rhs) - alternative_recommendation - | AssigningToField field_name -> - let rhs_description = - Nullability.( - match rhs with - | Null -> - "`null`" - | Nullable -> - "a nullable" - | other -> - Logging.die InternalError - "violation_description(assign_field):: invariant violation: unexpected \ - nullability %a" - Nullability.pp other) - in - Format.asprintf "%a is declared non-nullable but is assigned %s%s.%s" MF.pp_monospaced - (Fieldname.get_field_name field_name) - rhs_description nullability_evidence_as_suffix alternative_recommendation - | ReturningFromFunction function_proc_name -> - let return_description = - Nullability.( - match rhs with - | Null -> - (* Return `null` in all_whitelisted branches *) - "`null`" - | Nullable -> - "a nullable value" - | other -> - Logging.die InternalError - "violation_description(ret_fun):: invariant violation: unexpected \ - nullability %a" - Nullability.pp other) - in - Format.asprintf - "%a: return type is declared non-nullable but the method returns %s%s.%s" - MF.pp_monospaced - (Procname.to_simplified_string ~withclass:false function_proc_name) - return_description nullability_evidence_as_suffix alternative_recommendation - in - let issue_type = get_issue_type assignment_type in - (error_message, issue_type, assignment_location) - - -let violation_severity {nullsafe_mode} = NullsafeMode.severity nullsafe_mode + if is_non_reportable then None else Some ReportableViolation.{nullsafe_mode; violation} diff --git a/infer/src/nullsafe/AssignmentRule.mli b/infer/src/nullsafe/AssignmentRule.mli index 197098b40..3886aa4c4 100644 --- a/infer/src/nullsafe/AssignmentRule.mli +++ b/infer/src/nullsafe/AssignmentRule.mli @@ -12,29 +12,40 @@ open! IStd type violation [@@deriving compare] -val check : - nullsafe_mode:NullsafeMode.t -> lhs:Nullability.t -> rhs:Nullability.t -> (unit, violation) result - -type assignment_type = - | PassingParamToFunction of function_info - | AssigningToField of Fieldname.t - | ReturningFromFunction of Procname.t -[@@deriving compare] - -and function_info = - { param_signature: AnnotatedSignature.param_signature - ; model_source: AnnotatedSignature.model_source option - ; actual_param_expression: string - ; param_position: int - ; function_procname: Procname.t } - -val violation_description : - violation - -> assignment_location:Location.t - -> assignment_type - -> rhs_origin:TypeOrigin.t - -> string * IssueType.t * Location.t -(** Given context around violation, return error message together with the info where to put this - message *) - -val violation_severity : violation -> Exceptions.severity +val check : lhs:Nullability.t -> rhs:Nullability.t -> (unit, violation) result +(** If `null` can leak from a "less strict" type to "more strict" type, this is an Assignment Rule + violation. *) + +(** Violation that needs to be reported to the user. *) +module ReportableViolation : sig + type t + + type assignment_type = + | PassingParamToFunction of function_info + | AssigningToField of Fieldname.t + | ReturningFromFunction of Procname.t + [@@deriving compare] + + and function_info = + { param_signature: AnnotatedSignature.param_signature + ; model_source: AnnotatedSignature.model_source option + ; actual_param_expression: string + ; param_position: int + ; function_procname: Procname.t } + + val get_severity : t -> Exceptions.severity + (** Severity of the violation to be reported *) + + val get_description : + assignment_location:Location.t + -> assignment_type + -> rhs_origin:TypeOrigin.t + -> t + -> string * IssueType.t * Location.t + (** Given context around violation, return error message together with the info where to put this + message *) +end + +val to_reportable_violation : NullsafeMode.t -> violation -> ReportableViolation.t option +(** Depending on the mode, violation might or might not be important enough to be reported to the + user. If it should NOT be reported for that mode, this function will return None. *) diff --git a/infer/src/nullsafe/eradicate.ml b/infer/src/nullsafe/eradicate.ml index 022d1c450..d20704c35 100644 --- a/infer/src/nullsafe/eradicate.ml +++ b/infer/src/nullsafe/eradicate.ml @@ -242,7 +242,9 @@ module MkCallback (Extension : ExtensionT) : CallBackT = struct if is_important err_instance then Some err_instance else None ) |> List.length in - TypeErr.report_forall_checks_and_reset (EradicateCheckers.report_error tenv) proc_desc ; + TypeErr.report_forall_issues_and_reset + (EradicateCheckers.report_error tenv) + ~nullsafe_mode:annotated_signature.nullsafe_mode proc_desc ; Payload.update_summary NullsafeSummary.{type_violation_count} summary end diff --git a/infer/src/nullsafe/eradicateChecks.ml b/infer/src/nullsafe/eradicateChecks.ml index ae0a3cf0c..c75820b29 100644 --- a/infer/src/nullsafe/eradicateChecks.ml +++ b/infer/src/nullsafe/eradicateChecks.ml @@ -11,7 +11,7 @@ open! IStd module L = Logging -let report_error tenv = TypeErr.report_error (EradicateCheckers.report_error tenv) +let register_error tenv = TypeErr.register_error (EradicateCheckers.report_error tenv) let explain_expr tenv node e = match Errdesc.exp_rv_dexp tenv node e with @@ -44,14 +44,15 @@ let check_object_dereference ~nullsafe_mode tenv find_canonical_duplicate curr_p ; dereference_type ; nullable_object_origin } in - report_error tenv find_canonical_duplicate type_error (Some instr_ref) loc curr_pname ) + register_error tenv find_canonical_duplicate type_error (Some instr_ref) ~nullsafe_mode loc + curr_pname ) (** [expr] is an expression that was explicitly compared with `null`. At the same time, [expr] had [inferred_nullability] before the comparision. Check if the comparision is redundant and emit an issue, if this is the case. *) let check_condition_for_redundancy tenv ~is_always_true find_canonical_duplicate curr_pdesc node - expr typ inferred_nullability idenv linereader loc instr_ref : unit = + ~nullsafe_mode expr typ inferred_nullability idenv linereader loc instr_ref : unit = let contains_instanceof_throwable pdesc node = (* Check if the current procedure has a catch Throwable. *) (* That always happens in the bytecode generated by try-with-resources. *) @@ -111,9 +112,9 @@ let check_condition_for_redundancy tenv ~is_always_true find_canonical_duplicate *) let condition_descr = explain_expr tenv node expr in let nonnull_origin = InferredNullability.get_origin inferred_nullability in - report_error tenv find_canonical_duplicate + register_error tenv find_canonical_duplicate (TypeErr.Condition_redundant {is_always_true; condition_descr; nonnull_origin}) - (Some instr_ref) loc curr_pdesc + (Some instr_ref) ~nullsafe_mode loc curr_pdesc (** Check an assignment to a field. *) @@ -144,7 +145,7 @@ let check_field_assignment ~nullsafe_mode tenv find_canonical_duplicate curr_pde AnnotatedNullability.get_nullability annotated_field.annotated_type.nullability in let assignment_check_result = - AssignmentRule.check ~nullsafe_mode ~lhs:declared_nullability + AssignmentRule.check ~lhs:declared_nullability ~rhs:(InferredNullability.get_nullability inferred_nullability_rhs) in Result.iter_error assignment_check_result ~f:(fun assignment_violation -> @@ -157,13 +158,13 @@ let check_field_assignment ~nullsafe_mode tenv find_canonical_duplicate curr_pde in if should_report then let rhs_origin = InferredNullability.get_origin inferred_nullability_rhs in - report_error tenv find_canonical_duplicate + register_error tenv find_canonical_duplicate (TypeErr.Bad_assignment { assignment_violation ; assignment_location= loc ; rhs_origin - ; assignment_type= AssignmentRule.AssigningToField fname }) - (Some instr_ref) loc curr_pdesc ) ) + ; assignment_type= AssignmentRule.ReportableViolation.AssigningToField fname }) + (Some instr_ref) ~nullsafe_mode loc curr_pdesc ) ) (* Check if the field declared as not nullable (implicitly or explicitly). If the field is @@ -304,9 +305,9 @@ let check_constructor_initialization tenv find_canonical_duplicate curr_construc *) () else - report_error tenv find_canonical_duplicate + register_error tenv find_canonical_duplicate (TypeErr.Field_not_initialized {nullsafe_mode; field_name}) - None loc curr_constructor_pdesc ; + None ~nullsafe_mode loc curr_constructor_pdesc ; (* Check if field is over-annotated. *) match annotated_field with | None -> @@ -320,11 +321,11 @@ let check_constructor_initialization tenv find_canonical_duplicate curr_construc let by_rhs_upper_bound = field_nullability_upper_bound_over_all_typestates () in Result.iter_error (OverAnnotatedRule.check ~what ~by_rhs_upper_bound) ~f:(fun over_annotated_violation -> - report_error tenv find_canonical_duplicate + register_error tenv find_canonical_duplicate (TypeErr.Over_annotation { over_annotated_violation ; violation_type= OverAnnotatedRule.FieldOverAnnoted field_name }) - None loc curr_constructor_pdesc ) ) + ~nullsafe_mode None loc curr_constructor_pdesc ) ) in List.iter ~f:do_field fields | None -> @@ -338,19 +339,19 @@ let check_return_not_nullable ~nullsafe_mode tenv find_canonical_duplicate loc c (* Returning from a function is essentially an assignment the actual return value to the formal `return` *) let lhs = AnnotatedNullability.get_nullability ret_signature.ret_annotated_type.nullability in let rhs = InferredNullability.get_nullability ret_inferred_nullability in - Result.iter_error (AssignmentRule.check ~nullsafe_mode ~lhs ~rhs) ~f:(fun assignment_violation -> + Result.iter_error (AssignmentRule.check ~lhs ~rhs) ~f:(fun assignment_violation -> let rhs_origin = InferredNullability.get_origin ret_inferred_nullability in - report_error tenv find_canonical_duplicate + register_error tenv find_canonical_duplicate (TypeErr.Bad_assignment { assignment_violation ; assignment_location= loc ; rhs_origin - ; assignment_type= AssignmentRule.ReturningFromFunction curr_pname }) - None loc curr_pdesc ) + ; assignment_type= AssignmentRule.ReportableViolation.ReturningFromFunction curr_pname }) + None ~nullsafe_mode loc curr_pdesc ) let check_return_overrannotated tenv find_canonical_duplicate loc curr_pname curr_pdesc - (ret_signature : AnnotatedSignature.ret_signature) ret_inferred_nullability = + ~nullsafe_mode (ret_signature : AnnotatedSignature.ret_signature) ret_inferred_nullability = (* Returning from a function is essentially an assignment the actual return value to the formal `return` *) let what = AnnotatedNullability.get_nullability ret_signature.ret_annotated_type.nullability in (* In our CFG implementation, there is only one place where we return from a function @@ -360,11 +361,11 @@ let check_return_overrannotated tenv find_canonical_duplicate loc curr_pname cur let by_rhs_upper_bound = InferredNullability.get_nullability ret_inferred_nullability in Result.iter_error (OverAnnotatedRule.check ~what ~by_rhs_upper_bound) ~f:(fun over_annotated_violation -> - report_error tenv find_canonical_duplicate + register_error tenv find_canonical_duplicate (TypeErr.Over_annotation { over_annotated_violation ; violation_type= OverAnnotatedRule.ReturnOverAnnotated curr_pname }) - None loc curr_pdesc ) + None ~nullsafe_mode loc curr_pdesc ) (** Check the annotations when returning from a method. *) @@ -388,7 +389,8 @@ let check_return_annotation tenv find_canonical_duplicate curr_pdesc ret_range ret_inferred_nullability ; if Config.eradicate_return_over_annotated then check_return_overrannotated tenv find_canonical_duplicate loc curr_pname curr_pdesc - annotated_signature.ret ret_inferred_nullability + annotated_signature.ret ~nullsafe_mode:annotated_signature.nullsafe_mode + ret_inferred_nullability | None -> () @@ -422,7 +424,7 @@ let check_call_parameters ~nullsafe_mode ~callee_annotated_signature tenv find_c curr_pdesc node callee_attributes resolved_params loc instr_ref : unit = let callee_pname = callee_attributes.ProcAttributes.proc_name in let check {num= param_position; formal; actual= orig_e2, nullability_actual} = - let report assignment_violation = + let report ~nullsafe_mode assignment_violation = let actual_param_expression = match explain_expr tenv node orig_e2 with | Some descr -> @@ -431,26 +433,26 @@ let check_call_parameters ~nullsafe_mode ~callee_annotated_signature tenv find_c "formal parameter " ^ Mangled.to_string formal.mangled in let rhs_origin = InferredNullability.get_origin nullability_actual in - report_error tenv find_canonical_duplicate + register_error tenv find_canonical_duplicate (TypeErr.Bad_assignment { assignment_violation ; assignment_location= loc ; rhs_origin ; assignment_type= - AssignmentRule.PassingParamToFunction + AssignmentRule.ReportableViolation.PassingParamToFunction { param_signature= formal ; model_source= callee_annotated_signature.AnnotatedSignature.model_source ; actual_param_expression ; param_position ; function_procname= callee_pname } }) - (Some instr_ref) loc curr_pdesc + (Some instr_ref) ~nullsafe_mode loc curr_pdesc in if PatternMatch.type_is_class formal.param_annotated_type.typ then (* Passing a param to a function is essentially an assignment the actual param value to the formal param *) let lhs = AnnotatedNullability.get_nullability formal.param_annotated_type.nullability in let rhs = InferredNullability.get_nullability nullability_actual in - Result.iter_error (AssignmentRule.check ~nullsafe_mode ~lhs ~rhs) ~f:report + Result.iter_error (AssignmentRule.check ~lhs ~rhs) ~f:(report ~nullsafe_mode) in List.iter ~f:check resolved_params @@ -461,13 +463,13 @@ let check_inheritance_rule_for_return find_canonical_duplicate tenv loc ~nullsaf Result.iter_error (InheritanceRule.check ~nullsafe_mode InheritanceRule.Ret ~base:base_nullability ~overridden:overridden_nullability) ~f:(fun inheritance_violation -> - report_error tenv find_canonical_duplicate + register_error tenv find_canonical_duplicate (TypeErr.Inconsistent_subclass { inheritance_violation ; violation_type= InheritanceRule.InconsistentReturn ; overridden_proc_name ; base_proc_name }) - None loc overridden_proc_desc ) + None ~nullsafe_mode loc overridden_proc_desc ) let check_inheritance_rule_for_param find_canonical_duplicate tenv loc ~nullsafe_mode @@ -476,7 +478,7 @@ let check_inheritance_rule_for_param find_canonical_duplicate tenv loc ~nullsafe Result.iter_error (InheritanceRule.check ~nullsafe_mode InheritanceRule.Param ~base:base_nullability ~overridden:overridden_nullability) ~f:(fun inheritance_violation -> - report_error tenv find_canonical_duplicate + register_error tenv find_canonical_duplicate (TypeErr.Inconsistent_subclass { inheritance_violation ; violation_type= @@ -484,7 +486,7 @@ let check_inheritance_rule_for_param find_canonical_duplicate tenv loc ~nullsafe {param_position; param_description= Mangled.to_string overridden_param_name} ; base_proc_name ; overridden_proc_name }) - None loc overridden_proc_desc ) + None ~nullsafe_mode loc overridden_proc_desc ) let check_inheritance_rule_for_params find_canonical_duplicate tenv loc ~nullsafe_mode diff --git a/infer/src/nullsafe/typeCheck.ml b/infer/src/nullsafe/typeCheck.ml index 048967bd0..176a160a4 100644 --- a/infer/src/nullsafe/typeCheck.ml +++ b/infer/src/nullsafe/typeCheck.ml @@ -479,7 +479,7 @@ let do_preconditions_check_not_null instr_ref tenv find_canonical_duplicate node curr_pname curr_annotated_signature checks call_params idenv parameter_num ~is_vararg typestate' = (* clear the nullable flag of the first parameter of the procedure *) - let clear_nullable_flag typestate'' pvar = + let clear_nullable_flag ~nullsafe_mode typestate'' pvar = (* remove the nullable flag for the given pvar *) match TypeState.lookup_pvar pvar typestate'' with | Some (t, nullability) -> @@ -491,12 +491,12 @@ let do_preconditions_check_not_null instr_ref tenv find_canonical_duplicate node in ( if checks.eradicate && should_report then let cond = Exp.BinOp (Binop.Ne, Exp.Lvar pvar, Exp.null) in - EradicateChecks.report_error tenv find_canonical_duplicate + EradicateChecks.register_error tenv find_canonical_duplicate (TypeErr.Condition_redundant { is_always_true= true ; condition_descr= EradicateChecks.explain_expr tenv node cond ; nonnull_origin= InferredNullability.get_origin nullability }) - (Some instr_ref) loc curr_pdesc ) ; + (Some instr_ref) ~nullsafe_mode loc curr_pdesc ) ; let previous_origin = InferredNullability.get_origin nullability in let new_origin = TypeOrigin.InferredNonnull {previous_origin} in TypeState.add pvar @@ -521,14 +521,18 @@ let do_preconditions_check_not_null instr_ref tenv find_canonical_duplicate node match Idenv.expand_expr idenv e with | Exp.Lvar pvar1 -> pvar_apply instr_ref idenv tenv curr_pname curr_annotated_signature loc - clear_nullable_flag ts pvar1 node + (clear_nullable_flag + ~nullsafe_mode:curr_annotated_signature.AnnotatedSignature.nullsafe_mode) + ts pvar1 node | _ -> ts in let vararg_values = PatternMatch.java_get_vararg_values node pvar idenv in List.fold_right ~f:do_vararg_value vararg_values ~init:typestate' else - pvar_apply instr_ref idenv tenv curr_pname curr_annotated_signature loc clear_nullable_flag + pvar_apply instr_ref idenv tenv curr_pname curr_annotated_signature loc + (clear_nullable_flag + ~nullsafe_mode:curr_annotated_signature.AnnotatedSignature.nullsafe_mode) typestate' pvar node | None -> typestate' @@ -808,8 +812,8 @@ let rec check_condition_for_sil_prune tenv idenv calls_this find_canonical_dupli in if checks.eradicate then EradicateChecks.check_condition_for_redundancy ~is_always_true:true_branch tenv - find_canonical_duplicate curr_pdesc original_node pvar_expr typ inferred_nullability idenv - linereader loc instr_ref ) ; + find_canonical_duplicate curr_pdesc original_node pvar_expr typ inferred_nullability + ~nullsafe_mode idenv linereader loc instr_ref ) ; set_nonnull pvar_expr typestate ~descr in (* Assuming [expr] is a boolean, this is the branch where, according to PRUNE semantics, diff --git a/infer/src/nullsafe/typeErr.ml b/infer/src/nullsafe/typeErr.ml index 2797b691b..b182e0600 100644 --- a/infer/src/nullsafe/typeErr.ml +++ b/infer/src/nullsafe/typeErr.ml @@ -78,7 +78,7 @@ type err_instance = | Bad_assignment of { assignment_violation: AssignmentRule.violation ; assignment_location: Location.t - ; assignment_type: AssignmentRule.assignment_type + ; assignment_type: AssignmentRule.ReportableViolation.assignment_type ; rhs_origin: TypeOrigin.t } [@@deriving compare] @@ -151,28 +151,6 @@ let add_err find_canonical_duplicate err_instance instr_ref_opt loc = not is_forall -let err_instance_get_severity err_instance = - match err_instance with - | Condition_redundant _ -> - (* Condition redundant is a very non-precise issue. Depending on the origin of what is compared with null, - this can have a lot of reasons to be actually nullable. - Until it is made non-precise, it is recommended to not turn this warning on. - But even when it is on, this should not be more than advice. - *) - Exceptions.Advice - | Over_annotation _ -> - (* Very non-precise issue. Should be actually turned off unless for experimental purposes. *) - Exceptions.Advice - | Nullable_dereference {dereference_violation} -> - DereferenceRule.violation_severity dereference_violation - | Field_not_initialized {nullsafe_mode} -> - NullsafeMode.severity nullsafe_mode - | Bad_assignment {assignment_violation} -> - AssignmentRule.violation_severity assignment_violation - | Inconsistent_subclass {inheritance_violation} -> - InheritanceRule.violation_severity inheritance_violation - - type st_report_error = Procname.t -> Procdesc.t @@ -229,37 +207,55 @@ let get_nonnull_explanation_for_condition_redudant (nonnull_origin : TypeOrigin. " according to the existing annotations" -let get_error_info err_instance = +(** If error is reportable to the user, return description, severity etc. Otherwise return None. *) +let get_error_info_if_reportable ~nullsafe_mode err_instance = + let open IOption.Let_syntax in match err_instance with | Condition_redundant {is_always_true; condition_descr; nonnull_origin} -> - ( P.sprintf "The condition %s might be always %b%s." - (Option.value condition_descr ~default:"") - is_always_true - (get_nonnull_explanation_for_condition_redudant nonnull_origin) - , IssueType.eradicate_condition_redundant - , None ) + Some + ( P.sprintf "The condition %s might be always %b%s." + (Option.value condition_descr ~default:"") + is_always_true + (get_nonnull_explanation_for_condition_redudant nonnull_origin) + , IssueType.eradicate_condition_redundant + , None + , (* Condition redundant is a very non-precise issue. Depending on the origin of what is compared with null, + this can have a lot of reasons to be actually nullable. + Until it is made non-precise, it is recommended to not turn this warning on. + But even when it is on, this should not be more than advice. + *) + Exceptions.Advice ) | Over_annotation {over_annotated_violation; violation_type} -> - ( OverAnnotatedRule.violation_description over_annotated_violation violation_type - , ( match violation_type with - | OverAnnotatedRule.FieldOverAnnoted _ -> - IssueType.eradicate_field_over_annotated - | OverAnnotatedRule.ReturnOverAnnotated _ -> - IssueType.eradicate_return_over_annotated ) - , None ) + Some + ( OverAnnotatedRule.violation_description over_annotated_violation violation_type + , ( match violation_type with + | OverAnnotatedRule.FieldOverAnnoted _ -> + IssueType.eradicate_field_over_annotated + | OverAnnotatedRule.ReturnOverAnnotated _ -> + IssueType.eradicate_return_over_annotated ) + , None + , (* Very non-precise issue. Should be actually turned off unless for experimental purposes. *) + Exceptions.Advice ) | Field_not_initialized {field_name} -> - ( Format.asprintf - "Field %a is declared non-nullable, so it should be initialized in the constructor or in \ - an `@Initializer` method" - MF.pp_monospaced - (Fieldname.get_field_name field_name) - , IssueType.eradicate_field_not_initialized - , None ) + Some + ( Format.asprintf + "Field %a is declared non-nullable, so it should be initialized in the constructor or \ + in an `@Initializer` method" + MF.pp_monospaced + (Fieldname.get_field_name field_name) + , IssueType.eradicate_field_not_initialized + , None + , NullsafeMode.severity nullsafe_mode ) | Bad_assignment {rhs_origin; assignment_location; assignment_type; assignment_violation} -> + let+ reportable_violation = + AssignmentRule.to_reportable_violation nullsafe_mode assignment_violation + in let description, issue_type, error_location = - AssignmentRule.violation_description ~assignment_location assignment_violation - assignment_type ~rhs_origin + AssignmentRule.ReportableViolation.get_description ~assignment_location assignment_type + ~rhs_origin reportable_violation in - (description, issue_type, Some error_location) + let severity = AssignmentRule.ReportableViolation.get_severity reportable_violation in + (description, issue_type, Some error_location, severity) | Nullable_dereference { dereference_violation ; dereference_location @@ -270,48 +266,55 @@ let get_error_info err_instance = DereferenceRule.violation_description ~dereference_location dereference_violation dereference_type ~nullable_object_descr ~nullable_object_origin in - (description, issue_type, Some error_location) + Some + ( description + , issue_type + , Some error_location + , DereferenceRule.violation_severity dereference_violation ) | Inconsistent_subclass {inheritance_violation; violation_type; base_proc_name; overridden_proc_name} -> - ( InheritanceRule.violation_description inheritance_violation violation_type ~base_proc_name - ~overridden_proc_name - , ( match violation_type with - | InconsistentReturn -> - IssueType.eradicate_inconsistent_subclass_return_annotation - | InconsistentParam _ -> - IssueType.eradicate_inconsistent_subclass_parameter_annotation ) - , None ) - - -(** Report an error right now. *) -let report_error_now (st_report_error : st_report_error) err_instance loc pdesc : unit = + Some + ( InheritanceRule.violation_description inheritance_violation violation_type ~base_proc_name + ~overridden_proc_name + , ( match violation_type with + | InconsistentReturn -> + IssueType.eradicate_inconsistent_subclass_return_annotation + | InconsistentParam _ -> + IssueType.eradicate_inconsistent_subclass_parameter_annotation ) + , None + , InheritanceRule.violation_severity inheritance_violation ) + + +let report_now_if_reportable (st_report_error : st_report_error) err_instance ~nullsafe_mode loc + pdesc = let pname = Procdesc.get_proc_name pdesc in - let err_description, infer_issue_type, updated_location = get_error_info err_instance in - let field_name = get_field_name_for_error_suppressing err_instance in - let severity = err_instance_get_severity err_instance in - let error_location = Option.value updated_location ~default:loc in - st_report_error pname pdesc infer_issue_type error_location ~field_name - ~exception_kind:(fun k d -> Exceptions.Eradicate (k, d)) - ~severity err_description - - -(** Report an error unless is has been reported already, or unless it's a forall error since it - requires waiting until the end of the analysis and be printed by flush. *) -let report_error (st_report_error : st_report_error) find_canonical_duplicate err_instance - instr_ref_opt loc pdesc = + get_error_info_if_reportable ~nullsafe_mode err_instance + |> Option.iter ~f:(fun (err_description, infer_issue_type, updated_location, severity) -> + let field_name = get_field_name_for_error_suppressing err_instance in + let error_location = Option.value updated_location ~default:loc in + st_report_error pname pdesc infer_issue_type error_location ~field_name + ~exception_kind:(fun k d -> Exceptions.Eradicate (k, d)) + ~severity err_description ) + + +(** Register issue (unless exactly the same issue was already registered). If needed, report this + error immediately. *) +let register_error (st_report_error : st_report_error) find_canonical_duplicate err_instance + ~nullsafe_mode instr_ref_opt loc pdesc = let should_report_now = add_err find_canonical_duplicate err_instance instr_ref_opt loc in - if should_report_now then report_error_now st_report_error err_instance loc pdesc + if should_report_now then + report_now_if_reportable st_report_error err_instance ~nullsafe_mode loc pdesc -(** Report the forall checks at the end of the analysis and reset the error table *) -let report_forall_checks_and_reset st_report_error proc_desc = +let report_forall_issues_and_reset st_report_error ~nullsafe_mode proc_desc = let iter (err_instance, instr_ref_opt) err_state = match (instr_ref_opt, get_forall err_instance) with | Some instr_ref, is_forall -> let node = InstrRef.get_node instr_ref in State.set_node node ; if is_forall && err_state.always then - report_error_now st_report_error err_instance err_state.loc proc_desc + report_now_if_reportable st_report_error err_instance err_state.loc ~nullsafe_mode + proc_desc | None, _ -> () in diff --git a/infer/src/nullsafe/typeErr.mli b/infer/src/nullsafe/typeErr.mli index fbcdb7756..2470ab151 100644 --- a/infer/src/nullsafe/typeErr.mli +++ b/infer/src/nullsafe/typeErr.mli @@ -54,7 +54,7 @@ type err_instance = | Bad_assignment of { assignment_violation: AssignmentRule.violation ; assignment_location: Location.t - ; assignment_type: AssignmentRule.assignment_type + ; assignment_type: AssignmentRule.ReportableViolation.assignment_type ; rhs_origin: TypeOrigin.t } [@@deriving compare] @@ -71,16 +71,21 @@ type st_report_error = -> string -> unit -val report_error : +val register_error : st_report_error -> (Procdesc.Node.t -> Procdesc.Node.t) -> err_instance + -> nullsafe_mode:NullsafeMode.t -> InstrRef.t option -> Location.t -> Procdesc.t -> unit +(** Register the fact that issue happened. Depending on the error and mode, this error might or + might not be reported to the user. *) -val report_forall_checks_and_reset : st_report_error -> Procdesc.t -> unit +val report_forall_issues_and_reset : + st_report_error -> nullsafe_mode:NullsafeMode.t -> Procdesc.t -> unit +(** Report registered "forall" issues (if needed), and reset the error table *) val reset : unit -> unit diff --git a/infer/tests/codetoanalyze/java/nullsafe-default/issues.exp b/infer/tests/codetoanalyze/java/nullsafe-default/issues.exp index 905455963..6a5e8439f 100644 --- a/infer/tests/codetoanalyze/java/nullsafe-default/issues.exp +++ b/infer/tests/codetoanalyze/java/nullsafe-default/issues.exp @@ -111,7 +111,7 @@ codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java, Linters codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_IS_CLEAN, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_IS_CLEAN, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_IS_CLEAN, no_bucket, INFO, [] -codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_IS_CLEAN, no_bucket, INFO, [] +codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_NEEDS_FIXING, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_NEEDS_FIXING, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_NEEDS_FIXING, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_NEEDS_FIXING, no_bucket, INFO, [] @@ -199,7 +199,7 @@ codetoanalyze/java/nullsafe-default/NullMethodCall.java, Linters_dummy_method, 1 codetoanalyze/java/nullsafe-default/NullMethodCall.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_IS_CLEAN, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/NullMethodCall.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_IS_CLEAN, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/NullMethodCall.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_IS_CLEAN, no_bucket, INFO, [] -codetoanalyze/java/nullsafe-default/NullMethodCall.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_IS_CLEAN, no_bucket, INFO, [] +codetoanalyze/java/nullsafe-default/NullMethodCall.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_NEEDS_FIXING, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/NullMethodCall.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_NEEDS_FIXING, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/NullMethodCall.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_NEEDS_FIXING, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/NullMethodCall.java, codetoanalyze.java.nullsafe_default.NullMethodCall$Inner.outerField():int, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `length()`: field fld at line 69.] @@ -230,7 +230,7 @@ codetoanalyze/java/nullsafe-default/NullsafeMode.java, Linters_dummy_method, 1, codetoanalyze/java/nullsafe-default/NullsafeMode.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_IS_CLEAN, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/NullsafeMode.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_IS_CLEAN, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/NullsafeMode.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_IS_CLEAN, no_bucket, INFO, [] -codetoanalyze/java/nullsafe-default/NullsafeMode.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_IS_CLEAN, no_bucket, INFO, [] +codetoanalyze/java/nullsafe-default/NullsafeMode.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_NEEDS_FIXING, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/NullsafeMode.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_NEEDS_FIXING, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/NullsafeMode.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_NEEDS_FIXING, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/NullsafeMode.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_NEEDS_FIXING, no_bucket, INFO, [] @@ -317,7 +317,7 @@ codetoanalyze/java/nullsafe-default/PropagatesNullable.java, codetoanalyze.java. codetoanalyze/java/nullsafe-default/PropagatesNullable.java, codetoanalyze.java.nullsafe_default.TestPropagatesNullable$TestSecondParameter.test(java.lang.String,java.lang.String):void, 9, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`nullable(...)` is nullable and is not locally checked for null when calling `length()`.] codetoanalyze/java/nullsafe-default/PropagatesNullable.java, codetoanalyze.java.nullsafe_default.TestPropagatesNullable$TestSecondParameter.test(java.lang.String,java.lang.String):void, 13, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`nullable(...)` is nullable and is not locally checked for null when calling `length()`.] codetoanalyze/java/nullsafe-default/ReturnNotNullable.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_IS_CLEAN, no_bucket, INFO, [] -codetoanalyze/java/nullsafe-default/ReturnNotNullable.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_IS_CLEAN, no_bucket, INFO, [] +codetoanalyze/java/nullsafe-default/ReturnNotNullable.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_NEEDS_FIXING, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/ReturnNotNullable.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_NEEDS_FIXING, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/ReturnNotNullable.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_NEEDS_FIXING, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/ReturnNotNullable.java, codetoanalyze.java.nullsafe_default.ReturnNotNullable$ConditionalAssignment.test(boolean):java.lang.Object, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`test(...)`: return type is declared non-nullable but the method returns a nullable value: field f1 at line 199.] @@ -334,7 +334,7 @@ codetoanalyze/java/nullsafe-default/ReturnNotNullable.java, codetoanalyze.java.n codetoanalyze/java/nullsafe-default/ReturnNotNullable.java, codetoanalyze.java.nullsafe_default.ReturnNotNullable.tryWithResourcesReturnNullable(java.lang.String):java.lang.Object, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`tryWithResourcesReturnNullable(...)`: return type is declared non-nullable but the method returns a nullable value: call to nullToNullableIsOK() at line 142.] codetoanalyze/java/nullsafe-default/StrictMode.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_IS_CLEAN, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/StrictMode.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_IS_CLEAN, no_bucket, INFO, [] -codetoanalyze/java/nullsafe-default/StrictMode.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_IS_CLEAN, no_bucket, INFO, [] +codetoanalyze/java/nullsafe-default/StrictMode.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_NEEDS_FIXING, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/StrictMode.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_NEEDS_FIXING, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/StrictMode.java, codetoanalyze.java.nullsafe_default.Strict.(), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, ERROR, [Field `notInitializedIsBAD` is declared non-nullable, so it should be initialized in the constructor or in an `@Initializer` method] codetoanalyze/java/nullsafe-default/StrictMode.java, codetoanalyze.java.nullsafe_default.Strict.nonStrictClass_convertingNonnullToNonnullIsBad():java.lang.String, 0, ERADICATE_UNCHECKED_USAGE_IN_NULLSAFE, no_bucket, ERROR, [`NonStrict.getNonnull()`: `@NullsafeStrict` mode prohibits using values coming from non-strict classes without a check. Result of this call is used at line 163. Either add a local check for null or assertion, or make NonStrict nullsafe strict.] @@ -362,7 +362,7 @@ codetoanalyze/java/nullsafe-default/StrictModeForThirdParty.java, codetoanalyze. codetoanalyze/java/nullsafe-default/StrictModeForThirdParty.java, codetoanalyze.java.nullsafe_default.StrictModeForThirdParty.dereferenceUnspecifiedIsBAD():void, 0, ERADICATE_UNVETTED_THIRD_PARTY_IN_NULLSAFE, no_bucket, ERROR, [`ThirdPartyTestClass.returnUnspecified()`: `@NullsafeStrict` mode prohibits using values coming from not vetted third party methods without a check. Result of this call is used at line 41. Either add a local check for null or assertion, or add the correct signature to nullsafe-default/third-party-signatures/some.test.pckg.sig.] codetoanalyze/java/nullsafe-default/StrictModeForThirdParty.java, codetoanalyze.java.nullsafe_default.StrictModeForThirdParty.passingNullableParamToUnspecifiedIsBAD():void, 0, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, ERROR, [Third-party `ThirdPartyTestClass.paramUnspecified(...)` is missing a signature that would allow passing a nullable to param #1(`param`). Actual argument `getNullable()` is nullable. Consider adding the correct signature of `ThirdPartyTestClass.paramUnspecified(...)` to nullsafe-default/third-party-signatures/some.test.pckg.sig.] codetoanalyze/java/nullsafe-default/StrictModeForThirdParty.java, codetoanalyze.java.nullsafe_default.StrictModeForThirdParty.passingNullableToParamSpecifiedAsNonnullIsBAD():void, 0, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, ERROR, [`ThirdPartyTestClass.secondParamSpecifiedAsNonnull(...)`: parameter #2(`specifiedAsNonnull`) is declared non-nullable (see nullsafe-default/third-party-signatures/some.test.pckg.sig at line 3) but the argument `getNullable()` is nullable.] -codetoanalyze/java/nullsafe-default/SwitchCase.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_IS_CLEAN, no_bucket, INFO, [] +codetoanalyze/java/nullsafe-default/SwitchCase.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_NEEDS_FIXING, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/SwitchCase.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_NEEDS_FIXING, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/SwitchCase.java, codetoanalyze.java.nullsafe_default.SwitchCase.getNullableColor():codetoanalyze.java.nullsafe_default.Color, 0, ERADICATE_RETURN_OVER_ANNOTATED, no_bucket, ADVICE, [Method `getNullableColor()` is annotated with `@Nullable` but never returns null.] codetoanalyze/java/nullsafe-default/SwitchCase.java, codetoanalyze.java.nullsafe_default.SwitchCase.switchOnNullIsBad():java.lang.String, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [NullPointerException will be thrown at this line! `color` is `null` and is dereferenced via calling `ordinal()`: null constant at line 14.]