From 7ec8f599986b8a33b8276ef239a69999406db2e9 Mon Sep 17 00:00:00 2001 From: Sam Blackshear Date: Fri, 27 May 2016 04:12:08 -0700 Subject: [PATCH] adding find_in_node_or_preds to clean up errdesc Reviewed By: cristianoc Differential Revision: D3346710 fbshipit-source-id: ba729e0 --- infer/src/backend/errdesc.ml | 122 ++++++------------ .../java/eradicate/ReturnNotNullable.java | 6 + .../java/eradicate/ReturnNotNullableTest.java | 1 + 3 files changed, 49 insertions(+), 80 deletions(-) diff --git a/infer/src/backend/errdesc.ml b/infer/src/backend/errdesc.ml index e799d4255..5f7df1798 100644 --- a/infer/src/backend/errdesc.ml +++ b/infer/src/backend/errdesc.ml @@ -69,17 +69,26 @@ let explain_deallocate_constant_string s ra = let verbose = Config.trace_error +let find_in_node_or_preds start_node f_node_instr = + let visited = ref Cfg.NodeSet.empty in + let rec find node = + if Cfg.NodeSet.mem node !visited then None + else + begin + visited := Cfg.NodeSet.add node !visited; + let instrs = Cfg.Node.get_instrs node in + match IList.find_map_opt (f_node_instr node) (IList.rev instrs) with + | Some res -> Some res + | None -> IList.find_map_opt find (Cfg.Node.get_preds node) + end in + find start_node + (** Find the Set instruction used to assign [id] to a program variable, if any *) let find_variable_assigment node id : Sil.instr option = - let res = ref None in - let node_instrs = Cfg.Node.get_instrs node in - let find_set instr = match instr with - | Sil.Set (Sil.Lvar _, _, e, _) when Sil.exp_equal (Sil.Var id) e -> - res := Some instr; - true - | _ -> false in - ignore (IList.exists find_set node_instrs); - !res + let find_set _ instr = match instr with + | Sil.Set (Sil.Lvar _, _, e, _) when Sil.exp_equal (Sil.Var id) e -> Some instr + | _ -> None in + find_in_node_or_preds node find_set (** Check if a nullify instruction exists for the program variable after the given instruction *) let find_nullify_after_instr node instr pvar : bool = @@ -127,15 +136,12 @@ let id_is_assigned_then_dead node id = let find_normal_variable_funcall (node: Cfg.Node.t) (id: Ident.t): (Sil.exp * (Sil.exp list) * Location.t * Sil.call_flags) option = - let res = ref None in - let node_instrs = Cfg.Node.get_instrs node in - let find_declaration = function + let find_declaration _ = function | Sil.Call ([id0], fun_exp, args, loc, call_flags) when Ident.equal id id0 -> - res := Some (fun_exp, IList.map fst args, loc, call_flags); - true - | _ -> false in - ignore (IList.exists find_declaration node_instrs); - if verbose && !res == None + Some (fun_exp, IList.map fst args, loc, call_flags) + | _ -> None in + let res = find_in_node_or_preds node find_declaration in + if verbose && res == None then (L.d_str ("find_normal_variable_funcall could not find " ^ @@ -143,62 +149,23 @@ let find_normal_variable_funcall " in node " ^ string_of_int (Cfg.Node.get_id node :> int)); L.d_ln ()); - !res + res (** Find a program variable assignment in the current node or predecessors. *) let find_program_variable_assignment node pvar : (Cfg.Node.t * Ident.t) option = - let visited = ref Cfg.NodeSet.empty in - let rec find node = - if Cfg.NodeSet.mem node !visited then None - else - begin - visited := Cfg.NodeSet.add node !visited; - let res = ref None in - let find_instr = function - | Sil.Set (Sil.Lvar _pvar, _, Sil.Var id, _) - when Pvar.equal pvar _pvar && Ident.is_normal id -> - res := Some (node, id); - true - | _ -> false in - if IList.exists find_instr (Cfg.Node.get_instrs node) - then !res - else match Cfg.Node.get_preds node with - | [pred_node] -> - find pred_node - | [pn1; pn2] -> - (match find pn1 with - | None -> find pn2 - | x -> x) - | _ -> None (* either 0 or >2 predecessors *) - end in - find node + let find_instr node = function + | Sil.Set (Sil.Lvar _pvar, _, Sil.Var id, _) when Pvar.equal pvar _pvar && Ident.is_normal id -> + Some (node, id) + | _ -> + None in + find_in_node_or_preds node find_instr (** Find a program variable assignment to id in the current node or predecessors. *) let find_ident_assignment node id : (Cfg.Node.t * Sil.exp) option = - let visited = ref Cfg.NodeSet.empty in - let rec find node = - if Cfg.NodeSet.mem node !visited then None - else - begin - visited := Cfg.NodeSet.add node !visited; - let res = ref None in - let find_instr = function - | Sil.Letderef(_id, e, _, _) when Ident.equal _id id -> - res := Some (node, e); - true - | _ -> false in - if IList.exists find_instr (Cfg.Node.get_instrs node) - then !res - else match Cfg.Node.get_preds node with - | [pred_node] -> - find pred_node - | [pn1; pn2] -> - (match find pn1 with - | None -> find pn2 - | x -> x) - | _ -> None (* either 0 or >2 predecessors *) - end in - find node + let find_instr node = function + | Sil.Letderef(_id, e, _, _) when Ident.equal _id id -> Some (node, e) + | _ -> None in + find_in_node_or_preds node 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. *) @@ -239,24 +206,20 @@ let pvar_is_frontend_tmp pvar = (** Find the Letderef instruction used to declare normal variable [id], and return the expression dereferenced to initialize [id] *) let rec _find_normal_variable_letderef (seen : Sil.ExpSet.t) node id : Sil.dexp option = - let res = ref None in - let node_instrs = Cfg.Node.get_instrs node in - let find_declaration = function + let find_declaration node = function | Sil.Letderef (id0, e, _, _) when Ident.equal id id0 -> if verbose then (L.d_str "find_normal_variable_letderef defining "; Sil.d_exp e; L.d_ln ()); - res := _exp_lv_dexp seen node e; - true + _exp_lv_dexp seen node e | Sil.Call ([id0], Sil.Const (Sil.Cfun pn), (e, _):: _, _, _) when Ident.equal id id0 && Procname.equal pn (Procname.from_string_c_fun "__cast") -> if verbose then (L.d_str "find_normal_variable_letderef cast on "; Sil.d_exp e; L.d_ln ()); - res := _exp_rv_dexp seen node e; - true + _exp_rv_dexp seen node e | Sil.Call ([id0], (Sil.Const (Sil.Cfun pname) as fun_exp), args, loc, call_flags) when Ident.equal id id0 -> if verbose @@ -273,11 +236,10 @@ let rec _find_normal_variable_letderef (seen : Sil.ExpSet.t) node id : Sil.dexp let unNone = function Some x -> x | None -> assert false in IList.map unNone args_dexpo in - res := Some (Sil.Dretcall (fun_dexp, args_dexp, loc, call_flags)); - true - | _ -> false in - ignore (IList.exists find_declaration node_instrs); - if verbose && !res == None + Some (Sil.Dretcall (fun_dexp, args_dexp, loc, call_flags)) + | _ -> None in + let res = find_in_node_or_preds node find_declaration in + if verbose && res == None then (L.d_str ("find_normal_variable_letderef could not find " ^ @@ -285,7 +247,7 @@ let rec _find_normal_variable_letderef (seen : Sil.ExpSet.t) node id : Sil.dexp " in node " ^ string_of_int (Cfg.Node.get_id node :> int)); L.d_ln ()); - !res + res (** describe lvalue [e] as a dexp *) and _exp_lv_dexp (_seen : Sil.ExpSet.t) node e : Sil.dexp option = diff --git a/infer/tests/codetoanalyze/java/eradicate/ReturnNotNullable.java b/infer/tests/codetoanalyze/java/eradicate/ReturnNotNullable.java index 58f7c0ebb..7cab668ee 100644 --- a/infer/tests/codetoanalyze/java/eradicate/ReturnNotNullable.java +++ b/infer/tests/codetoanalyze/java/eradicate/ReturnNotNullable.java @@ -86,6 +86,12 @@ public class ReturnNotNullable { } } + Object tryWithResourcesReturnNullable(String path) throws IOException { + try (BufferedReader br = nn(new BufferedReader(new FileReader(path)))) { + return returnNullOK(); + } + } + /* Check that orNull is modelled and RETURN_OVER_ANNOTATED is not returned. */ diff --git a/infer/tests/endtoend/java/eradicate/ReturnNotNullableTest.java b/infer/tests/endtoend/java/eradicate/ReturnNotNullableTest.java index fa19a0f9f..1babf4dbb 100644 --- a/infer/tests/endtoend/java/eradicate/ReturnNotNullableTest.java +++ b/infer/tests/endtoend/java/eradicate/ReturnNotNullableTest.java @@ -59,6 +59,7 @@ public class ReturnNotNullableTest { "return_null_in_catch", "return_null_in_catch_after_throw", "getResourceNullable", + "tryWithResourcesReturnNullable", }; List errorPatterns = createPatterns( RETURN_NOT_NULLABLE,