[ocamlformat] Enable parsing and reformatting docstrings

Summary:
This diff enables parsing and auto-formatting documentation
comments (aka docstrings).

I have looked at this entire diff and manually made some changes to
improve the formatting. In some cases it looked like it would take too
much time, or benefit from someone more familiar with the code doing
it, and I instead disabled auto-formatting docstrings in those files.

Also, there are some source files where the docstrings are invalid,
and some where the structure detected by the parser appears not to
match what was intended. Auto-formatting has been disabled for these
files.

Reviewed By: ezgicicek

Differential Revision: D18755888

fbshipit-source-id: 68d72465d
master
Josh Berdine 5 years ago committed by Facebook Github Bot
parent 0319dac803
commit 3c6e2469de

@ -2,4 +2,5 @@ profile = ocamlformat
break-before-in = fit-or-vertical
let-binding-spacing = sparse
margin = 100
parse-docstrings = true
version = 0.12-21-gdd929ea

@ -17,23 +17,23 @@ type access =
[@@deriving compare]
(** root var, and a list of accesses. closest to the root var is first that is, x.f.g is
representedas (x, [f; g]) *)
representedas (x, [f; g]) *)
and t = base * access list [@@deriving compare]
val truncate : t -> t * access option
(** remove and return the last access of the access path if the access list is non-empty. returns
(** remove and return the last access of the access path if the access list is non-empty. returns
the original access path * None if the access list is empty *)
val get_last_access : t -> access option
(** get the last access in the list. returns None if the list is empty *)
val get_field_and_annotation : t -> Tenv.t -> (Typ.Fieldname.t * Annot.Item.t) option
(** get the field name and the annotation of the last access in the list of accesses if
the list is non-empty and the last access is a field access *)
(** get the field name and the annotation of the last access in the list of accesses if the list is
non-empty and the last access is a field access *)
val get_typ : t -> Tenv.t -> Typ.t option
(** get the typ of the last access in the list of accesses if the list is non-empty, or the base
if the list is empty. that is, for x.f.g, return typ(g), and for x, return typ(x) *)
(** get the typ of the last access in the list of accesses if the list is non-empty, or the base if
the list is empty. that is, for x.f.g, return typ(g), and for x, return typ(x) *)
val base_of_pvar : Pvar.t -> Typ.t -> base
(** create a base from a pvar *)
@ -46,7 +46,8 @@ val of_id : Ident.t -> Typ.t -> t
val of_exp :
include_array_indexes:bool -> Exp.t -> Typ.t -> f_resolve_id:(Var.t -> t option) -> t list
(** extract the access paths that occur in [exp], resolving identifiers using [f_resolve_id]. don't include index expressions in array accesses if [include_array_indexes] is false *)
(** extract the access paths that occur in [exp], resolving identifiers using [f_resolve_id]. don't
include index expressions in array accesses if [include_array_indexes] is false *)
val of_lhs_exp :
include_array_indexes:bool -> Exp.t -> Typ.t -> f_resolve_id:(Var.t -> t option) -> t option
@ -62,15 +63,13 @@ val is_prefix : t -> t -> bool
val replace_prefix : prefix:t -> t -> t -> t option [@@warning "-32"]
val inner_class_normalize : t -> t
(** transform an access path that starts on "this" of an inner class but which breaks out to
access outer class fields to the outermost one.
Cases handled (recursively):
- (this:InnerClass* ).(this$n:OuterClassAccessor).f. ... -> (this:OuterClass* ).f . ...
- this$n.(this$m:OuterClassAccessor).f ... -> (this$m:OuterClass* ).f . ...
(happens in ctrs only)
- this$n.f ... -> this.f . ...
(happens in ctrs only)
*)
(** transform an access path that starts on "this" of an inner class but which breaks out to access
outer class fields to the outermost one. Cases handled (recursively):
- (this:InnerClass* ).(this$n:OuterClassAccessor).f. ... -> (this:OuterClass* ).f . ...
- this$n.(this$m:OuterClassAccessor).f ... -> (this$m:OuterClass* ).f . ... (happens in ctrs
only)
- this$n.f ... -> this.f . ... (happens in ctrs only) *)
val equal : t -> t -> bool
@ -99,11 +98,11 @@ module Abs : sig
val get_footprint_index_base : base -> int option
(** return the formal index associated with the base of this access path if there is one, or None
otherwise *)
otherwise *)
val with_base : base -> t -> t
(** swap base of existing access path for [base_var] (e.g., `with_base_bvar x y.f.g` produces
`x.f.g` *)
`x.f.g` *)
val extract : t -> raw
(** extract a raw access path from its wrapper *)

@ -15,7 +15,7 @@ type parameter = {name: string option; value: string} [@@deriving compare]
type parameters = parameter list [@@deriving compare]
(** Type to represent one @Annotation. *)
(** Type to represent one [@Annotation]. *)
type t =
{ class_name: string (** name of the annotation *)
; parameters: parameters (** currently only one string parameter *) }

@ -15,7 +15,7 @@ type parameter = {name: string option; value: string}
type parameters = parameter list
(** Type to represent one @Annotation. *)
(** Type to represent one [@Annotation]. *)
type t =
{ class_name: string (** name of the annotation *)
; parameters: parameters (** currently only one string parameter *) }

@ -23,8 +23,7 @@ val load_defined : Typ.Procname.t -> ProcAttributes.t option
(** Load attributes for the procedure but only if is_defined is true *)
val find_file_capturing_procedure : Typ.Procname.t -> (SourceFile.t * [`Include | `Source]) option
(** Find the file where the procedure was captured, if a cfg for that file exists.
Return also a boolean indicating whether the procedure is defined in an
include file. *)
(** Find the file where the procedure was captured, if a cfg for that file exists. Return also a
boolean indicating whether the procedure is defined in an include file. *)
val pp_attributes_kind : Format.formatter -> attributes_kind -> unit

@ -26,8 +26,8 @@ type t =
| Mod (** % *)
| Shiftlt (** shift left *)
| Shiftrt (** shift right *)
| Lt (** < (arithmetic comparison) *)
| Gt (** > (arithmetic comparison) *)
| Lt (** < (arithmetic comparison) *)
| Gt (** > (arithmetic comparison) *)
| Le (** <= (arithmetic comparison) *)
| Ge (** >= (arithmetic comparison) *)
| Eq (** == (arithmetic comparison) *)
@ -41,13 +41,12 @@ type t =
let equal = [%compare.equal: t]
(** This function returns true if the operation is injective
wrt. each argument: op(e,-) and op(-, e) is injective for all e.
The return value false means "don't know". *)
(** This function returns true if the operation is injective wrt. each argument: op(e,-) and op(-,
e) is injective for all e. The return value false means "don't know". *)
let injective = function PlusA _ | PlusPI | MinusA _ | MinusPI | MinusPP -> true | _ -> false
(** This function returns true if 0 is the right unit of [binop].
The return value false means "don't know". *)
(** This function returns true if 0 is the right unit of [binop]. The return value false means
"don't know". *)
let is_zero_runit = function PlusA _ | PlusPI | MinusA _ | MinusPI | MinusPP -> true | _ -> false
let text = function

@ -22,8 +22,8 @@ type t =
| Mod (** % *)
| Shiftlt (** shift left *)
| Shiftrt (** shift right *)
| Lt (** < (arithmetic comparison) *)
| Gt (** > (arithmetic comparison) *)
| Lt (** < (arithmetic comparison) *)
| Gt (** > (arithmetic comparison) *)
| Le (** <= (arithmetic comparison) *)
| Ge (** >= (arithmetic comparison) *)
| Eq (** == (arithmetic comparison) *)
@ -38,13 +38,12 @@ type t =
val equal : t -> t -> bool
val injective : t -> bool
(** This function returns true if the operation is injective
wrt. each argument: op(e,-) and op(-, e) is injective for all e.
The return value false means "don't know". *)
(** This function returns true if the operation is injective wrt. each argument: op(e,-) and op(-,
e) is injective for all e. The return value false means "don't know". *)
val is_zero_runit : t -> bool
(** This function returns true if 0 is the right unit of [binop].
The return value false means "don't know". *)
(** This function returns true if 0 is the right unit of [binop]. The return value false means
"don't know". *)
val str : Pp.env -> t -> string
(** String representation of a binary operator. *)

@ -18,7 +18,7 @@ val get_all_defined_proc_names : t -> Typ.Procname.t list
val store : SourceFile.t -> t -> unit
(** Save the individual [Procdesc.t] and [ProcAttributes.t] to the database for the procedures in
the cfg. *)
the cfg. *)
(** {2 Functions for manipulating an interprocedural CFG} *)

@ -27,8 +27,8 @@ type t =
| Dunknown
| Dretcall of t * t list * Location.t * CallFlags.t
(** Value paths: identify an occurrence of a value in a symbolic heap
each expression represents a path, with Dpvar being the simplest one *)
(** Value paths: identify an occurrence of a value in a symbolic heap each expression represents a
path, with Dpvar being the simplest one *)
type vpath = t option
let eradicate_java () = Config.eradicate && Language.curr_language_is Java

@ -27,8 +27,8 @@ type t =
| Dunknown
| Dretcall of t * t list * Location.t * CallFlags.t
(** Value paths: identify an occurrence of a value in a symbolic heap
each expression represents a path, with Dpvar being the simplest one *)
(** Value paths: identify an occurrence of a value in a symbolic heap each expression represents a
path, with Dpvar being the simplest one *)
type vpath = t option
val pp : F.formatter -> t -> unit

@ -141,9 +141,8 @@ module ErrLogHash = struct
include Hashtbl.Make (Key)
end
(** Type of the error log, to be reset once per function.
Map severity, footprint / re - execution flag, error name,
error description, severity, to set of err_data. *)
(** Type of the error log, to be reset once per function. Map severity, footprint / re - execution
flag, error name, error description, severity, to set of err_data. *)
type t = ErrDataSet.t ErrLogHash.t
(** Empty error log *)
@ -215,8 +214,8 @@ let pp_html source path_to_root fmt (errlog : t) =
List.iter Exceptions.[Advice; Error; Info; Like; Warning] ~f:pp
(** Add an error description to the error log unless there is
one already at the same node + session; return true if added *)
(** Add an error description to the error log unless there is one already at the same node +
session; return true if added *)
let add_issue tbl err_key (err_datas : ErrDataSet.t) : bool =
try
let current_eds = ErrLogHash.find tbl err_key in

@ -31,9 +31,8 @@ type loc_trace = loc_trace_elem list
val concat_traces : (string * loc_trace) list -> loc_trace
val compute_local_exception_line : loc_trace -> int option
(** Look at all the trace steps and find those that are arising any exception,
then bind them to the closest step at level 0.
This extra information adds value to the report itself, and may avoid
(** Look at all the trace steps and find those that are arising any exception, then bind them to the
closest step at level 0. This extra information adds value to the report itself, and may avoid
digging into the trace to understand the cause of the report. *)
type node =
@ -52,8 +51,8 @@ val merge_err_key :
-> merge_descriptions:(string list -> string list -> string)
-> err_key
(** Merges two error keys, setting the result's severity to the maximum of that of the two arguments
and giving the user the opportunity to pass a function to merge the IssueTypes and descriptions
of the two. *)
and giving the user the opportunity to pass a function to merge the IssueTypes and descriptions
of the two. *)
(** Data associated to a specific error *)
type err_data = private
@ -73,8 +72,8 @@ type err_data = private
val merge_err_data : err_data -> err_data -> err_data
(** Merges two err_datas, throwing out most information and setting the trace of the result to the
concatenation of the traces of the two arguments with a separator in between. Used specifically
for QuandaryBO. *)
concatenation of the traces of the two arguments with a separator in between. Used specifically
for QuandaryBO. *)
(** Type of the error log *)
type t
@ -104,7 +103,7 @@ val pp_html : SourceFile.t -> DB.Results_dir.path -> Format.formatter -> t -> un
(** Print an error log in html format *)
val size : (Exceptions.severity -> bool) -> t -> int
(** Return the number of elements in the error log which satisfy the filter. *)
(** Return the number of elements in the error log which satisfy the filter. *)
val update : t -> t -> unit
(** Update an old error log with a new one *)

@ -585,7 +585,7 @@ let severity_string = function
"WARNING"
(** pretty print an error *)
(** pretty print an error *)
let pp_err loc severity ex_name desc ocaml_pos_opt fmt () =
let kind = severity_string (if equal_severity severity Info then Warning else severity) in
F.fprintf fmt "%a:%d: %s: %a %a%a@\n" SourceFile.pp loc.Location.file loc.Location.line kind

@ -81,8 +81,8 @@ let rec is_const = function Const _ -> true | Cast (_, x) -> is_const x | _ -> f
(** {2 Utility Functions for Expressions} *)
(** Turn an expression representing a type into the type it represents
If not a sizeof, return the default type if given, otherwise raise an exception *)
(** Turn an expression representing a type into the type it represents If not a sizeof, return the
default type if given, otherwise raise an exception *)
let texp_to_typ default_opt = function
| Sizeof {typ} ->
typ
@ -111,8 +111,8 @@ let rec root_of_lexp lexp =
lexp
(** Checks whether an expression denotes a location by pointer arithmetic.
Currently, catches array-indexing expressions such as a[i] only. *)
(** Checks whether an expression denotes a location by pointer arithmetic. Currently, catches
array-indexing expressions such as a[i] only. *)
let rec pointer_arith = function
| Lfield (e, _, _) ->
pointer_arith e

@ -8,8 +8,7 @@
(** The Smallfoot Intermediate Language: Expressions
NOTE: For doing substitutionson expressions, there are some functions in [Sil].
*)
NOTE: For doing substitutionson expressions, there are some functions in [Sil]. *)
open! IStd
module F = Format
@ -68,19 +67,19 @@ val is_const : t -> bool
(** {2 Utility Functions for Expressions} *)
val texp_to_typ : Typ.t option -> t -> Typ.t
(** Turn an expression representing a type into the type it represents
If not a sizeof, return the default type if given, otherwise raise an exception *)
(** Turn an expression representing a type into the type it represents If not a sizeof, return the
default type if given, otherwise raise an exception *)
val root_of_lexp : t -> t
(** Return the root of [lexp]. *)
val get_undefined : bool -> t
(** Get an expression "undefined", the boolean indicates
whether the undefined value goest into the footprint *)
(** Get an expression "undefined", the boolean indicates whether the undefined value goest into the
footprint *)
val pointer_arith : t -> bool
(** Checks whether an expression denotes a location using pointer arithmetic.
Currently, catches array - indexing expressions such as a[i] only. *)
(** Checks whether an expression denotes a location using pointer arithmetic. Currently, catches
array - indexing expressions such as a[i] only. *)
val has_local_addr : t -> bool
(** returns true if the expression operates on address of local variable *)
@ -134,7 +133,7 @@ val program_vars : t -> Pvar.t Sequence.t
val rename_pvars : f:(string -> string) -> t -> t
(** Rename all Pvars according to the function [f]. WARNING: You want to rename pvars before you
combine expressions from different symbolic states, which you RARELY want to.*)
combine expressions from different symbolic states, which you RARELY want to.*)
val fold_captured : f:('a -> t -> 'a) -> t -> 'a -> 'a
(** Fold over the expressions captured by this expression. *)

@ -36,12 +36,12 @@ module Access = struct
end
(** Module where unsafe construction of [access_expression] is allowed. In the rest of the code, and
especially in clients of the whole [AccessExpression] module, we do not want to allow
constructing access expressions directly as they could introduce de-normalized expressions of the
form [AddressOf (Dereference t)] or [Dereference (AddressOf t)].
especially in clients of the whole [AccessExpression] module, we do not want to allow
constructing access expressions directly as they could introduce de-normalized expressions of
the form [AddressOf (Dereference t)] or [Dereference (AddressOf t)].
We could make only the types of [AddressOf] and [Dereference] private but that proved too
cumbersome... *)
cumbersome... *)
module T : sig
type t =
| AccessExpression of access_expression
@ -648,9 +648,8 @@ let rec eval_boolean_binop op var e1 e2 =
|> Option.map ~f:(fun (b1, b2) -> op b1 b2)
(** return [Some bool_value] if the given boolean expression evaluates to bool_value when
[var] is set to true. return None if it has free variables that stop us from
evaluating it *)
(** return [Some bool_value] if the given boolean expression evaluates to bool_value when [var] is
set to true. return None if it has free variables that stop us from evaluating it *)
and eval_boolean_exp var = function
| AccessExpression access_expr when AccessExpression.equal access_expr var ->
Some true

@ -31,8 +31,8 @@ type t =
| Constant of Const.t (** Constants *)
| Cast of Typ.t * t (** Type cast *)
| Sizeof of Typ.t * t option
(** C-style sizeof(), and also used to treate a type as an expression. Refer to [Exp] module for
canonical documentation *)
(** C-style sizeof(), and also used to treate a type as an expression. Refer to [Exp] module
for canonical documentation *)
and access_expression = private
| Base of AccessPath.base
@ -81,12 +81,12 @@ module AccessExpression : sig
val add_access : access_expression -> t option Access.t -> access_expression option
val truncate : access_expression -> (access_expression * t option Access.t) option
(** remove and return the prefix and the last access of the expression if it's a base;
otherwise return None *)
(** remove and return the prefix and the last access of the expression if it's a base; otherwise
return None *)
val append : onto:access_expression -> access_expression -> access_expression option
(** [append ~onto y] replaces the base of [y] with [onto] itself; this makes sense if no
[Dereference (AddressOf _)] instances are introduced *)
(** [append ~onto y] replaces the base of [y] with [onto] itself; this makes sense if no
[Dereference (AddressOf _)] instances are introduced *)
type nonrec t = access_expression = private
| Base of AccessPath.base
@ -124,9 +124,9 @@ val is_int_zero : t -> bool
val eval : t -> Const.t option
val eval_boolean_exp : AccessExpression.t -> t -> bool option
(** [eval_boolean_exp var exp] returns [Some bool_value] if the given boolean expression [exp]
evaluates to [bool_value] when [var] is set to true. Return None if it has free variables
that stop us from evaluating it, or is not a boolean expression. *)
(** [eval_boolean_exp var exp] returns [Some bool_value] if the given boolean expression [exp]
evaluates to [bool_value] when [var] is set to true. Return None if it has free variables that
stop us from evaluating it, or is not a boolean expression. *)
val ignore_cast : t -> t

@ -42,9 +42,9 @@ let pp fmt = function
type translation = Instr of t | Bind of Var.t * HilExp.AccessExpression.t
(** convert an SIL instruction into an HIL instruction. The [f_resolve_id] function should map an
SSA temporary variable to the access path it represents. Evaluating the HIL instruction should
produce the same result as evaluating the SIL instruction and replacing the temporary variables
using [f_resolve_id]. *)
SSA temporary variable to the access path it represents. Evaluating the HIL instruction should
produce the same result as evaluating the SIL instruction and replacing the temporary variables
using [f_resolve_id]. *)
let of_sil ~include_array_indexes ~f_resolve_id (instr : Sil.instr) =
let exp_of_sil ?(add_deref = false) =
HilExp.of_sil ~include_array_indexes ~f_resolve_id ~add_deref

@ -36,6 +36,6 @@ val of_sil :
-> Sil.instr
-> translation
(** convert an SIL instruction into an HIL instruction. The [f_resolve_id] function should map an
SSA temporary variable to the access path it represents. Evaluating the HIL instruction should
produce the same result as evaluating the SIL instruction and replacing the temporary variables
using [f_resolve_id]. *)
SSA temporary variable to the access path it represents. Evaluating the HIL instruction should
produce the same result as evaluating the SIL instruction and replacing the temporary variables
using [f_resolve_id]. *)

@ -46,8 +46,9 @@ let equal_name = [%compare.equal: name]
type kind =
| KNone
(** special kind of "null ident" (basically, a more compact way of implementing an ident option).
useful for situations when an instruction requires an id, but no one should read the result. *)
(** special kind of "null ident" (basically, a more compact way of implementing an ident
option). useful for situations when an instruction requires an id, but no one should read
the result. *)
| KFootprint
| KNormal
| KPrimed

@ -98,7 +98,8 @@ val create_fresh : kind -> t
(** Create a fresh identifier with default name for the given kind. *)
val create_fresh_specialized_with_blocks : kind -> t
(** Create a fresh identifier with default name for the given kind, with a non-clashing id for objc block specialization *)
(** Create a fresh identifier with default name for the given kind, with a non-clashing id for objc
block specialization *)
val create_path : string -> t
(** Generate a normal identifier whose name encodes a path given as a string. *)

@ -20,8 +20,8 @@ val compare : t -> t -> int
(** compare integers ignoring the distinction between pointers and non-pointers *)
val compare_value : t -> t -> int
(** compare the value of the integers, notice this is different from const compare,
which distinguished between signed and unsigned +1 *)
(** compare the value of the integers, notice this is different from const compare, which
distinguished between signed and unsigned +1 *)
val div : t -> t -> t

@ -50,10 +50,9 @@ module Html : sig
-> Format.formatter
-> int
-> unit
(** Print an html link to the given node.
Usage: [pp_node_link path_to_root ... fmt id].
[path_to_root] is the path to the dir for the procedure in the spec db.
[id] is the node identifier. *)
(** Print an html link to the given node. Usage: [pp_node_link path_to_root ... fmt id].
[path_to_root] is the path to the dir for the procedure in the spec db. [id] is the node
identifier. *)
val pp_proc_link : DB.Results_dir.path -> Typ.Procname.t -> Format.formatter -> string -> unit
(** Print an html link to the given proc *)

@ -16,12 +16,12 @@ val iter : f:(Typ.Procname.t -> Errlog.t -> unit) -> t -> unit
(** iterate a function on map contents *)
val get_or_add : proc:Typ.Procname.t -> t -> t * Errlog.t
(** Get the error log for a given procname. If there is none, add an empty one to the map.
Return the resulting map together with the errlog. *)
(** Get the error log for a given procname. If there is none, add an empty one to the map. Return
the resulting map together with the errlog. *)
val store : dir:string -> file:SourceFile.t -> t -> unit
(** If there are any issues in the log, [store ~dir ~file] stores map to [infer-out/dir/file].
Otherwise, no file is written. *)
val load : string -> t
(** [load directory] walks [infer-out/directory], merging maps stored in files into one map. *)
(** [load directory] walks [infer-out/directory], merging maps stored in files into one map. *)

@ -34,7 +34,7 @@ module Tags = struct
(** describes a NPE that comes from field not nullable *)
let field_not_null_checked = "field_not_null_checked"
(** @Nullable-annoted field/param/retval that causes a warning *)
(** [@Nullable]-annoted field/param/retval that causes a warning *)
let nullable_src = "nullable_src"
(** Weak variable captured in a block that causes a warning *)

@ -7,10 +7,11 @@
open! IStd
(** This module handles C or Objective-C types for which there are special rules for memory management *)
(** This module handles C or Objective-C types for which there are special rules for memory
management *)
(** This module models special c struct types from the Apple's Core Foundation libraries
for which there are particular rules for memory management. *)
(** This module models special c struct types from the Apple's Core Foundation libraries for which
there are particular rules for memory management. *)
module Core_foundation_model = struct
let core_foundation =

@ -7,8 +7,8 @@
open! IStd
(** This module models special c struct types from the Apple's Core Foundation libraries
for which there are particular rules for memory management. *)
(** This module models special c struct types from the Apple's Core Foundation libraries for which
there are particular rules for memory management. *)
val is_core_lib_type : Typ.t -> bool

@ -47,8 +47,7 @@ let equal_res_act_kind = [%compare.equal: res_act_kind]
type dangling_kind =
| DAuninit (** pointer is dangling because it is uninitialized *)
| DAaddr_stack_var
(** pointer is dangling because it is the address
of a stack variable which went out of scope *)
(** pointer is dangling because it is the address of a stack variable which went out of scope *)
| DAminusone (** pointer is -1 *)
[@@deriving compare]
@ -84,11 +83,11 @@ type path_pos_ = path_pos
let compare_path_pos_ _ _ = 0
(** Attributes are nary function symbols that are applied to expression arguments in Apred and
Anpred atomic formulas. Many operations don't make much sense for nullary predicates, and are
generally treated as no-ops. The first argument is treated specially, as the "anchor" of the
predicate application. For example, adding or removing an attribute uses the anchor to identify
the atom to operate on. Also, abstraction and normalization operations treat the anchor
specially and maintain more information on it than other arguments. Therefore when attaching an
Anpred atomic formulas. Many operations don't make much sense for nullary predicates, and are
generally treated as no-ops. The first argument is treated specially, as the "anchor" of the
predicate application. For example, adding or removing an attribute uses the anchor to identify
the atom to operate on. Also, abstraction and normalization operations treat the anchor
specially and maintain more information on it than other arguments. Therefore when attaching an
attribute to an expression, that expression should be the first argument, optionally followed by
additional related expressions. *)
type t =

@ -55,11 +55,11 @@ type res_action =
; ra_vpath: DecompiledExp.vpath (** vpath of the resource value *) }
(** Attributes are nary function symbols that are applied to expression arguments in Apred and
Anpred atomic formulas. Many operations don't make much sense for nullary predicates, and are
generally treated as no-ops. The first argument is treated specially, as the "anchor" of the
predicate application. For example, adding or removing an attribute uses the anchor to identify
the atom to operate on. Also, abstraction and normalization operations treat the anchor
specially and maintain more information on it than other arguments. Therefore when attaching an
Anpred atomic formulas. Many operations don't make much sense for nullary predicates, and are
generally treated as no-ops. The first argument is treated specially, as the "anchor" of the
predicate application. For example, adding or removing an attribute uses the anchor to identify
the atom to operate on. Also, abstraction and normalization operations treat the anchor
specially and maintain more information on it than other arguments. Therefore when attaching an
attribute to an expression, that expression should be the first argument, optionally followed by
additional related expressions. *)
type t =
@ -104,7 +104,7 @@ type category =
val equal_category : category -> category -> bool
val to_category : t -> category
(** Return the category to which the attribute belongs. *)
(** Return the category to which the attribute belongs. *)
val is_undef : t -> bool

@ -17,7 +17,8 @@ type var_data =
{ name: Mangled.t
; typ: Typ.t
; modify_in_block: bool
(** __block attribute of Objective-C variables, means that it will be modified inside a block *)
(** __block attribute of Objective-C variables, means that it will be modified inside a
block *)
; is_constexpr: bool }
type t =

@ -627,9 +627,9 @@ let create_node pdesc loc kind instrs =
create_node_from_not_reversed pdesc loc kind (Instrs.of_list instrs)
(** Set the successor and exception nodes.
If this is a join node right before the exit node, add an extra node in the middle,
otherwise nullify and abstract instructions cannot be added after a conditional. *)
(** Set the successor and exception nodes. If this is a join node right before the exit node, add an
extra node in the middle, otherwise nullify and abstract instructions cannot be added after a
conditional. *)
let node_set_succs pdesc (node : Node.t) ~normal:succs ~exn =
match (node.kind, succs) with
| Join_node, [({Node.kind= Exit_node} as exit_node)] ->
@ -676,10 +676,9 @@ let get_wto pdesc =
wto
(** Get loop heads for widening.
It collects all target nodes of back-edges in a depth-first traversal.
We need to use the exceptional CFG otherwise we will miss loop heads in catch clauses.
*)
(** Get loop heads for widening. It collects all target nodes of back-edges in a depth-first
traversal. We need to use the exceptional CFG otherwise we will miss loop heads in catch
clauses. *)
let get_loop_heads pdesc =
match pdesc.loop_heads with
| Some lh ->

@ -202,8 +202,8 @@ val compute_distance_to_exit_node : t -> unit
(** Compute the distance of each node to the exit node, if not computed already *)
val create_node : t -> Location.t -> Node.nodekind -> Sil.instr list -> Node.t
(** Create a new cfg node with the given location, kind, list of instructions,
and add it to the procdesc. *)
(** Create a new cfg node with the given location, kind, list of instructions, and add it to the
procdesc. *)
val create_node_from_not_reversed :
t -> Location.t -> Node.nodekind -> Instrs.not_reversed_t -> Node.t
@ -269,12 +269,12 @@ val iter_instrs : (Node.t -> Sil.instr -> unit) -> t -> unit
(** iterate over all nodes and their instructions *)
val replace_instrs : t -> f:(Node.t -> Sil.instr -> Sil.instr) -> bool
(** Map and replace the instructions to be executed.
Returns true if at least one substitution occured. *)
(** Map and replace the instructions to be executed. Returns true if at least one substitution
occured. *)
val replace_instrs_by : t -> f:(Node.t -> Sil.instr -> Sil.instr array) -> bool
(** Like [replace_instrs], but slower, and each instruction may be replaced
by 0, 1, or more instructions. *)
(** Like [replace_instrs], but slower, and each instruction may be replaced by 0, 1, or more
instructions. *)
val iter_nodes : (Node.t -> unit) -> t -> unit
(** iterate over all the nodes of a procedure *)
@ -319,6 +319,6 @@ val is_connected : t -> (unit, [`Join | `Other]) Result.t
module SQLite : SqliteUtils.Data with type t = t option
(** per-procedure CFGs are stored in the SQLite "procedures" table as NULL if the procedure has no
CFG *)
CFG *)
val load : Typ.Procname.t -> t option

@ -776,8 +776,8 @@ module Call = struct
fun m ~on_args:_ _context _args _f_capt -> RetryWith m
(** Retries matching with another matcher if the function does not have the
exact number/types of args *)
(** Retries matching with another matcher if the function does not have the exact number/types of
args *)
let exact_args_or_retry :
('context, 'f, 'arg_payload) matcher -> ('context, _, _, 'arg_payload) func_args_end =
fun m -> alternative_args_end no_args_left (args_end_retry m)
@ -869,7 +869,7 @@ module type NameCommon = sig
-> 'f_in
-> ('context, 'f_out, 'arg_payload) matcher
(** After a name, accepts ALL template arguments, accepts ALL path tails (names, templates),
accepts ALL function arguments, binds the function *)
accepts ALL function arguments, binds the function *)
end
module NameCommon = struct

@ -103,8 +103,8 @@ module type Common = sig
('context, 'f_in, 'f_out, 'arg_payload) name_matcher
-> ('context -> string -> bool)
-> ('context, 'f_in, 'f_out, 'arg_payload) name_matcher
(** Separates names that satisfies the given function (accepts ALL
template arguments on the left one) *)
(** Separates names that satisfies the given function (accepts ALL template arguments on the left
one) *)
val ( <>:: ) :
('context, 'f_in, 'f_out, 'arg_payload) name_matcher
@ -137,7 +137,7 @@ module type NameCommon = sig
-> ('context, 'f_out, 'arg_payload) matcher
(** After a name, accepts ALL template arguments, accepts ALL path tails (names, templates),
accepts ALL function arguments, binds the function *)
accepts ALL function arguments, binds the function *)
end
module ProcName :
@ -179,7 +179,7 @@ module Call : sig
val capt_arg_payload :
('context, 'arg_payload, 'wrapped_arg, 'wrapped_arg -> 'f, 'f, 'arg_payload) one_arg
(** Captures the payload of one arg at current state *)
(** Captures the payload of one arg at current state *)
val capt_exp : ('context, Exp.t, 'wrapped_arg, 'wrapped_arg -> 'f, 'f, 'arg_payload) one_arg
(** Captures one arg expression *)
@ -279,7 +279,8 @@ module Call : sig
('context, 'f_in, 'f_out, 'arg_payload) name_matcher
-> 'f_in
-> ('context, 'f_out, 'arg_payload) matcher
(** After a name, accepts ALL template arguments, accepts NO function arguments, binds the function *)
(** After a name, accepts ALL template arguments, accepts NO function arguments, binds the
function *)
val ( <>$$--> ) :
('context, 'f_in, 'f_out, 'arg_payload) name_matcher
@ -291,20 +292,22 @@ module Call : sig
('context, 'f_in, 'f_out, 'arg_payload) name_matcher
-> 'f_in
-> ('context, 'f_out, 'arg_payload) matcher
(** After a name, accepts ALL template arguments, accepts ALL function arguments, binds the function *)
(** After a name, accepts ALL template arguments, accepts ALL function arguments, binds the
function *)
val ( <>--> ) :
('context, 'f_in, 'f_out, 'arg_payload) name_matcher
-> 'f_in
-> ('context, 'f_out, 'arg_payload) matcher
(** After a name, accepts NO template arguments, accepts ALL function arguments, binds the function *)
(** After a name, accepts NO template arguments, accepts ALL function arguments, binds the
function *)
val ( &++> ) :
('context, 'f_in, 'arg_payload FuncArg.t list -> 'f_out, 'arg_payload) name_matcher
-> 'f_in
-> ('context, 'f_out, 'arg_payload) matcher
(** After a name, accepts ALL template arguments, captures ALL function arguments as a list, binds
the function *)
the function *)
val ( &::.*--> ) :
('context, 'f_in, 'f_out, 'arg_payload) name_matcher
@ -317,7 +320,7 @@ module Call : sig
('context, 'f_in, 'f_proc_out, 'f_out, 'arg_payload) args_matcher
-> 'f_in
-> ('context, 'f_out, 'arg_payload) matcher
(** Ends function arguments, accepts NO more function arguments.
If the args do not match, raise an internal error. *)
(** Ends function arguments, accepts NO more function arguments. If the args do not match, raise
an internal error. *)
end
[@@warning "-32"]

@ -226,8 +226,8 @@ let get_ret_pvar pname = mk Ident.name_return pname
let get_ret_param_pvar pname = mk Ident.name_return_param pname
(** [mk_callee name proc_name] creates a program var
for a callee function with the given function name *)
(** [mk_callee name proc_name] creates a program var for a callee function with the given function
name *)
let mk_callee (name : Mangled.t) (proc_name : Typ.Procname.t) : t =
{pv_hash= name_hash name; pv_name= name; pv_kind= Callee_var proc_name}

@ -14,11 +14,11 @@ module F = Format
type translation_unit = SourceFile.t option [@@deriving compare]
(** Type for program variables. There are 4 kinds of variables:
1) local variables, used for local variables and formal parameters
2) callee program variables, used to handle recursion ([x | callee] is distinguished from [x])
3) global variables
4) seed variables, used to store the initial value of formal parameters
*)
+ local variables, used for local variables and formal parameters
+ callee program variables, used to handle recursion ([x | callee] is distinguished from [x])
+ global variables
+ seed variables, used to store the initial value of formal parameters *)
type t [@@deriving compare]
val compare_modulo_this : t -> t -> int
@ -83,7 +83,8 @@ val is_ssa_frontend_tmp : t -> bool
once on a non-looping control-flow path *)
val is_cpp_temporary : t -> bool
(** return true if this pvar represents a C++ temporary object (see http://en.cppreference.com/w/cpp/language/lifetime) *)
(** return true if this pvar represents a C++ temporary object (see
http://en.cppreference.com/w/cpp/language/lifetime) *)
val mk : Mangled.t -> Typ.Procname.t -> t
(** [mk name proc_name] creates a program var with the given function name *)
@ -95,8 +96,8 @@ val mk_abduced_ret : Typ.Procname.t -> Location.t -> t
(** create an abduced return variable for a call to [proc_name] at [loc] *)
val mk_callee : Mangled.t -> Typ.Procname.t -> t
(** [mk_callee name proc_name] creates a program var
for a callee function with the given function name *)
(** [mk_callee name proc_name] creates a program var for a callee function with the given function
name *)
val mk_global :
?is_constexpr:bool
@ -151,8 +152,8 @@ val get_initializer_pname : t -> Typ.Procname.t option
(** Get the procname of the initializer function for the given global variable *)
val get_name_of_local_with_procname : t -> Mangled.t
(** [get_name_of_local_with_procname var] Return a name that is composed of the name of
var and the name of the procname in case of locals *)
(** [get_name_of_local_with_procname var] Return a name that is composed of the name of var and the
name of the procname in case of locals *)
val materialized_cpp_temporary : string

@ -28,12 +28,13 @@ val extract_last : t -> (string * t) option
val strip_template_args : t -> t
(** returns qualified name without template arguments. For example:
input: std::shared_ptr<int>::shared_ptr<long>
output: std::shared_ptr::shared_ptr *)
- input: std::shared_ptr<int>::shared_ptr<long>
- output: std::shared_ptr::shared_ptr *)
val append_template_args_to_last : t -> args:string -> t
(** append template arguments to the last qualifier. Fails if qualified name is empty or it already has
template args *)
(** append template arguments to the last qualifier. Fails if qualified name is empty or it already
has template args *)
val to_list : t -> string list
(** returns list of qualifiers *)
@ -54,30 +55,32 @@ val pp : Format.formatter -> t -> unit
(** Module to match qualified C++ procnames "fuzzily", that is up to namescapes and templating. In
particular, this deals with the following issues:
1. 'std::' namespace may have inline namespace afterwards: std::move becomes std::__1::move. This
happens on libc++ and to some extent on libstdc++. To work around this problem, make matching
against 'std::' more fuzzier: std::X::Y::Z will match std::.*::X::Y::Z (but only for the
'std' namespace).
2. The names are allowed not to commit to a template specialization: we want std::move to match
std::__1::move<const X&> and std::__1::move<int>. To do so, comparison function for qualifiers
will ignore template specializations.
For example:
["std", "move"]:
matches: ["std", "blah", "move"]
matches: ["std", "blah<int>", "move"]
does not match: ["std","blah", "move", "BAD"] - we don't want std::.*::X::.* to pass
does not match: ["stdBAD", "move"], - it's not std namespace anymore
["folly", "someFunction"]
matches: ["folly","someFunction"]
matches: ["folly","someFunction<int>"]
matches: ["folly<int>","someFunction"]
does not match: ["folly", "BAD", "someFunction"] - unlike 'std' any other namespace needs all
qualifiers to match
does not match: ["folly","someFunction<int>", "BAD"] - same as previous example
*)
+ 'std::' namespace may have inline namespace afterwards: std::move becomes std::__1::move. This
happens on libc++ and to some extent on libstdc++. To work around this problem, make matching
against 'std::' more fuzzier: std::X::Y::Z will match std::.*::X::Y::Z (but only for the 'std'
namespace).
+ The names are allowed not to commit to a template specialization: we want std::move to match
std::__1::move<const X&> and std::__1::move<int>. To do so, comparison function for qualifiers
will ignore template specializations.
For example:
["std", "move"]:
- matches: ["std", "blah", "move"]
- matches: ["std", "blah<int>", "move"]
- does not match: ["std","blah", "move", "BAD"] - we don't want std::.*::X::.* to pass
- does not match: ["stdBAD", "move"], - it's not std namespace anymore
["folly", "someFunction"]
- matches: ["folly","someFunction"]
- matches: ["folly","someFunction<int>"]
- matches: ["folly<int>","someFunction"]
- does not match: ["folly", "BAD", "someFunction"] - unlike 'std' any other namespace needs all
qualifiers to match
- does not match: ["folly","someFunction<int>", "BAD"] - same as previous example *)
module Match : sig
type quals_matcher

@ -43,23 +43,25 @@ type instr =
| Load of {id: Ident.t; e: Exp.t; root_typ: Typ.t; typ: Typ.t; loc: Location.t}
(** Load a value from the heap into an identifier.
[id = *exp:typ(root_typ)] where
[exp] is an expression denoting a heap address
[typ] is typ of [exp] and [id]
[root_typ] is the root type of [exp]
[id = *exp:typ(root_typ)] where
The [root_typ] is deprecated: it is broken in C/C++. We are removing [root_typ] in the
- [exp] is an expression denoting a heap address
- [typ] is typ of [exp] and [id]
- [root_typ] is the root type of [exp]
The [root_typ] is deprecated: it is broken in C/C++. We are removing [root_typ] in the
future, so please use [typ] instead. *)
| Store of {e1: Exp.t; root_typ: Typ.t; typ: Typ.t; e2: Exp.t; loc: Location.t}
(** Store the value of an expression into the heap.
[*exp1:typ(root_typ) = exp2] where
[exp1] is an expression denoting a heap address
[typ] is typ of [*exp1] and [exp2]
[root_typ] is the root type of [exp1]
[exp2] is the expression whose value is stored.
[*exp1:typ(root_typ) = exp2] where
- [exp1] is an expression denoting a heap address
- [typ] is typ of [*exp1] and [exp2]
- [root_typ] is the root type of [exp1]
- [exp2] is the expression whose value is stored.
The [root_typ] is deprecated: it is broken in C/C++. We are removing [root_typ] in the
The [root_typ] is deprecated: it is broken in C/C++. We are removing [root_typ] in the
future, so please use [typ] instead. *)
| Prune of Exp.t * Location.t * bool * if_kind
(** prune the state based on [exp=1], the boolean indicates whether true branch *)
@ -143,13 +145,11 @@ type 'inst strexp0 =
| Eexp of Exp.t * 'inst (** Base case: expression with instrumentation *)
| Estruct of (Typ.Fieldname.t * 'inst strexp0) list * 'inst (** C structure *)
| Earray of Exp.t * (Exp.t * 'inst strexp0) list * 'inst
(** Array of given length
There are two conditions imposed / used in the array case.
First, if some index and value pair appears inside an array
in a strexp, then the index is less than the length of the array.
For instance, x |->[10 | e1: v1] implies that e1 <= 9.
Second, if two indices appear in an array, they should be different.
For instance, x |->[10 | e1: v1, e2: v2] implies that e1 != e2. *)
(** Array of given length There are two conditions imposed / used in the array case. First, if
some index and value pair appears inside an array in a strexp, then the index is less than
the length of the array. For instance, [x |->\[10 | e1: v1\]] implies that [e1 <= 9].
Second, if two indices appear in an array, they should be different. For instance,
[x |->\[10 | e1: v1, e2: v2\]] implies that [e1 != e2]. *)
[@@deriving compare]
type strexp = inst strexp0
@ -163,30 +163,26 @@ let equal_strexp ?(inst = false) se1 se2 = Int.equal (compare_strexp ~inst se1 s
(** an atomic heap predicate *)
type 'inst hpred0 =
| Hpointsto of Exp.t * 'inst strexp0 * Exp.t
(** represents [exp|->strexp:typexp] where [typexp]
is an expression representing a type, e.h. [sizeof(t)]. *)
(** represents [exp|->strexp:typexp] where [typexp] is an expression representing a type, e.h.
[sizeof(t)]. *)
| Hlseg of lseg_kind * 'inst hpara0 * Exp.t * Exp.t * Exp.t list
(** higher - order predicate for singly - linked lists.
Should ensure that exp1!= exp2 implies that exp1 is allocated.
This assumption is used in the rearrangement. The last [exp list] parameter
is used to denote the shared links by all the nodes in the list. *)
(** higher - order predicate for singly - linked lists. Should ensure that exp1!= exp2 implies
that exp1 is allocated. This assumption is used in the rearrangement. The last [exp list]
parameter is used to denote the shared links by all the nodes in the list. *)
| Hdllseg of lseg_kind * 'inst hpara_dll0 * Exp.t * Exp.t * Exp.t * Exp.t * Exp.t list
(** higher-order predicate for doubly-linked lists.
Parameter for the higher-order singly-linked list predicate.
Means "lambda (root,next,svars). Exists evars. body".
Assume that root, next, svars, evars are disjoint sets of
primed identifiers, and include all the free primed identifiers in body.
body should not contain any non - primed identifiers or program
variables (i.e. pvars). *)
(** higher-order predicate for doubly-linked lists. Parameter for the higher-order
singly-linked list predicate. Means "lambda (root,next,svars). Exists evars. body". Assume
that root, next, svars, evars are disjoint sets of primed identifiers, and include all the
free primed identifiers in body. body should not contain any non - primed identifiers or
program variables (i.e. pvars). *)
[@@deriving compare]
and 'inst hpara0 =
{root: Ident.t; next: Ident.t; svars: Ident.t list; evars: Ident.t list; body: 'inst hpred0 list}
[@@deriving compare]
(** parameter for the higher-order doubly-linked list predicates.
Assume that all the free identifiers in body_dll should belong to
cell, blink, flink, svars_dll, evars_dll. *)
(** parameter for the higher-order doubly-linked list predicates. Assume that all the free
identifiers in body_dll should belong to cell, blink, flink, svars_dll, evars_dll. *)
and 'inst hpara_dll0 =
{ cell: Ident.t (** address cell *)
; blink: Ident.t (** backward link *)
@ -380,7 +376,7 @@ let exps_of_instr = function
exps_of_instr_metadata metadata
(** Convert an if_kind to string *)
(** Convert an if_kind to string *)
let if_kind_to_string = function
| Ik_bexp ->
"boolean exp"
@ -481,9 +477,8 @@ let pp_lseg_kind f = function Lseg_NE -> F.pp_print_string f "ne" | Lseg_PE -> (
(** Print a *-separated sequence. *)
let pp_star_seq pp f l = Pp.seq ~sep:" * " pp f l
(** Module Predicates records the occurrences of predicates as parameters
of (doubly -)linked lists and Epara. Provides unique numbering
for predicates and an iterator. *)
(** Module Predicates records the occurrences of predicates as parameters of (doubly -)linked lists
and Epara. Provides unique numbering for predicates and an iterator. *)
module Predicates : sig
(** predicate environment *)
@ -505,8 +500,8 @@ module Predicates : sig
val get_hpara_dll_id : env -> hpara_dll -> int
(** [iter env f f_dll] iterates [f] and [f_dll] on all the hpara and hpara_dll,
passing the unique id to the functions. The iterator can only be used once. *)
(** [iter env f f_dll] iterates [f] and [f_dll] on all the hpara and hpara_dll, passing the unique
id to the functions. The iterator can only be used once. *)
val iter : env -> (int -> hpara -> unit) -> (int -> hpara_dll -> unit) -> unit
@ -593,11 +588,9 @@ end = struct
{num= 0; hash= HparaHash.create 3; todo= []; hash_dll= HparaDllHash.create 3; todo_dll= []}
(** iterator for predicates which are marked as todo in env,
unless they have been visited already.
This can in turn extend the todo list for the nested predicates,
which are then visited as well.
Can be applied only once, as it destroys the todo list *)
(** iterator for predicates which are marked as todo in env, unless they have been visited
already. This can in turn extend the todo list for the nested predicates, which are then
visited as well. Can be applied only once, as it destroys the todo list *)
let iter (env : env) f f_dll =
while env.todo <> [] || env.todo_dll <> [] do
match env.todo with
@ -1113,9 +1106,8 @@ let equal_subst = [%compare.equal: subst]
let sub_no_duplicated_ids sub = not (List.contains_dup ~compare:compare_ident_exp_ids sub)
(** Create a substitution from a list of pairs.
For all (id1, e1), (id2, e2) in the input list,
if id1 = id2, then e1 = e2. *)
(** Create a substitution from a list of pairs. For all (id1, e1), (id2, e2) in the input list, if
id1 = id2, then e1 = e2. *)
let subst_of_list sub =
let sub' = List.dedup_and_sort ~compare:compare_ident_exp sub in
assert (sub_no_duplicated_ids sub') ;
@ -1133,18 +1125,16 @@ let sub_empty = subst_of_list []
let is_sub_empty = List.is_empty
(** Join two substitutions into one.
For all id in dom(sub1) cap dom(sub2), sub1(id) = sub2(id). *)
(** Join two substitutions into one. For all id in dom(sub1) cap dom(sub2), sub1(id) = sub2(id). *)
let sub_join sub1 sub2 =
let sub = IList.merge_dedup ~compare:compare_ident_exp sub1 sub2 in
assert (sub_no_duplicated_ids sub) ;
sub
(** Compute the common id-exp part of two inputs [subst1] and [subst2].
The first component of the output is this common part.
The second and third components are the remainder of [subst1]
and [subst2], respectively. *)
(** Compute the common id-exp part of two inputs [subst1] and [subst2]. The first component of the
output is this common part. The second and third components are the remainder of [subst1] and
[subst2], respectively. *)
let sub_symmetric_difference sub1_in sub2_in =
let rec diff sub_common sub1_only sub2_only sub1 sub2 =
match (sub1, sub2) with
@ -1162,24 +1152,23 @@ let sub_symmetric_difference sub1_in sub2_in =
diff [] [] [] sub1_in sub2_in
(** [sub_find filter sub] returns the expression associated to the first identifier
that satisfies [filter]. Raise [Not_found] if there isn't one. *)
(** [sub_find filter sub] returns the expression associated to the first identifier that satisfies
[filter]. Raise [Not_found] if there isn't one. *)
let sub_find filter (sub : subst) = snd (List.find_exn ~f:(fun (i, _) -> filter i) sub)
(** [sub_filter filter sub] restricts the domain of [sub] to the
identifiers satisfying [filter]. *)
(** [sub_filter filter sub] restricts the domain of [sub] to the identifiers satisfying [filter]. *)
let sub_filter filter (sub : subst) = List.filter ~f:(fun (i, _) -> filter i) sub
(** [sub_filter_pair filter sub] restricts the domain of [sub] to the
identifiers satisfying [filter(id, sub(id))]. *)
(** [sub_filter_pair filter sub] restricts the domain of [sub] to the identifiers satisfying
[filter(id, sub(id))]. *)
let sub_filter_pair = List.filter
(** [sub_range_partition filter sub] partitions [sub] according to
whether range expressions satisfy [filter]. *)
(** [sub_range_partition filter sub] partitions [sub] according to whether range expressions satisfy
[filter]. *)
let sub_range_partition filter (sub : subst) = List.partition_tf ~f:(fun (_, e) -> filter e) sub
(** [sub_domain_partition filter sub] partitions [sub] according to
whether domain identifiers satisfy [filter]. *)
(** [sub_domain_partition filter sub] partitions [sub] according to whether domain identifiers
satisfy [filter]. *)
let sub_domain_partition filter (sub : subst) = List.partition_tf ~f:(fun (i, _) -> filter i) sub
(** Return the list of identifiers in the domain of the substitution. *)
@ -1191,8 +1180,8 @@ let sub_range sub = List.map ~f:snd sub
(** [sub_range_map f sub] applies [f] to the expressions in the range of [sub]. *)
let sub_range_map f sub = subst_of_list (List.map ~f:(fun (i, e) -> (i, f e)) sub)
(** [sub_map f g sub] applies the renaming [f] to identifiers in the domain
of [sub] and the substitution [g] to the expressions in the range of [sub]. *)
(** [sub_map f g sub] applies the renaming [f] to identifiers in the domain of [sub] and the
substitution [g] to the expressions in the range of [sub]. *)
let sub_map f g sub = subst_of_list (List.map ~f:(fun (i, e) -> (f i, g e)) sub)
let mem_sub id sub = List.exists ~f:(fun (id1, _) -> Ident.equal id id1) sub
@ -1329,8 +1318,8 @@ let hpred_sub subst =
(** {2 Functions for replacing occurrences of expressions.} *)
(** The first parameter should define a partial function.
No parts of hpara are replaced by these functions. *)
(** The first parameter should define a partial function. No parts of hpara are replaced by these
functions. *)
let rec exp_replace_exp epairs e =
(* First we check if there is an exact match *)
match List.find ~f:(fun (e1, _) -> Exp.equal e e1) epairs with
@ -1505,10 +1494,9 @@ let sigma_to_sigma_ne sigma : (atom list * hpred list) list =
else [([], sigma)]
(** [hpara_instantiate para e1 e2 elist] instantiates [para] with [e1],
[e2] and [elist]. If [para = lambda (x, y, xs). exists zs. b],
then the result of the instantiation is [b\[e1 / x, e2 / y, elist / xs, zs'_/ zs\]]
for some fresh [_zs'].*)
(** [hpara_instantiate para e1 e2 elist] instantiates [para] with [e1], [e2] and [elist]. If
[para = lambda (x, y, xs). exists zs. b], then the result of the instantiation is
[b\[e1 / x, e2 / y, elist / xs, zs'_/ zs\]] for some fresh [_zs'].*)
let hpara_instantiate para e1 e2 elist =
let subst_for_svars =
let g id e = (id, e) in
@ -1528,11 +1516,10 @@ let hpara_instantiate para e1 e2 elist =
(ids_evars, List.map ~f:(hpred_sub subst) para.body)
(** [hpara_dll_instantiate para cell blink flink elist] instantiates [para] with [cell],
[blink], [flink], and [elist]. If [para = lambda (x, y, z, xs). exists zs. b],
then the result of the instantiation is
[b\[cell / x, blink / y, flink / z, elist / xs, zs'_/ zs\]]
for some fresh [_zs'].*)
(** [hpara_dll_instantiate para cell blink flink elist] instantiates [para] with [cell], [blink],
[flink], and [elist]. If [para = lambda (x, y, z, xs). exists zs. b], then the result of the
instantiation is [b\[cell / x, blink / y, flink / z, elist / xs, zs'_/ zs\]] for some fresh
[_zs'].*)
let hpara_dll_instantiate (para : hpara_dll) cell blink flink elist =
let subst_for_svars =
let g id e = (id, e) in

@ -41,23 +41,25 @@ type instr =
| Load of {id: Ident.t; e: Exp.t; root_typ: Typ.t; typ: Typ.t; loc: Location.t}
(** Load a value from the heap into an identifier.
[id = *exp:typ(root_typ)] where
[exp] is an expression denoting a heap address
[typ] is typ of [exp] and [id]
[root_typ] is the root type of [exp]
[id = *exp:typ(root_typ)] where
The [root_typ] is deprecated: it is broken in C/C++. We are removing [root_typ] in the
- [exp] is an expression denoting a heap address
- [typ] is typ of [exp] and [id]
- [root_typ] is the root type of [exp]
The [root_typ] is deprecated: it is broken in C/C++. We are removing [root_typ] in the
future, so please use [typ] instead. *)
| Store of {e1: Exp.t; root_typ: Typ.t; typ: Typ.t; e2: Exp.t; loc: Location.t}
(** Store the value of an expression into the heap.
[*exp1:typ(root_typ) = exp2] where
[exp1] is an expression denoting a heap address
[typ] is typ of [*exp1] and [exp2]
[root_typ] is the root type of [exp1]
[exp2] is the expression whose value is stored.
[*exp1:typ(root_typ) = exp2] where
- [exp1] is an expression denoting a heap address
- [typ] is typ of [*exp1] and [exp2]
- [root_typ] is the root type of [exp1]
- [exp2] is the expression whose value is stored.
The [root_typ] is deprecated: it is broken in C/C++. We are removing [root_typ] in the
The [root_typ] is deprecated: it is broken in C/C++. We are removing [root_typ] in the
future, so please use [typ] instead. *)
| Prune of Exp.t * Location.t * bool * if_kind
(** prune the state based on [exp=1], the boolean indicates whether true branch *)
@ -166,54 +168,46 @@ type 'inst strexp0 =
| Eexp of Exp.t * 'inst (** Base case: expression with instrumentation *)
| Estruct of (Typ.Fieldname.t * 'inst strexp0) list * 'inst (** C structure *)
| Earray of Exp.t * (Exp.t * 'inst strexp0) list * 'inst
(** Array of given length
There are two conditions imposed / used in the array case.
First, if some index and value pair appears inside an array
in a strexp, then the index is less than the length of the array.
For instance, x |->[10 | e1: v1] implies that e1 <= 9.
Second, if two indices appear in an array, they should be different.
For instance, x |->[10 | e1: v1, e2: v2] implies that e1 != e2. *)
(** Array of given length There are two conditions imposed / used in the array case. First, if
some index and value pair appears inside an array in a strexp, then the index is less than
the length of the array. For instance, [x |->\[10 | e1: v1\]] implies that [e1 <= 9].
Second, if two indices appear in an array, they should be different. For instance,
[x |->\[10 | e1: v1, e2: v2\]] implies that [e1 != e2]. *)
[@@deriving compare]
type strexp = inst strexp0
val compare_strexp : ?inst:bool -> strexp -> strexp -> int
(** Comparison function for strexp.
The inst:: parameter specifies whether instumentations should also
be considered (false by default). *)
(** Comparison function for strexp. The inst:: parameter specifies whether instumentations should
also be considered (false by default). *)
val equal_strexp : ?inst:bool -> strexp -> strexp -> bool
(** Equality function for strexp.
The inst:: parameter specifies whether instumentations should also
be considered (false by default). *)
(** Equality function for strexp. The inst:: parameter specifies whether instumentations should also
be considered (false by default). *)
(** an atomic heap predicate *)
type 'inst hpred0 =
| Hpointsto of Exp.t * 'inst strexp0 * Exp.t
(** represents [exp|->strexp:typexp] where [typexp]
is an expression representing a type, e.h. [sizeof(t)]. *)
(** represents [exp|->strexp:typexp] where [typexp] is an expression representing a type, e.h.
[sizeof(t)]. *)
| Hlseg of lseg_kind * 'inst hpara0 * Exp.t * Exp.t * Exp.t list
(** higher - order predicate for singly - linked lists.
Should ensure that exp1!= exp2 implies that exp1 is allocated.
This assumption is used in the rearrangement. The last [exp list] parameter
is used to denote the shared links by all the nodes in the list. *)
(** higher - order predicate for singly - linked lists. Should ensure that exp1!= exp2 implies
that exp1 is allocated. This assumption is used in the rearrangement. The last [exp list]
parameter is used to denote the shared links by all the nodes in the list. *)
| Hdllseg of lseg_kind * 'inst hpara_dll0 * Exp.t * Exp.t * Exp.t * Exp.t * Exp.t list
(** higher-order predicate for doubly-linked lists.
Parameter for the higher-order singly-linked list predicate.
Means "lambda (root,next,svars). Exists evars. body".
Assume that root, next, svars, evars are disjoint sets of
primed identifiers, and include all the free primed identifiers in body.
body should not contain any non - primed identifiers or program
variables (i.e. pvars). *)
(** higher-order predicate for doubly-linked lists. Parameter for the higher-order
singly-linked list predicate. Means "lambda (root,next,svars). Exists evars. body". Assume
that root, next, svars, evars are disjoint sets of primed identifiers, and include all the
free primed identifiers in body. body should not contain any non - primed identifiers or
program variables (i.e. pvars). *)
[@@deriving compare]
and 'inst hpara0 =
{root: Ident.t; next: Ident.t; svars: Ident.t list; evars: Ident.t list; body: 'inst hpred0 list}
[@@deriving compare]
(** parameter for the higher-order doubly-linked list predicates.
Assume that all the free identifiers in body_dll should belong to
cell, blink, flink, svars_dll, evars_dll. *)
(** parameter for the higher-order doubly-linked list predicates. Assume that all the free
identifiers in body_dll should belong to cell, blink, flink, svars_dll, evars_dll. *)
and 'inst hpara_dll0 =
{ cell: Ident.t (** address cell *)
; blink: Ident.t (** backward link *)
@ -230,14 +224,12 @@ type hpara = inst hpara0
type hpara_dll = inst hpara_dll0
val compare_hpred : ?inst:bool -> hpred -> hpred -> int
(** Comparison function for hpred.
The inst:: parameter specifies whether instumentations should also
be considered (false by default). *)
(** Comparison function for hpred. The inst:: parameter specifies whether instumentations should
also be considered (false by default). *)
val equal_hpred : ?inst:bool -> hpred -> hpred -> bool
(** Equality function for hpred.
The inst:: parameter specifies whether instumentations should also
be considered (false by default). *)
(** Equality function for hpred. The inst:: parameter specifies whether instumentations should also
be considered (false by default). *)
module HpredSet : Caml.Set.S with type elt = hpred
(** Sets of heap predicates *)
@ -262,9 +254,8 @@ val is_block_pvar : Pvar.t -> bool
(** Check if a pvar is a local pointing to a block in objc *)
val add_with_block_parameters_flag : instr -> instr
(** Adds a with_blocks_parameters flag to a method call, when the arguments
contain an Objective-C block, and the method is an Objective-C method
(to be extended to other methods) *)
(** Adds a with_blocks_parameters flag to a method call, when the arguments contain an Objective-C
block, and the method is an Objective-C method (to be extended to other methods) *)
(** {2 Pretty Printing} *)
@ -338,9 +329,8 @@ val pp_hpara : Pp.env -> F.formatter -> hpara -> unit
val pp_hpara_dll : Pp.env -> F.formatter -> hpara_dll -> unit
(** Pretty print a hpara_dll. *)
(** Module Predicates records the occurrences of predicates as parameters
of (doubly -)linked lists and Epara.
Provides unique numbering for predicates and an iterator. *)
(** Module Predicates records the occurrences of predicates as parameters of (doubly -)linked lists
and Epara. Provides unique numbering for predicates and an iterator. *)
module Predicates : sig
(** predicate environment *)
type env
@ -352,8 +342,8 @@ module Predicates : sig
(** return true if the environment is empty *)
val iter : env -> (int -> hpara -> unit) -> (int -> hpara_dll -> unit) -> unit
(** [iter env f f_dll] iterates [f] and [f_dll] on all the hpara and hpara_dll,
passing the unique id to the functions. The iterator can only be used once. *)
(** [iter env f f_dll] iterates [f] and [f_dll] on all the hpara and hpara_dll, passing the unique
id to the functions. The iterator can only be used once. *)
val process_hpred : env -> hpred -> unit
(** Process one hpred, updating the predicate environment *)
@ -365,23 +355,19 @@ val pp_hpred_env : Pp.env -> Predicates.env option -> F.formatter -> hpred -> un
(** {2 Functions for traversing SIL data types} *)
val strexp_expmap : (Exp.t * inst option -> Exp.t * inst option) -> strexp -> strexp
(** Change exps in strexp using [f].
WARNING: the result might not be normalized. *)
(** Change exps in strexp using [f]. WARNING: the result might not be normalized. *)
val hpred_expmap : (Exp.t * inst option -> Exp.t * inst option) -> hpred -> hpred
(** Change exps in hpred by [f].
WARNING: the result might not be normalized. *)
(** Change exps in hpred by [f]. WARNING: the result might not be normalized. *)
val hpred_instmap : (inst -> inst) -> hpred -> hpred
(** Change instrumentations in hpred using [f]. *)
val hpred_list_expmap : (Exp.t * inst option -> Exp.t * inst option) -> hpred list -> hpred list
(** Change exps in hpred list by [f].
WARNING: the result might not be normalized. *)
(** Change exps in hpred list by [f]. WARNING: the result might not be normalized. *)
val atom_expmap : (Exp.t -> Exp.t) -> atom -> atom
(** Change exps in atom by [f].
WARNING: the result might not be normalized. *)
(** Change exps in atom by [f]. WARNING: the result might not be normalized. *)
val hpred_list_get_lexps : (Exp.t -> bool) -> hpred list -> Exp.t list
@ -408,9 +394,8 @@ val equal_subst : subst -> subst -> bool
(** Equality for substitutions. *)
val subst_of_list : (Ident.t * Exp.t) list -> subst
(** Create a substitution from a list of pairs.
For all (id1, e1), (id2, e2) in the input list,
if id1 = id2, then e1 = e2. *)
(** Create a substitution from a list of pairs. For all (id1, e1), (id2, e2) in the input list, if
id1 = id2, then e1 = e2. *)
val subst_of_list_duplicates : (Ident.t * Exp.t) list -> subst
(** like subst_of_list, but allow duplicate ids and only keep the first occurrence *)
@ -424,37 +409,33 @@ val sub_empty : subst
val is_sub_empty : subst -> bool
val sub_join : subst -> subst -> subst
(** Compute the common id-exp part of two inputs [subst1] and [subst2].
The first component of the output is this common part.
The second and third components are the remainder of [subst1]
and [subst2], respectively. *)
(** Compute the common id-exp part of two inputs [subst1] and [subst2]. The first component of the
output is this common part. The second and third components are the remainder of [subst1] and
[subst2], respectively. *)
val sub_symmetric_difference : subst -> subst -> subst * subst * subst
(** Compute the common id-exp part of two inputs [subst1] and [subst2].
The first component of the output is this common part.
The second and third components are the remainder of [subst1]
and [subst2], respectively. *)
(** Compute the common id-exp part of two inputs [subst1] and [subst2]. The first component of the
output is this common part. The second and third components are the remainder of [subst1] and
[subst2], respectively. *)
val sub_find : (Ident.t -> bool) -> subst -> Exp.t
(** [sub_find filter sub] returns the expression associated to the first identifier
that satisfies [filter].
Raise [Not_found] if there isn't one. *)
(** [sub_find filter sub] returns the expression associated to the first identifier that satisfies
[filter]. Raise [Not_found] if there isn't one. *)
val sub_filter : (Ident.t -> bool) -> subst -> subst
(** [sub_filter filter sub] restricts the domain of [sub] to the
identifiers satisfying [filter]. *)
(** [sub_filter filter sub] restricts the domain of [sub] to the identifiers satisfying [filter]. *)
val sub_filter_pair : subst -> f:(Ident.t * Exp.t -> bool) -> subst
(** [sub_filter_exp filter sub] restricts the domain of [sub] to the
identifiers satisfying [filter(id, sub(id))]. *)
(** [sub_filter_exp filter sub] restricts the domain of [sub] to the identifiers satisfying
[filter(id, sub(id))]. *)
val sub_range_partition : (Exp.t -> bool) -> subst -> subst * subst
(** [sub_range_partition filter sub] partitions [sub] according to
whether range expressions satisfy [filter]. *)
(** [sub_range_partition filter sub] partitions [sub] according to whether range expressions satisfy
[filter]. *)
val sub_domain_partition : (Ident.t -> bool) -> subst -> subst * subst
(** [sub_domain_partition filter sub] partitions [sub] according to
whether domain identifiers satisfy [filter]. *)
(** [sub_domain_partition filter sub] partitions [sub] according to whether domain identifiers
satisfy [filter]. *)
val sub_domain : subst -> Ident.t list
(** Return the list of identifiers in the domain of the substitution. *)
@ -466,8 +447,8 @@ val sub_range_map : (Exp.t -> Exp.t) -> subst -> subst
(** [sub_range_map f sub] applies [f] to the expressions in the range of [sub]. *)
val sub_map : (Ident.t -> Ident.t) -> (Exp.t -> Exp.t) -> subst -> subst
(** [sub_map f g sub] applies the renaming [f] to identifiers in the domain
of [sub] and the substitution [g] to the expressions in the range of [sub]. *)
(** [sub_map f g sub] applies the renaming [f] to identifiers in the domain of [sub] and the
substitution [g] to the expressions in the range of [sub]. *)
val extend_sub : subst -> Ident.t -> Exp.t -> subst option
(** Extend substitution and return [None] if not possible. *)
@ -476,8 +457,7 @@ val subst_free_vars : subst -> Ident.t Sequence.t
val subst_gen_free_vars : subst -> (unit, Ident.t) Sequence.Generator.t
(** substitution functions
WARNING: these functions do not ensure that the results are normalized. *)
(** substitution functions WARNING: these functions do not ensure that the results are normalized. *)
val exp_sub : subst -> Exp.t -> Exp.t
@ -507,17 +487,15 @@ val exp_add_offsets : Exp.t -> offset list -> Exp.t
val sigma_to_sigma_ne : hpred list -> (atom list * hpred list) list
val hpara_instantiate : hpara -> Exp.t -> Exp.t -> Exp.t list -> Ident.t list * hpred list
(** [hpara_instantiate para e1 e2 elist] instantiates [para] with [e1],
[e2] and [elist]. If [para = lambda (x, y, xs). exists zs. b],
then the result of the instantiation is [b\[e1 / x, e2 / y, elist / xs, _zs'/ zs\]]
for some fresh [_zs'].*)
(** [hpara_instantiate para e1 e2 elist] instantiates [para] with [e1], [e2] and [elist]. If
[para = lambda (x, y, xs). exists zs. b], then the result of the instantiation is
[b\[e1 / x, e2 / y, elist / xs, _zs'/ zs\]] for some fresh [_zs'].*)
val hpara_dll_instantiate :
hpara_dll -> Exp.t -> Exp.t -> Exp.t -> Exp.t list -> Ident.t list * hpred list
(** [hpara_dll_instantiate para cell blink flink elist] instantiates [para] with [cell],
[blink], [flink], and [elist]. If [para = lambda (x, y, z, xs). exists zs. b],
then the result of the instantiation is
[b\[cell / x, blink / y, flink / z, elist / xs, _zs'/ zs\]]
for some fresh [_zs'].*)
(** [hpara_dll_instantiate para cell blink flink elist] instantiates [para] with [cell], [blink],
[flink], and [elist]. If [para = lambda (x, y, z, xs). exists zs. b], then the result of the
instantiation is [b\[cell / x, blink / y, flink / z, elist / xs, _zs'/ zs\]] for some fresh
[_zs'].*)
val custom_error : Pvar.t

@ -43,8 +43,7 @@ let convert_cfg ~callee_pdesc ~resolved_pdesc ~f_instr_list =
resolved_pdesc
(** clone a procedure description and apply the type substitutions where
the parameters are used *)
(** clone a procedure description and apply the type substitutions where the parameters are used *)
let with_formals_types_proc callee_pdesc resolved_pdesc substitutions =
let resolved_pname = Procdesc.get_proc_name resolved_pdesc in
let convert_pvar pvar = Pvar.mk (Pvar.get_name pvar) resolved_pname in
@ -132,10 +131,10 @@ let with_formals_types_proc callee_pdesc resolved_pdesc substitutions =
exception UnmatchedParameters
(** Creates a copy of a procedure description and a list of type substitutions of the form
(name, typ) where name is a parameter. The resulting proc desc is isomorphic but
all the type of the parameters are replaced in the instructions according to the list.
The virtual calls are also replaced to match the parameter types *)
(** Creates a copy of a procedure description and a list of type substitutions of the form (name,
typ) where name is a parameter. The resulting proc desc is isomorphic but all the type of the
parameters are replaced in the instructions according to the list. The virtual calls are also
replaced to match the parameter types *)
let with_formals_types ?(has_clang_model = false) callee_pdesc resolved_pname args =
let callee_attributes = Procdesc.get_attributes callee_pdesc in
let resolved_params, substitutions =

@ -11,14 +11,15 @@ exception UnmatchedParameters
val with_formals_types :
?has_clang_model:bool -> Procdesc.t -> Typ.Procname.t -> (Exp.t * Typ.t) list -> Procdesc.t
(** Creates a copy of a procedure description and a list of type substitutions of the form
(name, typ) where name is a parameter. The resulting procdesc is isomorphic but
all the type of the parameters are replaced in the instructions according to the list.
The virtual calls are also replaced to match the parameter types *)
(** Creates a copy of a procedure description and a list of type substitutions of the form (name,
typ) where name is a parameter. The resulting procdesc is isomorphic but all the type of the
parameters are replaced in the instructions according to the list. The virtual calls are also
replaced to match the parameter types *)
val with_block_args : Procdesc.t -> Typ.Procname.t -> Exp.closure option list -> Procdesc.t
(** Creates a copy of a procedure description given a list of possible closures
that are passed as arguments to the method. The resulting procdesc is isomorphic but
a) the block parameters are replaces with the closures
b) the parameters of the method are extended with parameters for the captured variables
in the closures *)
(** Creates a copy of a procedure description given a list of possible closures that are passed as
arguments to the method. The resulting procdesc is isomorphic but
- the block parameters are replaces with the closures
- the parameters of the method are extended with parameters for the captured variables in the
closures *)

@ -21,7 +21,7 @@ type t' = Exact (** denotes the current type only *) | Subtypes of Typ.Name.t l
let equal_modulo_flag (st1, _) (st2, _) = [%compare.equal: t'] st1 st2
(** denotes the current type and a list of types that are not their subtypes *)
(** denotes the current type and a list of types that are not their subtypes *)
type kind = CAST | INSTOF | NORMAL [@@deriving compare]
let equal_kind = [%compare.equal: kind]
@ -264,13 +264,11 @@ let case_analysis_basic tenv (c1, st) (c2, (_, flag2)) =
(change_flag pos_st c1 c2 flag2, change_flag neg_st c1 c2 flag2)
(** [case_analysis (c1, st1) (c2, st2) f] performs case analysis on [c1 <: c2]
according to [st1] and [st2]
where f c1 c2 is true if c1 is a subtype of c2.
get_subtypes returning a pair:
(** [case_analysis (c1, st1) (c2, st2) f] performs case analysis on [c1 <: c2] according to [st1]
and [st2] where f c1 c2 is true if c1 is a subtype of c2. get_subtypes returning a pair:
- whether [st1] and [st2] admit [c1 <: c2], and in case return the updated subtype [st1]
- whether [st1] and [st2] admit [not(c1 <: c2)],
and in case return the updated subtype [st1] *)
- whether [st1] and [st2] admit [not(c1 <: c2)], and in case return the updated subtype [st1] *)
let case_analysis tenv (c1, st1) (c2, st2) =
if Config.subtype_multirange then get_subtypes tenv (c1, st1) (c2, st2)
else case_analysis_basic tenv (c1, st1) (c2, st2)

@ -28,17 +28,16 @@ val subtypes_instof : t
val join : t -> t -> t
val case_analysis : Tenv.t -> Typ.Name.t * t -> Typ.Name.t * t -> t option * t option
(** [case_analysis tenv (c1, st1) (c2, st2)] performs case analysis on [c1 <: c2] according
to [st1] and [st2].
[case_analysis] returns a pair:
(** [case_analysis tenv (c1, st1) (c2, st2)] performs case analysis on [c1 <: c2] according to [st1]
and [st2]. [case_analysis] returns a pair:
- whether [st1] and [st2] admit [c1 <: c2], and in case returns the updated subtype [st1]
- whether [st1] and [st2] admit [not(c1 <: c2)], and in case returns the updated subtype [st1] *)
val is_known_subtype : Tenv.t -> Typ.Name.t -> Typ.Name.t -> bool
(** [is_known_subtype tenv c1 c2] returns true if there is enough information in [tenv] to prove
that [c1] is a subtype of [c2].
Note that [not (is_known_subtype tenv c1 c2) == true] does not imply
that [is_known_not_subtype tenv c1 c2 == true] *)
that [c1] is a subtype of [c2]. Note that [not (is_known_subtype tenv c1 c2) == true] does not
imply that [is_known_not_subtype tenv c1 c2 == true] *)
val is_cast : t -> bool

@ -63,6 +63,6 @@ val merge : src:t -> dst:t -> unit
val merge_per_file : src:per_file -> dst:per_file -> per_file
(** Best-effort merge of [src] into [dst]. If a procedure is both in [dst] and [src], the one in
[dst] will get overwritten. *)
[dst] will get overwritten. *)
module SQLite : SqliteUtils.Data with type t = per_file

@ -6,6 +6,8 @@
* LICENSE file in the root directory of this source tree.
*)
[@@@ocamlformat "parse-docstrings = false"]
(** The Smallfoot Intermediate Language: Types *)
open! IStd

@ -6,6 +6,8 @@
* LICENSE file in the root directory of this source tree.
*)
[@@@ocamlformat "parse-docstrings = false"]
(** The Smallfoot Intermediate Language: Types *)
open! IStd

@ -112,11 +112,9 @@ module Bourdoncle_SCC (CFG : PreProcCfg) = struct
module CFG = CFG
module Dfn = CFG.Node.IdMap
(**
[dfn] contains a DFS pre-order indexing. A node is not in the map if it has never been visited.
A node's dfn is +oo if it has been fully visited (head of cross-edges) or we want to hide it
for building a subcomponent partition (head of highest back-edges).
*)
(** [dfn] contains a DFS pre-order indexing. A node is not in the map if it has never been
visited. A node's dfn is +oo if it has been fully visited (head of cross-edges) or we want to
hide it for building a subcomponent partition (head of highest back-edges). *)
(*
Unlike Bourdoncle's paper version or OCamlGraph implementation, this implementation handles

@ -12,13 +12,10 @@ module F = Format
by François Bourdoncle.
*)
(**
A hierarchical ordering of a set is a well-parenthesized permutation of its elements without two
consecutive "(". I defines a total order <= over its elements.
The elements between two matching parentheses are called a Component.
The first element of a Component is called the head.
Let denote by H(v) the set of head of the nested components containing v.
*)
(** A hierarchical ordering of a set is a well-parenthesized permutation of its elements without two
consecutive "(". I defines a total order <= over its elements. The elements between two matching
parentheses are called a Component. The first element of a Component is called the head. Let
denote by H(v) the set of head of the nested components containing v. *)
module Partition : sig
type 'node t = private
@ -31,10 +28,8 @@ module Partition : sig
val fold_heads : ('node t, 'node, _) Container.fold
val expand : fold_right:('a, 'b, 'b t) Container.fold -> 'a t -> 'b t
(** Maps a partition nodes from ['a] to ['b] using the expansion [fold_right].
[fold_right] should not return its [~init] directly but must always provide
a non-empty sequence.
*)
(** Maps a partition nodes from ['a] to ['b] using the expansion [fold_right]. [fold_right] should
not return its [~init] directly but must always provide a non-empty sequence. *)
val pp : pp_node:(F.formatter -> 'node -> unit) -> F.formatter -> 'node t -> unit
end
@ -57,18 +52,19 @@ module type PreProcCfg = sig
val start_node : t -> Node.t
end
(**
A weak topological ordering (WTO) of a directed graph is a hierarchical ordering of its vertices
such that for every edge u -> v,
u < v and v is not in H(u) (forward edge)
or
v <= u and v is in H(u) (feedback edge)
(** A weak topological ordering (WTO) of a directed graph is a hierarchical ordering of its vertices
such that for every edge u -> v,
where H(u) is the set of heads of the nested components containing u.
u < v and v is not in H(u) (forward edge)
A WTO of a directed graph is such that the head v of every feedback edge u -> v is the head of a
component containing its tail u.
*)
or
v <= u and v is in H(u) (feedback edge)
where H(u) is the set of heads of the nested components containing u.
A WTO of a directed graph is such that the head v of every feedback edge u -> v is the head of a
component containing its tail u. *)
module type S = sig
module CFG : PreProcCfg
@ -79,7 +75,5 @@ end
module type Make = functor (CFG : PreProcCfg) -> S with module CFG = CFG
module Bourdoncle_SCC : Make
(**
Implementation of Bourdoncle's "Hierarchical decomposition of a directed graph into strongly
connected components and subcomponents". See [Bou] Figure 4, page 10.
*)
(** Implementation of Bourdoncle's "Hierarchical decomposition of a directed graph into strongly
connected components and subcomponents". See [Bou] Figure 4, page 10. *)

@ -97,7 +97,8 @@ end
include sig
[@@@warning "-60"]
(** Stacked abstract domain: tagged union of [Below] and [Above] domains where all elements of [Below] are strictly smaller than elements of [Above] *)
(** Stacked abstract domain: tagged union of [Below] and [Above] domains where all elements of
[Below] are strictly smaller than elements of [Above] *)
module Stacked (Below : S) (Above : S) : S with type t = (Below.t, Above.t) below_above
end
@ -135,8 +136,8 @@ module StackedUtils : sig
('b, 'a) below_above -> f_below:('b -> 'b2) -> f_above:('a -> 'a2) -> ('b2, 'a2) below_above
end
(** Abstracts a set of [Element]s by keeping its smallest representative only.
The widening is terminating only if the order fulfills the descending chain condition. *)
(** Abstracts a set of [Element]s by keeping its smallest representative only. The widening is
terminating only if the order fulfills the descending chain condition. *)
module MinReprSet (Element : PrettyPrintable.PrintableOrderedType) : sig
type elt = Element.t
@ -166,8 +167,8 @@ end
include sig
[@@@warning "-60"]
(** Lift a PPSet to a powerset domain ordered by subset. The elements of the set should be drawn from
a *finite* collection of possible values, since the widening operator here is just union. *)
(** Lift a PPSet to a powerset domain ordered by subset. The elements of the set should be drawn
from a *finite* collection of possible values, since the widening operator here is just union. *)
module FiniteSetOfPPSet (PPSet : PrettyPrintable.PPSet) : FiniteSetS with type elt = PPSet.elt
end
@ -196,8 +197,8 @@ include sig
[@@@warning "-60"]
(** Map domain ordered by union over the set of bindings, so the bottom element is the empty map.
Every element implicitly maps to bottom unless it is explicitly bound to something else.
Uses PPMap as the underlying map *)
Every element implicitly maps to bottom unless it is explicitly bound to something else. Uses
PPMap as the underlying map *)
module MapOfPPMap (PPMap : PrettyPrintable.PPMap) (ValueDomain : S) :
MapS with type key = PPMap.key and type value = ValueDomain.t and type t = ValueDomain.t PPMap.t
end
@ -218,9 +219,9 @@ end
module InvertedMap (Key : PrettyPrintable.PrintableOrderedType) (ValueDomain : S) :
InvertedMapS with type key = Key.t and type value = ValueDomain.t
(** Similar to [InvertedMap] but it guarantees that it has a canonical form. For example, both [{a
-> top_v}] and [empty] represent the same abstract value [top] in [InvertedMap], but in this
implementation, [top] is always implemented as [empty] by not adding the [top_v] explicitly. *)
(** Similar to [InvertedMap] but it guarantees that it has a canonical form. For example, both
[{a -> top_v}] and [empty] represent the same abstract value [top] in [InvertedMap], but in this
implementation, [top] is always implemented as [empty] by not adding the [top_v] explicitly. *)
module SafeInvertedMap (Key : PrettyPrintable.PrintableOrderedType) (ValueDomain : WithTop) :
InvertedMapS with type key = Key.t and type value = ValueDomain.t
@ -273,8 +274,8 @@ module CountDomain (MaxCount : MaxCount) : sig
(** capped sum of two states *)
end
(** Domain keeping a non-negative count with a bounded maximum value.
[join] is minimum and [top] is zero. *)
(** Domain keeping a non-negative count with a bounded maximum value. [join] is minimum and [top] is
zero. *)
module DownwardIntDomain (MaxCount : MaxCount) : sig
include WithTop with type t = private int
(** top is zero *)

@ -62,7 +62,9 @@ module type Make = functor (TransferFunctions : TransferFunctions.SIL) ->
S with module TransferFunctions = TransferFunctions
module MakeRPO : Make
(** create an intraprocedural abstract interpreter from transfer functions using the reverse post-order scheduler *)
(** create an intraprocedural abstract interpreter from transfer functions using the reverse
post-order scheduler *)
module MakeWTO : Make
(** create an intraprocedural abstract interpreter from transfer functions using Bourdoncle's strongly connected component weak topological order *)
(** create an intraprocedural abstract interpreter from transfer functions using Bourdoncle's
strongly connected component weak topological order *)

@ -46,23 +46,26 @@ end
(** Wrapper around Interpreter to prevent clients from having to deal with IdAccessPathMapDomain.
CAVEAT: the translation does not attempt to preserve the semantics in the case where side-effects
happen between an assignment to a logical variable and the assignement of that logical variable
to a program variable. For instance the following SIL program
CAVEAT: the translation does not attempt to preserve the semantics in the case where
side-effects happen between an assignment to a logical variable and the assignement of that
logical variable to a program variable. For instance the following SIL program
{v
n$0 = *&x.f
_ = delete(&x)
*&y = n$0
v}
becomes
{v
_ = delete(&x)
*&y = *&x.f
v}
The latter is a use-after-delete of &x whereas the original SIL program is well behaved.
Only use HIL if that is not something your checker needs to care about.
*)
Only use HIL if that is not something your checker needs to care about. *)
module MakeAbstractInterpreterWithConfig
(MakeAbstractInterpreter : AbstractInterpreter.Make)
(HilConfig : HilConfig)

@ -15,7 +15,5 @@ val with_session :
-> Procdesc.Node.t
-> f:(unit -> 'a)
-> 'a
(**
Wraps [f] in an html debug session.
Will swallow timeouts so do *not* use from within biabduction.
*)
(** Wraps [f] in an html debug session. Will swallow timeouts so do *not* use from within
biabduction. *)

@ -418,8 +418,8 @@ let get_fields_nullified procdesc =
(** Checks if the class name is a Java exception *)
let is_throwable tenv typename = is_subtype_of_str tenv typename "java.lang.Throwable"
(** tests whether any class attributes (e.g., @ThreadSafe) pass check of first argument,
including for supertypes*)
(** tests whether any class attributes (e.g., [@ThreadSafe]) pass check of first argument, including
for supertypes*)
let check_class_attributes check tenv = function
| Typ.Procname.Java java_pname ->
let check_class_annots _ {Typ.Struct.annots} = check annots in
@ -428,8 +428,8 @@ let check_class_attributes check tenv = function
false
(** tests whether any class attributes (e.g., @ThreadSafe) pass check of first argument,
for the current class only*)
(** tests whether any class attributes (e.g., [@ThreadSafe]) pass check of first argument, for the
current class only*)
let check_current_class_attributes check tenv = function
| Typ.Procname.Java java_pname -> (
match Tenv.lookup tenv (Typ.Procname.Java.get_class_type_name java_pname) with
@ -441,7 +441,7 @@ let check_current_class_attributes check tenv = function
false
(** find superclasss with attributes (e.g., @ThreadSafe), including current class*)
(** find superclasss with attributes (e.g., [@ThreadSafe]), including current class*)
let rec find_superclasses_with_attributes check tenv tname =
match Tenv.lookup tenv tname with
| Some struct_typ ->

@ -10,7 +10,8 @@ open! IStd
(** Module for Pattern matching. *)
val get_this_type_nonstatic_methods_only : ProcAttributes.t -> Typ.t option
(** Get the `this` type of a procedure. Should not be called on non-static methods, otherwise it can return a wrong type *)
(** Get the `this` type of a procedure. Should not be called on non-static methods, otherwise it can
return a wrong type *)
val get_type_name : Typ.t -> string
(** Get the name of a type *)
@ -46,8 +47,7 @@ val implements_collections : Tenv.t -> string -> bool
(** Check whether class implements a Java's Collections *)
val implements_pseudo_collection : Tenv.t -> string -> bool
(** Check whether class implements a pseudo Collection with support
for get() and size() methods *)
(** Check whether class implements a pseudo Collection with support for get() and size() methods *)
val implements_enumeration : Tenv.t -> string -> bool
(** Check whether class implements a Java's Enumeration *)
@ -89,13 +89,13 @@ val implements_list : Tenv.t -> string -> bool
(** Check whether class implements a Java's list *)
val implements_google : string -> Tenv.t -> string -> bool
(** Check whether class implements a class of Google *)
(** Check whether class implements a class of Google *)
val implements_android : string -> Tenv.t -> string -> bool
(** Check whether class implements a class of Android *)
(** Check whether class implements a class of Android *)
val implements_xmob_utils : string -> Tenv.t -> string -> bool
(** Check whether class implements a class of xmod.utils *)
(** Check whether class implements a class of xmod.utils *)
val supertype_exists : Tenv.t -> (Typ.Name.t -> Typ.Struct.t -> bool) -> Typ.Name.t -> bool
(** Holds iff the predicate holds on a supertype of the named type, including the type itself *)
@ -116,7 +116,8 @@ val proc_calls :
val override_exists :
?check_current_type:bool -> (Typ.Procname.t -> bool) -> Tenv.t -> Typ.Procname.t -> bool
(** Return true if applying the given predicate to an override of [procname] (including [procname] itself when [check_current_type] is true, which it is by default) returns true. *)
(** Return true if applying the given predicate to an override of [procname] (including [procname]
itself when [check_current_type] is true, which it is by default) returns true. *)
val override_iter : (Typ.Procname.t -> unit) -> Tenv.t -> Typ.Procname.t -> unit
(** Apply the given predicate to procname and each override of [procname]. For the moment, this only
@ -139,15 +140,15 @@ val is_throwable : Tenv.t -> Typ.Name.t -> bool
(** [is_throwable tenv class_name] checks if class_name is of type java.lang.Throwable *)
val check_class_attributes : (Annot.Item.t -> bool) -> Tenv.t -> Typ.Procname.t -> bool
(** tests whether any class attributes (e.g., @ThreadSafe) pass check of first argument,
including supertypes*)
(** tests whether any class attributes (e.g., [@ThreadSafe]) pass check of first argument, including
supertypes*)
val check_current_class_attributes : (Annot.Item.t -> bool) -> Tenv.t -> Typ.Procname.t -> bool
(** tests whether any class attributes (e.g., @ThreadSafe) pass check of first argument,
for current class only*)
(** tests whether any class attributes (e.g., [@ThreadSafe]) pass check of first argument, for
current class only*)
val find_superclasses_with_attributes :
(Annot.Item.t -> bool) -> Tenv.t -> Typ.Name.t -> Typ.Name.t list
(** find superclasss with attributes (e.g., @ThreadSafe), including current class*)
(** find superclasss with attributes (e.g., [@ThreadSafe]), including current class*)
val is_override_of : Typ.Procname.t -> (Typ.Procname.t -> bool) Staged.t

@ -16,8 +16,8 @@ module type S = sig
(** schedule the successors of [node] *)
val pop : t -> (CFG.Node.t * CFG.Node.id list * t) option
(** remove and return the node with the highest priority, the ids of its visited
predecessors, and the new schedule *)
(** remove and return the node with the highest priority, the ids of its visited predecessors, and
the new schedule *)
val empty : CFG.t -> t
end
@ -90,7 +90,7 @@ module ReversePostorder (CFG : ProcCfg.S) = struct
quick popping, and do a linear search only when this list is empty *)
(** remove and return the node with the highest priority (note that smaller integers have higher
priority), the ids of its visited predecessors, and new schedule *)
priority), the ids of its visited predecessors, and new schedule *)
let pop t =
try
let init_id, init_work = M.min_binding t.worklist in

@ -24,7 +24,7 @@ module type S = sig
val read_full : caller_summary:Summary.t -> callee_pname:Typ.Procname.t -> (Procdesc.t * t) option
(** Return the proc desc and payload for the given procedure. Runs the analysis on-demand if
necessary. *)
necessary. *)
val read : caller_summary:Summary.t -> callee_pname:Typ.Procname.t -> t option
(** Return the payload for the given procedure. Runs the analysis on-demand if necessary. *)

@ -5,6 +5,8 @@
* LICENSE file in the root directory of this source tree.
*)
[@@@ocamlformat "parse-docstrings = false"]
open! IStd
(** Transfer functions that push abstract states across instructions. A typical client should

@ -202,7 +202,8 @@ let string_to_issue_mode m =
type parsed_issue_type =
{ name: string option
(** issue name, if no name is given name will be a readable version of id, by removing underscores and capitalizing first letters of words *)
(** issue name, if no name is given name will be a readable version of id, by removing
underscores and capitalizing first letters of words *)
; doc_url: string option }
(** Convert a parsed checker in list of linters *)

@ -19,13 +19,12 @@ val filter_parsed_linters : linter list -> SourceFile.t -> linter list
val pp_linters : Format.formatter -> linter list -> unit
(** map used to expand macro. It maps a formula id to a triple
(visited, parameters, definition).
Visited is used during the expansion phase to understand if the
formula was already expanded and, if yes we have a cyclic definifion *)
(** map used to expand macro. It maps a formula id to a triple (visited, parameters, definition).
Visited is used during the expansion phase to understand if the formula was already expanded
and, if yes we have a cyclic definifion *)
type macros_map = (bool * ALVar.t list * CTLTypes.t) ALVar.FormulaIdMap.t
(** Map a path name to a list of paths. *)
(** Map a path name to a list of paths. *)
type paths_map = ALVar.t list ALVar.VarMap.t
(* Module for warnings detected at translation time by the frontend *)

@ -18,8 +18,8 @@ type context =
; parent_methods: Clang_ast_t.decl list
; in_synchronized_block: bool
; is_ck_translation_unit: bool
(** True if the translation unit contains an ObjC class impl that's a subclass
of CKComponent or CKComponentController. *)
(** True if the translation unit contains an ObjC class impl that's a subclass of
CKComponent or CKComponentController. *)
; current_objc_class: Clang_ast_t.decl option
(** If inside an objc class, contains the objc class (impl or interface) decl. *)
; current_objc_category: Clang_ast_t.decl option

@ -18,8 +18,8 @@ type context =
; parent_methods: Clang_ast_t.decl list
; in_synchronized_block: bool
; is_ck_translation_unit: bool
(** True if the translation unit contains an ObjC class impl that's a subclass
of CKComponent or CKComponentController. *)
(** True if the translation unit contains an ObjC class impl that's a subclass of
CKComponent or CKComponentController. *)
; current_objc_class: Clang_ast_t.decl option
(** If inside an objc class, contains the objc class (impl or interface) decl. *)
; current_objc_category: Clang_ast_t.decl option

@ -7,31 +7,27 @@
open! IStd
(**
This module defines a language to define checkers. These checkers are interpreted over the AST of
the program. A checker is defined by a CTL formula which expresses a condition saying when the
checker should report a problem.
*)
(** This module defines a language to define checkers. These checkers are interpreted over the AST
of the program. A checker is defined by a CTL formula which expresses a condition saying when
the checker should report a problem. *)
(** "set" clauses are used for defining mandatory variables that will be used
by when reporting issues: eg for defining the condition.
(** "set" clauses are used for defining mandatory variables that will be used by when reporting
issues: eg for defining the condition.
"desc" clauses are used for defining the error message,
the suggestion, the severity.
"desc" clauses are used for defining the error message, the suggestion, the severity.
"let" clauses are used to define temporary formulas which are then
used to abbreviate the another formula. For example
"let" clauses are used to define temporary formulas which are then used to abbreviate the
another formula. For example
let f = a And B
{v
let f = a And B
set formula = f OR f
set message = "bla"
*)
set formula = f OR f
set message = "bla"
v} *)
type clause =
| CLet of ALVar.formula_id * ALVar.t list * CTLTypes.t (** Let clause: let id = definifion; *)
| CLet of ALVar.formula_id * ALVar.t list * CTLTypes.t (** Let clause: let id = definifion; *)
| CSet of ALVar.keyword * CTLTypes.t (** Set clause: set id = definition *)
| CDesc of ALVar.keyword * string (** Description clause eg: set message = "..." *)
| CPath of [`WhitelistPath | `BlacklistPath] * ALVar.t list

@ -8,7 +8,7 @@
open! IStd
exception ALParserInvariantViolationException of string
(** Raised when the parser encounters a violation of a certain invariant *)
(** Raised when the parser encounters a violation of a certain invariant *)
type exc_info

@ -47,16 +47,20 @@ type t =
(** EF phi <=> there exits a a path from the current node with a descendant where phi hold *)
| AG of transitions option * t (** AG phi <=> for all discendant of the current node phi hold *)
| EG of transitions option * t
(** EG phi <=> there exists a path (of descendants) from the current node where phi hold at each node of the path *)
(** EG phi <=> there exists a path (of descendants) from the current node where phi hold at
each node of the path *)
| AU of transitions option * t * t
(** AU(phi1, phi2) <=> for all paths from the current node phi1 holds in every node until ph2 holds *)
(** AU(phi1, phi2) <=> for all paths from the current node phi1 holds in every node until ph2
holds *)
| EU of transitions option * t * t
(** EU(phi1, phi2) <=> there exists a path from the current node such that phi1 holds until phi2 holds *)
(** EU(phi1, phi2) <=> there exists a path from the current node such that phi1 holds until
phi2 holds *)
| EH of ALVar.alexp list * t
(** EH[classes]phi <=> there exists a node defining a super class in the hierarchy of the class defined by the current node (if any) where phi holds *)
(** EH[classes]phi <=> there exists a node defining a super class in the hierarchy of the
class defined by the current node (if any) where phi holds *)
| ET of ALVar.alexp list * transitions option * t
(** ET [T] [l] phi <=> there exists a descentant an of the current node such that an is of type in set T
making a transition to a node an' via label l, such that in an phi holds. *)
(** ET [T] [l] phi <=> there exists a descentant an of the current node such that an is of
type in set T making a transition to a node an' via label l, such that in an phi holds. *)
| InObjCClass of t * t
[@@deriving compare]

@ -36,15 +36,15 @@ let is_ck_context (context : CLintersContext.context) an =
&& CGeneral_utils.is_objc_extension context.translation_unit_context
(** Recursively go up the inheritance hierarchy of a given ObjCInterfaceDecl.
(Returns false on decls other than that one.) *)
(** Recursively go up the inheritance hierarchy of a given ObjCInterfaceDecl. (Returns false on
decls other than that one.) *)
let is_component_or_controller_if decl =
let open CFrontend_config in
CAst_utils.is_objc_if_descendant decl [ckcomponent_cl; ckcomponentcontroller_cl]
(** True if it's an objc class impl that extends from CKComponent or
CKComponentController, false otherwise *)
(** True if it's an objc class impl that extends from CKComponent or CKComponentController, false
otherwise *)
let rec is_component_or_controller_descendant_impl decl =
match decl with
| Clang_ast_t.ObjCImplementationDecl _ ->
@ -55,9 +55,8 @@ let rec is_component_or_controller_descendant_impl decl =
false
(** Returns true if the passed-in list of decls contains an
ObjCImplementationDecl of a descendant of CKComponent or
CKComponentController.
(** Returns true if the passed-in list of decls contains an ObjCImplementationDecl of a descendant
of CKComponent or CKComponentController.
Does not recurse into hierarchy. *)
and contains_ck_impl decl_list = List.exists ~f:is_component_or_controller_descendant_impl decl_list
@ -65,27 +64,27 @@ and contains_ck_impl decl_list = List.exists ~f:is_component_or_controller_desce
(** An easy way to fix the component kit best practice
http://componentkit.org/docs/avoid-local-variables.html
Local variables that are const or const pointers by definition cannot be
assigned to after declaration, which means the entire class of bugs stemming
from value mutation after assignment are gone.
Local variables that are const or const pointers by definition cannot be assigned to after
declaration, which means the entire class of bugs stemming from value mutation after assignment
are gone.
Note we want const pointers, not mutable pointers to const instances.
OK:
```
{v
const int a;
int *const b;
NSString *const c;
const int *const d;
```
v}
Not OK:
```
{v
const int *z;
const NSString *y;
``` *)
v} *)
let mutable_local_vars_advice context an =
try
let rec get_referenced_type (qual_type : Clang_ast_t.qual_type) : Clang_ast_t.decl option =
@ -202,9 +201,9 @@ let component_factory_function_advice context an =
(* Should only be called with FunctionDecl *)
(** Components should not inherit from each other. They should instead
inherit from CKComponent, CKCompositeComponent, or
CKStatefulViewComponent. (Similar rule applies to component controllers.) *)
(** Components should not inherit from each other. They should instead inherit from CKComponent,
CKCompositeComponent, or CKStatefulViewComponent. (Similar rule applies to component
controllers.) *)
let component_with_unconventional_superclass_advice context an =
let check_interface if_decl =
match if_decl with
@ -265,17 +264,15 @@ let component_with_unconventional_superclass_advice context an =
(** Components should only have one factory method.
(They could technically have none if they re-use the parent class's factory
method.)
(They could technically have none if they re-use the parent class's factory method.)
We care about ones that are declared in the interface. In other words, if
additional factory methods are implementation-only, the rule doesn't catch
it. While its existence is probably not good, I can't think of any reason
there would be factory methods that aren't exposed outside of a class is
not useful if there's only one public factory method.
We care about ones that are declared in the interface. In other words, if additional factory
methods are implementation-only, the rule doesn't catch it. While its existence is probably not
good, I can't think of any reason there would be factory methods that aren't exposed outside of
a class is not useful if there's only one public factory method.
Given n factory methods, the rule should emit n-1 issues. Each issue's
location should point to the method declaration. *)
Given n factory methods, the rule should emit n-1 issues. Each issue's location should point to
the method declaration. *)
let component_with_multiple_factory_methods_advice context an =
let is_unavailable_attr attr = match attr with `UnavailableAttr _ -> true | _ -> false in
let is_available_factory_method if_decl (decl : Clang_ast_t.decl) =
@ -347,11 +344,10 @@ let is_in_factory_method (context : CLintersContext.context) =
http://componentkit.org/docs/no-side-effects.html
The only current way we look for side-effects is by looking for
asynchronous execution (dispatch_async, dispatch_after) and execution that
relies on other threads (dispatch_sync). Other side-effects, like reading
of global variables, is not checked by this analyzer, although still an
infraction of the rule. *)
The only current way we look for side-effects is by looking for asynchronous execution
(dispatch_async, dispatch_after) and execution that relies on other threads (dispatch_sync).
Other side-effects, like reading of global variables, is not checked by this analyzer, although
still an infraction of the rule. *)
let rec component_initializer_with_side_effects_advice_ (context : CLintersContext.context)
call_stmt =
let condition =
@ -398,8 +394,8 @@ let component_initializer_with_side_effects_advice (context : CLintersContext.co
(** Returns one issue per line of code, with the column set to 0.
This still needs to be in infer b/c only files that have a valid component
kit class impl should be analyzed. *)
This still needs to be in infer b/c only files that have a valid component kit class impl should
be analyzed. *)
let component_file_line_count_info (context : CLintersContext.context) dec =
let condition = Config.compute_analytics && context.is_ck_translation_unit in
match dec with

@ -8,9 +8,8 @@
open! IStd
val contains_ck_impl : Clang_ast_t.decl list -> bool
(** Returns true if the passed-in list of decls contains an
ObjCImplementationDecl of a descendant of CKComponent or
CKComponentController.
(** Returns true if the passed-in list of decls contains an ObjCImplementationDecl of a descendant
of CKComponent or CKComponentController.
Does not recurse into hierarchy. *)

@ -5,6 +5,8 @@
* LICENSE file in the root directory of this source tree.
*)
[@@@ocamlformat "parse-docstrings = false"]
open! IStd
type t = ALVar.formula_id * ALVar.alexp list [@@deriving compare]

@ -263,11 +263,8 @@ let get_direct_successor_nodes an =
let infer_prefix = "__infer_ctl_"
(** Data structures for type parser.
Correspondence with clang types inferred from
StringRef BuiltinType::getName in
https://clang.llvm.org/doxygen/Type_8cpp_source.html
*)
(** Data structures for type parser. Correspondence with clang types inferred from StringRef
BuiltinType::getName in https://clang.llvm.org/doxygen/Type_8cpp_source.html *)
type builtin_kind =
| Void (** void *)
| Bool (** bool *)

@ -37,11 +37,8 @@ val get_direct_successor_nodes : ast_node -> ast_node list
val infer_prefix : string
(** Data structures for type parser.
Correspondence with clang types inferred from
StringRef BuiltinType::getName in
https://clang.llvm.org/doxygen/Type_8cpp_source.html
*)
(** Data structures for type parser. Correspondence with clang types inferred from StringRef
BuiltinType::getName in https://clang.llvm.org/doxygen/Type_8cpp_source.html *)
type builtin_kind =
| Void (** void *)
| Bool (** bool *)

@ -9,11 +9,10 @@
type t =
| Analyze (** analyze previously captured source files *)
| Capture
(** capture compilation commands and translate source files into infer's intermediate
language *)
(** capture compilation commands and translate source files into infer's intermediate language *)
| Compile
(** set up the infer environment then run the compilation commands without capturing the
source files *)
source files *)
| Events (** dump logged events into stdout *)
| Explore (** explore infer reports *)
| Report (** post-process infer results and reports *)

@ -46,9 +46,9 @@ end
module IdMap = Typ.Procname.Hash
module NodeMap = Caml.Hashtbl.Make (Int)
(** [node_map] is a map from ids (unique ints) to nodes corresponding to defined procedures.
[id_map] is a map from all encountered (not necessarily defined) procnames to their ids,
and thus its image is a superset of the domain of [node_map], and usually a strict superset.
(** [node_map] is a map from ids (unique ints) to nodes corresponding to defined procedures.
[id_map] is a map from all encountered (not necessarily defined) procnames to their ids, and
thus its image is a superset of the domain of [node_map], and usually a strict superset.
[trim_id_map] makes the image equal to the domain of [node_map]. *)
type t = {id_map: int IdMap.t; node_map: Node.t NodeMap.t}

@ -321,11 +321,11 @@ let issue_of_cost kind CostIssues.{complexity_increase_issue; zero_issue; infini
else None
(** Differential of cost reports, based on degree variations.
Compare degree_before (DB), and degree_after (DA):
DB > DA => fixed
DB < DA => introduced
*)
(** Differential of cost reports, based on degree variations. Compare degree_before (DB), and
degree_after (DA):
- DB > DA => fixed
- DB < DA => introduced *)
let of_costs ~(current_costs : Jsonbug_t.costs_report) ~(previous_costs : Jsonbug_t.costs_report) =
let fold_aux kind issue_spec ~key:_ ~data (left, both, right) =
match data with

@ -1064,8 +1064,8 @@ let process_summary filters formats_by_report_kind linereader stats summary issu
issues_acc'
(** Although the out_file is an Option type, the None option is strictly meant for the
logs format_kind, and all other formats should contain an outfile value. *)
(** Although the out_file is an Option type, the None option is strictly meant for the logs
format_kind, and all other formats should contain an outfile value. *)
let mk_format format_kind fname =
Option.value_map
~f:(fun out_file -> [(format_kind, Some out_file)])

@ -10,8 +10,8 @@ module L = Logging
let compilation_db = lazy (CompilationDatabase.from_json_files !Config.clang_compilation_dbs)
(** Given proc_attributes try to produce proc_attributes' where proc_attributes'.is_defined = true
It may trigger capture of extra files to do so and when it does, it waits for
frontend to finish before returning *)
It may trigger capture of extra files to do so and when it does, it waits for frontend to finish
before returning *)
let try_capture (attributes : ProcAttributes.t) : ProcAttributes.t option =
let (lazy cdb) = compilation_db in
( if Option.is_none (Attributes.load_defined attributes.proc_name) then

@ -222,19 +222,15 @@ module OnDisk = struct
load_summary_to_spec_table proc_name
(** Check if the procedure is from a library:
It's not defined, and there is no spec file for it. *)
(** Check if the procedure is from a library: It's not defined, and there is no spec file for it. *)
let proc_is_library proc_attributes =
if not proc_attributes.ProcAttributes.is_defined then
match get proc_attributes.ProcAttributes.proc_name with None -> true | Some _ -> false
else false
(** Try to find the attributes for a defined proc.
First look at specs (to get attributes computed by analysis)
then look at the attributes table.
If no attributes can be found, return None.
*)
(** Try to find the attributes for a defined proc. First look at specs (to get attributes computed
by analysis) then look at the attributes table. If no attributes can be found, return None. *)
let proc_resolve_attributes proc_name =
match get proc_name with
| Some summary ->

@ -9,7 +9,7 @@
open! IStd
(** Procedure summaries: the results of the capture and all the analysis for a single procedure,
plus some statistics *)
plus some statistics *)
module Stats : sig
(** Execution statistics *)
@ -86,8 +86,8 @@ module OnDisk : sig
(** Remove all the elements from the cache of summaries *)
val remove_from_cache : Typ.Procname.t -> unit
(** Remove an element from the cache of summaries. Contrast to reset which re-initializes a summary
keeping the same Procdesc and updates the cache accordingly. *)
(** Remove an element from the cache of summaries. Contrast to reset which re-initializes a
summary keeping the same Procdesc and updates the cache accordingly. *)
val get : Typ.Procname.t -> t option
(** Return the summary option for the procedure name *)
@ -102,15 +102,11 @@ module OnDisk : sig
(** Load procedure summary from the given file *)
val proc_resolve_attributes : Typ.Procname.t -> ProcAttributes.t option
(** Try to find the attributes for a defined proc.
First look at specs (to get attributes computed by analysis)
then look at the attributes table.
If no attributes can be found, return None.
*)
(** Try to find the attributes for a defined proc. First look at specs (to get attributes computed
by analysis) then look at the attributes table. If no attributes can be found, return None. *)
val proc_is_library : ProcAttributes.t -> bool
(** Check if the procedure is from a library:
It's not defined, and there is no spec file for it. *)
(** Check if the procedure is from a library: It's not defined, and there is no spec file for it. *)
val store : t -> unit
(** Save summary for the procedure into the spec database *)

@ -7,8 +7,8 @@
open! IStd
val bottom_up : SourceFile.t list -> SchedulerTypes.target ProcessPool.TaskGenerator.t
(** task generator that works by
- loading the syntactic call graph from the capture DB
(** task generator that works by
- loading the syntactic call graph from the capture DB
- restricting it to the reachable procs from the modified files
- scheduling leaves only and removing them from the graph when analysed.
*)
- scheduling leaves only and removing them from the graph when analysed. *)

@ -13,6 +13,7 @@ type proc_callback_args =
{get_procs_in_file: Typ.Procname.t -> Typ.Procname.t list; summary: Summary.t; exe_env: Exe_env.t}
(** Type of a procedure callback:
- List of all the procedures the callback will be called on.
- get_proc_desc to get a proc desc from a proc name
- Type environment.

@ -6,6 +6,8 @@
* LICENSE file in the root directory of this source tree.
*)
[@@@ocamlformat "parse-docstrings = false"]
open! IStd
(** Create descriptions of analysis errors *)

@ -11,16 +11,16 @@ open! IStd
(** Create descriptions of analysis errors *)
val vpath_find : Tenv.t -> 'a Prop.t -> Exp.t -> DecompiledExp.vpath * Typ.t option
(** find the dexp, if any, where the given value is stored
also return the type of the value if found *)
(** find the dexp, if any, where the given value is stored also return the type of the value if
found *)
val hpred_is_open_resource : Tenv.t -> 'a Prop.t -> Sil.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 *)
(** 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
@ -30,8 +30,8 @@ val find_ident_assignment : Procdesc.Node.t -> Ident.t -> (Procdesc.Node.t * Exp
(** 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. *)
(** 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 *)
@ -84,8 +84,8 @@ val explain_dereference_as_caller_expression :
-> Location.t
-> Pvar.t list
-> Localise.error_desc
(** return a description explaining value [exp] in [prop] in terms of a source expression
using the formal parameters of the call *)
(** return a description explaining value [exp] in [prop] in terms of a source expression using the
formal parameters of the call *)
val explain_divide_by_zero : Tenv.t -> Exp.t -> Procdesc.Node.t -> Location.t -> Localise.error_desc
(** explain a division by zero *)
@ -112,10 +112,9 @@ val explain_leak :
-> PredSymb.t option
-> string option
-> Exceptions.visibility * Localise.error_desc
(** Produce a description of a leak by looking at the current state.
If the current instruction is a variable nullify, blame the variable.
If it is an abstraction, blame any variable nullify at the current node.
If there is an alloc attribute, print the function call and line number. *)
(** Produce a description of a leak by looking at the current state. If the current instruction is a
variable nullify, blame the variable. If it is an abstraction, blame any variable nullify at the
current node. If there is an alloc attribute, print the function call and line number. *)
val explain_null_test_after_dereference :
Tenv.t -> Exp.t -> Procdesc.Node.t -> int -> Location.t -> Localise.error_desc

@ -10,7 +10,7 @@ open! IStd
module F = Format
(** Execution environments: basically a cache of where procedures are and what is their CFG and type
environment *)
environment *)
module L = Logging

@ -9,7 +9,7 @@
open! IStd
(** Execution environments: basically a cache of where procedures are and what is their type
environment *)
environment *)
type file_data

@ -325,8 +325,8 @@ let create_filters () =
(** This function loads and list the path that are being filtered by the analyzer. The results are
of the form: path/to/file.java -> true/false meaning that analysis results will be reported on
path/to/file.java or not *)
of the form: path/to/file.java -> true/false meaning that analysis results will be reported on
path/to/file.java or not *)
let test () =
let filters = create_filters () in
let matches path = filters.path_filter path in

@ -14,11 +14,11 @@ val get_proc_desc : Typ.Procname.t -> Procdesc.t option
val analyze_proc_desc : caller_summary:Summary.t -> Procdesc.t -> Summary.t option
(** [analyze_proc_desc ~caller_summary callee_pdesc] performs an on-demand analysis of callee_pdesc
triggered during the analysis of caller_summary *)
triggered during the analysis of caller_summary *)
val analyze_proc_name : caller_summary:Summary.t -> Typ.Procname.t -> Summary.t option
(** [analyze_proc_name ~caller_summary callee_pname] performs an on-demand analysis of callee_pname
triggered during the analysis of caller_summary *)
triggered during the analysis of caller_summary *)
val analyze_proc_name_no_caller : Typ.Procname.t -> Summary.t option
(** [analyze_proc_name_no_caller callee_pname] performs an on-demand analysis of callee_pname

@ -9,8 +9,7 @@
open! IStd
module L = Logging
(** add Abstract instructions into the IR to give hints about when abstraction should be
performed *)
(** add Abstract instructions into the IR to give hints about when abstraction should be performed *)
module AddAbstractionInstructions = struct
let process pdesc =
let open Procdesc in
@ -45,16 +44,16 @@ module Liveness = struct
module VarDomain = Liveness.Domain
(** computes the non-nullified reaching definitions at the end of each node by building on the
results of a liveness analysis to be precise, what we want to compute is:
results of a liveness analysis to be precise, what we want to compute is:
to_nullify := (live_before U non_nullifed_reaching_defs) - live_after
to_nullify := (live_before U non_nullifed_reaching_defs) - live_after
non_nullified_reaching_defs := non_nullified_reaching_defs - to_nullify
non_nullified_reaching_defs := non_nullified_reaching_defs - to_nullify
Note that this can't be done with by combining the results of reaching definitions and liveness
after the fact, nor can it be done with liveness alone. We will insert nullify instructions for
each pvar in to_nullify afer we finish the analysis. Nullify instructions speed up the analysis
by enabling it to GC state that will no longer be read. *)
Note that this can't be done with by combining the results of reaching definitions and
liveness after the fact, nor can it be done with liveness alone. We will insert nullify
instructions for each pvar in to_nullify afer we finish the analysis. Nullify instructions
speed up the analysis by enabling it to GC state that will no longer be read. *)
module NullifyTransferFunctions = struct
module Domain = AbstractDomain.Pair (VarDomain) (VarDomain)
(** (reaching non-nullified vars) * (vars to nullify) *)

@ -14,8 +14,8 @@ module Hashtbl = Caml.Hashtbl
module L = Logging
module F = Format
(** Module to read specific lines from files.
The data from any file will stay in memory until the handle is collected by the gc. *)
(** Module to read specific lines from files. The data from any file will stay in memory until the
handle is collected by the gc. *)
module LineReader = struct
(** Map a file name to an array of string, one for each line in the file. *)
type t = (SourceFile.t, string array) Hashtbl.t
@ -100,8 +100,7 @@ let pp_node_link_seq =
Pp.seq pp_one fmt nodes
(** Print information into html files for nodes
when starting and finishing the processing of a node *)
(** Print information into html files for nodes when starting and finishing the processing of a node *)
module NodesHtml : sig
val start_session : pp_name:(Format.formatter -> unit) -> Procdesc.Node.t -> int -> unit

@ -10,8 +10,8 @@ open! IStd
(** Printers for the analysis results *)
(** Module to read specific lines from files.
The data from any file will stay in memory until the handle is collected by the gc *)
(** Module to read specific lines from files. The data from any file will stay in memory until the
handle is collected by the gc *)
module LineReader : sig
type t

@ -20,8 +20,7 @@ val log_issue_deprecated_using_state :
-> exn
-> unit
(** Report an issue in the given procedure using biabduction state (DO NOT USE ELSEWHERE).
DEPRECATED as it can create race conditions between checkers.
Use log_error/warning instead *)
DEPRECATED as it can create race conditions between checkers. Use log_error/warning instead *)
val log_frontend_issue :
Typ.Procname.t

@ -912,7 +912,7 @@ let decode_env_to_argv env =
String.split ~on:env_var_sep env |> List.filter ~f:(Fn.non String.is_empty)
(** [prefix_before_rest (prefix @ ["--" :: rest])] is [prefix] where "--" is not in [prefix]. *)
(** [prefix_before_rest (prefix @ \["--" :: rest\])] is [prefix] where "--" is not in [prefix]. *)
let rev_prefix_before_rest args =
let rec rev_prefix_before_rest_ rev_keep = function
| [] | "--" :: _ ->

@ -26,6 +26,7 @@ val init_work_dir : string
declared options.
The arguments of the declaration functions are largely treated uniformly:
- [long] declares the option [--long]
- [short] declares the option [-short] as an alias
- [deprecated] declares the option [-key] as an alias, for each [key] in [deprecated]
@ -38,8 +39,7 @@ val init_work_dir : string
- [in_help] indicates the man pages in which the command should be documented, as generated by
calling infer with --help. Otherwise it appears only in --help-full.
- [meta] is a meta-variable naming the parsed value for documentation purposes
- a documentation string
*)
- a documentation string *)
type 'a t =
?deprecated:string list
-> long:string
@ -54,11 +54,11 @@ val mk_set : 'a ref -> 'a -> unit t
(** [mk_set variable value] defines a command line option which sets [variable] to [value]. *)
val mk_bool : ?deprecated_no:string list -> ?default:bool -> ?f:(bool -> bool) -> bool ref t
(** [mk_bool long short doc] defines a [bool ref] set by the command line flag [--long] (and
[-s]), and cleared by the flag [--no-long] (and [-S]). If [long] already has a "no-" prefix,
or [s] is capital, then the existing prefixes will instead be removed. The default
value is [false] unless overridden by [~default:true]. The [doc] string will be prefixed with
either "Activates:" or "Deactivates:", so should be phrased accordingly. *)
(** [mk_bool long short doc] defines a [bool ref] set by the command line flag [--long] (and [-s]),
and cleared by the flag [--no-long] (and [-S]). If [long] already has a "no-" prefix, or [s] is
capital, then the existing prefixes will instead be removed. The default value is [false] unless
overridden by [~default:true]. The [doc] string will be prefixed with either "Activates:" or
"Deactivates:", so should be phrased accordingly. *)
val mk_bool_group :
?deprecated_no:string list
@ -87,19 +87,19 @@ val mk_string_opt :
-> ?mk_reset:bool
-> string option ref t
(** An option "--[long]-reset" is automatically created that resets the reference to None when found
on the command line, unless [mk_reset] is false. *)
on the command line, unless [mk_reset] is false. *)
val mk_string_list :
?default:string list
-> ?default_to_string:(string list -> string)
-> ?f:(string -> string)
-> string list ref t
(** [mk_string_list] defines a [string list ref], initialized to [[]] unless overridden by
[~default]. Each argument of an occurrence of the option will be prepended to the list, so the
(** [mk_string_list] defines a [string list ref], initialized to [\[\]] unless overridden by
[~default]. Each argument of an occurrence of the option will be prepended to the list, so the
final value will be in the reverse order they appeared on the command line.
An option "--[long]-reset" is automatically created that resets the list to [] when found on the
command line. *)
command line. *)
val mk_string_map :
?default:string String.Map.t
@ -125,7 +125,8 @@ val mk_symbol :
an element of [symbols]. *)
val mk_symbol_opt : symbols:(string * 'a) list -> ?f:('a -> 'a) -> ?mk_reset:bool -> 'a option ref t
(** [mk_symbol_opt] is similar to [mk_symbol] but defaults to [None]. If [mk_reset] is false then do not create an additional --[long]-reset option to reset the value of the option to [None]. *)
(** [mk_symbol_opt] is similar to [mk_symbol] but defaults to [None]. If [mk_reset] is false then do
not create an additional --[long]-reset option to reset the value of the option to [None]. *)
val mk_symbol_seq :
?default:'a list -> symbols:(string * 'a) list -> eq:('a -> 'a -> bool) -> 'a list ref t
@ -148,9 +149,9 @@ val mk_rest_actions :
-> string list ref
(** [mk_rest_actions doc ~usage command_to_parse_mode] defines a [string list ref] of the command
line arguments following ["--"], in the reverse order they appeared on the command line. [usage]
is the usage message in case of parse errors or if --help is passed. For example, calling
is the usage message in case of parse errors or if --help is passed. For example, calling
[mk_action] and parsing [exe -opt1 -opt2 -- arg1 arg2] will result in the returned ref
containing [arg2; arg1]. Additionally, the first arg following ["--"] is passed to
containing [arg2; arg1]. Additionally, the first arg following ["--"] is passed to
[command_to_parse_mode] to obtain the parse action that will be used to parse the remaining
arguments. *)
@ -185,11 +186,10 @@ val mk_command_doc :
- [date] is the date of the last modification of the manual
- [short_description] is a one-line description of the command
- [options] can be either [`Replace blocks], which populates the OPTIONS section with [blocks],
or [`Prepend blocks], in which case the options from the command are used, prepended by
[blocks]. If unspecified it defaults to [`Prepend []].
or [`Prepend blocks], in which case the options from the command are used, prepended by
[blocks]. If unspecified it defaults to [`Prepend \[\]].
- All the other [section_name] options correspond to the contents of the section [section_name].
Some are mandatory and some are not.
*)
Some are mandatory and some are not. *)
val mk_subcommand :
InferCommand.t
@ -203,9 +203,8 @@ val mk_subcommand :
(** [mk_subcommand command ~long command_doc] defines the subcommand [command]. A subcommand is
activated by passing [name], and by passing [--deprecated_long] if specified. A man page is
automatically generated for [command] based on the information in [command_doc], if available
(otherwise the command is considered internal). [on_unknown_arg] is the action taken on unknown
anonymous arguments; it is `Reject by default.
*)
(otherwise the command is considered internal). [on_unknown_arg] is the action taken on unknown
anonymous arguments; it is `Reject by default. *)
val args_env_var : string
(** environment variable use to pass arguments from parent to child processes *)
@ -228,6 +227,7 @@ val parse :
-> InferCommand.t option * (int -> 'a)
(** [parse ~usage parse_mode command] parses command line arguments as specified by preceding calls
to the [mk_*] functions, and returns:
- the command selected by the user on the command line, except if [command] is not None in which
case it is considered "pre-selected" for the user;
- a function that prints the usage message and help text then exits with the code passed as
@ -255,8 +255,8 @@ val show_manual :
-> InferCommand.t option
-> unit
(** Display the manual of [command] to the user, or [command_doc] if [command] is None. [format] is
used as for [Cmdliner.Manpage.print]. If [internal_section] is specified, add a section titled
[internal_section] about internal (hidden) options. If [scrub_defaults] then do not print default
values for options. *)
used as for [Cmdliner.Manpage.print]. If [internal_section] is specified, add a section titled
[internal_section] about internal (hidden) options. If [scrub_defaults] then do not print
default values for options. *)
val keep_args_file : bool ref

@ -9,8 +9,8 @@
open! IStd
open PolyVariantEqual
(** Configuration values: either constant, determined at compile time, or set at startup
time by system calls, environment variables, or command line options *)
(** Configuration values: either constant, determined at compile time, or set at startup time by
system calls, environment variables, or command line options *)
module F = Format
module CLOpt = CommandLineOption
@ -157,9 +157,8 @@ let assign = "<\"Assign\">"
let backend_stats_dir_name = "backend_stats"
(** If true, a procedure call succeeds even when there is a bound error this mimics what
happens with a direct array access where an error is produced and the analysis
continues *)
(** If true, a procedure call succeeds even when there is a bound error this mimics what happens
with a direct array access where an error is produced and the analysis continues *)
let bound_error_allowed_in_procedure_call = true
let buck_infer_deps_file_name = "infer-deps.txt"
@ -254,10 +253,8 @@ let max_narrows = 5
operator *)
let max_widens = 10000
(** Flag to tune the level of applying the meet operator for
preconditions during the footprint analysis:
0 = do not use the meet
1 = use the meet to generate new preconditions *)
(** Flag to tune the level of applying the meet operator for preconditions during the footprint
analysis: 0 = do not use the meet 1 = use the meet to generate new preconditions *)
let meet_level = 1
let nsnotification_center_checker_backend = false
@ -399,8 +396,7 @@ let version_string = F.asprintf "%a" pp_version ()
(** System call configuration values *)
(** Initial time of the analysis, i.e. when this module is loaded, gotten from
Unix.time *)
(** Initial time of the analysis, i.e. when this module is loaded, gotten from Unix.time *)
let initial_analysis_time = Unix.time ()
let clang_exe_aliases =
@ -484,17 +480,15 @@ let locate_sdk_root () =
let infer_sdkroot_env_var = "INFER_SDKROOT"
(** Try to locate current SDK root on MacOS *unless* [SDKROOT] is
explicitly provided. The implicit SDK root is propagated to child
processes using a custom [INFER_SDKROOT] env var. The reason for
this is twofold:
(** Try to locate current SDK root on MacOS *unless* [SDKROOT] is explicitly provided. The implicit
SDK root is propagated to child processes using a custom [INFER_SDKROOT] env var. The reason for
this is twofold:
1. With make and buck integrations infer is exec'ed by make/buck
for each source file. That's why we propagate the value by using an
env var instead of calling [locate_sdk_root] each time.
1. With make and buck integrations infer is exec'ed by make/buck for each source file. That's
why we propagate the value by using an env var instead of calling [locate_sdk_root] each time.
2. We don't use [SDKROOT] because it can mess up with other parts
of the toolchain not owned by infer. *)
2. We don't use [SDKROOT] because it can mess up with other parts of the toolchain not owned by
infer. *)
let implicit_sdk_root =
match Sys.getenv "SDKROOT" with
| Some _ ->
@ -1076,8 +1070,8 @@ and compute_analytics =
cyclomatic complexity"
(** Continue the capture for reactive mode:
If a procedure was changed beforehand, keep the changed marking. *)
(** Continue the capture for reactive mode: If a procedure was changed beforehand, keep the changed
marking. *)
and continue =
CLOpt.mk_bool ~deprecated:["continue"] ~long:"continue"
~in_help:InferCommand.[(Analyze, manual_generic)]
@ -2472,9 +2466,10 @@ and version =
(** visit mode for the worklist:
0 depth - fist visit
1 bias towards exit node
2 least visited first *)
- 0 depth - fist visit
- 1 bias towards exit node
- 2 least visited first *)
and worklist_mode =
let var = ref 0 in
CLOpt.mk_set var 2 ~long:"coverage" "analysis mode to maximize coverage (can take longer)" ;

@ -8,15 +8,15 @@
open! IStd
(** Configuration values: either constant, determined at compile time, or set at startup
time by system calls, environment variables, or command line options *)
(** Configuration values: either constant, determined at compile time, or set at startup time by
system calls, environment variables, or command line options *)
type os_type = Unix | Win32 | Cygwin
type compilation_database_dependencies =
| Deps of int option
(** get the compilation database of the dependencies up to depth n
by [Deps (Some n)], or all by [Deps None] *)
(** get the compilation database of the dependencies up to depth n by [Deps (Some n)], or all
by [Deps None] *)
| NoDeps
[@@deriving compare]

@ -56,10 +56,8 @@ module Results_dir : sig
end
val append_crc_cutoff : ?key:string -> ?crc_only:bool -> string -> string
(** Append a crc to the string, using string_crc_hex32.
Cut the string if it exceeds the cutoff limit.
Use an optional key to compute the crc.
Return only the crc if [crc_only] is true. *)
(** Append a crc to the string, using string_crc_hex32. Cut the string if it exceeds the cutoff
limit. Use an optional key to compute the crc. Return only the crc if [crc_only] is true. *)
val source_file_encoding : SourceFile.t -> string
(** string encoding of a source file (including path) as a single filename *)

@ -251,9 +251,10 @@ module Server = struct
let socket_domain = Unix.domain_of_sockaddr socket_addr
(** Unix socket *paths* have a historical length limit of ~100 chars (!?*@&*$). However, this only applies
to the argument passed in the system call to create the socket, not to the actual path.
Thus a workaround is to cd into the parent dir of the socket and then use it, hence this function. *)
(** Unix socket *paths* have a historical length limit of ~100 chars (!?*\@&*$). However, this
only applies to the argument passed in the system call to create the socket, not to the actual
path. Thus a workaround is to cd into the parent dir of the socket and then use it, hence this
function. *)
let in_results_dir ~f = Utils.do_in_dir ~dir:Config.toplevel_results_dir ~f
let rec server_loop socket =

@ -13,8 +13,8 @@ val register : f:(unit -> unit) -> description:string -> unit
val register_late : f:(unit -> unit) -> description:string -> unit
(** Register a function to run when the program exits or is interrupted. Registered functions are
run in the reverse order in which they were registered, but *after* the ones registered with
{!register}. *)
run in the reverse order in which they were registered, but *after* the ones registered with
{!register}. *)
val run : unit -> unit

@ -83,7 +83,7 @@ module VISIBLE_FOR_TESTING_DO_NOT_USE_DIRECTLY = struct
end
(** Given a difference between two files, return the relevant lines in the new file; a line is
relevant when a change took place in it, or nearby. To generate a valid input for this
parser, use unix-diff command with the following formatter arguments:
diff --unchanged-line-format="U" --old-line-format="O" --new-line-format="N" File1 File2 *)
relevant when a change took place in it, or nearby. To generate a valid input for this parser,
use unix-diff command with the following formatter arguments: diff --unchanged-line-format="U"
\--old-line-format="O" --new-line-format="N" File1 File2 *)
let parse_unix_diff str = UnixDiff.process_raw_directives str |> parse_directives

@ -25,6 +25,6 @@ end
val parse_unix_diff : string -> int list
(** Given a difference between two files, return the relevant lines in the new file; a line is
relevant when a change took place in it, or nearby. To generate a valid input for this
parser, use unix-diff command with the following formatter arguments:
diff --unchanged-line-format="U" --old-line-format="O" --new-line-format="N" File1 File2 *)
relevant when a change took place in it, or nearby. To generate a valid input for this parser,
use unix-diff command with the following formatter arguments: diff --unchanged-line-format="U"
\--old-line-format="O" --new-line-format="N" File1 File2 *)

@ -61,15 +61,16 @@ end = struct
let set_enabled issue b = issue.enabled <- b
(** Avoid creating new issue types. The idea is that there are three types of issue types:
1. Statically pre-defined issue types, namely the ones in this module
2. Dynamically created ones, eg from custom errors defined in the models, or defined by the
+ Statically pre-defined issue types, namely the ones in this module
+ Dynamically created ones, eg from custom errors defined in the models, or defined by the
user in AL linters
3. Issue types created at command-line-parsing time. These can mention issues of type 1. or
2., but issues of type 2. have not yet been defined. Thus, we record only there [enabled]
status definitely. The [hum]an-readable description can be updated when we encounter the
definition of the issue type, eg in AL. *)
+ Issue types created at command-line-parsing time. These can mention issues of type 1. or 2.,
but issues of type 2. have not yet been defined. Thus, we record only there [enabled] status
definitely. The [hum]an-readable description can be updated when we encounter the definition
of the issue type, eg in AL. *)
let register_from_string ?(enabled = true) ?hum:hum0 ?doc_url ?linters_def_file unique_id =
let hum = match hum0 with Some str -> str | _ -> prettify unique_id in
let issue = {unique_id; enabled; hum; doc_url; linters_def_file} in

@ -26,14 +26,11 @@ val pp : Format.formatter -> t -> unit
val register_from_string :
?enabled:bool -> ?hum:string -> ?doc_url:string -> ?linters_def_file:string -> string -> t
(** Create a new issue and register it in the list of all issues.
NOTE: if the issue with the same string id is already registered,
overrides `hum`, `doc_url`, and `linters_def_file`, but DOES NOT override
`enabled`. This trick allows to deal with disabling/enabling dynamic AL issues
from the config, when we don't know all params yet.
Thus, the human-readable description can be updated when we encounter the
definition of the issue type, eg in AL.
*)
(** Create a new issue and register it in the list of all issues. NOTE: if the issue with the same
string id is already registered, overrides `hum`, `doc_url`, and `linters_def_file`, but DOES
NOT override `enabled`. This trick allows to deal with disabling/enabling dynamic AL issues from
the config, when we don't know all params yet. Thus, the human-readable description can be
updated when we encounter the definition of the issue type, eg in AL. *)
val set_enabled : t -> bool -> unit
@ -74,18 +71,18 @@ val buffer_overrun_u5 : t
val cannot_star : t
val checkers_allocates_memory : t
(** Warning name when a performance critical method directly or indirectly
calls a method allocating memory *)
(** Warning name when a performance critical method directly or indirectly calls a method allocating
memory *)
val checkers_annotation_reachability_error : t
val checkers_calls_expensive_method : t
(** Warning name when a performance critical method directly or indirectly
calls a method annotatd as expensive *)
(** Warning name when a performance critical method directly or indirectly calls a method annotatd
as expensive *)
val checkers_expensive_overrides_unexpensive : t
(** Warning name for the subtyping rule: method not annotated as expensive cannot be overridden
by a method annotated as expensive *)
(** Warning name for the subtyping rule: method not annotated as expensive cannot be overridden by a
method annotated as expensive *)
val checkers_fragment_retain_view : t

@ -32,8 +32,7 @@ val task_progress : f:(unit -> unit) -> (F.formatter -> 'a -> unit) -> 'a -> uni
val result : ('a, F.formatter, unit) format -> 'a
(** Emit a result to stdout. Use only if the output format is stable and useful enough that it may
conceivably get piped to another program, ie, almost never (use [progress] instead otherwise).
*)
conceivably get piped to another program, ie, almost never (use [progress] instead otherwise). *)
val user_error : ('a, F.formatter, unit) format -> 'a
(** bad input, etc. detected *)

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save