diff --git a/infer/src/checkers/ConfigImpactAnalysis.ml b/infer/src/checkers/ConfigImpactAnalysis.ml index 280f57836..dbffa3ae6 100644 --- a/infer/src/checkers/ConfigImpactAnalysis.ml +++ b/infer/src/checkers/ConfigImpactAnalysis.ml @@ -166,23 +166,28 @@ module Summary = struct { unchecked_callees: UncheckedCallees.t (** Set of unchecked callees *) ; unchecked_callees_cond: UncheckedCalleesCond.t (** Sets of unchecked callees that are called conditionally by object fields *) - ; has_call_stmt: bool (** True if a function includes a call statement *) } + ; has_call_stmt: bool (** True if a function includes a call statement *) + ; config_fields: Fields.t + (** Intra-procedurally collected fields that may have config values *) } - let pp f {unchecked_callees; unchecked_callees_cond; has_call_stmt} = + let pp f {unchecked_callees; unchecked_callees_cond; has_call_stmt; config_fields} = F.fprintf f - "@[@[unchecked callees:@,%a@],@ @[unchecked callees cond:@,%a@],@ @[has_call_stmt:%b@]@]" - UncheckedCallees.pp unchecked_callees UncheckedCalleesCond.pp unchecked_callees_cond - has_call_stmt + "@[@[unchecked callees:@,\ + %a@],@ @[unchecked callees cond:@,\ + %a@],@ @[has_call_stmt:%b@],@ @[config fields:%a@]@]" UncheckedCallees.pp unchecked_callees + UncheckedCalleesCond.pp unchecked_callees_cond has_call_stmt Fields.pp config_fields + let get_config_fields {config_fields} = config_fields + let get_unchecked_callees {unchecked_callees} = unchecked_callees - let instantiate_unchecked_callees_cond ~config_fields + let instantiate_unchecked_callees_cond ~all_config_fields ({unchecked_callees; unchecked_callees_cond} as x) = let unchecked_callees = UncheckedCalleesCond.fold (fun fields callees acc -> - if Fields.is_empty (Fields.inter config_fields fields) then + if Fields.is_empty (Fields.inter all_config_fields fields) then UncheckedCallees.union acc callees else acc ) unchecked_callees_cond unchecked_callees @@ -196,17 +201,22 @@ module Dom = struct ; field_checks: FieldChecks.t ; unchecked_callees: UncheckedCallees.t ; unchecked_callees_cond: UncheckedCalleesCond.t - ; mem: Mem.t } + ; mem: Mem.t + ; config_fields: Fields.t } - let pp f {config_checks; field_checks; unchecked_callees; unchecked_callees_cond; mem} = + let pp f + {config_checks; field_checks; unchecked_callees; unchecked_callees_cond; mem; config_fields} = F.fprintf f "@[@[config checks:@,\ %a@]@ @[field checks:@,\ %a@]@ @[unchecked callees:@,\ %a@]@ @[unchecked callees cond:@,\ - %a@]@ @[mem:%,%a@]@]" ConfigChecks.pp config_checks FieldChecks.pp field_checks - UncheckedCallees.pp unchecked_callees UncheckedCalleesCond.pp unchecked_callees_cond Mem.pp - mem + %a@]@ @[mem:@,\ + %a@]@ @[config fields:@,\ + %a@]@]" + ConfigChecks.pp config_checks FieldChecks.pp field_checks UncheckedCallees.pp + unchecked_callees UncheckedCalleesCond.pp unchecked_callees_cond Mem.pp mem Fields.pp + config_fields let leq ~lhs ~rhs = @@ -215,6 +225,7 @@ module Dom = struct && UncheckedCallees.leq ~lhs:lhs.unchecked_callees ~rhs:rhs.unchecked_callees && UncheckedCalleesCond.leq ~lhs:lhs.unchecked_callees_cond ~rhs:rhs.unchecked_callees_cond && Mem.leq ~lhs:lhs.mem ~rhs:rhs.mem + && Fields.leq ~lhs:lhs.config_fields ~rhs:rhs.config_fields let join x y = @@ -223,7 +234,8 @@ module Dom = struct ; unchecked_callees= UncheckedCallees.join x.unchecked_callees y.unchecked_callees ; unchecked_callees_cond= UncheckedCalleesCond.join x.unchecked_callees_cond y.unchecked_callees_cond - ; mem= Mem.join x.mem y.mem } + ; mem= Mem.join x.mem y.mem + ; config_fields= Fields.join x.config_fields y.config_fields } let widen ~prev ~next ~num_iters = @@ -234,11 +246,12 @@ module Dom = struct ; unchecked_callees_cond= UncheckedCalleesCond.widen ~prev:prev.unchecked_callees_cond ~next:next.unchecked_callees_cond ~num_iters - ; mem= Mem.widen ~prev:prev.mem ~next:next.mem ~num_iters } + ; mem= Mem.widen ~prev:prev.mem ~next:next.mem ~num_iters + ; config_fields= Fields.widen ~prev:prev.config_fields ~next:next.config_fields ~num_iters } - let to_summary has_call_stmt {unchecked_callees; unchecked_callees_cond} = - {Summary.unchecked_callees; unchecked_callees_cond; has_call_stmt} + let to_summary has_call_stmt {unchecked_callees; unchecked_callees_cond; config_fields} = + {Summary.unchecked_callees; unchecked_callees_cond; has_call_stmt; config_fields} let init = @@ -246,7 +259,8 @@ module Dom = struct ; field_checks= FieldChecks.top ; unchecked_callees= UncheckedCallees.bottom ; unchecked_callees_cond= UncheckedCalleesCond.bottom - ; mem= Mem.bottom } + ; mem= Mem.bottom + ; config_fields= Fields.bottom } let add_mem loc v ({mem} as astate) = {astate with mem= Mem.add loc v mem} @@ -261,6 +275,12 @@ module Dom = struct let store_config pvar id astate = copy_mem ~tgt:(Loc.of_pvar pvar) ~src:(Loc.of_id id) astate + let store_field fn id ({mem; config_fields} as astate) = + let {Val.config} = Mem.lookup (Loc.of_id id) mem in + if ConfigLifted.is_top config then astate + else {astate with config_fields= Fields.add fn config_fields} + + let boolean_value id_tgt id_src astate = copy_mem ~tgt:(Loc.of_id id_tgt) ~src:(Loc.of_id id_src) astate @@ -371,6 +391,8 @@ module TransferFunctions = struct Dom.load_field id fn astate | Store {e1= Lvar pvar; e2= Var id} -> Dom.store_config pvar id astate + | Store {e1= Lfield (_, fn, _); e2= Var id} -> + Dom.store_field fn id astate | Call ((ret, _), Const (Cfun callee), [(Var id, _)], _, _) when is_java_boolean_value_method callee -> Dom.boolean_value ret id astate diff --git a/infer/src/checkers/ConfigImpactAnalysis.mli b/infer/src/checkers/ConfigImpactAnalysis.mli index 76a8d7d27..f9250a61d 100644 --- a/infer/src/checkers/ConfigImpactAnalysis.mli +++ b/infer/src/checkers/ConfigImpactAnalysis.mli @@ -32,9 +32,11 @@ module Summary : sig val pp : Format.formatter -> t -> unit + val get_config_fields : t -> Fields.t + val get_unchecked_callees : t -> UncheckedCallees.t - val instantiate_unchecked_callees_cond : config_fields:Fields.t -> t -> t + val instantiate_unchecked_callees_cond : all_config_fields:Fields.t -> t -> t end val checker : Summary.t InterproceduralAnalysis.t -> Summary.t option diff --git a/infer/src/integration/JsonReports.ml b/infer/src/integration/JsonReports.ml index ab114b599..66655d3d2 100644 --- a/infer/src/integration/JsonReports.ml +++ b/infer/src/integration/JsonReports.ml @@ -343,15 +343,26 @@ let write_costs proc_name loc cost_opt (outfile : Utils.outfile) = JsonCostsPrinter.pp outfile.fmt {loc; proc_name; cost_opt} -let write_config_impact proc_name loc config_impact_opt (outfile : Utils.outfile) = +let get_all_config_fields () = + lazy + (let all_config_fields = ref ConfigImpactAnalysis.Fields.empty in + Summary.OnDisk.iter_specs ~f:(fun summary -> + Payloads.config_impact_analysis summary.payloads + |> Option.iter ~f:(fun summary -> + all_config_fields := + ConfigImpactAnalysis.Fields.union !all_config_fields + (ConfigImpactAnalysis.Summary.get_config_fields summary) ) ) ; + !all_config_fields ) + + +let write_config_impact all_config_fields proc_name loc config_impact_opt (outfile : Utils.outfile) + = if ExternalConfigImpactData.is_in_config_data_file proc_name then - let config_fields = - (* TODO: This value should be non-empty by an additional analysis. *) - ConfigImpactAnalysis.Fields.empty - in let config_impact_opt = Option.map config_impact_opt - ~f:(ConfigImpactAnalysis.Summary.instantiate_unchecked_callees_cond ~config_fields) + ~f: + (ConfigImpactAnalysis.Summary.instantiate_unchecked_callees_cond + ~all_config_fields:(Lazy.force all_config_fields)) in JsonConfigImpactPrinter.pp outfile.fmt {loc; proc_name; config_impact_opt} @@ -363,9 +374,9 @@ let write_lint_issues filters (issues_outf : Utils.outfile) linereader procname let process_summary proc_name loc ~cost:(cost_opt, costs_outf) - ~config_impact:(config_impact_opt, config_impact_outf) err_log issues_acc = + ~config_impact:(config_impact_opt, config_impact_outf, all_config_fields) err_log issues_acc = write_costs proc_name loc cost_opt costs_outf ; - write_config_impact proc_name loc config_impact_opt config_impact_outf ; + write_config_impact all_config_fields proc_name loc config_impact_opt config_impact_outf ; collect_issues proc_name loc err_log issues_acc @@ -373,11 +384,12 @@ let process_all_summaries_and_issues ~issues_outf ~costs_outf ~config_impact_out let linereader = LineReader.create () in let filters = Inferconfig.create_filters () in let all_issues = ref [] in + let all_config_fields = get_all_config_fields () in Summary.OnDisk.iter_report_summaries_from_config ~f:(fun proc_name loc cost_opt config_impact_opt err_log -> all_issues := process_summary proc_name loc ~cost:(cost_opt, costs_outf) - ~config_impact:(config_impact_opt, config_impact_outf) + ~config_impact:(config_impact_opt, config_impact_outf, all_config_fields) err_log !all_issues ) ; all_issues := Issue.sort_filter_issues !all_issues ; List.iter