From 0bf5298c2111b734e19f3abf819acdb339cee1d5 Mon Sep 17 00:00:00 2001 From: Mitya Lyubarskiy Date: Wed, 18 Mar 2020 04:22:59 -0700 Subject: [PATCH] [nullsafe] Decouple nullsafe mode from issue calculation, AssignmentRule Summary: # Problem In current design, Rules (assignment rule, dereference rule, inheritance rule) decide, depending on the mode, wether the issue is legit or not. If the issue is not actionable for the given mode, it won't be created and registered. For meta-issues, we want to be able to do smart things like: - Identify if we can raise strictness of the mode without introducing new issues - Classify classes on "clean" vs "broken", taking into account issues that are currently invisible. # Solution In the new design: 1. Rules are issuing violations independently of mode. This makes sense semantically. Mode is "level of trust we have for suspicious things", but the thing does not cease to be suspicious in any mode. 2. Each Rule decides if it is reportable or not in a given mode. 3. `nullsafe_mode` is passed to the function `register_error`, that 1) adds error so it can be recorded in summary for file-level analysis phase 2) reports some of them to the user. # This diff This diff converts only AssignmentRule, follow up will include conversion of other rules, so no issue encapsutes the mode. Reviewed By: jvillard Differential Revision: D20491716 fbshipit-source-id: af17dd66d --- infer/src/nullsafe/AssignmentRule.ml | 435 +++++++++--------- infer/src/nullsafe/AssignmentRule.mli | 63 +-- infer/src/nullsafe/eradicate.ml | 4 +- infer/src/nullsafe/eradicateChecks.ml | 62 +-- infer/src/nullsafe/typeCheck.ml | 18 +- infer/src/nullsafe/typeErr.ml | 157 +++---- infer/src/nullsafe/typeErr.mli | 11 +- .../java/nullsafe-default/issues.exp | 12 +- 8 files changed, 396 insertions(+), 366 deletions(-) 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.]