[nullsafe] Decouple nullsafe mode from issue calculation, Inheritance Rule

Summary:
This diff continues work in D20491716.
This time for Inheritance Rule.

Reviewed By: jvillard

Differential Revision: D20492889

fbshipit-source-id: c4dfd95c3
master
Mitya Lyubarskiy 5 years ago committed by Facebook GitHub Bot
parent 371eed9309
commit 2af561c593

@ -6,35 +6,12 @@
*) *)
open! IStd open! IStd
type violation = {nullsafe_mode: NullsafeMode.t; base: Nullability.t; overridden: Nullability.t} type violation = {base: Nullability.t; overridden: Nullability.t} [@@deriving compare]
[@@deriving compare]
type type_role = Param | Ret type type_role = Param | Ret
let is_whitelisted_violation ~subtype ~supertype = module ReportableViolation = struct
(* When both nullabilities are kind-of non-nullable we don't want to raise the type t = {nullsafe_mode: NullsafeMode.t; violation: violation}
issue. Without this suppression there will be a lot of non-actionable issues
raised for classes in one [NullsafeMode] inheriting from classes in the other
[NullsafeMode]. *)
(* TODO(T62521386): consider using caller context when determining nullability to get
rid of white-lists. *)
Nullability.is_nonnullish subtype && Nullability.is_nonnullish supertype
let check ~nullsafe_mode type_role ~base ~overridden =
let subtype, supertype =
match type_role with
| Ret ->
(* covariance for ret *)
(overridden, base)
| Param ->
(* contravariance for param *)
(base, overridden)
in
Result.ok_if_true
(Nullability.is_subtype ~subtype ~supertype || is_whitelisted_violation ~subtype ~supertype)
~error:{nullsafe_mode; base; overridden}
type violation_type = type violation_type =
| InconsistentParam of {param_description: string; param_position: int} | InconsistentParam of {param_description: string; param_position: int}
@ -43,7 +20,9 @@ type violation_type =
let is_java_lang_object_equals = function let is_java_lang_object_equals = function
| Procname.Java java_procname -> ( | Procname.Java java_procname -> (
match (Procname.Java.get_class_name java_procname, Procname.Java.get_method java_procname) with match
(Procname.Java.get_class_name java_procname, Procname.Java.get_method java_procname)
with
| "java.lang.Object", "equals" -> | "java.lang.Object", "equals" ->
true true
| _ -> | _ ->
@ -52,7 +31,7 @@ let is_java_lang_object_equals = function
false false
let violation_description _ violation_type ~base_proc_name ~overridden_proc_name = let get_description _ violation_type ~base_proc_name ~overridden_proc_name =
let module MF = MarkupFormatter in let module MF = MarkupFormatter in
let base_method_descr = Procname.to_simplified_string ~withclass:true base_proc_name in let base_method_descr = Procname.to_simplified_string ~withclass:true base_proc_name in
let overridden_method_descr = let overridden_method_descr =
@ -84,12 +63,38 @@ let violation_description _ violation_type ~base_proc_name ~overridden_proc_name
string_of_int n ^ "th" string_of_int n ^ "th"
in in
Format.asprintf Format.asprintf
"%s parameter %a of method %a is missing `@Nullable` declaration when overriding %a. The \ "%s parameter %a of method %a is missing `@Nullable` declaration when overriding %a. \
parent method declared it can handle `null` for this param, so the child should also \ The parent method declared it can handle `null` for this param, so the child should \
declare that." also declare that."
(translate_position param_position) (translate_position param_position)
MF.pp_monospaced param_description MF.pp_monospaced overridden_method_descr MF.pp_monospaced param_description MF.pp_monospaced overridden_method_descr
MF.pp_monospaced base_method_descr MF.pp_monospaced base_method_descr
let violation_severity {nullsafe_mode} = NullsafeMode.severity nullsafe_mode let get_severity {nullsafe_mode} = NullsafeMode.severity nullsafe_mode
end
let check type_role ~base ~overridden =
let subtype, supertype =
match type_role with
| Ret ->
(* covariance for ret *)
(overridden, base)
| Param ->
(* contravariance for param *)
(base, overridden)
in
Result.ok_if_true (Nullability.is_subtype ~subtype ~supertype) ~error:{base; overridden}
let to_reportable_violation nullsafe_mode ({base; overridden} as violation) =
if
Nullability.is_nonnullish base && Nullability.is_nonnullish overridden
(* When both nullabilities are kind-of non-nullable we don't want to raise the
issue. Without this suppression there will be a lot of non-actionable issues
raised for classes in one [NullsafeMode] inheriting from classes in the other
[NullsafeMode]. *)
(* TODO(T62521386): consider using caller context when determining nullability to get
rid of white-lists. *)
then None
else Some ReportableViolation.{nullsafe_mode; violation}

@ -19,25 +19,30 @@ open! IStd
type violation [@@deriving compare] type violation [@@deriving compare]
type type_role = Param | Ret
val check : type_role -> base:Nullability.t -> overridden:Nullability.t -> (unit, violation) result
(** See description of the rule in the header of the file. Note that formal fact of violation might
or might not be reported to the user, depending on the mode. See [to_reportable_violation] *)
(** Violation that needs to be reported to the user. *)
module ReportableViolation : sig
type t
type violation_type = type violation_type =
| InconsistentParam of {param_description: string; param_position: int} | InconsistentParam of {param_description: string; param_position: int}
| InconsistentReturn | InconsistentReturn
[@@deriving compare] [@@deriving compare]
type type_role = Param | Ret val get_severity : t -> Exceptions.severity
(** Severity of the violation to be reported *)
val get_description :
t -> violation_type -> base_proc_name:Procname.t -> overridden_proc_name:Procname.t -> string
(** Given context around violation, return error message together with the info where to put this
message *)
end
val check : val to_reportable_violation : NullsafeMode.t -> violation -> ReportableViolation.t option
nullsafe_mode:NullsafeMode.t (** Depending on the mode, violation might or might not be important enough to be reported to the
-> type_role user. If it should NOT be reported for that mode, this function will return None. *)
-> base:Nullability.t
-> overridden:Nullability.t
-> (unit, violation) result
val violation_description :
violation
-> violation_type
-> base_proc_name:Procname.t
-> overridden_proc_name:Procname.t
-> string
val violation_severity : violation -> Exceptions.severity

@ -460,12 +460,12 @@ let check_inheritance_rule_for_return find_canonical_duplicate tenv loc ~nullsaf
~base_proc_name ~overridden_proc_name ~overridden_proc_desc ~base_nullability ~base_proc_name ~overridden_proc_name ~overridden_proc_desc ~base_nullability
~overridden_nullability = ~overridden_nullability =
Result.iter_error Result.iter_error
(InheritanceRule.check ~nullsafe_mode InheritanceRule.Ret ~base:base_nullability (InheritanceRule.check InheritanceRule.Ret ~base:base_nullability
~overridden:overridden_nullability) ~f:(fun inheritance_violation -> ~overridden:overridden_nullability) ~f:(fun inheritance_violation ->
register_error tenv find_canonical_duplicate register_error tenv find_canonical_duplicate
(TypeErr.Inconsistent_subclass (TypeErr.Inconsistent_subclass
{ inheritance_violation { inheritance_violation
; violation_type= InheritanceRule.InconsistentReturn ; violation_type= InheritanceRule.ReportableViolation.InconsistentReturn
; overridden_proc_name ; overridden_proc_name
; base_proc_name }) ; base_proc_name })
None ~nullsafe_mode loc overridden_proc_desc ) None ~nullsafe_mode loc overridden_proc_desc )
@ -475,13 +475,13 @@ let check_inheritance_rule_for_param find_canonical_duplicate tenv loc ~nullsafe
~overridden_param_name ~base_proc_name ~overridden_proc_name ~overridden_proc_desc ~overridden_param_name ~base_proc_name ~overridden_proc_name ~overridden_proc_desc
~param_position ~base_nullability ~overridden_nullability = ~param_position ~base_nullability ~overridden_nullability =
Result.iter_error Result.iter_error
(InheritanceRule.check ~nullsafe_mode InheritanceRule.Param ~base:base_nullability (InheritanceRule.check InheritanceRule.Param ~base:base_nullability
~overridden:overridden_nullability) ~f:(fun inheritance_violation -> ~overridden:overridden_nullability) ~f:(fun inheritance_violation ->
register_error tenv find_canonical_duplicate register_error tenv find_canonical_duplicate
(TypeErr.Inconsistent_subclass (TypeErr.Inconsistent_subclass
{ inheritance_violation { inheritance_violation
; violation_type= ; violation_type=
InheritanceRule.InconsistentParam InheritanceRule.ReportableViolation.InconsistentParam
{param_position; param_description= Mangled.to_string overridden_param_name} {param_position; param_description= Mangled.to_string overridden_param_name}
; base_proc_name ; base_proc_name
; overridden_proc_name }) ; overridden_proc_name })

@ -62,7 +62,7 @@ type err_instance =
{is_always_true: bool; condition_descr: string option; nonnull_origin: TypeOrigin.t} {is_always_true: bool; condition_descr: string option; nonnull_origin: TypeOrigin.t}
| Inconsistent_subclass of | Inconsistent_subclass of
{ inheritance_violation: InheritanceRule.violation { inheritance_violation: InheritanceRule.violation
; violation_type: InheritanceRule.violation_type ; violation_type: InheritanceRule.ReportableViolation.violation_type
; base_proc_name: Procname.t ; base_proc_name: Procname.t
; overridden_proc_name: Procname.t } ; overridden_proc_name: Procname.t }
| Field_not_initialized of {nullsafe_mode: NullsafeMode.t; field_name: Fieldname.t} | Field_not_initialized of {nullsafe_mode: NullsafeMode.t; field_name: Fieldname.t}
@ -273,16 +273,18 @@ let get_error_info_if_reportable ~nullsafe_mode err_instance =
(description, issue_type, Some error_location, severity) (description, issue_type, Some error_location, severity)
| Inconsistent_subclass | Inconsistent_subclass
{inheritance_violation; violation_type; base_proc_name; overridden_proc_name} -> {inheritance_violation; violation_type; base_proc_name; overridden_proc_name} ->
Some let+ reportable_violation =
( InheritanceRule.violation_description inheritance_violation violation_type ~base_proc_name InheritanceRule.to_reportable_violation nullsafe_mode inheritance_violation
~overridden_proc_name in
( InheritanceRule.ReportableViolation.get_description reportable_violation violation_type
~base_proc_name ~overridden_proc_name
, ( match violation_type with , ( match violation_type with
| InconsistentReturn -> | InconsistentReturn ->
IssueType.eradicate_inconsistent_subclass_return_annotation IssueType.eradicate_inconsistent_subclass_return_annotation
| InconsistentParam _ -> | InconsistentParam _ ->
IssueType.eradicate_inconsistent_subclass_parameter_annotation ) IssueType.eradicate_inconsistent_subclass_parameter_annotation )
, None , None
, InheritanceRule.violation_severity inheritance_violation ) , InheritanceRule.ReportableViolation.get_severity reportable_violation )
let report_now_if_reportable (st_report_error : st_report_error) err_instance ~nullsafe_mode loc let report_now_if_reportable (st_report_error : st_report_error) err_instance ~nullsafe_mode loc

@ -38,7 +38,7 @@ type err_instance =
{is_always_true: bool; condition_descr: string option; nonnull_origin: TypeOrigin.t} {is_always_true: bool; condition_descr: string option; nonnull_origin: TypeOrigin.t}
| Inconsistent_subclass of | Inconsistent_subclass of
{ inheritance_violation: InheritanceRule.violation { inheritance_violation: InheritanceRule.violation
; violation_type: InheritanceRule.violation_type ; violation_type: InheritanceRule.ReportableViolation.violation_type
; base_proc_name: Procname.t ; base_proc_name: Procname.t
; overridden_proc_name: Procname.t } ; overridden_proc_name: Procname.t }
| Field_not_initialized of {nullsafe_mode: NullsafeMode.t; field_name: Fieldname.t} | Field_not_initialized of {nullsafe_mode: NullsafeMode.t; field_name: Fieldname.t}

@ -372,7 +372,7 @@ codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, Linters_dummy_method,
codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_IS_CLEAN, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_IS_CLEAN, no_bucket, INFO, []
codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_IS_CLEAN, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_IS_CLEAN, no_bucket, INFO, []
codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_IS_CLEAN, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_IS_CLEAN, no_bucket, INFO, []
codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_IS_CLEAN, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_NEEDS_FIXING, no_bucket, INFO, []
codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_NEEDS_FIXING, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_NEEDS_FIXING, no_bucket, INFO, []
codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_NEEDS_FIXING, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_NEEDS_FIXING, no_bucket, INFO, []
codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_NEEDS_FIXING, no_bucket, INFO, [] codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, Linters_dummy_method, 1, ERADICATE_META_CLASS_NEEDS_FIXING, no_bucket, INFO, []

Loading…
Cancel
Save