diff --git a/infer/src/backend/specs.ml b/infer/src/backend/specs.ml index 9b6230350..28996379c 100644 --- a/infer/src/backend/specs.ml +++ b/infer/src/backend/specs.ml @@ -393,11 +393,15 @@ let pp_specs pe fmt specs = let cnt = ref 0 in match pe.Pp.kind with | TEXT -> - IList.iter (fun spec -> incr cnt; F.fprintf fmt "%a@\n" (pp_spec pe (Some (!cnt, total))) spec) specs + IList.iter (fun spec -> incr cnt; + F.fprintf fmt "%a" (pp_spec pe (Some (!cnt, total))) spec) specs | HTML -> - IList.iter (fun spec -> incr cnt; F.fprintf fmt "%a
@\n" (pp_spec pe (Some (!cnt, total))) spec) specs + IList.iter (fun spec -> incr cnt; + F.fprintf fmt "%a
@\n" (pp_spec pe (Some (!cnt, total))) spec) specs | LATEX -> - IList.iter (fun spec -> incr cnt; F.fprintf fmt "\\subsection*{Spec %d of %d}@\n\\(%a\\)@\n" !cnt total (pp_spec pe None) spec) specs + IList.iter (fun spec -> incr cnt; + F.fprintf fmt "\\subsection*{Spec %d of %d}@\n\\(%a\\)@\n" + !cnt total (pp_spec pe None) spec) specs (** Print the decpendency map *) let pp_dependency_map fmt dependency_map = @@ -450,7 +454,7 @@ let pp_payload pe fmt { preposts; typestate; crashcontext_frame; quandary; siof; let pp_opt pp fmt = function | Some x -> pp fmt x | None -> () in - F.fprintf fmt "%a%a%a%a%a%a" + F.fprintf fmt "%a%a%a%a%a%a@\n" (pp_specs pe) (get_specs_from_preposts preposts) (pp_opt (TypeState.pp TypeState.unit_ext)) typestate (pp_opt Crashcontext.pp_stacktree) crashcontext_frame diff --git a/infer/src/checkers/SinkTrace.ml b/infer/src/checkers/SinkTrace.ml index 09cde50f7..985fe6a36 100644 --- a/infer/src/checkers/SinkTrace.ml +++ b/infer/src/checkers/SinkTrace.ml @@ -63,4 +63,13 @@ module Make (TraceElem : TraceElem.S) = struct add_sink callee_sink t_acc) initial (Sinks.elements (sinks t)) + + let pp fmt t = + let pp_passthroughs_if_not_empty fmt p = + if not (Passthroughs.is_empty p) then + F.fprintf fmt " via %a" Passthroughs.pp p in + F.fprintf + fmt + "%a%a" + Sinks.pp (sinks t) pp_passthroughs_if_not_empty (passthroughs t) end diff --git a/infer/src/checkers/Siof.ml b/infer/src/checkers/Siof.ml index 12ea45e4d..4ab565020 100644 --- a/infer/src/checkers/Siof.ml +++ b/infer/src/checkers/Siof.ml @@ -12,6 +12,8 @@ open! IStd module F = Format module L = Logging +module GlobalsAccesses = SiofTrace.GlobalsAccesses + module Summary = Summary.Make (struct type summary = SiofDomain.astate @@ -37,40 +39,44 @@ module TransferFunctions (CFG : ProcCfg.S) = struct | _ -> false - let get_globals astate pdesc loc e = + let get_globals pdesc loc e = let is_dangerous_global pv = Pvar.is_global pv && not (Pvar.is_static_local pv) && not (Pvar.is_pod pv) && not (Pvar.is_compile_constant pv) && not (is_compile_time_constructed pdesc pv) in - let globals = Exp.get_vars e |> snd |> IList.filter is_dangerous_global in - if globals = [] then - Domain.Bottom + let globals_accesses = + Exp.get_vars e |> snd |> IList.filter is_dangerous_global + |> IList.map (fun v -> (v, loc)) in + GlobalsAccesses.of_list globals_accesses + + let add_globals astate outer_loc globals = + if GlobalsAccesses.is_empty globals then + astate else let trace = match astate with | Domain.Bottom -> SiofTrace.initial | Domain.NonBottom t -> t in let globals_trace = - IList.fold_left (fun trace_acc global -> - SiofTrace.add_sink (SiofTrace.make_access global loc) trace_acc) - trace - globals in + SiofTrace.add_sink (SiofTrace.make_access globals outer_loc) trace in Domain.NonBottom globals_trace - let add_params_globals astate pdesc loc params = - IList.map fst params - |> IList.map (fun e -> get_globals astate pdesc loc e) - |> IList.fold_left Domain.join astate + let add_params_globals astate pdesc call_loc params = + IList.map (fun (e, _) -> get_globals pdesc call_loc e) params + |> IList.fold_left GlobalsAccesses.union GlobalsAccesses.empty + |> add_globals astate (Procdesc.get_loc pdesc) - let at_least_bottom = + let at_least_nonbottom = Domain.join (Domain.NonBottom SiofTrace.initial) - let exec_instr astate { ProcData.pdesc; } _ (instr : Sil.instr) = match instr with + let exec_instr astate { ProcData.pdesc; } _ (instr : Sil.instr) = + match instr with | Load (_, exp, _, loc) | Store (_, _, exp, loc) | Prune (exp, loc, _, _) -> - Domain.join astate (get_globals astate pdesc loc exp) + let proc_loc = Procdesc.get_loc pdesc in + get_globals pdesc loc exp |> add_globals astate proc_loc | Call (_, Const (Cfun callee_pname), _::params_without_self, loc, _) when Procname.is_c_method callee_pname && Procname.is_constructor callee_pname && Procname.is_constexpr callee_pname -> @@ -81,18 +87,18 @@ module TransferFunctions (CFG : ProcCfg.S) = struct match Summary.read_summary pdesc callee_pname with | Some (Domain.NonBottom trace) -> Domain.NonBottom (SiofTrace.with_callsite trace callsite) - | _ -> + | None | Some Domain.Bottom -> Domain.Bottom in add_params_globals astate pdesc loc params |> Domain.join callee_globals |> (* make sure it's not Bottom: we made a function call so this needs initialization *) - at_least_bottom + at_least_nonbottom | Call (_, _, params, loc, _) -> add_params_globals astate pdesc loc params |> (* make sure it's not Bottom: we made a function call so this needs initialization *) - at_least_bottom + at_least_nonbottom | Declare_locals _ | Remove_temps _ | Abstract _ | Nullify _ -> astate end @@ -106,7 +112,7 @@ module Analyzer = module Interprocedural = AbstractInterpreter.Interprocedural (Summary) -let is_foreign tu_opt v = +let is_foreign tu_opt (v, _) = let is_orig_file f = match tu_opt with | Some orig_file -> let orig_path = SourceFile.to_abs_path orig_file in @@ -123,62 +129,59 @@ let report_siof trace pdesc gname loc = | Some (SiofDomain.NonBottom summary) -> summary | _ -> SiofTrace.initial in - let pp_sink f sink = - let pp_source f v = match Pvar.get_source_file v with - | Some source_file when not (SourceFile.equal SourceFile.empty source_file) -> - F.fprintf f " from file %a" SourceFile.pp source_file - | _ -> - () in - let v = SiofTrace.Sink.kind sink in - F.fprintf f "%s%a" (Pvar.get_simplified_name v) pp_source v in - - let trace_of_error path = - let desc_of_sink sink = - let callsite = SiofTrace.Sink.call_site sink in - if SiofTrace.is_intraprocedural_access sink then - Format.asprintf "access to %s" (Pvar.get_simplified_name (SiofTrace.Sink.kind sink)) - else - Format.asprintf "call to %a" Procname.pp (CallSite.pname callsite) in - let sink_should_nest sink = not (SiofTrace.is_intraprocedural_access sink) in - let trace_elem_of_global = - Errlog.make_trace_element 0 loc - (Format.asprintf "initialization of %s" gname) - [] in - trace_elem_of_global::(SiofTrace.to_sink_loc_trace ~desc_of_sink ~sink_should_nest path) in - - let report_one_path ((_, path) as sink_path) = - let final_sink = fst (IList.hd path) in - if is_foreign tu_opt (SiofTrace.Sink.kind final_sink) then ( - let description = - F.asprintf - "The initializer of %s accesses global variables in another translation unit: %a" - gname - pp_sink final_sink in - let ltr = trace_of_error sink_path in - let caller_pname = Procdesc.get_proc_name pdesc in - let msg = Localise.to_string Localise.static_initialization_order_fiasco in - let exn = Exceptions.Checkers (msg, Localise.verbatim_desc description) in - Reporting.log_error caller_pname ~loc ~ltr exn - ); in - - IList.iter report_one_path (SiofTrace.get_reportable_sink_paths trace ~trace_of_pname) + let report_one_path (passthroughs, path) = + let description, sink_path' = match path with + | [] -> assert false + | (final_sink, pt)::rest -> + let foreign_globals = + SiofTrace.Sink.kind final_sink |> GlobalsAccesses.filter (is_foreign tu_opt) in + let final_sink' = + let loc = CallSite.loc (SiofTrace.Sink.call_site final_sink) in + SiofTrace.make_access foreign_globals loc in + let description = + F.asprintf + "Initializer of %s accesses global variables from a different translation unit: %a" + gname + GlobalsAccesses.pp foreign_globals in + description, (passthroughs, (final_sink', pt)::rest) in + let ltr = SiofTrace.trace_of_error loc gname sink_path' in + let caller_pname = Procdesc.get_proc_name pdesc in + let msg = Localise.to_string Localise.static_initialization_order_fiasco in + let exn = Exceptions.Checkers (msg, Localise.verbatim_desc description) in + Reporting.log_error caller_pname ~loc ~ltr exn in + + let has_foreign_sink (_, path) = + IList.exists + (fun (sink, _) -> + GlobalsAccesses.exists (is_foreign tu_opt) + (SiofTrace.Sink.kind sink)) + path in + + SiofTrace.get_reportable_sink_paths trace ~trace_of_pname + |> IList.filter has_foreign_sink + |> IList.iter report_one_path let siof_check pdesc gname = function | Some (SiofDomain.NonBottom post) -> let attrs = Procdesc.get_attributes pdesc in - let foreign_global_sinks = - SiofTrace.Sinks.filter - (fun sink -> is_foreign attrs.ProcAttributes.translation_unit (SiofTrace.Sink.kind sink)) - (SiofTrace.sinks post) in - if not (SiofTrace.Sinks.is_empty foreign_global_sinks) - then report_siof post pdesc gname attrs.ProcAttributes.loc; + let all_globals = SiofTrace.Sinks.fold + (fun sink -> GlobalsAccesses.union (SiofTrace.Sink.kind sink)) + (SiofTrace.sinks post) GlobalsAccesses.empty in + let tu_opt = + let attrs = Procdesc.get_attributes pdesc in + attrs.ProcAttributes.translation_unit in + if GlobalsAccesses.exists (is_foreign tu_opt) all_globals then + report_siof post pdesc gname attrs.ProcAttributes.loc; | Some SiofDomain.Bottom | None -> () +let compute_post proc_data = + Analyzer.compute_post proc_data |> Option.map ~f:SiofDomain.normalize + let checker ({ Callbacks.proc_desc; } as callback) = let post = Interprocedural.compute_and_store_post - ~compute_post:Analyzer.compute_post + ~compute_post ~make_extras:ProcData.make_empty_extras callback in let pname = Procdesc.get_proc_name proc_desc in diff --git a/infer/src/checkers/SiofDomain.ml b/infer/src/checkers/SiofDomain.ml index c41e345d2..7b2becb97 100644 --- a/infer/src/checkers/SiofDomain.ml +++ b/infer/src/checkers/SiofDomain.ml @@ -20,3 +20,24 @@ open! IStd initialization, and which globals requiring initialization a given function (transitively) accesses. *) include AbstractDomain.BottomLifted(SiofTrace) + +(** group together procedure-local accesses *) +let normalize astate = match astate with + | Bottom -> astate + | NonBottom trace -> + let elems = SiofTrace.Sinks.elements (SiofTrace.sinks trace) in + let (direct, indirect) = IList.partition SiofTrace.is_intraprocedural_access elems in + match direct with + | [] | _::[] -> astate + | access::_ -> + (* [loc] should be the same for all local accesses: it's the loc of the enclosing + procdesc. Use the loc of the first access. *) + let loc = CallSite.loc (SiofTrace.Sink.call_site access) in + let kind = + IList.map SiofTrace.Sink.kind direct + |> IList.fold_left SiofTrace.GlobalsAccesses.union SiofTrace.GlobalsAccesses.empty in + let trace' = + SiofTrace.make_access kind loc::indirect + |> SiofTrace.Sinks.of_list + |> SiofTrace.update_sinks trace in + NonBottom trace' diff --git a/infer/src/checkers/SiofDomain.mli b/infer/src/checkers/SiofDomain.mli new file mode 100644 index 000000000..c67c36e99 --- /dev/null +++ b/infer/src/checkers/SiofDomain.mli @@ -0,0 +1,23 @@ +(* + * Copyright (c) 2016 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + *) + +(* The domain for the analysis is sets of global variables if an initialization is needed at + runtime, or Bottom if no initialization is needed. For instance, `int x = 32; int y = x * 52;` + gives a summary of Bottom for both initializers corresponding to these globals, but `int x = + foo();` gives a summary of at least "NonBottom {}" for x's initializer since x will need runtime + initialization. + + The encoding in terms of a BottomLifted domain is an efficiency hack to represent two pieces of + information: whether a global variable (via its initializer function) requires runtime + initialization, and which globals requiring initialization a given function (transitively) + accesses. *) +include module type of (AbstractDomain.BottomLifted(SiofTrace)) + +(** group together procedure-local accesses *) +val normalize : astate -> astate diff --git a/infer/src/checkers/SiofTrace.ml b/infer/src/checkers/SiofTrace.ml index 15b94bdc3..0347adf33 100644 --- a/infer/src/checkers/SiofTrace.ml +++ b/infer/src/checkers/SiofTrace.ml @@ -12,14 +12,20 @@ open! IStd module F = Format module L = Logging -module Global = struct - type t = Pvar.t - let compare = Pvar.compare - let pp fmt pvar = (Pvar.pp Pp.text) fmt pvar -end +module GlobalsAccesses = PrettyPrintable.MakePPSet (struct + type t = (Pvar.t * Location.t) + let compare (v1, l1) (v2, l2) = + (* compare by loc first to present reports in the right order *) + [%compare : (Location.t * Pvar.t)] (l1, v1) (l2, v2) + let pp_element fmt (v, _) = + F.fprintf fmt "%a" Mangled.pp (Pvar.get_name v); + match Pvar.get_source_file v with + | Some fname -> F.fprintf fmt "%a" SourceFile.pp fname + | None -> () + end) module TraceElem = struct - module Kind = Global + module Kind = GlobalsAccesses type t = { site : CallSite.t; @@ -35,9 +41,16 @@ module TraceElem = struct let with_callsite { kind=(_, kind); } site = { kind=(`Call, kind); site; } let pp fmt { site; kind; } = - F.fprintf fmt "Access to %a at %a" Kind.pp (snd kind) CallSite.pp site + F.fprintf fmt "%saccess to %a" + (match fst kind with | `Call -> "indirect " | `Access -> "") + Kind.pp (snd kind); + match fst kind with + | `Call -> F.fprintf fmt " at %a" CallSite.pp site + | `Access -> () module Set = PrettyPrintable.MakePPSet (struct + (* Don't use nonrec due to https://github.com/janestreet/ppx_compare/issues/2 *) + (* type nonrec t = t [@@deriving compare]; *) type nonrec t = t let compare = compare let pp_element = pp @@ -52,3 +65,35 @@ let make_access kind loc = { TraceElem.kind = (`Access, kind); site; } let is_intraprocedural_access { TraceElem.kind=(kind, _); } = kind = `Access + +let trace_of_error loc gname path = + let desc_of_sink sink = + let callsite = Sink.call_site sink in + if is_intraprocedural_access sink then + Format.asprintf "%a" Sink.pp sink + else + Format.asprintf "call to %a" Procname.pp (CallSite.pname callsite) in + let sink_should_nest sink = not (is_intraprocedural_access sink) in + let trace_elem_of_global = + Errlog.make_trace_element 0 loc + (Format.asprintf "initialization of %s" gname) + [] in + let trace = + let trace_with_set_of_globals = to_sink_loc_trace ~desc_of_sink ~sink_should_nest path in + (* the last element of the trace gotten by [to_sink_loc_trace] contains a set of procedure-local + accesses to globals. We want to remove it in exchange for as many trace elems as there are + accesses. *) + match (IList.rev trace_with_set_of_globals, snd path) with + | telem::rest, ({TraceElem.kind = (`Access, globals)}, _)::_ -> + let nesting = telem.Errlog.lt_level in + let add_trace_elem_of_access err_trace (global, loc) = + Errlog.make_trace_element nesting loc + (Format.asprintf "access to %a" Mangled.pp (Pvar.get_name global)) + [] + ::err_trace in + GlobalsAccesses.elements globals + |> IList.fold_left add_trace_elem_of_access rest + |> IList.rev + | _ -> trace_with_set_of_globals + in + trace_elem_of_global::trace diff --git a/infer/src/checkers/SiofTrace.mli b/infer/src/checkers/SiofTrace.mli index 452277543..a891e8225 100644 --- a/infer/src/checkers/SiofTrace.mli +++ b/infer/src/checkers/SiofTrace.mli @@ -9,15 +9,12 @@ open! IStd -module Global : -sig - type t = Pvar.t - val compare : t -> t -> int - val pp : Format.formatter -> t -> unit -end +module GlobalsAccesses : PrettyPrintable.PPSet with type elt = (Pvar.t * Location.t) -include SinkTrace.S with module Sink.Kind = Global +include SinkTrace.S with type Sink.Kind.t = GlobalsAccesses.t -val make_access : Global.t -> Location.t -> Sink.t +val make_access : GlobalsAccesses.t -> Location.t -> Sink.t val is_intraprocedural_access : Sink.t -> bool + +val trace_of_error : Location.t -> string -> sink_path -> Errlog.loc_trace_elem list diff --git a/infer/src/checkers/Trace.ml b/infer/src/checkers/Trace.ml index 430c47237..8fc59ad0b 100644 --- a/infer/src/checkers/Trace.ml +++ b/infer/src/checkers/Trace.ml @@ -70,6 +70,8 @@ module type S = sig (** add a sink to the current trace. *) val add_sink : Sink.t -> t -> t + val update_sinks : t -> Sinks.t -> t + (** append the trace for given call site to the current caller trace *) val append : t -> t -> CallSite.t -> t @@ -334,6 +336,8 @@ module Make (Spec : Spec) = struct let sinks = Sinks.add sink t.sinks in { t with sinks; } + let update_sinks t sinks = { t with sinks } + (** compute caller_trace + callee_trace *) let append caller_trace callee_trace callee_site = if is_empty callee_trace diff --git a/infer/src/checkers/Trace.mli b/infer/src/checkers/Trace.mli index a2f3fe791..29e388b9f 100644 --- a/infer/src/checkers/Trace.mli +++ b/infer/src/checkers/Trace.mli @@ -70,6 +70,9 @@ module type S = sig (** add a sink to the current trace. *) val add_sink : Sink.t -> t -> t + (** replace sinks with new ones *) + val update_sinks : t -> Sinks.t -> t + (** append the trace for given call site to the current caller trace *) val append : t -> t -> CallSite.t -> t diff --git a/infer/src/checkers/summary.ml b/infer/src/checkers/summary.ml index 285bc5a94..be05f1812 100644 --- a/infer/src/checkers/summary.ml +++ b/infer/src/checkers/summary.ml @@ -13,25 +13,24 @@ module type Helper = sig type summary (* type astate*) - (* update the specs payload with [summary] *) + (** update the specs payload with [summary] *) val update_payload : summary -> Specs.payload -> Specs.payload - (* extract [summmary] from the specs payload *) - val read_from_payload : Specs.payload -> summary option - (*val to_summary : astate -> summary*) + (** extract [summmary] from the specs payload *) + val read_from_payload : Specs.payload -> summary option end module type S = sig type summary (* type astate*) - (* write the summary for [name] to persistent storage *) + (** Write the [summary] for the procname to persistent storage. Returns the summary actually + written. *) val write_summary : Procname.t -> summary -> unit - (* read and return the summary for [callee_pname] called from [caller_pdesc]. does the analysis to - create the summary if needed *) - val read_summary : Procdesc.t -> Procname.t -> summary option - (* val to_summary : astate -> summary*) + (** read and return the summary for [callee_pname] called from [caller_pdesc]. does the analysis + to create the summary if needed *) + val read_summary : Procdesc.t -> Procname.t -> summary option end module Make (H : Helper) = struct diff --git a/infer/src/checkers/summary.mli b/infer/src/checkers/summary.mli new file mode 100644 index 000000000..2a0a5ce04 --- /dev/null +++ b/infer/src/checkers/summary.mli @@ -0,0 +1,33 @@ +(* + * Copyright (c) 2016 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + *) + +module type Helper = sig + type summary + + (** update the specs payload with [summary] *) + val update_payload : summary -> Specs.payload -> Specs.payload + + (** extract [summmary] from the specs payload *) + val read_from_payload : Specs.payload -> summary option +end + +module type S = sig + type summary + + (** Write the [summary] for the procname to persistent storage. Returns the summary actually + written. *) + val write_summary : Procname.t -> summary -> unit + + (** read and return the summary for [callee_pname] called from [caller_pdesc]. does the analysis + to create the summary if needed *) + val read_summary : Procdesc.t -> Procname.t -> summary option + +end + +module Make (H : Helper) : S with type summary = H.summary diff --git a/infer/src/checkers/transferFunctions.ml b/infer/src/checkers/transferFunctions.ml index cfec4b57a..e79d392ca 100644 --- a/infer/src/checkers/transferFunctions.ml +++ b/infer/src/checkers/transferFunctions.ml @@ -14,10 +14,14 @@ open! IStd module type S = sig module CFG : ProcCfg.S - module Domain : AbstractDomain.S (* abstract domain whose state we propagate *) - type extras (* read-only extra state (results of previous analyses, globals, etc.) *) - (* {A} instr {A'}. [node] is the node of the current instruction *) + (** abstract domain whose state we propagate *) + module Domain : AbstractDomain.S + + (** read-only extra state (results of previous analyses, globals, etc.) *) + type extras + + (** {A} instr {A'}. [node] is the node of the current instruction *) val exec_instr : Domain.astate -> extras ProcData.t -> CFG.node -> Sil.instr -> Domain.astate end diff --git a/infer/src/checkers/transferFunctions.mli b/infer/src/checkers/transferFunctions.mli index cfec4b57a..e79d392ca 100644 --- a/infer/src/checkers/transferFunctions.mli +++ b/infer/src/checkers/transferFunctions.mli @@ -14,10 +14,14 @@ open! IStd module type S = sig module CFG : ProcCfg.S - module Domain : AbstractDomain.S (* abstract domain whose state we propagate *) - type extras (* read-only extra state (results of previous analyses, globals, etc.) *) - (* {A} instr {A'}. [node] is the node of the current instruction *) + (** abstract domain whose state we propagate *) + module Domain : AbstractDomain.S + + (** read-only extra state (results of previous analyses, globals, etc.) *) + type extras + + (** {A} instr {A'}. [node] is the node of the current instruction *) val exec_instr : Domain.astate -> extras ProcData.t -> CFG.node -> Sil.instr -> Domain.astate end diff --git a/infer/tests/codetoanalyze/cpp/checkers/issues.exp b/infer/tests/codetoanalyze/cpp/checkers/issues.exp index fc5c5f10e..d8f3ec4fb 100644 --- a/infer/tests/codetoanalyze/cpp/checkers/issues.exp +++ b/infer/tests/codetoanalyze/cpp/checkers/issues.exp @@ -1,9 +1,9 @@ -codetoanalyze/cpp/checkers/siof/siof.cpp, __infer_globals_initializer_X::static_pod_accesses_non_pod, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of X::static_pod_accesses_non_pod,call to access_to_non_pod,access to global_object2] +codetoanalyze/cpp/checkers/siof/siof.cpp, __infer_globals_initializer_X::static_pod_accesses_non_pod, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of X::static_pod_accesses_non_pod,call to access_to_non_pod,access to global_object2,access to some_other_global_object2] codetoanalyze/cpp/checkers/siof/siof.cpp, __infer_globals_initializer_another_global_object, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of another_global_object,call to SomeOtherNonPODObject_SomeOtherNonPODObject,access to extern_global_object] -codetoanalyze/cpp/checkers/siof/siof.cpp, __infer_globals_initializer_another_global_object2, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of another_global_object2,call to access_to_non_pod,access to global_object2] +codetoanalyze/cpp/checkers/siof/siof.cpp, __infer_globals_initializer_another_global_object2, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of another_global_object2,call to access_to_non_pod,access to global_object2,access to some_other_global_object2] codetoanalyze/cpp/checkers/siof/siof.cpp, __infer_globals_initializer_another_global_object3, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of another_global_object3,call to access_to_templated_non_pod,access to global_object3] -codetoanalyze/cpp/checkers/siof/siof.cpp, __infer_globals_initializer_initWithGlobal, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of initWithGlobal,call to getGlobalNonPOD,access to global_object2] -codetoanalyze/cpp/checkers/siof/siof.cpp, __infer_globals_initializer_pod_accesses_non_pod, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of pod_accesses_non_pod,call to access_to_non_pod,access to global_object2] +codetoanalyze/cpp/checkers/siof/siof.cpp, __infer_globals_initializer_initWithGlobal, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of initWithGlobal,call to getGlobalNonPOD,access to some_other_global_object2,access to global_object2] +codetoanalyze/cpp/checkers/siof/siof.cpp, __infer_globals_initializer_pod_accesses_non_pod, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of pod_accesses_non_pod,call to access_to_non_pod,access to global_object2,access to some_other_global_object2] codetoanalyze/cpp/checkers/siof/siof_templated.cpp, __infer_globals_initializer_another_templated_global_object, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of another_templated_global_object,call to SomeOtherTemplatedNonPODObject<_Bool>_SomeOtherTemplatedNonPODObject,access to extern_global_object] -codetoanalyze/cpp/checkers/siof/siof_templated.cpp, __infer_globals_initializer_another_templated_global_object2, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of another_templated_global_object2,call to access_to_non_pod,access to global_object2] +codetoanalyze/cpp/checkers/siof/siof_templated.cpp, __infer_globals_initializer_another_templated_global_object2, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of another_templated_global_object2,call to access_to_non_pod,access to global_object2,access to some_other_global_object2] codetoanalyze/cpp/checkers/siof/siof_templated.cpp, __infer_globals_initializer_another_templated_global_object3, 0, STATIC_INITIALIZATION_ORDER_FIASCO, [initialization of another_templated_global_object3,call to access_to_templated_non_pod,access to global_object3] diff --git a/infer/tests/codetoanalyze/cpp/checkers/siof/siof_different_tu.cpp b/infer/tests/codetoanalyze/cpp/checkers/siof/siof_different_tu.cpp index 5809ad90d..cf7931766 100644 --- a/infer/tests/codetoanalyze/cpp/checkers/siof/siof_different_tu.cpp +++ b/infer/tests/codetoanalyze/cpp/checkers/siof/siof_different_tu.cpp @@ -10,9 +10,12 @@ #include "siof_types.h" SomeNonPODObject global_object2; +SomeNonPODObject some_other_global_object2; int access_to_non_pod() { global_object2.some_method(); + // access several global objects to check that we group the reports together + some_other_global_object2.some_method(); return 5; } @@ -25,7 +28,10 @@ SomeNonPODObject& getFunctionStaticNonPOD() { return instance; } -SomeNonPODObject& getGlobalNonPOD() { return global_object2; } +SomeNonPODObject& getGlobalNonPOD() { + some_other_global_object2.some_method(); + return global_object2; +} // initialise static class field SomeConstexprObject SomeConstexprObject::instance_;