nullsafe/dune

Summary:
- move a few files from checkers/ so that nullsafe can depend on them
- nullsafe depends on a few files in biabduction/ via Errdesc (not the
  biabduction analysis itself but some datatypes and functionality):
  - when possible, I've moved the individual functions elsewhere, in absint/Decompile.ml
  - nullsafe still depends on a function in Errdesc that unfortunately
    depends on a bunch of biabduction datatypes like Prop(!) and some
    other functionality that for now is embedded in biabduction
    (substitution in SIL instructions).

Reviewed By: mityal

Differential Revision: D21351906

fbshipit-source-id: 757528120
master
Jules Villard 5 years ago committed by Facebook GitHub Bot
parent 3e3b5b1f1d
commit 139098551a

@ -166,9 +166,8 @@ end
(* MakeDF *)
(** Example dataflow callback: compute the the distance from a node to the start node. *)
let _callback_test_dataflow {Callbacks.summary; _} =
let proc_desc = Summary.get_proc_desc summary in
(** Example dataflow analysis: compute the the distance from a node to the start node. *)
let _test_dataflow proc_desc =
let verbose = false in
let module DFCount = MakeDF (struct
type t = int
@ -189,5 +188,4 @@ let _callback_test_dataflow {Callbacks.summary; _} =
let do_node node =
match transitions node with DFCount.Transition _ -> () | DFCount.Dead_state -> ()
in
List.iter ~f:do_node (Procdesc.get_nodes proc_desc) ;
summary
List.iter ~f:do_node (Procdesc.get_nodes proc_desc)

@ -0,0 +1,58 @@
(*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open! IStd
module L = Logging
(** Find a boolean assignment to a temporary variable holding a boolean condition. The boolean
parameter indicates whether the true or false branch is required. *)
let rec find_boolean_assignment node pvar true_branch : Procdesc.Node.t option =
let find_instr n =
let filter = function
| Sil.Store {e1= Exp.Lvar pvar_; e2= Exp.Const (Const.Cint i)} when Pvar.equal pvar pvar_ ->
Bool.(IntLit.iszero i <> true_branch)
| _ ->
false
in
Instrs.exists ~f:filter (Procdesc.Node.get_instrs n)
in
match Procdesc.Node.get_preds node with
| [pred_node] ->
find_boolean_assignment pred_node pvar true_branch
| [n1; n2] ->
if find_instr n1 then Some n1 else if find_instr n2 then Some n2 else None
| _ ->
None
(** Find the function call instruction used to initialize normal variable [id], and return the
function name and arguments *)
let find_normal_variable_funcall (node : Procdesc.Node.t) (id : Ident.t) :
(Exp.t * Exp.t list * Location.t * CallFlags.t) option =
let find_declaration _ = function
| Sil.Call ((id0, _), fun_exp, args, loc, call_flags) when Ident.equal id id0 ->
Some (fun_exp, List.map ~f:fst args, loc, call_flags)
| _ ->
None
in
let res = Procdesc.Node.find_in_node_or_preds node ~f:find_declaration in
if Config.trace_error && is_none res then
L.d_printfln "find_normal_variable_funcall could not find %a in node %a" Ident.pp id
Procdesc.Node.pp node ;
res
(** Find a program variable assignment in the current node or predecessors. *)
let find_program_variable_assignment node pvar : (Procdesc.Node.t * Ident.t) option =
let find_instr node = function
| Sil.Store {e1= Exp.Lvar pvar_; e2= Exp.Var id}
when Pvar.equal pvar pvar_ && Ident.is_normal id ->
Some (node, id)
| _ ->
None
in
Procdesc.Node.find_in_node_or_preds node ~f:find_instr

@ -0,0 +1,21 @@
(*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open! IStd
val find_boolean_assignment : Procdesc.Node.t -> Pvar.t -> bool -> Procdesc.Node.t option
(** Find a boolean assignment to a temporary variable holding a boolean condition. The boolean
parameter indicates whether the true or false branch is required. *)
val find_normal_variable_funcall :
Procdesc.Node.t -> Ident.t -> (Exp.t * Exp.t list * Location.t * CallFlags.t) option
(** Find the function call instruction used to initialize normal variable [id], and return the
function name and arguments *)
val find_program_variable_assignment :
Procdesc.Node.t -> Pvar.t -> (Procdesc.Node.t * Ident.t) option
(** Find a program variable assignment in the current node or straightline predecessor. *)

@ -72,35 +72,6 @@ let explain_deallocate_constant_string s ra =
let verbose = Config.trace_error
(** Find the function call instruction used to initialize normal variable [id], and return the
function name and arguments *)
let find_normal_variable_funcall (node : Procdesc.Node.t) (id : Ident.t) :
(Exp.t * Exp.t list * Location.t * CallFlags.t) option =
let find_declaration _ = function
| Sil.Call ((id0, _), fun_exp, args, loc, call_flags) when Ident.equal id id0 ->
Some (fun_exp, List.map ~f:fst args, loc, call_flags)
| _ ->
None
in
let res = Procdesc.Node.find_in_node_or_preds node ~f:find_declaration in
if verbose && is_none res then
L.d_printfln "find_normal_variable_funcall could not find %a in node %a" Ident.pp id
Procdesc.Node.pp node ;
res
(** Find a program variable assignment in the current node or predecessors. *)
let find_program_variable_assignment node pvar : (Procdesc.Node.t * Ident.t) option =
let find_instr node = function
| Sil.Store {e1= Exp.Lvar pvar_; e2= Exp.Var id}
when Pvar.equal pvar pvar_ && Ident.is_normal id ->
Some (node, id)
| _ ->
None
in
Procdesc.Node.find_in_node_or_preds node ~f:find_instr
(** Special case for C++, where we translate code like [struct X; X getX() { X x; return X; }] as
[void getX(struct X * frontend_generated_pvar)]. This lets us recognize that X was returned from
getX *)
@ -131,27 +102,6 @@ let find_ident_assignment node id : (Procdesc.Node.t * Exp.t) option =
Procdesc.Node.find_in_node_or_preds node ~f:find_instr
(** Find a boolean assignment to a temporary variable holding a boolean condition. The boolean
parameter indicates whether the true or false branch is required. *)
let rec find_boolean_assignment node pvar true_branch : Procdesc.Node.t option =
let find_instr n =
let filter = function
| Sil.Store {e1= Exp.Lvar pvar_; e2= Exp.Const (Const.Cint i)} when Pvar.equal pvar pvar_ ->
Bool.(IntLit.iszero i <> true_branch)
| _ ->
false
in
Instrs.exists ~f:filter (Procdesc.Node.get_instrs n)
in
match Procdesc.Node.get_preds node with
| [pred_node] ->
find_boolean_assignment pred_node pvar true_branch
| [n1; n2] ->
if find_instr n1 then Some n1 else if find_instr n2 then Some n2 else None
| _ ->
None
(** Find the Load instruction used to declare normal variable [id], and return the expression
dereferenced to initialize [id] *)
let rec find_normal_variable_load_ tenv (seen : Exp.Set.t) node id : DExp.t option =
@ -243,7 +193,7 @@ and exp_lv_dexp_ tenv (seen_ : Exp.Set.t) node e : DExp.t option =
Exp.d_exp e ;
L.d_ln () ) ;
if Pvar.is_frontend_tmp pvar then
match find_program_variable_assignment node pvar with
match Decompile.find_program_variable_assignment node pvar with
| None -> (
match find_struct_by_value_assignment node pvar with
| Some (_, pname, loc, call_flags) ->
@ -251,7 +201,7 @@ and exp_lv_dexp_ tenv (seen_ : Exp.Set.t) node e : DExp.t option =
| None ->
None )
| Some (node', id) -> (
match find_normal_variable_funcall node' id with
match Decompile.find_normal_variable_funcall node' id with
| Some (fun_exp, eargs, loc, call_flags) ->
let fun_dexpo = exp_rv_dexp_ tenv seen node' fun_exp in
let blame_args = List.map ~f:(exp_rv_dexp_ tenv seen node') eargs in

@ -17,22 +17,9 @@ val vpath_find : Tenv.t -> 'a Prop.t -> Exp.t -> DecompiledExp.vpath * Typ.t opt
val hpred_is_open_resource : Tenv.t -> 'a Prop.t -> Predicates.hpred -> PredSymb.resource option
(** Check whether the hpred is a |-> representing a resource in the Racquire state *)
val find_normal_variable_funcall :
Procdesc.Node.t -> Ident.t -> (Exp.t * Exp.t list * Location.t * CallFlags.t) option
(** Find the function call instruction used to initialize normal variable [id], and return the
function name and arguments *)
val find_program_variable_assignment :
Procdesc.Node.t -> Pvar.t -> (Procdesc.Node.t * Ident.t) option
(** Find a program variable assignment in the current node or straightline predecessor. *)
val find_ident_assignment : Procdesc.Node.t -> Ident.t -> (Procdesc.Node.t * Exp.t) option
(** Find a program variable assignment to id in the current node or predecessors. *)
val find_boolean_assignment : Procdesc.Node.t -> Pvar.t -> bool -> Procdesc.Node.t option
(** Find a boolean assignment to a temporary variable holding a boolean condition. The boolean
parameter indicates whether the true or false branch is required. *)
val exp_rv_dexp : Tenv.t -> Procdesc.Node.t -> Exp.t -> DecompiledExp.t option
(** describe rvalue [e] as a dexp *)

@ -50,8 +50,8 @@ endif
# Note that we run find under _build directory. Since we copy some
# sources from subfolders to src/ folder to avoid duplicates we use
# $(DEPTH_ONE) and iteration over main and library folders.
LIBRARY_FOLDERS = . ./IR ./absint ./atd ./base ./biabduction ./c_stubs ./istd ./scripts
INCLUDE_FOLDERS = -I absint -I IR -I atd -I base -I biabduction -I c_stubs -I istd -I scripts
LIBRARY_FOLDERS = . ./IR ./absint ./atd ./base ./biabduction ./c_stubs ./istd ./nullsafe ./scripts
INCLUDE_FOLDERS = -I absint -I IR -I atd -I base -I biabduction -I c_stubs -I istd -I nullsafe -I scripts
ml_src_files:=$(shell \
cd $(INFER_BUILD_DIR); \

@ -19,7 +19,6 @@ let source_dirs =
; "cost"
; "integration"
; "labs"
; "nullsafe"
; "pulse"
; "quandary"
; "test_determinator"
@ -57,6 +56,8 @@ let infer_cflags =
; "-open"
; "Biabduction"
; "-open"
; "Nullsafe"
; "-open"
; "Absint"
; "-open"
; "OpenSource"
@ -89,7 +90,7 @@ let main_lib_stanza =
|}
infer_cflags
(String.concat " "
("biabduction" :: "absint" :: "InferCStubs" :: "InferIR" :: common_libraries))
("biabduction" :: "nullsafe" :: "absint" :: "InferCStubs" :: "InferIR" :: common_libraries))
(String.concat " " infer_binaries)

@ -38,7 +38,7 @@ let expand_expr_temps idenv node exp_ =
let exp = expand_expr idenv exp_ in
match exp with
| Exp.Lvar pvar when Pvar.is_frontend_tmp pvar -> (
match Errdesc.find_program_variable_assignment node pvar with
match Decompile.find_program_variable_assignment node pvar with
| None ->
exp
| Some (_, id) ->

@ -0,0 +1,14 @@
; Copyright (c) Facebook, Inc. and its affiliates.
;
; This source code is licensed under the MIT license found in the
; LICENSE file in the root directory of this source tree.
(library
(name nullsafe)
(public_name infer.nullsafe)
(flags
(:standard -open Core -open InferIR -open InferStdlib -open IStd -open InferGenerated
-open InferBase -open Absint -open Biabduction))
(libraries core InferStdlib InferGenerated InferBase InferIR absint biabduction)
(preprocess (pps ppx_compare ppx_sexp_conv))
)

@ -8,7 +8,6 @@
open! IStd
module L = Logging
module F = Format
open Dataflow
let callback1 ({IntraproceduralAnalysis.proc_desc= curr_pdesc; _} as analysis_data)
find_canonical_duplicate calls_this checks idenv annotated_signature linereader proc_loc :
@ -66,7 +65,7 @@ let callback1 ({IntraproceduralAnalysis.proc_desc= curr_pdesc; _} as analysis_da
let exit_node = Procdesc.get_exit_node curr_pdesc in
check_return find_canonical_duplicate exit_node final_typestate annotated_signature proc_loc
in
let module DFTypeCheck = MakeDF (struct
let module DFTypeCheck = DataFlow.MakeDF (struct
type t = TypeState.t
let equal = TypeState.equal
@ -76,7 +75,7 @@ let callback1 ({IntraproceduralAnalysis.proc_desc= curr_pdesc; _} as analysis_da
let pp_name fmt = F.pp_print_string fmt "eradicate"
let do_node node typestate =
NodePrinter.with_session ~pp_name node ~f:(fun () ->
AnalysisCallbacks.html_debug_new_node_session ~pp_name node ~f:(fun () ->
AnalysisState.set_node node ;
if Config.write_html then L.d_printfln "before:@\n%a@\n" TypeState.pp typestate ;
let {TypeCheck.normal_flow_typestate; exception_flow_typestates} =
@ -89,7 +88,7 @@ let callback1 ({IntraproceduralAnalysis.proc_desc= curr_pdesc; _} as analysis_da
(normal_flow_typestates, exception_flow_typestates) )
let proc_throws _ = DontKnow
let proc_throws _ = DataFlow.DontKnow
end) in
let initial_typestate = get_initial_typestate () in
do_before_dataflow initial_typestate ;
@ -104,7 +103,7 @@ let callback1 ({IntraproceduralAnalysis.proc_desc= curr_pdesc; _} as analysis_da
let analyze_one_procedure ({IntraproceduralAnalysis.tenv; proc_desc; _} as analysis_data) calls_this
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_canonical_duplicate node =
let duplicate_nodes = find_duplicate_nodes node in
@ -131,7 +130,7 @@ let analyze_one_procedure ({IntraproceduralAnalysis.tenv; proc_desc; _} as analy
(Procdesc.get_attributes pdesc)
in
let loc = Procdesc.get_loc pdesc in
(ann_sig, loc, Idenv.create pdesc)
(ann_sig, loc, IDEnv.create pdesc)
in
let checks', calls_this' =
if do_checks then (checks, calls_this)

@ -87,7 +87,7 @@ let check_condition_for_redundancy
| None ->
false
in
let is_temp = Idenv.exp_is_temp idenv expr in
let is_temp = IDEnv.exp_is_temp idenv expr in
let should_report =
(* NOTE: We have different levels of non-nullability. In practice there is a big difference
between certainty for different cases: whether expression is the result of something that can not be null

@ -206,7 +206,7 @@ let handle_field_access_via_temporary idenv curr_pname typestate exp =
None
in
let handle_temporary e =
match Idenv.expand_expr idenv e with
match IDEnv.expand_expr idenv e with
| Exp.Lvar pvar when name_is_temporary (Pvar.to_string pvar) -> (
match pvar_get_origin pvar with
| Some TypeOrigin.This ->
@ -230,7 +230,7 @@ let handle_field_access_via_temporary idenv curr_pname typestate exp =
(* Try to convert a function call to a pvar that originated it; fallback to an original expression in case of failure *)
let funcall_exp_to_original_pvar_exp tenv curr_pname typestate exp ~is_assignment ~call_node ~node
id =
match Errdesc.find_normal_variable_funcall call_node id with
match Decompile.find_normal_variable_funcall call_node id with
| Some (Exp.Const (Const.Cfun pn), _, _, _)
when not (ComplexExpressions.procname_used_in_condition pn) -> (
match ComplexExpressions.exp_to_string tenv node exp with
@ -276,17 +276,17 @@ let convert_complex_exp_to_pvar_and_register_field_in_typestate tenv idenv curr_
F.fprintf f "Exp: %a;@\nTypestate: @\n%a" Exp.pp exp TypeState.pp typestate )
(fun () ->
let exp =
handle_field_access_via_temporary idenv curr_pname typestate (Idenv.expand_expr idenv exp_)
handle_field_access_via_temporary idenv curr_pname typestate (IDEnv.expand_expr idenv exp_)
in
let default = (exp, typestate) in
match exp with
| Exp.Var id when Option.is_some (Errdesc.find_normal_variable_funcall node id) ->
| Exp.Var id when Option.is_some (Decompile.find_normal_variable_funcall node id) ->
( funcall_exp_to_original_pvar_exp tenv curr_pname typestate exp ~is_assignment
~call_node:node ~node id
, typestate )
| Exp.Lvar pvar when Pvar.is_frontend_tmp pvar -> (
let frontend_variable_assignment =
Errdesc.find_program_variable_assignment original_node pvar
Decompile.find_program_variable_assignment original_node pvar
in
match frontend_variable_assignment with
| Some (call_node, id) ->
@ -310,7 +310,7 @@ let convert_complex_exp_to_pvar_and_register_field_in_typestate tenv idenv curr_
~f:(fun (_, nullability) -> InferredNullability.get_origin nullability)
~default:TypeOrigin.OptimisticFallback
in
let exp' = Idenv.expand_expr_temps idenv original_node exp_ in
let exp' = IDEnv.expand_expr_temps idenv original_node exp_ in
let is_parameter_field pvar =
(* parameter.field *)
let name = Pvar.get_name pvar in
@ -440,7 +440,7 @@ let pvar_apply instr_ref idenv tenv curr_pname curr_annotated_signature loc hand
let typestate' = handle_pvar typestate pvar in
let curr_node = TypeErr.InstrRef.get_node instr_ref in
let frontent_variable_assignment =
if Pvar.is_frontend_tmp pvar then Errdesc.find_program_variable_assignment curr_node pvar
if Pvar.is_frontend_tmp pvar then Decompile.find_program_variable_assignment curr_node pvar
else None
in
match frontent_variable_assignment with
@ -448,7 +448,7 @@ let pvar_apply instr_ref idenv tenv curr_pname curr_annotated_signature loc hand
typestate'
| Some (node', id) -> (
(* handle the case where pvar is a frontend-generated program variable *)
let exp = Idenv.expand_expr idenv (Exp.Var id) in
let exp = IDEnv.expand_expr idenv (Exp.Var id) in
match
convert_complex_exp_to_pvar ~is_assignment:false tenv idenv curr_pname
curr_annotated_signature ~node:node' ~original_node:node exp typestate' loc
@ -481,7 +481,7 @@ let typecheck_expr_for_errors analysis_data ~nullsafe_mode find_canonical_duplic
let java_get_vararg_values node pvar idenv =
let values_of_instr acc = function
| Sil.Store {e1= Exp.Lindex (array_exp, _); e2= content_exp}
when Exp.equal (Exp.Lvar pvar) (Idenv.expand_expr idenv array_exp) ->
when Exp.equal (Exp.Lvar pvar) (IDEnv.expand_expr idenv array_exp) ->
(* Each vararg argument is an assignment to a pvar denoting an array of objects. *)
content_exp :: acc
| _ ->
@ -490,7 +490,7 @@ let java_get_vararg_values node pvar idenv =
let values_of_node acc n =
Procdesc.Node.get_instrs n |> Instrs.fold ~f:values_of_instr ~init:acc
in
match Errdesc.find_program_variable_assignment node pvar with
match Decompile.find_program_variable_assignment node pvar with
| Some (node', _) ->
Procdesc.fold_slope_range node' node ~f:values_of_node ~init:[]
| None ->
@ -543,7 +543,7 @@ let do_preconditions_check_not_null
let curr_pname = Procdesc.get_proc_name curr_pdesc in
if is_vararg then
let do_vararg_value e ts =
match Idenv.expand_expr idenv e with
match IDEnv.expand_expr idenv e with
| Exp.Lvar pvar1 ->
pvar_apply instr_ref idenv tenv curr_pname curr_annotated_signature loc
(clear_nullable_flag
@ -588,7 +588,7 @@ let do_preconditions_check_state instr_ref idenv tenv curr_pname curr_annotated_
let handle_negated_condition cond_node =
let do_instr instr =
let set_flag expression =
let cond_e = Idenv.expand_expr_temps idenv cond_node expression in
let cond_e = IDEnv.expand_expr_temps idenv cond_node expression in
match
convert_complex_exp_to_pvar ~is_assignment:false tenv idenv curr_pname
curr_annotated_signature ~node:cond_node ~original_node:node cond_e typestate' loc
@ -615,7 +615,7 @@ let do_preconditions_check_state instr_ref idenv tenv curr_pname curr_annotated_
(* temporary variable for the value of the boolean condition *)
let curr_node = TypeErr.InstrRef.get_node instr_ref in
let branch = false in
match Errdesc.find_boolean_assignment curr_node pvar branch with
match Decompile.find_boolean_assignment curr_node pvar branch with
(* In foo(cond1 && cond2), the node that sets the result to false
has all the negated conditions as parents. *)
| Some boolean_assignment_node ->
@ -680,7 +680,7 @@ let handle_assignment_in_condition_for_sil_prune idenv node pvar =
let found = ref None in
let do_instr i =
match i with
| Sil.Store {e1= e; e2= e'} when Exp.equal (Exp.Lvar pvar) (Idenv.expand_expr idenv e') ->
| Sil.Store {e1= e; e2= e'} when Exp.equal (Exp.Lvar pvar) (IDEnv.expand_expr idenv e') ->
found := Some e
| _ ->
()
@ -701,13 +701,13 @@ let rec normalize_cond_for_sil_prune_rec idenv ~node ~original_node cond =
let node'', c2' = normalize_cond_for_sil_prune_rec idenv ~node:node' ~original_node c2 in
(node'', Exp.BinOp (bop, c1', c2'))
| Exp.Var _ ->
let c' = Idenv.expand_expr idenv cond in
let c' = IDEnv.expand_expr idenv cond in
if not (Exp.equal c' cond) then normalize_cond_for_sil_prune_rec idenv ~node ~original_node c'
else (node, c')
| Exp.Lvar pvar when Pvar.is_frontend_tmp pvar -> (
match handle_assignment_in_condition_for_sil_prune idenv original_node pvar with
| None -> (
match Errdesc.find_program_variable_assignment node pvar with
match Decompile.find_program_variable_assignment node pvar with
| Some (node', id) ->
(node', Exp.Var id)
| None ->
@ -732,7 +732,7 @@ let rec check_condition_for_sil_prune
let extract_arguments_from_call filter_callee expr =
match expr with
| Exp.Var id -> (
match Errdesc.find_normal_variable_funcall node id with
match Decompile.find_normal_variable_funcall node id with
| Some (Exp.Const (Const.Cfun pn), arguments, _, _) when filter_callee pn ->
Some arguments
| _ ->

@ -29,7 +29,7 @@ val typecheck_node :
IntraproceduralAnalysis.t
-> bool ref
-> checks
-> Idenv.t
-> IDEnv.t
-> find_canonical_duplicate
-> AnnotatedSignature.t
-> TypeState.t

Loading…
Cancel
Save