[nullsafe] stop using SummaryReporting

Summary:
This turned into a pretty big refactoring: the
`IntraproceduralAnalysis.t` needs to be passed around through quite a
few functions. These functions usually depended on
tenv/proc_desc/proc_name that are already in the `analysis_data` so
refactored that too (checking that these were indeed the same as in the
`analysis_data` record (using my own eyeballs). Interestingly there is
one place where we actually turn the analysis to other proc descs than
the original one in eradicate.ml so I've added a small comment there.

Reviewed By: mityal

Differential Revision: D21351909

fbshipit-source-id: 6e6330e5b
master
Jules Villard 5 years ago committed by Facebook GitHub Bot
parent bab005a835
commit f86d9193a9

@ -6,7 +6,6 @@
*) *)
open! IStd open! IStd
module L = Logging
type log_t = Reporting.log_t type log_t = Reporting.log_t
@ -25,18 +24,3 @@ let log_warning summary ~loc ?ltr ?extras issue_type error_message =
let log_error_using_state summary exn = let log_error_using_state summary exn =
BiabductionReporting.log_error_using_state (Summary.get_proc_desc summary) BiabductionReporting.log_error_using_state (Summary.get_proc_desc summary)
(Summary.get_err_log summary) exn (Summary.get_err_log summary) exn
let log_issue_deprecated_using_state severity proc_name ?node ?loc ?ltr exn =
if !BiabductionConfig.footprint then
match Summary.OnDisk.get proc_name with
| Some summary ->
let proc_attributes = Summary.get_attributes summary in
let err_log = Summary.get_err_log summary in
BiabductionReporting.log_issue_deprecated_using_state proc_attributes err_log severity ?node
?loc ?ltr exn
| None ->
L.(die InternalError)
"Trying to report error on procedure %a, but cannot because no summary exists for this \
procedure. Did you mean to log the error on the caller of %a instead?"
Procname.pp proc_name Procname.pp proc_name

@ -19,14 +19,3 @@ val log_warning : Summary.t -> loc:Location.t -> log_t
val log_error_using_state : Summary.t -> exn -> unit val log_error_using_state : Summary.t -> exn -> unit
(** Add an error to the given summary using biabduction state (DO NOT USE ELSEWHERE). *) (** Add an error to the given summary using biabduction state (DO NOT USE ELSEWHERE). *)
val log_issue_deprecated_using_state :
Exceptions.severity
-> Procname.t
-> ?node:Procdesc.Node.t
-> ?loc:Location.t
-> ?ltr:Errlog.loc_trace
-> exn
-> unit
(** Report an issue in the given procedure using biabduction state. DEPRECATED as it can create race
conditions between checkers. Use log_error_using_state instead *)

@ -10,8 +10,10 @@ module L = Logging
module F = Format module F = Format
open Dataflow open Dataflow
let callback1 tenv find_canonical_duplicate calls_this checks idenv curr_pname curr_pdesc let callback1 ({IntraproceduralAnalysis.proc_desc= curr_pdesc; tenv; _} as analysis_data)
annotated_signature linereader proc_loc : bool * TypeState.t option = find_canonical_duplicate calls_this checks idenv annotated_signature linereader proc_loc :
bool * TypeState.t option =
let curr_pname = Procdesc.get_proc_name curr_pdesc in
let add_formal typestate (param_signature : AnnotatedSignature.param_signature) = let add_formal typestate (param_signature : AnnotatedSignature.param_signature) =
let pvar = Pvar.mk param_signature.mangled curr_pname in let pvar = Pvar.mk param_signature.mangled curr_pname in
let formal_name = param_signature.mangled in let formal_name = param_signature.mangled in
@ -37,7 +39,7 @@ let callback1 tenv find_canonical_duplicate calls_this checks idenv curr_pname c
(* Check the nullable flag computed for the return value and report inconsistencies. *) (* Check the nullable flag computed for the return value and report inconsistencies. *)
let check_return find_canonical_duplicate exit_node final_typestate annotated_signature loc : unit let check_return find_canonical_duplicate exit_node final_typestate annotated_signature loc : unit
= =
let AnnotatedSignature.{ret_annotated_type} = annotated_signature.AnnotatedSignature.ret in let {AnnotatedSignature.ret_annotated_type} = annotated_signature.AnnotatedSignature.ret in
let ret_pvar = Procdesc.get_ret_var curr_pdesc in let ret_pvar = Procdesc.get_ret_var curr_pdesc in
let ret_range = TypeState.lookup_pvar ret_pvar final_typestate in let ret_range = TypeState.lookup_pvar ret_pvar final_typestate in
let typ_found_opt = let typ_found_opt =
@ -50,10 +52,10 @@ let callback1 tenv find_canonical_duplicate calls_this checks idenv curr_pname c
AnalysisState.set_node exit_node ; AnalysisState.set_node exit_node ;
if not (List.is_empty checks.TypeCheck.check_ret_type) then if not (List.is_empty checks.TypeCheck.check_ret_type) then
List.iter List.iter
~f:(fun f -> f curr_pname curr_pdesc ret_annotated_type.typ typ_found_opt loc) ~f:(fun f -> f curr_pdesc ret_annotated_type.typ typ_found_opt loc)
checks.TypeCheck.check_ret_type ; checks.TypeCheck.check_ret_type ;
if checks.TypeCheck.eradicate then if checks.TypeCheck.eradicate then
EradicateChecks.check_return_annotation tenv find_canonical_duplicate curr_pdesc ret_range EradicateChecks.check_return_annotation analysis_data find_canonical_duplicate ret_range
annotated_signature ret_implicitly_nullable loc annotated_signature ret_implicitly_nullable loc
in in
let do_before_dataflow initial_typestate = let do_before_dataflow initial_typestate =
@ -73,13 +75,13 @@ let callback1 tenv find_canonical_duplicate calls_this checks idenv curr_pname c
let pp_name fmt = F.pp_print_string fmt "eradicate" let pp_name fmt = F.pp_print_string fmt "eradicate"
let do_node tenv node typestate = let do_node _tenv node typestate =
NodePrinter.with_session ~pp_name node ~f:(fun () -> NodePrinter.with_session ~pp_name node ~f:(fun () ->
AnalysisState.set_node node ; AnalysisState.set_node node ;
if Config.write_html then L.d_printfln "before:@\n%a@\n" TypeState.pp typestate ; if Config.write_html then L.d_printfln "before:@\n%a@\n" TypeState.pp typestate ;
let TypeCheck.{normal_flow_typestate; exception_flow_typestates} = let {TypeCheck.normal_flow_typestate; exception_flow_typestates} =
TypeCheck.typecheck_node tenv calls_this checks idenv curr_pname curr_pdesc TypeCheck.typecheck_node analysis_data calls_this checks idenv find_canonical_duplicate
find_canonical_duplicate annotated_signature typestate node linereader annotated_signature typestate node linereader
in in
let normal_flow_typestates = let normal_flow_typestates =
Option.value_map normal_flow_typestate ~f:(fun a -> [a]) ~default:[] Option.value_map normal_flow_typestate ~f:(fun a -> [a]) ~default:[]
@ -100,14 +102,15 @@ let callback1 tenv find_canonical_duplicate calls_this checks idenv curr_pname c
(!calls_this, None) (!calls_this, None)
let analyze_one_procedure tenv proc_name proc_desc calls_this checks annotated_signature linereader let analyze_one_procedure ({IntraproceduralAnalysis.tenv; proc_desc; _} as analysis_data) calls_this
proc_loc : unit = checks annotated_signature linereader proc_loc : unit =
let idenv = Idenv.create proc_desc in let idenv = Idenv.create proc_desc in
let find_duplicate_nodes = State.mk_find_duplicate_nodes proc_desc in let find_duplicate_nodes = State.mk_find_duplicate_nodes proc_desc in
let find_canonical_duplicate node = let find_canonical_duplicate node =
let duplicate_nodes = find_duplicate_nodes node in let duplicate_nodes = find_duplicate_nodes node in
try Procdesc.NodeSet.min_elt duplicate_nodes with Caml.Not_found -> node try Procdesc.NodeSet.min_elt duplicate_nodes with Caml.Not_found -> node
in in
let proc_name = Procdesc.get_proc_name proc_desc in
let caller_nullsafe_mode = NullsafeMode.of_procname tenv proc_name in let caller_nullsafe_mode = NullsafeMode.of_procname tenv proc_name in
let typecheck_proc do_checks pname pdesc proc_details_opt = let typecheck_proc do_checks pname pdesc proc_details_opt =
let ann_sig, loc, idenv_pn = let ann_sig, loc, idenv_pn =
@ -134,8 +137,10 @@ let analyze_one_procedure tenv proc_name proc_desc calls_this checks annotated_s
if do_checks then (checks, calls_this) if do_checks then (checks, calls_this)
else ({TypeCheck.eradicate= false; check_ret_type= []}, ref false) else ({TypeCheck.eradicate= false; check_ret_type= []}, ref false)
in in
callback1 tenv find_canonical_duplicate calls_this' checks' idenv_pn pname pdesc ann_sig (* NOTE: [analysis_data] has the new [pdesc] *)
linereader loc callback1
{analysis_data with proc_desc= pdesc}
find_canonical_duplicate calls_this' checks' idenv_pn ann_sig linereader loc
in in
let do_final_typestate typestate_opt calls_this = let do_final_typestate typestate_opt calls_this =
let do_typestate typestate = let do_typestate typestate =
@ -151,8 +156,8 @@ let analyze_one_procedure tenv proc_name proc_desc calls_this checks annotated_s
let typestates_for_all_constructors_incl_current = let typestates_for_all_constructors_incl_current =
Initializers.final_constructor_typestates_lazy tenv proc_name typecheck_proc Initializers.final_constructor_typestates_lazy tenv proc_name typecheck_proc
in in
EradicateChecks.check_constructor_initialization tenv find_canonical_duplicate proc_name EradicateChecks.check_constructor_initialization analysis_data find_canonical_duplicate
proc_desc start_node ~nullsafe_mode:annotated_signature.AnnotatedSignature.nullsafe_mode start_node ~nullsafe_mode:annotated_signature.AnnotatedSignature.nullsafe_mode
~typestates_for_curr_constructor_and_all_initializer_methods ~typestates_for_curr_constructor_and_all_initializer_methods
~typestates_for_all_constructors_incl_current proc_loc ; ~typestates_for_all_constructors_incl_current proc_loc ;
if Config.eradicate_verbose then L.result "Final Typestate@\n%a@." TypeState.pp typestate ) if Config.eradicate_verbose then L.result "Final Typestate@\n%a@." TypeState.pp typestate )
@ -164,7 +169,7 @@ let analyze_one_procedure tenv proc_name proc_desc calls_this checks annotated_s
in in
do_final_typestate final_typestate_opt calls_this ; do_final_typestate final_typestate_opt calls_this ;
if checks.TypeCheck.eradicate then if checks.TypeCheck.eradicate then
EradicateChecks.check_overridden_annotations find_canonical_duplicate tenv proc_name proc_desc EradicateChecks.check_overridden_annotations analysis_data find_canonical_duplicate
annotated_signature ; annotated_signature ;
() ()
@ -188,7 +193,8 @@ let find_reason_to_skip_analysis proc_name proc_desc =
(** Entry point for the nullsafe procedure-level analysis. *) (** Entry point for the nullsafe procedure-level analysis. *)
let analyze checks {IntraproceduralAnalysis.proc_desc; tenv} : NullsafeSummary.t option = let analyze checks ({IntraproceduralAnalysis.proc_desc; tenv; _} as analysis_data) :
NullsafeSummary.t option =
let proc_name = Procdesc.get_proc_name proc_desc in let proc_name = Procdesc.get_proc_name proc_desc in
L.debug Analysis Medium "Analysis of %a@\n" Procname.pp proc_name ; L.debug Analysis Medium "Analysis of %a@\n" Procname.pp proc_name ;
match find_reason_to_skip_analysis proc_name proc_desc with match find_reason_to_skip_analysis proc_name proc_desc with
@ -210,14 +216,12 @@ let analyze checks {IntraproceduralAnalysis.proc_desc; tenv} : NullsafeSummary.t
(* The main method - during this the actual analysis will happen and TypeErr will be populated with (* The main method - during this the actual analysis will happen and TypeErr will be populated with
issues (and some of them - reported). issues (and some of them - reported).
*) *)
analyze_one_procedure tenv proc_name proc_desc calls_this checks annotated_signature analyze_one_procedure analysis_data calls_this checks annotated_signature linereader loc ;
linereader loc ;
(* Collect issues that were detected during analysis and put them in summary for further processing *) (* Collect issues that were detected during analysis and put them in summary for further processing *)
let issues = TypeErr.get_errors () |> List.map ~f:(fun (issues, _) -> issues) in let issues = TypeErr.get_errors () |> List.map ~f:(fun (issues, _) -> issues) in
(* Report errors of "farall" class - those could not be reported during analysis phase. *) (* Report errors of "farall" class - those could not be reported during analysis phase. *)
TypeErr.report_forall_issues_and_reset TypeErr.report_forall_issues_and_reset analysis_data
(EradicateCheckers.report_error tenv) ~nullsafe_mode:annotated_signature.nullsafe_mode ;
~nullsafe_mode:annotated_signature.nullsafe_mode proc_desc ;
Some {NullsafeSummary.issues} Some {NullsafeSummary.issues}

@ -9,7 +9,7 @@ open! IStd
(** Module for Eradicate-based user-defined checkers. *) (** Module for Eradicate-based user-defined checkers. *)
let report_error tenv proc_name proc_desc kind loc ?(field_name = None) let report_error {IntraproceduralAnalysis.proc_desc; tenv; err_log} kind loc ?(field_name = None)
?(exception_kind = fun k d -> Exceptions.Checkers (k, d)) ~severity description = ?(exception_kind = fun k d -> Exceptions.Checkers (k, d)) ~severity description =
let suppressed = Reporting.is_suppressed tenv proc_desc kind ~field_name in let suppressed = Reporting.is_suppressed tenv proc_desc kind ~field_name in
if suppressed then Logging.debug Analysis Medium "Reporting is suppressed!@\n" if suppressed then Logging.debug Analysis Medium "Reporting is suppressed!@\n"
@ -17,4 +17,9 @@ let report_error tenv proc_name proc_desc kind loc ?(field_name = None)
let localized_description = Localise.verbatim_desc description in let localized_description = Localise.verbatim_desc description in
let exn = exception_kind kind localized_description in let exn = exception_kind kind localized_description in
let trace = [Errlog.make_trace_element 0 loc description []] in let trace = [Errlog.make_trace_element 0 loc description []] in
SummaryReporting.log_issue_deprecated_using_state severity proc_name ~loc ~ltr:trace exn let attrs = Procdesc.get_attributes proc_desc in
let node = AnalysisState.get_node_exn () in
let session = AnalysisState.get_session () in
Reporting.log_issue_from_summary severity attrs err_log
~node:(BackendNode {node})
~session ~loc ~ltr:trace exn

@ -10,9 +10,7 @@ open! IStd
(** Module for Eradicate-based user-defined checkers. *) (** Module for Eradicate-based user-defined checkers. *)
val report_error : val report_error :
Tenv.t IntraproceduralAnalysis.t
-> Procname.t
-> Procdesc.t
-> IssueType.t -> IssueType.t
-> Location.t -> Location.t
-> ?field_name:Fieldname.t option -> ?field_name:Fieldname.t option

@ -11,8 +11,6 @@ open! IStd
module L = Logging module L = Logging
let register_error tenv = TypeErr.register_error (EradicateCheckers.report_error tenv)
let explain_expr tenv node e = let explain_expr tenv node e =
match Errdesc.exp_rv_dexp tenv node e with match Errdesc.exp_rv_dexp tenv node e with
| Some de -> | Some de ->
@ -28,8 +26,8 @@ let is_virtual = function
false false
let check_object_dereference ~nullsafe_mode tenv find_canonical_duplicate curr_pname node instr_ref let check_object_dereference ({IntraproceduralAnalysis.tenv; _} as analysis_data) ~nullsafe_mode
object_exp dereference_type inferred_nullability loc = find_canonical_duplicate node instr_ref object_exp dereference_type inferred_nullability loc =
Result.iter_error Result.iter_error
(DereferenceRule.check (InferredNullability.get_nullability inferred_nullability)) (DereferenceRule.check (InferredNullability.get_nullability inferred_nullability))
~f:(fun dereference_violation -> ~f:(fun dereference_violation ->
@ -43,15 +41,17 @@ let check_object_dereference ~nullsafe_mode tenv find_canonical_duplicate curr_p
; dereference_type ; dereference_type
; nullable_object_origin } ; nullable_object_origin }
in in
register_error tenv find_canonical_duplicate type_error (Some instr_ref) ~nullsafe_mode loc TypeErr.register_error analysis_data find_canonical_duplicate type_error (Some instr_ref)
curr_pname ) ~nullsafe_mode loc )
(** [expr] is an expression that was explicitly compared with `null`. At the same time, [expr] had (** [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 [inferred_nullability] before the comparision. Check if the comparision is redundant and emit an
issue, if this is the case. *) issue, if this is the case. *)
let check_condition_for_redundancy tenv ~is_always_true find_canonical_duplicate curr_pdesc node let check_condition_for_redundancy
~nullsafe_mode expr typ inferred_nullability idenv linereader loc instr_ref : unit = ({IntraproceduralAnalysis.tenv; proc_desc= curr_pdesc; _} as analysis_data) ~is_always_true
find_canonical_duplicate node ~nullsafe_mode expr typ inferred_nullability idenv linereader loc
instr_ref : unit =
let contains_instanceof_throwable pdesc node = let contains_instanceof_throwable pdesc node =
(* Check if the current procedure has a catch Throwable. *) (* Check if the current procedure has a catch Throwable. *)
(* That always happens in the bytecode generated by try-with-resources. *) (* That always happens in the bytecode generated by try-with-resources. *)
@ -111,21 +111,22 @@ let check_condition_for_redundancy tenv ~is_always_true find_canonical_duplicate
*) *)
let condition_descr = explain_expr tenv node expr in let condition_descr = explain_expr tenv node expr in
let nonnull_origin = InferredNullability.get_origin inferred_nullability in let nonnull_origin = InferredNullability.get_origin inferred_nullability in
register_error tenv find_canonical_duplicate TypeErr.register_error analysis_data find_canonical_duplicate
(TypeErr.Condition_redundant {is_always_true; condition_descr; nonnull_origin}) (TypeErr.Condition_redundant {is_always_true; condition_descr; nonnull_origin})
(Some instr_ref) ~nullsafe_mode loc curr_pdesc (Some instr_ref) ~nullsafe_mode loc
(** Check an assignment to a field. *) (** Check an assignment to a field. *)
let check_field_assignment ~nullsafe_mode tenv find_canonical_duplicate curr_pdesc node instr_ref let check_field_assignment
typestate ~expr_rhs ~field_type loc fname (annotated_field : AnnotatedField.t) typecheck_expr : ({IntraproceduralAnalysis.tenv; proc_desc= curr_pdesc; _} as analysis_data) ~nullsafe_mode
unit = find_canonical_duplicate node instr_ref typestate ~expr_rhs ~field_type loc fname
(annotated_field : AnnotatedField.t) typecheck_expr : unit =
L.d_with_indent ~name:"check_field_assignment" (fun () -> L.d_with_indent ~name:"check_field_assignment" (fun () ->
let curr_pname = Procdesc.get_proc_name curr_pdesc in let curr_pname = Procdesc.get_proc_name curr_pdesc in
let curr_pattrs = Procdesc.get_attributes curr_pdesc in let curr_pattrs = Procdesc.get_attributes curr_pdesc in
let _, inferred_nullability_rhs = let _, inferred_nullability_rhs =
L.d_strln "Typechecking rhs" ; L.d_strln "Typechecking rhs" ;
typecheck_expr node instr_ref curr_pdesc typestate expr_rhs typecheck_expr node instr_ref typestate expr_rhs
(* TODO(T54687014) optimistic default might be an unsoundness issue - investigate *) (* TODO(T54687014) optimistic default might be an unsoundness issue - investigate *)
(field_type, InferredNullability.create TypeOrigin.OptimisticFallback) (field_type, InferredNullability.create TypeOrigin.OptimisticFallback)
loc loc
@ -158,13 +159,13 @@ let check_field_assignment ~nullsafe_mode tenv find_canonical_duplicate curr_pde
in in
if should_report then if should_report then
let rhs_origin = InferredNullability.get_origin inferred_nullability_rhs in let rhs_origin = InferredNullability.get_origin inferred_nullability_rhs in
register_error tenv find_canonical_duplicate TypeErr.register_error analysis_data find_canonical_duplicate
(TypeErr.Bad_assignment (TypeErr.Bad_assignment
{ assignment_violation { assignment_violation
; assignment_location= loc ; assignment_location= loc
; rhs_origin ; rhs_origin
; assignment_type= AssignmentRule.ReportableViolation.AssigningToField fname }) ; assignment_type= AssignmentRule.ReportableViolation.AssigningToField fname })
(Some instr_ref) ~nullsafe_mode loc curr_pdesc ) ) (Some instr_ref) ~nullsafe_mode loc ) )
(* Check if the field declared as not nullable (implicitly or explicitly). If the field is (* Check if the field declared as not nullable (implicitly or explicitly). If the field is
@ -228,12 +229,13 @@ let is_generated_field field_name =
(** Check field initialization for a given constructor *) (** Check field initialization for a given constructor *)
let check_constructor_initialization tenv find_canonical_duplicate curr_constructor_pname let check_constructor_initialization
curr_constructor_pdesc start_node ~nullsafe_mode ({IntraproceduralAnalysis.tenv; proc_desc= curr_constructor_pdesc; _} as analysis_data)
find_canonical_duplicate start_node ~nullsafe_mode
~typestates_for_curr_constructor_and_all_initializer_methods ~typestates_for_curr_constructor_and_all_initializer_methods
~typestates_for_all_constructors_incl_current loc : unit = ~typestates_for_all_constructors_incl_current loc : unit =
AnalysisState.set_node start_node ; AnalysisState.set_node start_node ;
if Procname.is_constructor curr_constructor_pname then if Procname.is_constructor (Procdesc.get_proc_name curr_constructor_pdesc) then
match match
PatternMatch.get_this_type_nonstatic_methods_only PatternMatch.get_this_type_nonstatic_methods_only
(Procdesc.get_attributes curr_constructor_pdesc) (Procdesc.get_attributes curr_constructor_pdesc)
@ -305,9 +307,9 @@ let check_constructor_initialization tenv find_canonical_duplicate curr_construc
*) *)
() ()
else else
register_error tenv find_canonical_duplicate TypeErr.register_error analysis_data find_canonical_duplicate
(TypeErr.Field_not_initialized {field_name}) (TypeErr.Field_not_initialized {field_name})
None ~nullsafe_mode loc curr_constructor_pdesc ; None ~nullsafe_mode loc ;
(* Check if field is over-annotated. *) (* Check if field is over-annotated. *)
match annotated_field with match annotated_field with
| None -> | None ->
@ -321,11 +323,11 @@ let check_constructor_initialization tenv find_canonical_duplicate curr_construc
let by_rhs_upper_bound = field_nullability_upper_bound_over_all_typestates () in let by_rhs_upper_bound = field_nullability_upper_bound_over_all_typestates () in
Result.iter_error (OverAnnotatedRule.check ~what ~by_rhs_upper_bound) Result.iter_error (OverAnnotatedRule.check ~what ~by_rhs_upper_bound)
~f:(fun over_annotated_violation -> ~f:(fun over_annotated_violation ->
register_error tenv find_canonical_duplicate TypeErr.register_error analysis_data find_canonical_duplicate
(TypeErr.Over_annotation (TypeErr.Over_annotation
{ over_annotated_violation { over_annotated_violation
; violation_type= OverAnnotatedRule.FieldOverAnnoted field_name }) ; violation_type= OverAnnotatedRule.FieldOverAnnoted field_name })
~nullsafe_mode None loc curr_constructor_pdesc ) ) ~nullsafe_mode None loc ) )
in in
List.iter ~f:do_field fields List.iter ~f:do_field fields
| None -> | None ->
@ -334,24 +336,27 @@ let check_constructor_initialization tenv find_canonical_duplicate curr_construc
() ()
let check_return_not_nullable ~nullsafe_mode tenv find_canonical_duplicate loc curr_pname curr_pdesc let check_return_not_nullable ({IntraproceduralAnalysis.proc_desc= curr_pdesc; _} as analysis_data)
(ret_signature : AnnotatedSignature.ret_signature) ret_inferred_nullability = ~nullsafe_mode find_canonical_duplicate loc (ret_signature : AnnotatedSignature.ret_signature)
ret_inferred_nullability =
(* Returning from a function is essentially an assignment the actual return value to the formal `return` *) (* 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 lhs = AnnotatedNullability.get_nullability ret_signature.ret_annotated_type.nullability in
let rhs = InferredNullability.get_nullability ret_inferred_nullability in let rhs = InferredNullability.get_nullability ret_inferred_nullability in
Result.iter_error (AssignmentRule.check ~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 let rhs_origin = InferredNullability.get_origin ret_inferred_nullability in
register_error tenv find_canonical_duplicate let curr_pname = Procdesc.get_proc_name curr_pdesc in
(TypeErr.Bad_assignment TypeErr.register_error analysis_data find_canonical_duplicate
(Bad_assignment
{ assignment_violation { assignment_violation
; assignment_location= loc ; assignment_location= loc
; rhs_origin ; rhs_origin
; assignment_type= AssignmentRule.ReportableViolation.ReturningFromFunction curr_pname }) ; assignment_type= ReturningFromFunction curr_pname })
None ~nullsafe_mode loc curr_pdesc ) None ~nullsafe_mode loc )
let check_return_overrannotated tenv find_canonical_duplicate loc curr_pname curr_pdesc let check_return_overrannotated
~nullsafe_mode (ret_signature : AnnotatedSignature.ret_signature) ret_inferred_nullability = ({IntraproceduralAnalysis.proc_desc= curr_pdesc; _} as analysis_data) find_canonical_duplicate
loc ~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` *) (* 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 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 (* In our CFG implementation, there is only one place where we return from a function
@ -361,34 +366,29 @@ let check_return_overrannotated tenv find_canonical_duplicate loc curr_pname cur
let by_rhs_upper_bound = InferredNullability.get_nullability ret_inferred_nullability in let by_rhs_upper_bound = InferredNullability.get_nullability ret_inferred_nullability in
Result.iter_error (OverAnnotatedRule.check ~what ~by_rhs_upper_bound) Result.iter_error (OverAnnotatedRule.check ~what ~by_rhs_upper_bound)
~f:(fun over_annotated_violation -> ~f:(fun over_annotated_violation ->
register_error tenv find_canonical_duplicate let curr_pname = Procdesc.get_proc_name curr_pdesc in
(TypeErr.Over_annotation TypeErr.register_error analysis_data find_canonical_duplicate
{ over_annotated_violation (Over_annotation {over_annotated_violation; violation_type= ReturnOverAnnotated curr_pname})
; violation_type= OverAnnotatedRule.ReturnOverAnnotated curr_pname }) None ~nullsafe_mode loc )
None ~nullsafe_mode loc curr_pdesc )
(** Check the annotations when returning from a method. *) (** Check the annotations when returning from a method. *)
let check_return_annotation tenv find_canonical_duplicate curr_pdesc ret_range let check_return_annotation ({IntraproceduralAnalysis.proc_desc= curr_pdesc; _} as analysis_data)
(annotated_signature : AnnotatedSignature.t) ret_implicitly_nullable loc : unit = find_canonical_duplicate ret_range (annotated_signature : AnnotatedSignature.t)
ret_implicitly_nullable loc : unit =
let curr_pname = Procdesc.get_proc_name curr_pdesc in let curr_pname = Procdesc.get_proc_name curr_pdesc in
match ret_range with match ret_range with
(* Disables the warnings since it is not clear how to annotate the return value of lambdas *) (* Disables the warnings since it is not clear how to annotate the return value of lambdas *)
| Some _ | Some _
when match curr_pname with when match curr_pname with Java java_pname -> Procname.Java.is_lambda java_pname | _ -> false ->
| Procname.Java java_pname ->
Procname.Java.is_lambda java_pname
| _ ->
false ->
() ()
| Some (_, ret_inferred_nullability) -> | Some (_, ret_inferred_nullability) ->
(* TODO(T54308240) Model ret_implicitly_nullable in AnnotatedNullability *) (* TODO(T54308240) Model ret_implicitly_nullable in AnnotatedNullability *)
if not ret_implicitly_nullable then if not ret_implicitly_nullable then
check_return_not_nullable ~nullsafe_mode:annotated_signature.nullsafe_mode tenv check_return_not_nullable analysis_data ~nullsafe_mode:annotated_signature.nullsafe_mode
find_canonical_duplicate loc curr_pname curr_pdesc annotated_signature.ret find_canonical_duplicate loc annotated_signature.ret ret_inferred_nullability ;
ret_inferred_nullability ;
if Config.eradicate_return_over_annotated then if Config.eradicate_return_over_annotated then
check_return_overrannotated tenv find_canonical_duplicate loc curr_pname curr_pdesc check_return_overrannotated analysis_data find_canonical_duplicate loc
annotated_signature.ret ~nullsafe_mode:annotated_signature.nullsafe_mode annotated_signature.ret ~nullsafe_mode:annotated_signature.nullsafe_mode
ret_inferred_nullability ret_inferred_nullability
| None -> | None ->
@ -396,18 +396,18 @@ let check_return_annotation tenv find_canonical_duplicate curr_pdesc ret_range
(** Check the receiver of a virtual call. *) (** Check the receiver of a virtual call. *)
let check_call_receiver ~nullsafe_mode tenv find_canonical_duplicate curr_pdesc node typestate let check_call_receiver analysis_data ~nullsafe_mode find_canonical_duplicate node typestate
call_params callee_pname (instr_ref : TypeErr.InstrRef.t) loc typecheck_expr : unit = call_params callee_pname (instr_ref : TypeErr.InstrRef.t) loc typecheck_expr : unit =
match call_params with match call_params with
| ((original_this_e, this_e), typ) :: _ -> | ((original_this_e, this_e), typ) :: _ ->
let _, this_inferred_nullability = let _, this_inferred_nullability =
typecheck_expr tenv node instr_ref curr_pdesc typestate this_e typecheck_expr node instr_ref typestate this_e
(* TODO(T54687014) optimistic default might be an unsoundness issue - investigate *) (* TODO(T54687014) optimistic default might be an unsoundness issue - investigate *)
(typ, InferredNullability.create TypeOrigin.OptimisticFallback) (typ, InferredNullability.create TypeOrigin.OptimisticFallback)
loc loc
in in
check_object_dereference ~nullsafe_mode tenv find_canonical_duplicate curr_pdesc node check_object_dereference analysis_data ~nullsafe_mode find_canonical_duplicate node instr_ref
instr_ref original_this_e (DereferenceRule.ReportableViolation.MethodCall callee_pname) original_this_e (DereferenceRule.ReportableViolation.MethodCall callee_pname)
this_inferred_nullability loc this_inferred_nullability loc
| [] -> | [] ->
() ()
@ -420,8 +420,9 @@ type resolved_param =
; is_formal_propagates_nullable: bool } ; is_formal_propagates_nullable: bool }
(** Check the parameters of a call. *) (** Check the parameters of a call. *)
let check_call_parameters ~nullsafe_mode ~callee_annotated_signature tenv find_canonical_duplicate let check_call_parameters ({IntraproceduralAnalysis.tenv; _} as analysis_data) ~nullsafe_mode
curr_pdesc node callee_attributes resolved_params loc instr_ref : unit = ~callee_annotated_signature find_canonical_duplicate node callee_attributes resolved_params loc
instr_ref : unit =
let callee_pname = callee_attributes.ProcAttributes.proc_name in let callee_pname = callee_attributes.ProcAttributes.proc_name in
let check {num= param_position; formal; actual= orig_e2, nullability_actual} = let check {num= param_position; formal; actual= orig_e2, nullability_actual} =
let report ~nullsafe_mode assignment_violation = let report ~nullsafe_mode assignment_violation =
@ -433,19 +434,19 @@ let check_call_parameters ~nullsafe_mode ~callee_annotated_signature tenv find_c
"formal parameter " ^ Mangled.to_string formal.mangled "formal parameter " ^ Mangled.to_string formal.mangled
in in
let rhs_origin = InferredNullability.get_origin nullability_actual in let rhs_origin = InferredNullability.get_origin nullability_actual in
register_error tenv find_canonical_duplicate TypeErr.register_error analysis_data find_canonical_duplicate
(TypeErr.Bad_assignment (Bad_assignment
{ assignment_violation { assignment_violation
; assignment_location= loc ; assignment_location= loc
; rhs_origin ; rhs_origin
; assignment_type= ; assignment_type=
AssignmentRule.ReportableViolation.PassingParamToFunction PassingParamToFunction
{ param_signature= formal { param_signature= formal
; model_source= callee_annotated_signature.AnnotatedSignature.model_source ; model_source= callee_annotated_signature.AnnotatedSignature.model_source
; actual_param_expression ; actual_param_expression
; param_position ; param_position
; function_procname= callee_pname } }) ; function_procname= callee_pname } })
(Some instr_ref) ~nullsafe_mode loc curr_pdesc (Some instr_ref) ~nullsafe_mode loc
in in
if PatternMatch.type_is_class formal.param_annotated_type.typ then 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 (* Passing a param to a function is essentially an assignment the actual param value
@ -457,41 +458,42 @@ let check_call_parameters ~nullsafe_mode ~callee_annotated_signature tenv find_c
List.iter ~f:check resolved_params List.iter ~f:check resolved_params
let check_inheritance_rule_for_return find_canonical_duplicate tenv loc ~nullsafe_mode let check_inheritance_rule_for_return
~base_proc_name ~overridden_proc_name ~overridden_proc_desc ~base_nullability ({IntraproceduralAnalysis.proc_desc= overridden_proc_desc; _} as analysis_data)
find_canonical_duplicate loc ~nullsafe_mode ~base_proc_name ~base_nullability
~overridden_nullability = ~overridden_nullability =
Result.iter_error Result.iter_error
(InheritanceRule.check 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 TypeErr.register_error analysis_data find_canonical_duplicate
(TypeErr.Inconsistent_subclass (Inconsistent_subclass
{ inheritance_violation { inheritance_violation
; violation_type= InheritanceRule.ReportableViolation.InconsistentReturn ; violation_type= InconsistentReturn
; overridden_proc_name ; overridden_proc_name= Procdesc.get_proc_name overridden_proc_desc
; base_proc_name }) ; base_proc_name })
None ~nullsafe_mode loc overridden_proc_desc ) None ~nullsafe_mode loc )
let check_inheritance_rule_for_param find_canonical_duplicate tenv loc ~nullsafe_mode let check_inheritance_rule_for_param
~overridden_param_name ~base_proc_name ~overridden_proc_name ~overridden_proc_desc ({IntraproceduralAnalysis.proc_desc= overridden_proc_desc; _} as analysis_data)
find_canonical_duplicate loc ~nullsafe_mode ~overridden_param_name ~base_proc_name
~param_position ~base_nullability ~overridden_nullability = ~param_position ~base_nullability ~overridden_nullability =
Result.iter_error Result.iter_error
(InheritanceRule.check 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 TypeErr.register_error analysis_data find_canonical_duplicate
(TypeErr.Inconsistent_subclass (Inconsistent_subclass
{ inheritance_violation { inheritance_violation
; violation_type= ; violation_type=
InheritanceRule.ReportableViolation.InconsistentParam 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= Procdesc.get_proc_name overridden_proc_desc })
None ~nullsafe_mode loc overridden_proc_desc ) None ~nullsafe_mode loc )
let check_inheritance_rule_for_params find_canonical_duplicate tenv loc ~nullsafe_mode let check_inheritance_rule_for_params analysis_data find_canonical_duplicate loc ~nullsafe_mode
~base_proc_name ~overridden_proc_name ~overridden_proc_desc ~base_signature ~base_proc_name ~base_signature ~overridden_signature =
~overridden_signature =
let base_params = base_signature.AnnotatedSignature.params in let base_params = base_signature.AnnotatedSignature.params in
let overridden_params = overridden_signature.AnnotatedSignature.params in let overridden_params = overridden_signature.AnnotatedSignature.params in
let zipped_params = List.zip base_params overridden_params in let zipped_params = List.zip base_params overridden_params in
@ -506,8 +508,8 @@ let check_inheritance_rule_for_params find_canonical_duplicate tenv loc ~nullsaf
{ mangled= overridden_param_name { mangled= overridden_param_name
; param_annotated_type= {nullability= annotated_nullability_overridden} } ) ; param_annotated_type= {nullability= annotated_nullability_overridden} } )
-> ->
check_inheritance_rule_for_param find_canonical_duplicate tenv loc ~nullsafe_mode check_inheritance_rule_for_param analysis_data find_canonical_duplicate loc ~nullsafe_mode
~overridden_param_name ~base_proc_name ~overridden_proc_name ~overridden_proc_desc ~overridden_param_name ~base_proc_name
~param_position:(if should_index_from_zero then index else index + 1) ~param_position:(if should_index_from_zero then index else index + 1)
~base_nullability:(AnnotatedNullability.get_nullability annotated_nullability_base) ~base_nullability:(AnnotatedNullability.get_nullability annotated_nullability_base)
~overridden_nullability: ~overridden_nullability:
@ -518,13 +520,12 @@ let check_inheritance_rule_for_params find_canonical_duplicate tenv loc ~nullsaf
() ()
(* Check both params and return values for complying for co- and contravariance *) (** Check both params and return values for complying for co- and contravariance *)
let check_inheritance_rule_for_signature find_canonical_duplicate tenv loc ~nullsafe_mode let check_inheritance_rule_for_signature analysis_data find_canonical_duplicate loc ~nullsafe_mode
~base_proc_name ~overridden_proc_name ~overridden_proc_desc ~base_signature ~base_proc_name ~base_signature ~overridden_signature =
~overridden_signature =
(* Check params *) (* Check params *)
check_inheritance_rule_for_params find_canonical_duplicate tenv loc ~nullsafe_mode ~base_proc_name check_inheritance_rule_for_params analysis_data find_canonical_duplicate loc ~nullsafe_mode
~overridden_proc_name ~overridden_proc_desc ~base_signature ~overridden_signature ; ~base_proc_name ~base_signature ~overridden_signature ;
(* Check return value *) (* Check return value *)
match base_proc_name with match base_proc_name with
(* TODO model this as unknown nullability and get rid of that check *) (* TODO model this as unknown nullability and get rid of that check *)
@ -538,9 +539,8 @@ let check_inheritance_rule_for_signature find_canonical_duplicate tenv loc ~null
AnnotatedNullability.get_nullability AnnotatedNullability.get_nullability
overridden_signature.AnnotatedSignature.ret.ret_annotated_type.nullability overridden_signature.AnnotatedSignature.ret.ret_annotated_type.nullability
in in
check_inheritance_rule_for_return find_canonical_duplicate tenv loc ~nullsafe_mode check_inheritance_rule_for_return analysis_data find_canonical_duplicate loc ~nullsafe_mode
~base_proc_name ~overridden_proc_name ~overridden_proc_desc ~base_nullability ~base_proc_name ~base_nullability ~overridden_nullability
~overridden_nullability
| _ -> | _ ->
(* the analysis should not report return type inconsistencies with external code *) (* the analysis should not report return type inconsistencies with external code *)
() ()
@ -548,8 +548,8 @@ let check_inheritance_rule_for_signature find_canonical_duplicate tenv loc ~null
(** Checks if the annotations are consistent with the derived classes and with the implemented (** Checks if the annotations are consistent with the derived classes and with the implemented
interfaces *) interfaces *)
let check_overridden_annotations find_canonical_duplicate tenv proc_name proc_desc let check_overridden_annotations ({IntraproceduralAnalysis.tenv; proc_desc; _} as analysis_data)
annotated_signature = find_canonical_duplicate annotated_signature =
let start_node = Procdesc.get_start_node proc_desc in let start_node = Procdesc.get_start_node proc_desc in
let loc = Procdesc.Node.get_loc start_node in let loc = Procdesc.Node.get_loc start_node in
let check_if_base_signature_matches_current base_proc_name = let check_if_base_signature_matches_current base_proc_name =
@ -561,15 +561,16 @@ let check_overridden_annotations find_canonical_duplicate tenv proc_name proc_de
Models.get_modelled_annotated_signature ~is_callee_in_trust_list:false tenv Models.get_modelled_annotated_signature ~is_callee_in_trust_list:false tenv
base_attributes base_attributes
in in
check_inheritance_rule_for_signature check_inheritance_rule_for_signature analysis_data
~nullsafe_mode:annotated_signature.AnnotatedSignature.nullsafe_mode ~nullsafe_mode:annotated_signature.AnnotatedSignature.nullsafe_mode
find_canonical_duplicate tenv loc ~base_proc_name ~overridden_proc_name:proc_name find_canonical_duplicate loc ~base_proc_name ~base_signature
~overridden_proc_desc:proc_desc ~base_signature ~overridden_signature:annotated_signature ~overridden_signature:annotated_signature
| None -> | None ->
(* Could not find the attributes - optimistically skipping the check *) (* Could not find the attributes - optimistically skipping the check *)
(* TODO(T54687014) ensure this is not an issue in practice *) (* TODO(T54687014) ensure this is not an issue in practice *)
() ()
in in
let proc_name = Procdesc.get_proc_name proc_desc in
(* Iterate over all methods the current method overrides and see the current (* Iterate over all methods the current method overrides and see the current
method is compatible with all of them *) method is compatible with all of them *)
PatternMatch.override_iter check_if_base_signature_matches_current tenv proc_name PatternMatch.override_iter check_if_base_signature_matches_current tenv proc_name

@ -9,7 +9,7 @@ open! IStd
(** Check an implicit cast when returning an immutable collection from a method whose type is (** Check an implicit cast when returning an immutable collection from a method whose type is
mutable. *) mutable. *)
let check_immutable_cast tenv curr_pname curr_pdesc typ_expected typ_found_opt loc : unit = let check_immutable_cast analysis_data proc_desc typ_expected typ_found_opt loc : unit =
match typ_found_opt with match typ_found_opt with
| Some typ_found -> ( | Some typ_found -> (
let casts = let casts =
@ -32,16 +32,16 @@ let check_immutable_cast tenv curr_pname curr_pdesc typ_expected typ_found_opt l
Format.asprintf Format.asprintf
"Method %s returns %a but the return type is %a. Make sure that users of this \ "Method %s returns %a but the return type is %a. Make sure that users of this \
method do not try to modify the collection." method do not try to modify the collection."
(Procname.to_simplified_string curr_pname) (Procname.to_simplified_string (Procdesc.get_proc_name proc_desc))
Typ.Name.pp name_given Typ.Name.pp name_expected Typ.Name.pp name_given Typ.Name.pp name_expected
in in
EradicateCheckers.report_error tenv curr_pname curr_pdesc EradicateCheckers.report_error analysis_data IssueType.checkers_immutable_cast loc
IssueType.checkers_immutable_cast loc description ~severity:Exceptions.Warning description ~severity:Exceptions.Warning
| _ -> | _ ->
() ) () )
| None -> | None ->
() ()
let analyze ({IntraproceduralAnalysis.tenv} as analysis_data) = let analyze analysis_data =
Eradicate.analyze_for_immutable_cast_checker (check_immutable_cast tenv) analysis_data Eradicate.analyze_for_immutable_cast_checker (check_immutable_cast analysis_data) analysis_data

@ -104,15 +104,16 @@ end
(* ComplexExpressions *) (* ComplexExpressions *)
type check_return_type = Procname.t -> Procdesc.t -> Typ.t -> Typ.t option -> Location.t -> unit type check_return_type = Procdesc.t -> Typ.t -> Typ.t option -> Location.t -> unit
type find_canonical_duplicate = Procdesc.Node.t -> Procdesc.Node.t type find_canonical_duplicate = Procdesc.Node.t -> Procdesc.Node.t
type checks = {eradicate: bool; check_ret_type: check_return_type list} type checks = {eradicate: bool; check_ret_type: check_return_type list}
(** Typecheck an expression. *) (** Typecheck an expression. *)
let rec typecheck_expr ~nullsafe_mode find_canonical_duplicate visited checks tenv node instr_ref let rec typecheck_expr ({IntraproceduralAnalysis.tenv; _} as analysis_data) ~nullsafe_mode
(curr_pdesc : Procdesc.t) typestate e tr_default loc : TypeState.range = find_canonical_duplicate visited checks node instr_ref typestate e tr_default loc :
TypeState.range =
L.d_with_indent ~name:"typecheck_expr" ~pp_result:TypeState.pp_range (fun () -> L.d_with_indent ~name:"typecheck_expr" ~pp_result:TypeState.pp_range (fun () ->
L.d_printfln "Expr: %a" Exp.pp e ; L.d_printfln "Expr: %a" Exp.pp e ;
match e with match e with
@ -142,13 +143,13 @@ let rec typecheck_expr ~nullsafe_mode find_canonical_duplicate visited checks te
L.d_strln "WARNING: could not lookup Id in typestate: fallback to default" ; L.d_strln "WARNING: could not lookup Id in typestate: fallback to default" ;
tr_default ) tr_default )
| Exp.Exn e1 -> | Exp.Exn e1 ->
typecheck_expr ~nullsafe_mode find_canonical_duplicate visited checks tenv node instr_ref typecheck_expr analysis_data ~nullsafe_mode find_canonical_duplicate visited checks node
curr_pdesc typestate e1 tr_default loc instr_ref typestate e1 tr_default loc
| Exp.Lfield (exp, field_name, typ) -> | Exp.Lfield (exp, field_name, typ) ->
let _, _ = tr_default in let _, _ = tr_default in
let _, inferred_nullability = let _, inferred_nullability =
typecheck_expr ~nullsafe_mode find_canonical_duplicate visited checks tenv node typecheck_expr analysis_data ~nullsafe_mode find_canonical_duplicate visited checks node
instr_ref curr_pdesc typestate exp instr_ref typestate exp
(* TODO(T54687014) optimistic default might be an unsoundness issue - investigate *) (* TODO(T54687014) optimistic default might be an unsoundness issue - investigate *)
(typ, InferredNullability.create TypeOrigin.OptimisticFallback) (typ, InferredNullability.create TypeOrigin.OptimisticFallback)
loc loc
@ -165,23 +166,22 @@ let rec typecheck_expr ~nullsafe_mode find_canonical_duplicate visited checks te
tr_default tr_default
in in
if checks.eradicate then if checks.eradicate then
EradicateChecks.check_object_dereference ~nullsafe_mode tenv find_canonical_duplicate EradicateChecks.check_object_dereference analysis_data ~nullsafe_mode
curr_pdesc node instr_ref exp find_canonical_duplicate node instr_ref exp (AccessToField field_name)
(DereferenceRule.ReportableViolation.AccessToField field_name) inferred_nullability inferred_nullability loc ;
loc ;
tr_new tr_new
| Exp.Lindex (array_exp, index_exp) -> | Exp.Lindex (array_exp, index_exp) ->
let _, inferred_nullability = let _, inferred_nullability =
typecheck_expr ~nullsafe_mode find_canonical_duplicate visited checks tenv node typecheck_expr analysis_data ~nullsafe_mode find_canonical_duplicate visited checks node
instr_ref curr_pdesc typestate array_exp tr_default loc instr_ref typestate array_exp tr_default loc
in in
let index_desc = let index_desc =
match EradicateChecks.explain_expr tenv node index_exp with Some s -> s | None -> "?" match EradicateChecks.explain_expr tenv node index_exp with Some s -> s | None -> "?"
in in
if checks.eradicate then if checks.eradicate then
EradicateChecks.check_object_dereference ~nullsafe_mode tenv find_canonical_duplicate EradicateChecks.check_object_dereference analysis_data ~nullsafe_mode
curr_pdesc node instr_ref array_exp find_canonical_duplicate node instr_ref array_exp
(DereferenceRule.ReportableViolation.AccessByIndex {index_desc}) (AccessByIndex {index_desc})
inferred_nullability loc ; inferred_nullability loc ;
let typ, _ = tr_default in let typ, _ = tr_default in
(typ, InferredNullability.create TypeOrigin.ArrayAccess) (typ, InferredNullability.create TypeOrigin.ArrayAccess)
@ -460,19 +460,19 @@ let pvar_apply instr_ref idenv tenv curr_pname curr_annotated_signature loc hand
(* typecheck_expr with fewer parameters, using a common template for typestate range *) (* typecheck_expr with fewer parameters, using a common template for typestate range *)
let typecheck_expr_simple ~nullsafe_mode find_canonical_duplicate curr_pdesc calls_this checks tenv let typecheck_expr_simple analysis_data ~nullsafe_mode find_canonical_duplicate calls_this checks
node instr_ref typestate1 exp1 typ1 origin1 loc1 = node instr_ref typestate1 exp1 typ1 origin1 loc1 =
typecheck_expr ~nullsafe_mode find_canonical_duplicate calls_this checks tenv node instr_ref typecheck_expr analysis_data ~nullsafe_mode find_canonical_duplicate calls_this checks node
curr_pdesc typestate1 exp1 instr_ref typestate1 exp1
(typ1, InferredNullability.create origin1) (typ1, InferredNullability.create origin1)
loc1 loc1
(* check if there are errors in exp1 *) (* check if there are errors in exp1 *)
let typecheck_expr_for_errors ~nullsafe_mode find_canonical_duplicate curr_pdesc calls_this checks let typecheck_expr_for_errors analysis_data ~nullsafe_mode find_canonical_duplicate calls_this
tenv node instr_ref typestate1 exp1 loc1 : unit = checks node instr_ref typestate1 exp1 loc1 : unit =
ignore ignore
(typecheck_expr_simple ~nullsafe_mode find_canonical_duplicate curr_pdesc calls_this checks tenv (typecheck_expr_simple analysis_data ~nullsafe_mode find_canonical_duplicate calls_this checks
node instr_ref typestate1 exp1 Typ.void TypeOrigin.OptimisticFallback loc1) node instr_ref typestate1 exp1 Typ.void TypeOrigin.OptimisticFallback loc1)
@ -498,9 +498,10 @@ let java_get_vararg_values node pvar idenv =
(* Handle Preconditions.checkNotNull. *) (* Handle Preconditions.checkNotNull. *)
let do_preconditions_check_not_null instr_ref tenv find_canonical_duplicate node loc curr_pdesc let do_preconditions_check_not_null
curr_pname curr_annotated_signature checks call_params idenv parameter_num ~is_vararg typestate' ({IntraproceduralAnalysis.proc_desc= curr_pdesc; tenv; _} as analysis_data) instr_ref
= find_canonical_duplicate node loc curr_annotated_signature checks call_params idenv
parameter_num ~is_vararg typestate' =
(* clear the nullable flag of the first parameter of the procedure *) (* clear the nullable flag of the first parameter of the procedure *)
let clear_nullable_flag ~nullsafe_mode typestate'' pvar = let clear_nullable_flag ~nullsafe_mode typestate'' pvar =
(* remove the nullable flag for the given pvar *) (* remove the nullable flag for the given pvar *)
@ -514,12 +515,12 @@ let do_preconditions_check_not_null instr_ref tenv find_canonical_duplicate node
in in
( if checks.eradicate && should_report then ( if checks.eradicate && should_report then
let cond = Exp.BinOp (Binop.Ne, Exp.Lvar pvar, Exp.null) in let cond = Exp.BinOp (Binop.Ne, Exp.Lvar pvar, Exp.null) in
EradicateChecks.register_error tenv find_canonical_duplicate TypeErr.register_error analysis_data find_canonical_duplicate
(TypeErr.Condition_redundant (Condition_redundant
{ is_always_true= true { is_always_true= true
; condition_descr= EradicateChecks.explain_expr tenv node cond ; condition_descr= EradicateChecks.explain_expr tenv node cond
; nonnull_origin= InferredNullability.get_origin nullability }) ; nonnull_origin= InferredNullability.get_origin nullability })
(Some instr_ref) ~nullsafe_mode loc curr_pdesc ) ; (Some instr_ref) ~nullsafe_mode loc ) ;
let previous_origin = InferredNullability.get_origin nullability in let previous_origin = InferredNullability.get_origin nullability in
let new_origin = TypeOrigin.InferredNonnull {previous_origin} in let new_origin = TypeOrigin.InferredNonnull {previous_origin} in
TypeState.add pvar TypeState.add pvar
@ -539,6 +540,7 @@ let do_preconditions_check_not_null instr_ref tenv find_canonical_duplicate node
in in
match find_parameter parameter_num call_params with match find_parameter parameter_num call_params with
| Some (pvar, _) -> | Some (pvar, _) ->
let curr_pname = Procdesc.get_proc_name curr_pdesc in
if is_vararg then if is_vararg then
let do_vararg_value e ts = let do_vararg_value e ts =
match Idenv.expand_expr idenv e with match Idenv.expand_expr idenv e with
@ -626,8 +628,9 @@ let do_preconditions_check_state instr_ref idenv tenv curr_pname curr_annotated_
(* Handle m.put(k,v) as assignment pvar = v for the pvar associated to m.get(k) *) (* Handle m.put(k,v) as assignment pvar = v for the pvar associated to m.get(k) *)
let do_map_put call_params callee_pname tenv loc node curr_pname curr_pdesc calls_this checks let do_map_put ({IntraproceduralAnalysis.proc_desc= curr_pdesc; tenv; _} as analysis_data)
instr_ref ~nullsafe_mode find_canonical_duplicate typestate' = call_params callee_pname loc node calls_this checks instr_ref ~nullsafe_mode
find_canonical_duplicate typestate' =
(* Get the proc name for map.get() from map.put() *) (* Get the proc name for map.get() from map.put() *)
let pname_get_from_pname_put pname_put = let pname_get_from_pname_put pname_put =
let object_t = JavaSplitName.java_lang_object in let object_t = JavaSplitName.java_lang_object in
@ -656,11 +659,12 @@ let do_map_put call_params callee_pname tenv loc node curr_pname curr_pdesc call
ComplexExpressions.exp_to_string_map_dexp tenv convert_dexp_key_to_dexp_get node exp_key ComplexExpressions.exp_to_string_map_dexp tenv convert_dexp_key_to_dexp_get node exp_key
with with
| Some map_get_str -> | Some map_get_str ->
let curr_pname = Procdesc.get_proc_name curr_pdesc in
let pvar_map_get = Pvar.mk (Mangled.from_string map_get_str) curr_pname in let pvar_map_get = Pvar.mk (Mangled.from_string map_get_str) curr_pname in
TypeState.add pvar_map_get TypeState.add pvar_map_get
(typecheck_expr_simple ~nullsafe_mode find_canonical_duplicate curr_pdesc calls_this (typecheck_expr_simple analysis_data ~nullsafe_mode find_canonical_duplicate calls_this
checks tenv node instr_ref typestate' exp_value typ_value checks node instr_ref typestate' exp_value typ_value TypeOrigin.OptimisticFallback
TypeOrigin.OptimisticFallback loc) loc)
typestate' ~descr:"do_map_put" typestate' ~descr:"do_map_put"
| None -> | None ->
typestate' ) typestate' )
@ -719,9 +723,11 @@ let normalize_cond_for_sil_prune idenv ~node cond =
normalize_cond_for_sil_prune_rec idenv ~node ~original_node:node cond normalize_cond_for_sil_prune_rec idenv ~node ~original_node:node cond
let rec check_condition_for_sil_prune tenv idenv calls_this find_canonical_duplicate loc curr_pname let rec check_condition_for_sil_prune
curr_pdesc curr_annotated_signature linereader typestate checks true_branch instr_ref ({IntraproceduralAnalysis.proc_desc= curr_pdesc; tenv; _} as analysis_data) idenv calls_this
~nullsafe_mode ~original_node ~node c : TypeState.t = find_canonical_duplicate loc curr_annotated_signature linereader typestate checks true_branch
instr_ref ~nullsafe_mode ~original_node ~node c : TypeState.t =
let curr_pname = Procdesc.get_proc_name curr_pdesc in
(* check if the expression is coming from a call, and return the arguments *) (* check if the expression is coming from a call, and return the arguments *)
let extract_arguments_from_call filter_callee expr = let extract_arguments_from_call filter_callee expr =
match expr with match expr with
@ -829,14 +835,14 @@ let rec check_condition_for_sil_prune tenv idenv calls_this find_canonical_dupli
This means the corresponding condition (initiated this PRUNE branch) was redudant. This means the corresponding condition (initiated this PRUNE branch) was redudant.
*) *)
let typ, inferred_nullability = let typ, inferred_nullability =
typecheck_expr_simple ~nullsafe_mode find_canonical_duplicate curr_pdesc calls_this checks typecheck_expr_simple analysis_data ~nullsafe_mode find_canonical_duplicate calls_this
tenv original_node instr_ref typestate pvar_expr Typ.void TypeOrigin.OptimisticFallback checks original_node instr_ref typestate pvar_expr Typ.void TypeOrigin.OptimisticFallback
loc loc
in in
if checks.eradicate then if checks.eradicate then
EradicateChecks.check_condition_for_redundancy ~is_always_true:true_branch tenv EradicateChecks.check_condition_for_redundancy analysis_data ~is_always_true:true_branch
find_canonical_duplicate curr_pdesc original_node pvar_expr typ inferred_nullability find_canonical_duplicate original_node pvar_expr typ inferred_nullability ~nullsafe_mode
~nullsafe_mode idenv linereader loc instr_ref ) ; idenv linereader loc instr_ref ) ;
set_nonnull pvar_expr typestate ~descr set_nonnull pvar_expr typestate ~descr
in in
(* Assuming [expr] is a boolean, this is the branch where, according to PRUNE semantics, (* Assuming [expr] is a boolean, this is the branch where, according to PRUNE semantics,
@ -910,14 +916,14 @@ let rec check_condition_for_sil_prune tenv idenv calls_this find_canonical_dupli
*) *)
typestate |> handle_boolean_equal_true expr |> handle_object_not_equal_null expr typestate |> handle_boolean_equal_true expr |> handle_object_not_equal_null expr
| Exp.UnOp (Unop.LNot, Exp.BinOp (Binop.Eq, e1, e2), _) -> | Exp.UnOp (Unop.LNot, Exp.BinOp (Binop.Eq, e1, e2), _) ->
check_condition_for_sil_prune tenv idenv calls_this find_canonical_duplicate loc curr_pname check_condition_for_sil_prune analysis_data idenv calls_this find_canonical_duplicate loc
curr_pdesc curr_annotated_signature linereader typestate checks true_branch instr_ref curr_annotated_signature linereader typestate checks true_branch instr_ref ~nullsafe_mode
~nullsafe_mode ~original_node ~node ~original_node ~node
(Exp.BinOp (Binop.Ne, e1, e2)) (Exp.BinOp (Binop.Ne, e1, e2))
| Exp.UnOp (Unop.LNot, Exp.BinOp (Binop.Ne, e1, e2), _) -> | Exp.UnOp (Unop.LNot, Exp.BinOp (Binop.Ne, e1, e2), _) ->
check_condition_for_sil_prune tenv idenv calls_this find_canonical_duplicate loc curr_pname check_condition_for_sil_prune analysis_data idenv calls_this find_canonical_duplicate loc
curr_pdesc curr_annotated_signature linereader typestate checks true_branch instr_ref curr_annotated_signature linereader typestate checks true_branch instr_ref ~nullsafe_mode
~nullsafe_mode ~original_node ~node ~original_node ~node
(Exp.BinOp (Binop.Eq, e1, e2)) (Exp.BinOp (Binop.Eq, e1, e2))
| _ -> | _ ->
(* TODO(T54687014): silenced warning may be an unsoundeness issue; investigate *) (* TODO(T54687014): silenced warning may be an unsoundeness issue; investigate *)
@ -949,15 +955,17 @@ let clarify_ret_by_propagates_nullable ret (resolved_params : EradicateChecks.re
(upper_bound_nullability, ret_typ) (upper_bound_nullability, ret_typ)
let calc_typestate_after_call find_canonical_duplicate calls_this checks tenv idenv instr_ref let calc_typestate_after_call
signature_params cflags call_params ~is_anonymous_inner_class_constructor ({IntraproceduralAnalysis.proc_desc= curr_pdesc; tenv; _} as analysis_data)
~callee_annotated_signature ~callee_attributes ~callee_pname ~callee_pname_java ~curr_pname find_canonical_duplicate calls_this checks idenv instr_ref signature_params cflags call_params
~curr_pdesc ~curr_annotated_signature ~nullsafe_mode ~typestate ~typestate1 loc node = ~is_anonymous_inner_class_constructor ~callee_annotated_signature ~callee_attributes
~callee_pname ~callee_pname_java ~curr_annotated_signature ~nullsafe_mode ~typestate ~typestate1
loc node =
let resolve_param i (formal_param, actual_param) = let resolve_param i (formal_param, actual_param) =
let (orig_e2, e2), t2 = actual_param in let (orig_e2, e2), t2 = actual_param in
let _, inferred_nullability_actual = let _, inferred_nullability_actual =
typecheck_expr ~nullsafe_mode find_canonical_duplicate calls_this checks tenv node instr_ref typecheck_expr analysis_data ~nullsafe_mode find_canonical_duplicate calls_this checks node
curr_pdesc typestate e2 instr_ref typestate e2
(* TODO(T54687014) optimistic default might be an unsoundness issue - investigate *) (* TODO(T54687014) optimistic default might be an unsoundness issue - investigate *)
(t2, InferredNullability.create TypeOrigin.OptimisticFallback) (t2, InferredNullability.create TypeOrigin.OptimisticFallback)
loc loc
@ -1011,34 +1019,35 @@ let calc_typestate_after_call find_canonical_duplicate calls_this checks tenv id
let typestate_after_call = let typestate_after_call =
if not is_anonymous_inner_class_constructor then ( if not is_anonymous_inner_class_constructor then (
if cflags.CallFlags.cf_virtual && checks.eradicate then if cflags.CallFlags.cf_virtual && checks.eradicate then
EradicateChecks.check_call_receiver ~nullsafe_mode tenv find_canonical_duplicate curr_pdesc EradicateChecks.check_call_receiver analysis_data ~nullsafe_mode find_canonical_duplicate
node typestate1 call_params callee_pname instr_ref loc node typestate1 call_params callee_pname instr_ref loc
(typecheck_expr ~nullsafe_mode find_canonical_duplicate calls_this checks) ; (typecheck_expr analysis_data ~nullsafe_mode find_canonical_duplicate calls_this checks) ;
if checks.eradicate then if checks.eradicate then
EradicateChecks.check_call_parameters ~nullsafe_mode ~callee_annotated_signature tenv EradicateChecks.check_call_parameters analysis_data ~nullsafe_mode
find_canonical_duplicate curr_pdesc node callee_attributes resolved_params loc instr_ref ; ~callee_annotated_signature find_canonical_duplicate node callee_attributes
resolved_params loc instr_ref ;
if Models.is_check_not_null callee_pname then if Models.is_check_not_null callee_pname then
match Models.get_check_not_null_parameter callee_pname with match Models.get_check_not_null_parameter callee_pname with
| Some index -> | Some index ->
do_preconditions_check_not_null instr_ref tenv find_canonical_duplicate node loc do_preconditions_check_not_null analysis_data instr_ref find_canonical_duplicate node
curr_pdesc curr_pname curr_annotated_signature checks call_params idenv index loc curr_annotated_signature checks call_params idenv index ~is_vararg:false
~is_vararg:false typestate1 typestate1
| None when Procname.Java.is_vararg callee_pname_java -> | None when Procname.Java.is_vararg callee_pname_java ->
let last_parameter = List.length call_params in let last_parameter = List.length call_params in
do_preconditions_check_not_null instr_ref tenv find_canonical_duplicate node loc do_preconditions_check_not_null analysis_data instr_ref find_canonical_duplicate node
curr_pdesc curr_pname curr_annotated_signature checks call_params idenv last_parameter loc curr_annotated_signature checks call_params idenv last_parameter ~is_vararg:true
~is_vararg:true typestate1 typestate1
| None -> | None ->
(* assume the first parameter is checked for null *) (* assume the first parameter is checked for null *)
do_preconditions_check_not_null instr_ref tenv find_canonical_duplicate node loc do_preconditions_check_not_null analysis_data instr_ref find_canonical_duplicate node
curr_pdesc curr_pname curr_annotated_signature checks call_params idenv 1 loc curr_annotated_signature checks call_params idenv 1 ~is_vararg:false typestate1
~is_vararg:false typestate1
else if Models.is_check_state callee_pname || Models.is_check_argument callee_pname then else if Models.is_check_state callee_pname || Models.is_check_argument callee_pname then
let curr_pname = Procdesc.get_proc_name curr_pdesc in
do_preconditions_check_state instr_ref idenv tenv curr_pname curr_annotated_signature do_preconditions_check_state instr_ref idenv tenv curr_pname curr_annotated_signature
call_params loc node typestate1 call_params loc node typestate1
else if Models.is_mapPut callee_pname then else if Models.is_mapPut callee_pname then
do_map_put call_params callee_pname tenv loc node curr_pname curr_pdesc calls_this checks do_map_put analysis_data call_params callee_pname loc node calls_this checks instr_ref
instr_ref ~nullsafe_mode find_canonical_duplicate typestate1 ~nullsafe_mode find_canonical_duplicate typestate1
else typestate1 ) else typestate1 )
else typestate1 else typestate1
in in
@ -1046,9 +1055,10 @@ let calc_typestate_after_call find_canonical_duplicate calls_this checks tenv id
(* SIL instruction in form of [ret = fun(args);] where fun is a non-builtin Java function *) (* SIL instruction in form of [ret = fun(args);] where fun is a non-builtin Java function *)
let typecheck_sil_call_function find_canonical_duplicate checks tenv instr_ref typestate idenv let typecheck_sil_call_function
~callee_pname ~curr_pname curr_pdesc curr_annotated_signature calls_this ~nullsafe_mode ({IntraproceduralAnalysis.proc_desc= curr_pdesc; tenv; _} as analysis_data)
ret_id_typ etl_ loc callee_pname_java cflags node = find_canonical_duplicate checks instr_ref typestate idenv ~callee_pname curr_annotated_signature
calls_this ~nullsafe_mode ret_id_typ etl_ loc callee_pname_java cflags node =
L.d_with_indent ~name:"typecheck_sil_call_function" (fun () -> L.d_with_indent ~name:"typecheck_sil_call_function" (fun () ->
let callee_attributes = let callee_attributes =
match PatternMatch.lookup_attributes tenv callee_pname with match PatternMatch.lookup_attributes tenv callee_pname with
@ -1074,11 +1084,12 @@ let typecheck_sil_call_function find_canonical_duplicate checks tenv instr_ref t
in in
proc_attributes proc_attributes
in in
let curr_pname = Procdesc.get_proc_name curr_pdesc in
let etl = drop_unchecked_params calls_this curr_pname callee_attributes etl_ in let etl = drop_unchecked_params calls_this curr_pname callee_attributes etl_ in
let call_params, typestate1 = let call_params, typestate1 =
let handle_et (e1, t1) (etl1, typestate1) = let handle_et (e1, t1) (etl1, typestate1) =
typecheck_expr_for_errors ~nullsafe_mode find_canonical_duplicate curr_pdesc calls_this typecheck_expr_for_errors analysis_data ~nullsafe_mode find_canonical_duplicate calls_this
checks tenv node instr_ref typestate e1 loc ; checks node instr_ref typestate e1 loc ;
let e2 = let e2 =
convert_complex_exp_to_pvar tenv idenv curr_pname curr_annotated_signature convert_complex_exp_to_pvar tenv idenv curr_pname curr_annotated_signature
~is_assignment:false ~node ~original_node:node e1 typestate1 loc ~is_assignment:false ~node ~original_node:node e1 typestate1 loc
@ -1124,19 +1135,19 @@ let typecheck_sil_call_function find_canonical_duplicate checks tenv instr_ref t
TypeState.add_id id (mk_return_range ()) typestate' ~descr:"typecheck_sil_call_function" TypeState.add_id id (mk_return_range ()) typestate' ~descr:"typecheck_sil_call_function"
in in
let typestate_after_call, finally_resolved_ret = let typestate_after_call, finally_resolved_ret =
calc_typestate_after_call find_canonical_duplicate calls_this checks tenv idenv instr_ref calc_typestate_after_call analysis_data find_canonical_duplicate calls_this checks idenv
signature_params cflags call_params ~is_anonymous_inner_class_constructor instr_ref signature_params cflags call_params ~is_anonymous_inner_class_constructor
~callee_annotated_signature ~callee_attributes ~callee_pname ~callee_pname_java ~callee_annotated_signature ~callee_attributes ~callee_pname ~callee_pname_java
~curr_pname ~curr_pdesc ~curr_annotated_signature ~nullsafe_mode ~typestate ~typestate1 ~curr_annotated_signature ~nullsafe_mode ~typestate ~typestate1 loc node
loc node
in in
do_return finally_resolved_ret typestate_after_call ) do_return finally_resolved_ret typestate_after_call )
(** Typecheck an instruction. *) (** Typecheck an instruction. *)
let typecheck_instr tenv calls_this checks (node : Procdesc.Node.t) idenv curr_pname curr_pdesc let typecheck_instr ({IntraproceduralAnalysis.proc_desc= curr_pdesc; tenv; _} as analysis_data)
find_canonical_duplicate (curr_annotated_signature : AnnotatedSignature.t) instr_ref linereader calls_this checks (node : Procdesc.Node.t) idenv find_canonical_duplicate
typestate instr = (curr_annotated_signature : AnnotatedSignature.t) instr_ref linereader typestate instr =
let curr_pname = Procdesc.get_proc_name curr_pdesc in
let is_return pvar = let is_return pvar =
let ret_pvar = Procdesc.get_ret_var curr_pdesc in let ret_pvar = Procdesc.get_ret_var curr_pdesc in
Pvar.equal pvar ret_pvar Pvar.equal pvar ret_pvar
@ -1153,22 +1164,22 @@ let typecheck_instr tenv calls_this checks (node : Procdesc.Node.t) idenv curr_p
| Sil.Metadata (Abstract _ | Nullify _ | Skip | VariableLifetimeBegins _) -> | Sil.Metadata (Abstract _ | Nullify _ | Skip | VariableLifetimeBegins _) ->
typestate typestate
| Sil.Load {id; e; typ; loc} -> | Sil.Load {id; e; typ; loc} ->
typecheck_expr_for_errors ~nullsafe_mode find_canonical_duplicate curr_pdesc calls_this checks typecheck_expr_for_errors analysis_data ~nullsafe_mode find_canonical_duplicate calls_this
tenv node instr_ref typestate e loc ; checks node instr_ref typestate e loc ;
let e', typestate' = let e', typestate' =
convert_complex_exp_to_pvar_and_register_field_in_typestate tenv idenv curr_pname convert_complex_exp_to_pvar_and_register_field_in_typestate tenv idenv curr_pname
curr_annotated_signature ~node ~original_node:node ~is_assignment:false e typestate loc curr_annotated_signature ~node ~original_node:node ~is_assignment:false e typestate loc
in in
TypeState.add_id id TypeState.add_id id
(typecheck_expr_simple ~nullsafe_mode find_canonical_duplicate curr_pdesc calls_this checks (typecheck_expr_simple analysis_data ~nullsafe_mode find_canonical_duplicate calls_this
tenv node instr_ref typestate' e' typ TypeOrigin.OptimisticFallback loc) checks node instr_ref typestate' e' typ TypeOrigin.OptimisticFallback loc)
~descr:"Sil.Load" typestate' ~descr:"Sil.Load" typestate'
| Sil.Store {e1= Exp.Lvar pvar; e2= Exp.Exn _} when is_return pvar -> | Sil.Store {e1= Exp.Lvar pvar; e2= Exp.Exn _} when is_return pvar ->
(* skip assignment to return variable where it is an artifact of a throw instruction *) (* skip assignment to return variable where it is an artifact of a throw instruction *)
typestate typestate
| Sil.Store {e1; typ; e2; loc} -> | Sil.Store {e1; typ; e2; loc} ->
typecheck_expr_for_errors ~nullsafe_mode find_canonical_duplicate curr_pdesc calls_this checks typecheck_expr_for_errors analysis_data ~nullsafe_mode find_canonical_duplicate calls_this
tenv node instr_ref typestate e1 loc ; checks node instr_ref typestate e1 loc ;
let e1' = let e1' =
convert_complex_exp_to_pvar tenv idenv curr_pname curr_annotated_signature ~node convert_complex_exp_to_pvar tenv idenv curr_pname curr_annotated_signature ~node
~original_node:node ~is_assignment:true e1 typestate loc ~original_node:node ~is_assignment:true e1 typestate loc
@ -1179,10 +1190,11 @@ let typecheck_instr tenv calls_this checks (node : Procdesc.Node.t) idenv curr_p
match AnnotatedField.get tenv field_name field_class_type with match AnnotatedField.get tenv field_name field_class_type with
| Some annotated_field -> | Some annotated_field ->
if checks.eradicate then if checks.eradicate then
EradicateChecks.check_field_assignment ~nullsafe_mode tenv find_canonical_duplicate EradicateChecks.check_field_assignment analysis_data ~nullsafe_mode
curr_pdesc node instr_ref typestate ~expr_rhs:e2 ~field_type:typ loc field_name find_canonical_duplicate node instr_ref typestate ~expr_rhs:e2 ~field_type:typ loc
annotated_field field_name annotated_field
(typecheck_expr ~nullsafe_mode find_canonical_duplicate calls_this checks tenv) (typecheck_expr analysis_data ~nullsafe_mode find_canonical_duplicate calls_this
checks)
| None -> | None ->
L.d_strln "WARNING: could not fetch field declaration; skipping assignment check" ) L.d_strln "WARNING: could not fetch field declaration; skipping assignment check" )
| _ -> | _ ->
@ -1192,8 +1204,9 @@ let typecheck_instr tenv calls_this checks (node : Procdesc.Node.t) idenv curr_p
match e1' with match e1' with
| Exp.Lvar pvar -> | Exp.Lvar pvar ->
TypeState.add pvar TypeState.add pvar
(typecheck_expr_simple ~nullsafe_mode find_canonical_duplicate curr_pdesc calls_this (typecheck_expr_simple analysis_data ~nullsafe_mode find_canonical_duplicate
checks tenv node instr_ref typestate e2 typ TypeOrigin.OptimisticFallback loc) calls_this checks node instr_ref typestate e2 typ TypeOrigin.OptimisticFallback
loc)
typestate ~descr:"Sil.Store: Exp.Lvar case" typestate ~descr:"Sil.Store: Exp.Lvar case"
| Exp.Lfield _ -> | Exp.Lfield _ ->
typestate typestate
@ -1211,31 +1224,31 @@ let typecheck_instr tenv calls_this checks (node : Procdesc.Node.t) idenv curr_p
(* Type cast *) (* Type cast *)
| Sil.Call ((id, _), Exp.Const (Const.Cfun pn), (e, typ) :: _, loc, _) | Sil.Call ((id, _), Exp.Const (Const.Cfun pn), (e, typ) :: _, loc, _)
when Procname.equal pn BuiltinDecl.__cast -> when Procname.equal pn BuiltinDecl.__cast ->
typecheck_expr_for_errors ~nullsafe_mode find_canonical_duplicate curr_pdesc calls_this checks typecheck_expr_for_errors analysis_data ~nullsafe_mode find_canonical_duplicate calls_this
tenv node instr_ref typestate e loc ; checks node instr_ref typestate e loc ;
let e' = let e' =
convert_complex_exp_to_pvar tenv idenv curr_pname curr_annotated_signature convert_complex_exp_to_pvar tenv idenv curr_pname curr_annotated_signature
~is_assignment:false ~node ~original_node:node e typestate loc ~is_assignment:false ~node ~original_node:node e typestate loc
in in
(* cast copies the type of the first argument *) (* cast copies the type of the first argument *)
TypeState.add_id id TypeState.add_id id
(typecheck_expr_simple ~nullsafe_mode find_canonical_duplicate curr_pdesc calls_this checks (typecheck_expr_simple analysis_data ~nullsafe_mode find_canonical_duplicate calls_this
tenv node instr_ref typestate e' typ TypeOrigin.OptimisticFallback loc) checks node instr_ref typestate e' typ TypeOrigin.OptimisticFallback loc)
typestate ~descr:"type cast" typestate ~descr:"type cast"
(* myarray.length *) (* myarray.length *)
| Sil.Call ((id, _), Exp.Const (Const.Cfun pn), [(array_exp, t)], loc, _) | Sil.Call ((id, _), Exp.Const (Const.Cfun pn), [(array_exp, t)], loc, _)
when Procname.equal pn BuiltinDecl.__get_array_length -> when Procname.equal pn BuiltinDecl.__get_array_length ->
let _, ta = let _, ta =
typecheck_expr ~nullsafe_mode find_canonical_duplicate calls_this checks tenv node instr_ref typecheck_expr analysis_data ~nullsafe_mode find_canonical_duplicate calls_this checks node
curr_pdesc typestate array_exp instr_ref typestate array_exp
(* TODO(T54687014) optimistic default might be an unsoundness issue - investigate *) (* TODO(T54687014) optimistic default might be an unsoundness issue - investigate *)
(t, InferredNullability.create TypeOrigin.OptimisticFallback) (t, InferredNullability.create TypeOrigin.OptimisticFallback)
loc loc
in in
if checks.eradicate then if checks.eradicate then
EradicateChecks.check_object_dereference ~nullsafe_mode tenv find_canonical_duplicate EradicateChecks.check_object_dereference analysis_data ~nullsafe_mode
curr_pdesc node instr_ref array_exp DereferenceRule.ReportableViolation.ArrayLengthAccess find_canonical_duplicate node instr_ref array_exp
ta loc ; DereferenceRule.ReportableViolation.ArrayLengthAccess ta loc ;
TypeState.add_id id TypeState.add_id id
(Typ.mk (Tint Typ.IInt), InferredNullability.create TypeOrigin.ArrayLengthResult) (Typ.mk (Tint Typ.IInt), InferredNullability.create TypeOrigin.ArrayLengthResult)
typestate ~descr:"array.length" typestate ~descr:"array.length"
@ -1249,9 +1262,9 @@ let typecheck_instr tenv calls_this checks (node : Procdesc.Node.t) idenv curr_p
, etl_ , etl_
, loc , loc
, cflags ) -> , cflags ) ->
typecheck_sil_call_function find_canonical_duplicate checks tenv instr_ref typestate idenv typecheck_sil_call_function analysis_data find_canonical_duplicate checks instr_ref typestate
~callee_pname ~curr_pname curr_pdesc curr_annotated_signature calls_this ~nullsafe_mode idenv ~callee_pname curr_annotated_signature calls_this ~nullsafe_mode ret_id_typ etl_ loc
ret_id_typ etl_ loc callee_pname_java cflags node callee_pname_java cflags node
(* Calls instruction that is not a function call *) (* Calls instruction that is not a function call *)
| Sil.Call _ -> | Sil.Call _ ->
(* This is something weird, we don't normally expect this type of instruction (* This is something weird, we don't normally expect this type of instruction
@ -1261,9 +1274,9 @@ let typecheck_instr tenv calls_this checks (node : Procdesc.Node.t) idenv curr_p
typestate typestate
| Sil.Prune (cond, loc, true_branch, _) -> | Sil.Prune (cond, loc, true_branch, _) ->
let node', normalized_cond = normalize_cond_for_sil_prune idenv ~node cond in let node', normalized_cond = normalize_cond_for_sil_prune idenv ~node cond in
check_condition_for_sil_prune tenv idenv calls_this find_canonical_duplicate loc curr_pname check_condition_for_sil_prune analysis_data idenv calls_this find_canonical_duplicate loc
curr_pdesc curr_annotated_signature linereader typestate checks true_branch instr_ref curr_annotated_signature linereader typestate checks true_branch instr_ref ~nullsafe_mode
~nullsafe_mode ~node:node' ~original_node:node normalized_cond ~node:node' ~original_node:node normalized_cond
let can_instrunction_throw tenv node instr = let can_instrunction_throw tenv node instr =
@ -1298,8 +1311,8 @@ let is_noreturn_instruction = function
(** Typecheck the instructions in a cfg node. *) (** Typecheck the instructions in a cfg node. *)
let typecheck_node tenv calls_this checks idenv curr_pname curr_pdesc find_canonical_duplicate let typecheck_node ({IntraproceduralAnalysis.tenv; _} as analysis_data) calls_this checks idenv
annotated_signature typestate node linereader = find_canonical_duplicate annotated_signature typestate node linereader =
if Procdesc.Node.equal_nodekind (Procdesc.Node.get_kind node) Procdesc.Node.exn_sink_kind then if Procdesc.Node.equal_nodekind (Procdesc.Node.get_kind node) Procdesc.Node.exn_sink_kind then
{normal_flow_typestate= None; exception_flow_typestates= []} {normal_flow_typestate= None; exception_flow_typestates= []}
else else
@ -1324,9 +1337,8 @@ let typecheck_node tenv calls_this checks idenv curr_pname curr_pdesc find_canon
TypeErr.InstrRef.gen instr_ref_gen TypeErr.InstrRef.gen instr_ref_gen
in in
let normal_flow_typestate = let normal_flow_typestate =
typecheck_instr tenv calls_this checks node idenv curr_pname curr_pdesc typecheck_instr analysis_data calls_this checks node idenv find_canonical_duplicate
find_canonical_duplicate annotated_signature instr_ref linereader annotated_signature instr_ref linereader normal_flow_typestate_prev instr
normal_flow_typestate_prev instr
in in
if Config.write_html then if Config.write_html then
L.d_printfln "New state: @\n%a@\n" TypeState.pp normal_flow_typestate ; L.d_printfln "New state: @\n%a@\n" TypeState.pp normal_flow_typestate ;

@ -9,7 +9,7 @@ open! IStd
(** Module type for the type checking functions. *) (** Module type for the type checking functions. *)
type check_return_type = Procname.t -> Procdesc.t -> Typ.t -> Typ.t option -> Location.t -> unit type check_return_type = Procdesc.t -> Typ.t -> Typ.t option -> Location.t -> unit
type find_canonical_duplicate = Procdesc.Node.t -> Procdesc.Node.t type find_canonical_duplicate = Procdesc.Node.t -> Procdesc.Node.t
@ -26,12 +26,10 @@ type typecheck_result =
together) will be passed to all "exception output" nodes of the current node. *) } together) will be passed to all "exception output" nodes of the current node. *) }
val typecheck_node : val typecheck_node :
Tenv.t IntraproceduralAnalysis.t
-> bool ref -> bool ref
-> checks -> checks
-> Idenv.t -> Idenv.t
-> Procname.t
-> Procdesc.t
-> find_canonical_duplicate -> find_canonical_duplicate
-> AnnotatedSignature.t -> AnnotatedSignature.t
-> TypeState.t -> TypeState.t

@ -169,17 +169,6 @@ let add_err find_canonical_duplicate err_instance instr_ref_opt loc =
not is_forall not is_forall
type st_report_error =
Procname.t
-> Procdesc.t
-> IssueType.t
-> Location.t
-> ?field_name:Fieldname.t option
-> ?exception_kind:(IssueType.t -> Localise.error_desc -> exn)
-> severity:Exceptions.severity
-> string
-> unit
(* If an error is related to a particular field, we support suppressing the (* If an error is related to a particular field, we support suppressing the
error via a supress annotation placed near the field declaration *) error via a supress annotation placed near the field declaration *)
let get_field_name_for_error_suppressing = function let get_field_name_for_error_suppressing = function
@ -338,37 +327,33 @@ let is_reportable ~nullsafe_mode err_instance =
get_error_info_fetcher_if_reportable ~nullsafe_mode err_instance |> Option.is_some get_error_info_fetcher_if_reportable ~nullsafe_mode err_instance |> Option.is_some
let report_now_if_reportable (st_report_error : st_report_error) err_instance ~nullsafe_mode loc let report_now_if_reportable analysis_data err_instance ~nullsafe_mode loc =
pdesc =
let pname = Procdesc.get_proc_name pdesc in
get_error_info_if_reportable ~nullsafe_mode err_instance get_error_info_if_reportable ~nullsafe_mode err_instance
|> Option.iter ~f:(fun (err_description, infer_issue_type, updated_location, severity) -> |> Option.iter ~f:(fun (err_description, infer_issue_type, updated_location, severity) ->
Logging.debug Analysis Medium "About to report: %s" err_description ; Logging.debug Analysis Medium "About to report: %s" err_description ;
let field_name = get_field_name_for_error_suppressing err_instance in let field_name = get_field_name_for_error_suppressing err_instance in
let error_location = Option.value updated_location ~default:loc in let error_location = Option.value updated_location ~default:loc in
st_report_error pname pdesc infer_issue_type error_location ~field_name EradicateCheckers.report_error analysis_data infer_issue_type error_location ~field_name
~exception_kind:(fun k d -> Exceptions.Eradicate (k, d)) ~exception_kind:(fun k d -> Exceptions.Eradicate (k, d))
~severity err_description ) ~severity err_description )
(** Register issue (unless exactly the same issue was already registered). If needed, report this (** Register issue (unless exactly the same issue was already registered). If needed, report this
error immediately. *) error immediately. *)
let register_error (st_report_error : st_report_error) find_canonical_duplicate err_instance let register_error analysis_data find_canonical_duplicate err_instance ~nullsafe_mode instr_ref_opt
~nullsafe_mode instr_ref_opt loc pdesc = loc =
let should_report_now = add_err find_canonical_duplicate err_instance instr_ref_opt loc in let should_report_now = add_err find_canonical_duplicate err_instance instr_ref_opt loc in
if should_report_now then if should_report_now then report_now_if_reportable analysis_data err_instance ~nullsafe_mode loc
report_now_if_reportable st_report_error err_instance ~nullsafe_mode loc pdesc
let report_forall_issues_and_reset st_report_error ~nullsafe_mode proc_desc = let report_forall_issues_and_reset analysis_data ~nullsafe_mode =
let iter (err_instance, instr_ref_opt) err_state = let iter (err_instance, instr_ref_opt) err_state =
match (instr_ref_opt, get_forall err_instance) with match (instr_ref_opt, get_forall err_instance) with
| Some instr_ref, is_forall -> | Some instr_ref, is_forall ->
let node = InstrRef.get_node instr_ref in let node = InstrRef.get_node instr_ref in
AnalysisState.set_node node ; AnalysisState.set_node node ;
if is_forall && err_state.always then if is_forall && err_state.always then
report_now_if_reportable st_report_error err_instance err_state.loc ~nullsafe_mode report_now_if_reportable analysis_data err_instance err_state.loc ~nullsafe_mode
proc_desc
| None, _ -> | None, _ ->
() ()
in in

@ -62,31 +62,19 @@ val pp_err_instance : Format.formatter -> err_instance -> unit
val node_reset_forall : Procdesc.Node.t -> unit val node_reset_forall : Procdesc.Node.t -> unit
type st_report_error =
Procname.t
-> Procdesc.t
-> IssueType.t
-> Location.t
-> ?field_name:Fieldname.t option
-> ?exception_kind:(IssueType.t -> Localise.error_desc -> exn)
-> severity:Exceptions.severity
-> string
-> unit
val register_error : val register_error :
st_report_error IntraproceduralAnalysis.t
-> (Procdesc.Node.t -> Procdesc.Node.t) -> (Procdesc.Node.t -> Procdesc.Node.t)
-> err_instance -> err_instance
-> nullsafe_mode:NullsafeMode.t -> nullsafe_mode:NullsafeMode.t
-> InstrRef.t option -> InstrRef.t option
-> Location.t -> Location.t
-> Procdesc.t
-> unit -> unit
(** Register the fact that issue happened. Depending on the error and mode, this error might or (** Register the fact that issue happened. Depending on the error and mode, this error might or
might not be reported to the user. *) might not be reported to the user. *)
val report_forall_issues_and_reset : val report_forall_issues_and_reset :
st_report_error -> nullsafe_mode:NullsafeMode.t -> Procdesc.t -> unit IntraproceduralAnalysis.t -> nullsafe_mode:NullsafeMode.t -> unit
(** Report registered "forall" issues (if needed), and reset the error table *) (** Report registered "forall" issues (if needed), and reset the error table *)
val is_reportable : nullsafe_mode:NullsafeMode.t -> err_instance -> bool val is_reportable : nullsafe_mode:NullsafeMode.t -> err_instance -> bool

Loading…
Cancel
Save