Skip to content

Commit

Permalink
[uninit method] Pass unresolved_reason in more places
Browse files Browse the repository at this point in the history
Summary:
This diff passes the `unresolved_reason` in more places, which were missed in the preivous code.

- It added two more `resolved_reason` cases: `ClassNameNotFound` and `CurryInfoNotFound`.
- It passes more `unresolved_reason` in `find_override`, which now returns `Result.t` instead of `Option.t`.

Reviewed By: geralt-encore

Differential Revision: D64535061

fbshipit-source-id: 3cf6bcf9d30764b72d54d47723b2b9346d5568f0
  • Loading branch information
skcho authored and facebook-github-bot committed Oct 17, 2024
1 parent a7f80d8 commit 44325b2
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 45 deletions.
11 changes: 10 additions & 1 deletion infer/src/IR/Tenv.ml
Original file line number Diff line number Diff line change
Expand Up @@ -407,10 +407,19 @@ module MethodInfo = struct
let get_hack_kind = function HackInfo {kind} -> Some kind | _ -> None
end

type unresolved_reason = MaybeMissingDueToMissedCapture | MaybeMissingDueToIncompleteModel
type unresolved_reason =
| ClassNameNotFound
| CurryInfoNotFound
| MaybeMissingDueToMissedCapture
| MaybeMissingDueToIncompleteModel
[@@deriving show {with_path= false}]

type unresolved_data = {missed_captures: Typ.Name.Set.t; unresolved_reason: unresolved_reason option}

let mk_unresolved_data ?(missed_captures = Typ.Name.Set.empty) unresolved_reason =
{missed_captures; unresolved_reason}


type resolution_result = (MethodInfo.t, unresolved_data) Result.t

let is_hack_model source_file =
Expand Down
10 changes: 9 additions & 1 deletion infer/src/IR/Tenv.mli
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,18 @@ module MethodInfo : sig
val get_hack_kind : t -> Hack.kind option
end

type unresolved_reason = MaybeMissingDueToMissedCapture | MaybeMissingDueToIncompleteModel
type unresolved_reason =
| ClassNameNotFound
| CurryInfoNotFound
| MaybeMissingDueToMissedCapture
| MaybeMissingDueToIncompleteModel
[@@deriving show]

type unresolved_data = {missed_captures: Typ.Name.Set.t; unresolved_reason: unresolved_reason option}

val mk_unresolved_data :
?missed_captures:Typ.Name.Set.t -> unresolved_reason option -> unresolved_data

type resolution_result = (MethodInfo.t, unresolved_data) Result.t

val resolve_method :
Expand Down
78 changes: 36 additions & 42 deletions infer/src/pulse/Pulse.ml
Original file line number Diff line number Diff line change
Expand Up @@ -330,19 +330,19 @@ module PulseTransferFunctions = struct
let method_exists proc_name methods = List.mem ~equal:Procname.equal methods proc_name in
Tenv.resolve_method ~method_exists tenv type_name proc_name
in
let no_missed_captures = Typ.Name.Set.empty in
let open IOption.Let_syntax in
match get_dynamic_type_name astate receiver with
| Some (dynamic_type_name, _) when Typ.Name.Hack.is_generated_curry dynamic_type_name ->
(* this is a ...$static_...$curry'.__invoke() call to a Hack function reference *)
( (let+ class_name, function_name = Typ.Name.Hack.extract_curry_info dynamic_type_name in
let arity = Procname.get_hack_arity proc_name in
let proc_name = Procname.make_hack ~class_name:(Some class_name) ~function_name ~arity in
L.d_printfln "function pointer on %a detected" Procname.pp proc_name ;
(* TODO (dpichardie): we need to modify the first argument because this is not the expected class object *)
(Tenv.MethodInfo.mk_class proc_name, HackFunctionReference) )
, no_missed_captures )
| Some (dynamic_type_name, source_file_opt) -> (
| Some (dynamic_type_name, _) when Typ.Name.Hack.is_generated_curry dynamic_type_name -> (
(* this is a ...$static_...$curry'.__invoke() call to a Hack function reference *)
match Typ.Name.Hack.extract_curry_info dynamic_type_name with
| None ->
Result.Error (Tenv.mk_unresolved_data (Some CurryInfoNotFound))
| Some (class_name, function_name) ->
let arity = Procname.get_hack_arity proc_name in
let proc_name = Procname.make_hack ~class_name:(Some class_name) ~function_name ~arity in
L.d_printfln "function pointer on %a detected" Procname.pp proc_name ;
(* TODO (dpichardie): we need to modify the first argument because this is not the expected class object *)
Result.Ok (Tenv.MethodInfo.mk_class proc_name, HackFunctionReference) )
| Some (dynamic_type_name, source_file_opt) ->
(* if we have a source file then do the look up in the (local) tenv
for that source file instead of in the tenv for the current file *)
L.d_printfln "finding override for dynamic type %a, proc_name %a with sourcefile %a"
Expand All @@ -352,29 +352,20 @@ module PulseTransferFunctions = struct
Option.bind source_file_opt ~f:(Exe_env.get_source_tenv exe_env)
|> Option.value ~default:tenv
in
match tenv_resolve_method tenv dynamic_type_name proc_name with
| Ok method_info ->
Result.map (tenv_resolve_method tenv dynamic_type_name proc_name) ~f:(fun method_info ->
L.d_printfln "method_info is %a" Tenv.MethodInfo.pp method_info ;
(Some (method_info, ExactDevirtualization), no_missed_captures)
| Error {missed_captures} ->
(None, missed_captures) )
(method_info, ExactDevirtualization) )
| None ->
let opt_proc_name, missed_captures =
if Language.curr_language_is Hack || Language.curr_language_is Python then
( if Language.curr_language_is Hack || Language.curr_language_is Python then
(* contrary to the Java frontend, the Hack and Python frontends do not perform
static resolution so we have to do it here *)
Procname.get_class_type_name proc_name
|> Option.value_map ~default:(None, no_missed_captures) ~f:(fun type_name ->
match tenv_resolve_method tenv type_name proc_name with
| Ok method_info ->
(Some method_info, no_missed_captures)
| Error {missed_captures} ->
(None, missed_captures) )
else (Some (Tenv.MethodInfo.mk_class proc_name), no_missed_captures)
in
( (let+ proc_name = opt_proc_name in
(proc_name, ApproxDevirtualization) )
, missed_captures )
match Procname.get_class_type_name proc_name with
| None ->
Result.Error (Tenv.mk_unresolved_data (Some ClassNameNotFound))
| Some type_name ->
tenv_resolve_method tenv type_name proc_name
else Result.Ok (Tenv.MethodInfo.mk_class proc_name) )
|> Result.map ~f:(fun method_info -> (method_info, ApproxDevirtualization))


let string_of_devirtualization_status = function
Expand All @@ -389,14 +380,14 @@ module PulseTransferFunctions = struct
let resolve_virtual_call exe_env tenv astate receiver proc_name_opt =
Option.map proc_name_opt ~f:(fun proc_name ->
match find_override exe_env tenv astate receiver proc_name with
| Some (info, devirtualization_status), missed_captures ->
| Ok (info, devirtualization_status) ->
let proc_name' = Tenv.MethodInfo.get_procname info in
L.d_printfln "Dynamic dispatch: %a %s resolved to %a" Procname.pp_verbose proc_name
(string_of_devirtualization_status devirtualization_status)
Procname.pp_verbose proc_name' ;
(info, devirtualization_status, missed_captures)
| None, missed_captures ->
(Tenv.MethodInfo.mk_class proc_name, ApproxDevirtualization, missed_captures) )
(info, devirtualization_status, Tenv.mk_unresolved_data None)
| Error unresolved_data ->
(Tenv.MethodInfo.mk_class proc_name, ApproxDevirtualization, unresolved_data) )


let need_dynamic_type_specialization astate receiver_addr =
Expand Down Expand Up @@ -602,29 +593,32 @@ module PulseTransferFunctions = struct
improve_receiver_static_type astate (ValueOrigin.value receiver) callee_pname
|> resolve_virtual_call exe_env tenv astate (ValueOrigin.value receiver)
with
| Some (info, HackFunctionReference, missed_captures) ->
| Some (info, HackFunctionReference, {missed_captures; unresolved_reason= unresolved_reason1})
->
let callee = Tenv.MethodInfo.get_procname info in
let unresolved_reason, info_opt, astate =
let unresolved_reason2, info_opt, astate =
resolve_hack_static_method path call_loc astate tenv caller (Some callee)
in
let info = Option.value ~default:info info_opt in
let astate =
record_call_resolution_if_closure ResolvedUsingDynamicType astate
|> AbductiveDomain.add_missed_captures missed_captures
in
(unresolved_reason, Some info, astate)
| Some (info, ExactDevirtualization, missed_captures) ->
(Option.first_some unresolved_reason1 unresolved_reason2, Some info, astate)
| Some (info, ExactDevirtualization, {missed_captures; unresolved_reason}) ->
let astate =
record_call_resolution_if_closure ResolvedUsingDynamicType astate
|> AbductiveDomain.add_missed_captures missed_captures
in
(None, Some info, astate)
| Some (info, ApproxDevirtualization, missed_captures) ->
(unresolved_reason, Some info, astate)
| Some (info, ApproxDevirtualization, {missed_captures; unresolved_reason}) ->
let astate =
record_call_resolution_if_closure Unresolved astate
|> AbductiveDomain.add_missed_captures missed_captures
in
(None, Some info, need_dynamic_type_specialization astate (ValueOrigin.value receiver))
( unresolved_reason
, Some info
, need_dynamic_type_specialization astate (ValueOrigin.value receiver) )
| None ->
let astate = record_call_resolution_if_closure Unresolved astate in
(None, None, astate) )
Expand Down
6 changes: 5 additions & 1 deletion infer/src/pulse/PulseCallOperations.ml
Original file line number Diff line number Diff line change
Expand Up @@ -896,7 +896,11 @@ let call ?disjunct_limit ({InterproceduralAnalysis.analyze_dependency} as analys
astate
in
( match (unresolved_reason : Tenv.unresolved_reason option) with
| Some (MaybeMissingDueToMissedCapture | MaybeMissingDueToIncompleteModel) ->
| Some
( ClassNameNotFound
| CurryInfoNotFound
| MaybeMissingDueToMissedCapture
| MaybeMissingDueToIncompleteModel ) ->
()
| None ->
check_uninit_method analysis_data call_loc callee_pname actuals astate ) ;
Expand Down

0 comments on commit 44325b2

Please sign in to comment.