Infer# integration (#1361)

Summary:
Dear Infer team,

To contribute to Infer community, I would like to integrate infer#'s language agnostic layer into Infer.

Please help to review, discuss and consider to merge this feature.

Thanks,
Xiaoyu

Pull Request resolved: https://github.com/facebook/infer/pull/1361

Reviewed By: skcho

Differential Revision: D25928458

Pulled By: jvillard

fbshipit-source-id: 7726150b8
master
Xiaoyu Liu 4 years ago committed by Facebook GitHub Bot
parent e0f0022fa1
commit 285ddb4a98

1
.gitignore vendored

@ -52,6 +52,7 @@ duplicates.txt
/infer/tests/build_systems/incremental_analysis_add_procedure/src /infer/tests/build_systems/incremental_analysis_add_procedure/src
/infer/tests/build_systems/java_test_determinator/*.test /infer/tests/build_systems/java_test_determinator/*.test
/infer/tests/build_systems/java_source_parser/*.test /infer/tests/build_systems/java_source_parser/*.test
/infer/tests/codetoanalyze/dotnet/*/*.json
/_release /_release
/infer-source /infer-source

@ -190,6 +190,23 @@ BUILD_SYSTEMS_TESTS += mvn
endif endif
endif endif
DIRECT_TESTS += \
dotnet_arithmetic \
dotnet_array \
dotnet_bgeble \
dotnet_box \
dotnet_fieldderef \
dotnet_isinst \
dotnet_ldstr \
dotnet_logical \
dotnet_nullderef-interproc \
dotnet_nullderef-simple \
dotnet_nullparam \
dotnet_numcomparison \
dotnet_reference \
dotnet_resourceleak \
dotnet_starg
ifeq ($(BUILD_C_ANALYZERS)+$(BUILD_JAVA_ANALYZERS),yes+yes) ifeq ($(BUILD_C_ANALYZERS)+$(BUILD_JAVA_ANALYZERS),yes+yes)
BUILD_SYSTEMS_TESTS += make utf8_in_pwd waf BUILD_SYSTEMS_TESTS += make utf8_in_pwd waf
endif endif

@ -111,8 +111,9 @@ OPTIONS
--no-default-checkers --no-default-checkers
Deactivates: Default checkers: --biabduction, Deactivates: Default checkers: --biabduction,
--fragment-retains-view, --inefficient-keyset-iterator, --linters, --fragment-retains-view, --inefficient-keyset-iterator, --linters,
--liveness, --racerd, --siof, --self-in-block, --starvation, --liveness, --racerd, --dotnet-resource-leak, --siof,
--uninit (Conversely: --default-checkers) --self-in-block, --starvation, --uninit (Conversely:
--default-checkers)
--eradicate --eradicate
Activates: checker eradicate: The eradicate `@Nullable` checker Activates: checker eradicate: The eradicate `@Nullable` checker

@ -210,6 +210,9 @@ OPTIONS
`<reason_string>` is a non-empty string used to explain why the `<reason_string>` is a non-empty string used to explain why the
issue was filtered. See also infer-report(1) and infer-run(1). issue was filtered. See also infer-report(1) and infer-run(1).
--cfg-json file
Path to CFG json file See also infer-analyzejson(1).
--changed-files-index file --changed-files-index file
Specify the file containing the list of source files from which Specify the file containing the list of source files from which
reactive analysis should start. Source files should be specified reactive analysis should start. Source files should be specified
@ -313,8 +316,9 @@ OPTIONS
--developer-mode, --print-buckets, --print-types, --developer-mode, --print-buckets, --print-types,
--reports-include-ml-loc, --no-only-cheap-debug, --trace-error, --reports-include-ml-loc, --no-only-cheap-debug, --trace-error,
--write-dotty, --write-html) (Conversely: --no-debug | -G) --write-dotty, --write-html) (Conversely: --no-debug | -G)
See also infer-analyze(1), infer-capture(1), infer-compile(1), See also infer-analyze(1), infer-analyzejson(1),
infer-report(1), infer-reportdiff(1), and infer-run(1). infer-capture(1), infer-compile(1), infer-report(1),
infer-reportdiff(1), and infer-run(1).
--debug-level level --debug-level level
Debug level (sets --bo-debug level, --debug-level-analysis level, Debug level (sets --bo-debug level, --debug-level-analysis level,
@ -322,23 +326,27 @@ OPTIONS
- 0: only basic debugging enabled - 0: only basic debugging enabled
- 1: verbose debugging enabled - 1: verbose debugging enabled
- 2: very verbose debugging enabled - 2: very verbose debugging enabled
See also infer-analyze(1), infer-capture(1), infer-compile(1), See also infer-analyze(1), infer-analyzejson(1),
infer-report(1), infer-reportdiff(1), and infer-run(1). infer-capture(1), infer-compile(1), infer-report(1),
infer-reportdiff(1), and infer-run(1).
--debug-level-analysis int --debug-level-analysis int
Debug level for the analysis. See --debug-level for accepted Debug level for the analysis. See --debug-level for accepted
values. See also infer-analyze(1), infer-capture(1), infer-compile(1), values. See also infer-analyze(1), infer-analyzejson(1),
infer-report(1), infer-reportdiff(1), and infer-run(1). infer-capture(1), infer-compile(1), infer-report(1),
infer-reportdiff(1), and infer-run(1).
--debug-level-capture int --debug-level-capture int
Debug level for the capture. See --debug-level for accepted Debug level for the capture. See --debug-level for accepted
values. See also infer-analyze(1), infer-capture(1), infer-compile(1), values. See also infer-analyze(1), infer-analyzejson(1),
infer-report(1), infer-reportdiff(1), and infer-run(1). infer-capture(1), infer-compile(1), infer-report(1),
infer-reportdiff(1), and infer-run(1).
--debug-level-linters int --debug-level-linters int
Debug level for the linters. See --debug-level for accepted Debug level for the linters. See --debug-level for accepted
values. See also infer-analyze(1), infer-capture(1), infer-compile(1), values. See also infer-analyze(1), infer-analyzejson(1),
infer-report(1), infer-reportdiff(1), and infer-run(1). infer-capture(1), infer-compile(1), infer-report(1),
infer-reportdiff(1), and infer-run(1).
--no-deduplicate --no-deduplicate
Deactivates: Apply issue-specific deduplication during analysis Deactivates: Apply issue-specific deduplication during analysis
@ -348,8 +356,9 @@ OPTIONS
--no-default-checkers --no-default-checkers
Deactivates: Default checkers: --biabduction, Deactivates: Default checkers: --biabduction,
--fragment-retains-view, --inefficient-keyset-iterator, --linters, --fragment-retains-view, --inefficient-keyset-iterator, --linters,
--liveness, --racerd, --siof, --self-in-block, --starvation, --liveness, --racerd, --dotnet-resource-leak, --siof,
--uninit (Conversely: --default-checkers) See also infer-analyze(1). --self-in-block, --starvation, --uninit (Conversely:
--default-checkers) See also infer-analyze(1).
--no-default-linters --no-default-linters
Deactivates: Use the default linters for the analysis. Deactivates: Use the default linters for the analysis.
@ -420,6 +429,7 @@ OPTIONS
DIRECT_ATOMIC_PROPERTY_ACCESS (enabled by default), DIRECT_ATOMIC_PROPERTY_ACCESS (enabled by default),
DISCOURAGED_WEAK_PROPERTY_CUSTOM_SETTER (enabled by default), DISCOURAGED_WEAK_PROPERTY_CUSTOM_SETTER (enabled by default),
DIVIDE_BY_ZERO (disabled by default), DIVIDE_BY_ZERO (disabled by default),
DOTNET_RESOURCE_LEAK (enabled by default),
DO_NOT_REPORT (enabled by default), DO_NOT_REPORT (enabled by default),
EMPTY_VECTOR_ACCESS (enabled by default), EMPTY_VECTOR_ACCESS (enabled by default),
ERADICATE_ANNOTATION_GRAPH (enabled by default), ERADICATE_ANNOTATION_GRAPH (enabled by default),
@ -624,8 +634,9 @@ OPTIONS
See also infer-capture(1). See also infer-capture(1).
--help --help
Show this manual See also infer-analyze(1), infer-capture(1), infer-compile(1), Show this manual See also infer-analyze(1), infer-analyzejson(1),
infer-debug(1), infer-explore(1), infer-help(1), infer-report(1), infer-capture(1), infer-compile(1), infer-debug(1),
infer-explore(1), infer-help(1), infer-report(1),
infer-reportdiff(1), and infer-run(1). infer-reportdiff(1), and infer-run(1).
--help-checker +checker-id --help-checker +checker-id
@ -635,14 +646,16 @@ OPTIONS
--help-format { auto | groff | pager | plain } --help-format { auto | groff | pager | plain }
Show this help in the specified format. auto sets the format to Show this help in the specified format. auto sets the format to
plain if the environment variable TERM is "dumb" or undefined, and plain if the environment variable TERM is "dumb" or undefined, and
to pager otherwise. See also infer-analyze(1), infer-capture(1), infer-compile(1), to pager otherwise. See also infer-analyze(1), infer-analyzejson(1),
infer-debug(1), infer-explore(1), infer-help(1), infer-report(1), infer-capture(1), infer-compile(1), infer-debug(1),
infer-explore(1), infer-help(1), infer-report(1),
infer-reportdiff(1), and infer-run(1). infer-reportdiff(1), and infer-run(1).
--help-full --help-full
Show this manual with all internal options in the INTERNAL OPTIONS Show this manual with all internal options in the INTERNAL OPTIONS
section See also infer-analyze(1), infer-capture(1), infer-compile(1), section See also infer-analyze(1), infer-analyzejson(1),
infer-debug(1), infer-explore(1), infer-help(1), infer-report(1), infer-capture(1), infer-compile(1), infer-debug(1),
infer-explore(1), infer-help(1), infer-report(1),
infer-reportdiff(1), and infer-run(1). infer-reportdiff(1), and infer-run(1).
--help-issue-type +UNIQUE_ID --help-issue-type +UNIQUE_ID
@ -1179,6 +1192,9 @@ OPTIONS
Activates: Enable starvation and disable all other checkers Activates: Enable starvation and disable all other checkers
(Conversely: --no-starvation-only) See also infer-analyze(1). (Conversely: --no-starvation-only) See also infer-analyze(1).
--tenv-json file
Path to TEnv json file See also infer-analyzejson(1).
--threadsafe-aliases json --threadsafe-aliases json
Specify custom annotations that should be considered aliases of Specify custom annotations that should be considered aliases of
@ThreadSafe See also infer-analyze(1). @ThreadSafe See also infer-analyze(1).
@ -1338,6 +1354,9 @@ INTERNAL OPTIONS
--censor-report-reset --censor-report-reset
Set --censor-report to the empty list. Set --censor-report to the empty list.
--cfg-json-reset
Cancel the effect of --cfg-json.
--changed-files-index-reset --changed-files-index-reset
Cancel the effect of --changed-files-index. Cancel the effect of --changed-files-index.
@ -1461,6 +1480,10 @@ INTERNAL OPTIONS
--disable-issue-type-reset --disable-issue-type-reset
Set --disable-issue-type to the empty list. Set --disable-issue-type to the empty list.
--no-dotnet-resource-leak
Deactivates: checker dotnet-resource-leak: "resource leak" checker
for .NET. (Conversely: --dotnet-resource-leak)
--no-dotty-cfg-libs --no-dotty-cfg-libs
Deactivates: Print the cfg of the code coming from the libraries Deactivates: Print the cfg of the code coming from the libraries
(Conversely: --dotty-cfg-libs) (Conversely: --dotty-cfg-libs)
@ -1957,6 +1980,9 @@ INTERNAL OPTIONS
--symops-per-iteration-reset --symops-per-iteration-reset
Cancel the effect of --symops-per-iteration. Cancel the effect of --symops-per-iteration.
--tenv-json-reset
Cancel the effect of --tenv-json.
--test-determinator --test-determinator
Activates: Run infer in Test Determinator mode. It is used Activates: Run infer in Test Determinator mode. It is used
together with the --modified-lines and --profiler-samples flags, together with the --modified-lines and --profiler-samples flags,
@ -2120,9 +2146,9 @@ FILES
SEE ALSO SEE ALSO
infer-analyze(1), infer-capture(1), infer-compile(1), infer-debug(1), infer-analyze(1), infer-analyzejson(1), infer-capture(1),
infer-explore(1), infer-help(1), infer-report(1), infer-reportdiff(1), infer-compile(1), infer-debug(1), infer-explore(1), infer-help(1),
infer-run(1) infer-report(1), infer-reportdiff(1), infer-run(1)

@ -132,6 +132,7 @@ OPTIONS
DIRECT_ATOMIC_PROPERTY_ACCESS (enabled by default), DIRECT_ATOMIC_PROPERTY_ACCESS (enabled by default),
DISCOURAGED_WEAK_PROPERTY_CUSTOM_SETTER (enabled by default), DISCOURAGED_WEAK_PROPERTY_CUSTOM_SETTER (enabled by default),
DIVIDE_BY_ZERO (disabled by default), DIVIDE_BY_ZERO (disabled by default),
DOTNET_RESOURCE_LEAK (enabled by default),
DO_NOT_REPORT (enabled by default), DO_NOT_REPORT (enabled by default),
EMPTY_VECTOR_ACCESS (enabled by default), EMPTY_VECTOR_ACCESS (enabled by default),
ERADICATE_ANNOTATION_GRAPH (enabled by default), ERADICATE_ANNOTATION_GRAPH (enabled by default),

@ -210,6 +210,9 @@ OPTIONS
`<reason_string>` is a non-empty string used to explain why the `<reason_string>` is a non-empty string used to explain why the
issue was filtered. See also infer-report(1) and infer-run(1). issue was filtered. See also infer-report(1) and infer-run(1).
--cfg-json file
Path to CFG json file See also infer-analyzejson(1).
--changed-files-index file --changed-files-index file
Specify the file containing the list of source files from which Specify the file containing the list of source files from which
reactive analysis should start. Source files should be specified reactive analysis should start. Source files should be specified
@ -313,8 +316,9 @@ OPTIONS
--developer-mode, --print-buckets, --print-types, --developer-mode, --print-buckets, --print-types,
--reports-include-ml-loc, --no-only-cheap-debug, --trace-error, --reports-include-ml-loc, --no-only-cheap-debug, --trace-error,
--write-dotty, --write-html) (Conversely: --no-debug | -G) --write-dotty, --write-html) (Conversely: --no-debug | -G)
See also infer-analyze(1), infer-capture(1), infer-compile(1), See also infer-analyze(1), infer-analyzejson(1),
infer-report(1), infer-reportdiff(1), and infer-run(1). infer-capture(1), infer-compile(1), infer-report(1),
infer-reportdiff(1), and infer-run(1).
--debug-level level --debug-level level
Debug level (sets --bo-debug level, --debug-level-analysis level, Debug level (sets --bo-debug level, --debug-level-analysis level,
@ -322,23 +326,27 @@ OPTIONS
- 0: only basic debugging enabled - 0: only basic debugging enabled
- 1: verbose debugging enabled - 1: verbose debugging enabled
- 2: very verbose debugging enabled - 2: very verbose debugging enabled
See also infer-analyze(1), infer-capture(1), infer-compile(1), See also infer-analyze(1), infer-analyzejson(1),
infer-report(1), infer-reportdiff(1), and infer-run(1). infer-capture(1), infer-compile(1), infer-report(1),
infer-reportdiff(1), and infer-run(1).
--debug-level-analysis int --debug-level-analysis int
Debug level for the analysis. See --debug-level for accepted Debug level for the analysis. See --debug-level for accepted
values. See also infer-analyze(1), infer-capture(1), infer-compile(1), values. See also infer-analyze(1), infer-analyzejson(1),
infer-report(1), infer-reportdiff(1), and infer-run(1). infer-capture(1), infer-compile(1), infer-report(1),
infer-reportdiff(1), and infer-run(1).
--debug-level-capture int --debug-level-capture int
Debug level for the capture. See --debug-level for accepted Debug level for the capture. See --debug-level for accepted
values. See also infer-analyze(1), infer-capture(1), infer-compile(1), values. See also infer-analyze(1), infer-analyzejson(1),
infer-report(1), infer-reportdiff(1), and infer-run(1). infer-capture(1), infer-compile(1), infer-report(1),
infer-reportdiff(1), and infer-run(1).
--debug-level-linters int --debug-level-linters int
Debug level for the linters. See --debug-level for accepted Debug level for the linters. See --debug-level for accepted
values. See also infer-analyze(1), infer-capture(1), infer-compile(1), values. See also infer-analyze(1), infer-analyzejson(1),
infer-report(1), infer-reportdiff(1), and infer-run(1). infer-capture(1), infer-compile(1), infer-report(1),
infer-reportdiff(1), and infer-run(1).
--no-deduplicate --no-deduplicate
Deactivates: Apply issue-specific deduplication during analysis Deactivates: Apply issue-specific deduplication during analysis
@ -348,8 +356,9 @@ OPTIONS
--no-default-checkers --no-default-checkers
Deactivates: Default checkers: --biabduction, Deactivates: Default checkers: --biabduction,
--fragment-retains-view, --inefficient-keyset-iterator, --linters, --fragment-retains-view, --inefficient-keyset-iterator, --linters,
--liveness, --racerd, --siof, --self-in-block, --starvation, --liveness, --racerd, --dotnet-resource-leak, --siof,
--uninit (Conversely: --default-checkers) See also infer-analyze(1). --self-in-block, --starvation, --uninit (Conversely:
--default-checkers) See also infer-analyze(1).
--no-default-linters --no-default-linters
Deactivates: Use the default linters for the analysis. Deactivates: Use the default linters for the analysis.
@ -420,6 +429,7 @@ OPTIONS
DIRECT_ATOMIC_PROPERTY_ACCESS (enabled by default), DIRECT_ATOMIC_PROPERTY_ACCESS (enabled by default),
DISCOURAGED_WEAK_PROPERTY_CUSTOM_SETTER (enabled by default), DISCOURAGED_WEAK_PROPERTY_CUSTOM_SETTER (enabled by default),
DIVIDE_BY_ZERO (disabled by default), DIVIDE_BY_ZERO (disabled by default),
DOTNET_RESOURCE_LEAK (enabled by default),
DO_NOT_REPORT (enabled by default), DO_NOT_REPORT (enabled by default),
EMPTY_VECTOR_ACCESS (enabled by default), EMPTY_VECTOR_ACCESS (enabled by default),
ERADICATE_ANNOTATION_GRAPH (enabled by default), ERADICATE_ANNOTATION_GRAPH (enabled by default),
@ -624,8 +634,9 @@ OPTIONS
See also infer-capture(1). See also infer-capture(1).
--help --help
Show this manual See also infer-analyze(1), infer-capture(1), infer-compile(1), Show this manual See also infer-analyze(1), infer-analyzejson(1),
infer-debug(1), infer-explore(1), infer-help(1), infer-report(1), infer-capture(1), infer-compile(1), infer-debug(1),
infer-explore(1), infer-help(1), infer-report(1),
infer-reportdiff(1), and infer-run(1). infer-reportdiff(1), and infer-run(1).
--help-checker +checker-id --help-checker +checker-id
@ -635,14 +646,16 @@ OPTIONS
--help-format { auto | groff | pager | plain } --help-format { auto | groff | pager | plain }
Show this help in the specified format. auto sets the format to Show this help in the specified format. auto sets the format to
plain if the environment variable TERM is "dumb" or undefined, and plain if the environment variable TERM is "dumb" or undefined, and
to pager otherwise. See also infer-analyze(1), infer-capture(1), infer-compile(1), to pager otherwise. See also infer-analyze(1), infer-analyzejson(1),
infer-debug(1), infer-explore(1), infer-help(1), infer-report(1), infer-capture(1), infer-compile(1), infer-debug(1),
infer-explore(1), infer-help(1), infer-report(1),
infer-reportdiff(1), and infer-run(1). infer-reportdiff(1), and infer-run(1).
--help-full --help-full
Show this manual with all internal options in the INTERNAL OPTIONS Show this manual with all internal options in the INTERNAL OPTIONS
section See also infer-analyze(1), infer-capture(1), infer-compile(1), section See also infer-analyze(1), infer-analyzejson(1),
infer-debug(1), infer-explore(1), infer-help(1), infer-report(1), infer-capture(1), infer-compile(1), infer-debug(1),
infer-explore(1), infer-help(1), infer-report(1),
infer-reportdiff(1), and infer-run(1). infer-reportdiff(1), and infer-run(1).
--help-issue-type +UNIQUE_ID --help-issue-type +UNIQUE_ID
@ -1179,6 +1192,9 @@ OPTIONS
Activates: Enable starvation and disable all other checkers Activates: Enable starvation and disable all other checkers
(Conversely: --no-starvation-only) See also infer-analyze(1). (Conversely: --no-starvation-only) See also infer-analyze(1).
--tenv-json file
Path to TEnv json file See also infer-analyzejson(1).
--threadsafe-aliases json --threadsafe-aliases json
Specify custom annotations that should be considered aliases of Specify custom annotations that should be considered aliases of
@ThreadSafe See also infer-analyze(1). @ThreadSafe See also infer-analyze(1).
@ -1307,9 +1323,9 @@ FILES
SEE ALSO SEE ALSO
infer-analyze(1), infer-capture(1), infer-compile(1), infer-debug(1), infer-analyze(1), infer-analyzejson(1), infer-capture(1),
infer-explore(1), infer-help(1), infer-report(1), infer-reportdiff(1), infer-compile(1), infer-debug(1), infer-explore(1), infer-help(1),
infer-run(1) infer-report(1), infer-reportdiff(1), infer-run(1)

@ -0,0 +1,62 @@
(*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open! IStd
module F = Format
module L = Logging
(** invariant: if [namespace = Some str] then [not (String.equal str "")]. [classname] appears first
so that the comparator fails earlier *)
type t = {classname: string; namespace: string option} [@@deriving compare, equal, yojson_of]
let make ~namespace ~classname =
match namespace with Some "" -> {namespace= None; classname} | _ -> {namespace; classname}
let from_string str =
match String.rsplit2 str ~on:'.' with
| None ->
{classname= str; namespace= None}
| Some ("", _) ->
L.die InternalError "Empty namespace path in CSharp qualified classname.@."
| Some (pkg, classname) ->
{classname; namespace= Some pkg}
let to_string = function
| {classname; namespace= None} ->
classname
| {classname; namespace= Some pkg} ->
String.concat ~sep:"." [pkg; classname]
let pp fmt = function
| {classname; namespace= None} ->
F.pp_print_string fmt classname
| {classname; namespace= Some pkg} ->
F.fprintf fmt "%s.%s" pkg classname
let classname {classname} = classname
let pp_with_verbosity ~verbose fmt t =
if verbose then pp fmt t else F.pp_print_string fmt (classname t)
module Normalizer = HashNormalizer.Make (struct
type nonrec t = t [@@deriving equal]
let hash = Hashtbl.hash
let normalize t =
let classname = HashNormalizer.StringNormalizer.normalize t.classname in
let namespace =
IOption.map_changed t.namespace ~equal:phys_equal ~f:HashNormalizer.StringNormalizer.normalize
in
if phys_equal classname t.classname && phys_equal namespace t.namespace then t
else {classname; namespace}
end)

@ -0,0 +1,27 @@
(*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open! IStd
type t [@@deriving compare, equal, yojson_of]
val make : namespace:string option -> classname:string -> t
val from_string : string -> t
val to_string : t -> string
(** [to_string (from_string "X.Y.Z") = "X.Y.Z"] *)
val pp : Format.formatter -> t -> unit
(** [pp] includes namespace if any *)
val pp_with_verbosity : verbose:bool -> Format.formatter -> t -> unit
(** if [verbose] then print namespace if present, otherwise only print class *)
val classname : t -> string
module Normalizer : HashNormalizer.S with type t = t

@ -10,6 +10,8 @@ module F = Format
type t = {name: Mangled.t; typ: Typ.t; capture_mode: Pvar.capture_mode} [@@deriving compare] type t = {name: Mangled.t; typ: Typ.t; capture_mode: Pvar.capture_mode} [@@deriving compare]
let make ~name ~typ ~capture_mode = {name; typ; capture_mode}
let pp fmt {name; typ; capture_mode} = let pp fmt {name; typ; capture_mode} =
F.fprintf fmt "(%a,@,%a,@,%s)" Mangled.pp name (Typ.pp_full Pp.text) typ F.fprintf fmt "(%a,@,%a,@,%s)" Mangled.pp name (Typ.pp_full Pp.text) typ
(Pvar.string_of_capture_mode capture_mode) (Pvar.string_of_capture_mode capture_mode)

@ -10,3 +10,5 @@ open! IStd
type t = {name: Mangled.t; typ: Typ.t; capture_mode: Pvar.capture_mode} [@@deriving compare] type t = {name: Mangled.t; typ: Typ.t; capture_mode: Pvar.capture_mode} [@@deriving compare]
val pp : Format.formatter -> t -> unit val pp : Format.formatter -> t -> unit
val make : name:Mangled.t -> typ:Typ.t -> capture_mode:Pvar.capture_mode -> t

@ -61,6 +61,8 @@ let knormal = KNormal
let kprimed = KPrimed let kprimed = KPrimed
let knone = KNone
let equal_kind = [%compare.equal: kind] let equal_kind = [%compare.equal: kind]
(* timestamp for a path identifier *) (* timestamp for a path identifier *)

@ -61,6 +61,8 @@ val knormal : kind
val kfootprint : kind val kfootprint : kind
val knone : kind
val name_spec : name val name_spec : name
(** Name used for spec variables *) (** Name used for spec variables *)
@ -79,6 +81,8 @@ val name_to_string : name -> string
val get_name : t -> name val get_name : t -> name
(** Name of the identifier. *) (** Name of the identifier. *)
val create_with_stamp : kind -> name -> int -> t
val create : kind -> int -> t val create : kind -> int -> t
(** Create an identifier with default name for the given kind *) (** Create an identifier with default name for the given kind *)

@ -643,7 +643,6 @@ let set_start_node pdesc node = pdesc.start_node <- node
(** Append the locals to the list of local variables *) (** Append the locals to the list of local variables *)
let append_locals pdesc new_locals = pdesc.attributes.locals <- pdesc.attributes.locals @ new_locals let append_locals pdesc new_locals = pdesc.attributes.locals <- pdesc.attributes.locals @ new_locals
(** Set the successor nodes and exception nodes, and build predecessor links *)
let set_succs (node : Node.t) ~normal:succs_opt ~exn:exn_opt = let set_succs (node : Node.t) ~normal:succs_opt ~exn:exn_opt =
let remove_pred pred_node (from_node : Node.t) = let remove_pred pred_node (from_node : Node.t) =
from_node.preds <- List.filter from_node.preds ~f:(fun pred -> not (Node.equal pred pred_node)) from_node.preds <- List.filter from_node.preds ~f:(fun pred -> not (Node.equal pred pred_node))

@ -15,6 +15,90 @@ type detail_level = Verbose | Non_verbose | Simple
let is_verbose v = match v with Verbose -> true | _ -> false let is_verbose v = match v with Verbose -> true | _ -> false
module CSharp = struct
type kind = Non_Static | Static [@@deriving compare, equal, yojson_of]
type t =
{ method_name: string
; parameters: Typ.t list
; class_name: Typ.Name.t
; return_type: Typ.t option (* option because constructors have no return type *)
; kind: kind }
[@@deriving compare, equal, yojson_of]
let ensure_csharp_type t =
if not (Typ.is_csharp_type t) then
L.die InternalError "Expected csharp type but got %a@." (Typ.pp_full Pp.text) t
let make ~class_name ~return_type ~method_name ~parameters ~kind () =
Option.iter return_type ~f:ensure_csharp_type ;
{class_name; return_type; method_name; parameters; kind}
let pp_return_type ~verbose fmt j = Option.iter j.return_type ~f:(Typ.pp_cs ~verbose fmt)
let constructor_method_name = ".ctor"
let get_class_name cs = Typ.Name.name cs.class_name
let get_class_type_name cs = cs.class_name
let get_csharp_class_name_exn cs =
match cs.class_name with
| Typ.CSharpClass csharp_class_name ->
csharp_class_name
| _ ->
L.die InternalError "Asked for csharp class name but got something else"
let get_simple_class_name cs = CSharpClassName.classname (get_csharp_class_name_exn cs)
let get_method cs = cs.method_name
let get_return_typ pname_csharp = Option.value ~default:StdTyp.void pname_csharp.return_type
let replace_parameters parameters cs = {cs with parameters}
let get_parameters cs = cs.parameters
let is_generated {method_name} = String.is_prefix ~prefix:"$" method_name
(** Prints a string of a csharp procname with the given level of verbosity *)
let pp ?(withclass = false) verbosity fmt cs =
let verbose = is_verbose verbosity in
let pp_class_name_dot fmt cs =
CSharpClassName.pp_with_verbosity ~verbose fmt (get_csharp_class_name_exn cs) ;
F.pp_print_char fmt '.'
in
let pp_package_method_and_params fmt cs =
let pp_param_list fmt params = Pp.seq ~sep:"," (Typ.pp_cs ~verbose) fmt params in
F.fprintf fmt "%a%s(%a)" pp_class_name_dot cs cs.method_name pp_param_list cs.parameters
in
match verbosity with
| Verbose ->
(* [package.class.method(params): rtype], used for example to create unique filenames *)
let separator = if Option.is_none cs.return_type then "" else ":" in
pp_package_method_and_params fmt cs ;
F.fprintf fmt "%s%a" separator (pp_return_type ~verbose) cs
| Non_verbose ->
(* [rtype package.class.method(params)], for creating reports *)
let separator = if Option.is_none cs.return_type then "" else " " in
F.fprintf fmt "%a%s" (pp_return_type ~verbose) cs separator ;
pp_package_method_and_params fmt cs
| Simple ->
(* [methodname(...)] or without ... if there are no parameters *)
let params = match cs.parameters with [] -> "" | _ -> "..." in
let pp_method_name fmt cs =
if String.equal cs.method_name constructor_method_name then
F.pp_print_string fmt (get_simple_class_name cs)
else (
if withclass then pp_class_name_dot fmt cs ;
F.pp_print_string fmt cs.method_name )
in
F.fprintf fmt "%a(%s)" pp_method_name cs params
end
module Java = struct module Java = struct
type kind = type kind =
| Non_Static | Non_Static
@ -202,7 +286,8 @@ module Parameter = struct
type clang_parameter = Typ.Name.t option [@@deriving compare, equal, yojson_of] type clang_parameter = Typ.Name.t option [@@deriving compare, equal, yojson_of]
(** Type for parameters in procnames, for java and clang. *) (** Type for parameters in procnames, for java and clang. *)
type t = JavaParameter of Typ.t | ClangParameter of clang_parameter [@@deriving compare, equal] type t = JavaParameter of Typ.t | ClangParameter of clang_parameter | CSharpParameter of Typ.t
[@@deriving compare, equal]
let of_typ typ = let of_typ typ =
match typ.Typ.desc with Typ.Tptr ({desc= Tstruct name}, Pk_pointer) -> Some name | _ -> None match typ.Typ.desc with Typ.Tptr ({desc= Tstruct name}, Pk_pointer) -> Some name | _ -> None
@ -405,6 +490,7 @@ end
(** Type of procedure names. *) (** Type of procedure names. *)
type t = type t =
| CSharp of CSharp.t
| Java of Java.t | Java of Java.t
| C of C.t | C of C.t
| Linters_dummy_method | Linters_dummy_method
@ -422,6 +508,8 @@ let with_block_parameters base blocks = WithBlockParameters (base, blocks)
let is_java = function Java _ -> true | _ -> false let is_java = function Java _ -> true | _ -> false
let is_csharp = function CSharp _ -> true | _ -> false
let as_java_exn ~explanation t = let as_java_exn ~explanation t =
match t with match t with
| Java java -> | Java java ->
@ -469,6 +557,8 @@ let rec replace_class t (new_class : Typ.Name.t) =
match t with match t with
| Java j -> | Java j ->
Java {j with class_name= new_class} Java {j with class_name= new_class}
| CSharp cs ->
CSharp {cs with class_name= new_class}
| ObjC_Cpp osig -> | ObjC_Cpp osig ->
ObjC_Cpp {osig with class_name= new_class} ObjC_Cpp {osig with class_name= new_class}
| WithBlockParameters (base, blocks) -> | WithBlockParameters (base, blocks) ->
@ -480,6 +570,8 @@ let rec replace_class t (new_class : Typ.Name.t) =
let get_class_type_name = function let get_class_type_name = function
| Java java_pname -> | Java java_pname ->
Some (Java.get_class_type_name java_pname) Some (Java.get_class_type_name java_pname)
| CSharp cs_pname ->
Some (CSharp.get_class_type_name cs_pname)
| ObjC_Cpp objc_pname -> | ObjC_Cpp objc_pname ->
Some (ObjC_Cpp.get_class_type_name objc_pname) Some (ObjC_Cpp.get_class_type_name objc_pname)
| _ -> | _ ->
@ -489,6 +581,8 @@ let get_class_type_name = function
let get_class_name = function let get_class_name = function
| Java java_pname -> | Java java_pname ->
Some (Java.get_class_name java_pname) Some (Java.get_class_name java_pname)
| CSharp cs_pname ->
Some (CSharp.get_class_name cs_pname)
| ObjC_Cpp objc_pname -> | ObjC_Cpp objc_pname ->
Some (ObjC_Cpp.get_class_name objc_pname) Some (ObjC_Cpp.get_class_name objc_pname)
| _ -> | _ ->
@ -505,7 +599,7 @@ let rec objc_cpp_replace_method_name t (new_method_name : string) =
ObjC_Cpp {osig with method_name= new_method_name} ObjC_Cpp {osig with method_name= new_method_name}
| WithBlockParameters (base, blocks) -> | WithBlockParameters (base, blocks) ->
WithBlockParameters (objc_cpp_replace_method_name base new_method_name, blocks) WithBlockParameters (objc_cpp_replace_method_name base new_method_name, blocks)
| C _ | Block _ | Linters_dummy_method | Java _ -> | C _ | Block _ | Linters_dummy_method | Java _ | CSharp _ ->
t t
@ -521,6 +615,8 @@ let rec get_method = function
name name
| Java j -> | Java j ->
j.method_name j.method_name
| CSharp cs ->
cs.method_name
| Linters_dummy_method -> | Linters_dummy_method ->
"Linters_dummy_method" "Linters_dummy_method"
@ -542,10 +638,14 @@ let get_language = function
Language.Clang Language.Clang
| Java _ -> | Java _ ->
Language.Java Language.Java
| CSharp _ ->
Language.CIL
(** [is_constructor pname] returns true if [pname] is a constructor *) (** [is_constructor pname] returns true if [pname] is a constructor *)
let is_constructor = function let is_constructor = function
| CSharp c ->
String.equal c.method_name CSharp.constructor_method_name
| Java js -> | Java js ->
String.equal js.method_name Java.constructor_method_name String.equal js.method_name Java.constructor_method_name
| ObjC_Cpp {kind= CPPConstructor _} -> | ObjC_Cpp {kind= CPPConstructor _} ->
@ -589,6 +689,8 @@ let pp_with_block_parameters verbose pp fmt base blocks =
let rec pp_unique_id fmt = function let rec pp_unique_id fmt = function
| Java j -> | Java j ->
Java.pp Verbose fmt j Java.pp Verbose fmt j
| CSharp cs ->
CSharp.pp Verbose fmt cs
| C osig -> | C osig ->
C.pp Verbose fmt osig C.pp Verbose fmt osig
| ObjC_Cpp osig -> | ObjC_Cpp osig ->
@ -609,6 +711,8 @@ let to_unique_id proc_name = F.asprintf "%a" pp_unique_id proc_name
let rec pp fmt = function let rec pp fmt = function
| Java j -> | Java j ->
Java.pp Non_verbose fmt j Java.pp Non_verbose fmt j
| CSharp cs ->
CSharp.pp Non_verbose fmt cs
| C osig -> | C osig ->
C.pp Non_verbose fmt osig C.pp Non_verbose fmt osig
| ObjC_Cpp osig -> | ObjC_Cpp osig ->
@ -629,6 +733,8 @@ let to_string proc_name = F.asprintf "%a" pp proc_name
let rec pp_simplified_string ?(withclass = false) fmt = function let rec pp_simplified_string ?(withclass = false) fmt = function
| Java j -> | Java j ->
Java.pp ~withclass Simple fmt j Java.pp ~withclass Simple fmt j
| CSharp cs ->
CSharp.pp ~withclass Simple fmt cs
| C osig -> | C osig ->
C.pp Simple fmt osig C.pp Simple fmt osig
| ObjC_Cpp osig -> | ObjC_Cpp osig ->
@ -649,6 +755,8 @@ let from_string_c_fun func = C (C.from_string func)
let java_inner_class_prefix_regex = Str.regexp "\\$[0-9]+" let java_inner_class_prefix_regex = Str.regexp "\\$[0-9]+"
let csharp_inner_class_prefix_regex = Str.regexp "\\$[0-9]+"
let hashable_name proc_name = let hashable_name proc_name =
match proc_name with match proc_name with
| Java pname -> ( | Java pname -> (
@ -660,6 +768,13 @@ let hashable_name proc_name =
Str.global_replace java_inner_class_prefix_regex "$_" name Str.global_replace java_inner_class_prefix_regex "$_" name
| exception Caml.Not_found -> | exception Caml.Not_found ->
name ) name )
| CSharp pname -> (
let name = F.asprintf "%a" (CSharp.pp ~withclass:true Simple) pname in
match Str.search_forward csharp_inner_class_prefix_regex name 0 with
| _ ->
Str.global_replace csharp_inner_class_prefix_regex "$_" name
| exception Caml.Not_found ->
name )
| ObjC_Cpp osig when ObjC_Cpp.is_objc_method osig -> | ObjC_Cpp osig when ObjC_Cpp.is_objc_method osig ->
(* In Objective C, the list of parameters is part of the method name. To prevent the bug (* In Objective C, the list of parameters is part of the method name. To prevent the bug
hash to change when a parameter is introduced or removed, only the part of the name hash to change when a parameter is introduced or removed, only the part of the name
@ -681,6 +796,8 @@ let rec get_parameters procname =
match procname with match procname with
| Java j -> | Java j ->
List.map ~f:(fun par -> Parameter.JavaParameter par) (Java.get_parameters j) List.map ~f:(fun par -> Parameter.JavaParameter par) (Java.get_parameters j)
| CSharp cs ->
List.map ~f:(fun par -> Parameter.CSharpParameter par) (CSharp.get_parameters cs)
| C osig -> | C osig ->
clang_param_to_param (C.get_parameters osig) clang_param_to_param (C.get_parameters osig)
| ObjC_Cpp osig -> | ObjC_Cpp osig ->
@ -716,9 +833,24 @@ let rec replace_parameters new_parameters procname =
"Expected Clang parameters in Clang procname, but got Java parameters" params ) "Expected Clang parameters in Clang procname, but got Java parameters" params )
params params
in in
let params_to_csharp_params params =
List.map
~f:(fun param ->
match param with
| Parameter.CSharpParameter par ->
par
| _ ->
Logging.(die InternalError)
"Expected CSharp parameters in CSharp procname, but got parameters of another \
language"
params )
params
in
match procname with match procname with
| Java j -> | Java j ->
Java (Java.replace_parameters (params_to_java_params new_parameters) j) Java (Java.replace_parameters (params_to_java_params new_parameters) j)
| CSharp cs ->
CSharp (CSharp.replace_parameters (params_to_csharp_params new_parameters) cs)
| C osig -> | C osig ->
C (C.replace_parameters (params_to_clang_params new_parameters) osig) C (C.replace_parameters (params_to_clang_params new_parameters) osig)
| ObjC_Cpp osig -> | ObjC_Cpp osig ->
@ -735,6 +867,8 @@ let parameter_of_name procname class_name =
match procname with match procname with
| Java _ -> | Java _ ->
Parameter.JavaParameter Typ.(mk_ptr (mk_struct class_name)) Parameter.JavaParameter Typ.(mk_ptr (mk_struct class_name))
| CSharp _ ->
Parameter.CSharpParameter Typ.(mk_ptr (mk_struct class_name))
| _ -> | _ ->
Parameter.ClangParameter (Parameter.clang_param_of_name class_name) Parameter.ClangParameter (Parameter.clang_param_of_name class_name)
@ -752,6 +886,10 @@ let make_java ~class_name ~return_type ~method_name ~parameters ~kind () =
Java (Java.make ~class_name ~return_type ~method_name ~parameters ~kind ()) Java (Java.make ~class_name ~return_type ~method_name ~parameters ~kind ())
let make_csharp ~class_name ~return_type ~method_name ~parameters ~kind () =
CSharp (CSharp.make ~class_name ~return_type ~method_name ~parameters ~kind ())
let make_objc_dealloc name = ObjC_Cpp (ObjC_Cpp.make_dealloc name) let make_objc_dealloc name = ObjC_Cpp (ObjC_Cpp.make_dealloc name)
let make_objc_copyWithZone ~is_mutable name = ObjC_Cpp (ObjC_Cpp.make_copyWithZone ~is_mutable name) let make_objc_copyWithZone ~is_mutable name = ObjC_Cpp (ObjC_Cpp.make_copyWithZone ~is_mutable name)

@ -10,6 +10,33 @@ module F = Format
(** Module for Procedure Names. *) (** Module for Procedure Names. *)
(** Type of csharp procedure names. *)
module CSharp : sig
type kind =
| Non_Static
(** in Java, procedures called with invokevirtual, invokespecial, and invokeinterface *)
| Static (** in Java, procedures called with invokestatic *)
type t [@@deriving compare]
val constructor_method_name : string
val get_method : t -> string
(** Return the method name of a csharp procedure name. *)
val get_class_type_name : t -> Typ.Name.t
(** Return the class name as a typename of a java procedure name. *)
val get_return_typ : t -> Typ.t
(** Return the return type of [pname_csharp]. return Tvoid if there's no return type *)
val get_class_name : t -> string
(** Return the class name of a java procedure name. *)
val is_generated : t -> bool
(** Check if the proc name comes from generated code *)
end
(** Type of java procedure names. *) (** Type of java procedure names. *)
module Java : sig module Java : sig
type kind = type kind =
@ -102,7 +129,8 @@ module Parameter : sig
type clang_parameter = Typ.Name.t option [@@deriving compare, equal] type clang_parameter = Typ.Name.t option [@@deriving compare, equal]
(** Type for parameters in procnames, for java and clang. *) (** Type for parameters in procnames, for java and clang. *)
type t = JavaParameter of Typ.t | ClangParameter of clang_parameter [@@deriving compare, equal] type t = JavaParameter of Typ.t | ClangParameter of clang_parameter | CSharpParameter of Typ.t
[@@deriving compare, equal]
val of_typ : Typ.t -> clang_parameter val of_typ : Typ.t -> clang_parameter
end end
@ -188,6 +216,7 @@ end
[foo_my_block() {my_block(); }] where foo_my_block is created with WithBlockParameters (foo, [foo_my_block() {my_block(); }] where foo_my_block is created with WithBlockParameters (foo,
[my_block]) *) [my_block]) *)
type t = type t =
| CSharp of CSharp.t
| Java of Java.t | Java of Java.t
| C of C.t | C of C.t
| Linters_dummy_method | Linters_dummy_method
@ -258,6 +287,16 @@ val make_java :
-> t -> t
(** Create a Java procedure name. *) (** Create a Java procedure name. *)
val make_csharp :
class_name:Typ.Name.t
-> return_type:Typ.t option
-> method_name:string
-> parameters:Typ.t list
-> kind:CSharp.kind
-> unit
-> t
(** Create a CSharp procedure name. *)
val make_objc_dealloc : Typ.Name.t -> t val make_objc_dealloc : Typ.Name.t -> t
(** Create a Objective-C dealloc name. This is a destructor for an Objective-C class. This procname (** Create a Objective-C dealloc name. This is a destructor for an Objective-C class. This procname
is given by the class name, since it is always an instance method with the name "dealloc" *) is given by the class name, since it is always an instance method with the name "dealloc" *)
@ -286,6 +325,9 @@ val is_c_method : t -> bool
val is_constructor : t -> bool val is_constructor : t -> bool
(** Check if this is a constructor. *) (** Check if this is a constructor. *)
val is_csharp : t -> bool
(** Check if this is a CSharp procedure name. *)
val is_java : t -> bool val is_java : t -> bool
(** Check if this is a Java procedure name. *) (** Check if this is a Java procedure name. *)

@ -44,6 +44,12 @@ module Name = struct
let java_lang_string = from_string "java.lang.String" let java_lang_string = from_string "java.lang.String"
end end
module CSharp = struct
open Typ.Name.CSharp
let system_string = from_string "System.String"
end
module Objc = struct module Objc = struct
open Typ.Name.Objc open Typ.Name.Objc

@ -43,6 +43,10 @@ module Name : sig
val java_lang_string : t val java_lang_string : t
end end
module CSharp : sig
val system_string : t
end
module Objc : sig module Objc : sig
val ns_enumerator : t val ns_enumerator : t
end end

@ -285,6 +285,14 @@ let merge typename ~newer ~current =
newer newer
| JavaClass _ -> | JavaClass _ ->
full_merge ~newer ~current full_merge ~newer ~current
| CSharpClass _ when is_dummy newer ->
current
| CSharpClass _ when is_dummy current ->
newer
| CSharpClass _ when equal newer current ->
newer
| CSharpClass _ ->
full_merge ~newer ~current
let is_not_java_interface = function let is_not_java_interface = function

@ -231,6 +231,9 @@ let resolve_method ~method_exists tenv class_name proc_name =
| JavaClass _ -> | JavaClass _ ->
(* multiple inheritance not possible, but cannot distinguish interfaces from typename so search all *) (* multiple inheritance not possible, but cannot distinguish interfaces from typename so search all *)
class_struct.supers class_struct.supers
| CSharpClass _ ->
(* multiple inheritance not possible, but cannot distinguish interfaces from typename so search all *)
class_struct.supers
| ObjcClass _ -> | ObjcClass _ ->
(* multiple inheritance impossible, but recursive calls will throw away protocols *) (* multiple inheritance impossible, but recursive calls will throw away protocols *)
class_struct.supers class_struct.supers

@ -186,6 +186,7 @@ module T = struct
{ name: QualifiedCppName.t { name: QualifiedCppName.t
; template_spec_info: template_spec_info ; template_spec_info: template_spec_info
; is_union: bool [@compare.ignore] } ; is_union: bool [@compare.ignore] }
| CSharpClass of CSharpClassName.t
| JavaClass of JavaClassName.t | JavaClass of JavaClassName.t
| ObjcClass of QualifiedCppName.t * name list | ObjcClass of QualifiedCppName.t * name list
| ObjcProtocol of QualifiedCppName.t | ObjcProtocol of QualifiedCppName.t
@ -313,6 +314,8 @@ and pp_name_c_syntax pe f = function
F.fprintf f "%a%a" QualifiedCppName.pp name (pp_template_spec_info pe) template_spec_info F.fprintf f "%a%a" QualifiedCppName.pp name (pp_template_spec_info pe) template_spec_info
| JavaClass name -> | JavaClass name ->
JavaClassName.pp f name JavaClassName.pp f name
| CSharpClass name ->
CSharpClassName.pp f name
and pp_template_spec_info pe f = function and pp_template_spec_info pe f = function
@ -383,7 +386,7 @@ module Name = struct
| CppClass {name; template_spec_info} -> | CppClass {name; template_spec_info} ->
let template_suffix = F.asprintf "%a" (pp_template_spec_info Pp.text) template_spec_info in let template_suffix = F.asprintf "%a" (pp_template_spec_info Pp.text) template_spec_info in
QualifiedCppName.append_template_args_to_last name ~args:template_suffix QualifiedCppName.append_template_args_to_last name ~args:template_suffix
| JavaClass _ -> | JavaClass _ | CSharpClass _ ->
QualifiedCppName.empty QualifiedCppName.empty
@ -392,7 +395,7 @@ module Name = struct
name name
| CppClass {name} -> | CppClass {name} ->
name name
| JavaClass _ -> | JavaClass _ | CSharpClass _ ->
QualifiedCppName.empty QualifiedCppName.empty
@ -409,6 +412,8 @@ module Name = struct
qual_name n |> QualifiedCppName.to_qual_string qual_name n |> QualifiedCppName.to_qual_string
| JavaClass name -> | JavaClass name ->
JavaClassName.to_string name JavaClassName.to_string name
| CSharpClass name ->
CSharpClassName.to_string name
let pp fmt tname = let pp fmt tname =
@ -417,7 +422,7 @@ module Name = struct
"struct" "struct"
| CUnion _ -> | CUnion _ ->
"union" "union"
| CppClass _ | JavaClass _ | ObjcClass _ -> | CppClass _ | CSharpClass _ | JavaClass _ | ObjcClass _ ->
"class" "class"
| ObjcProtocol _ -> | ObjcProtocol _ ->
"protocol" "protocol"
@ -427,7 +432,12 @@ module Name = struct
let to_string = F.asprintf "%a" pp let to_string = F.asprintf "%a" pp
let is_class = function CppClass _ | JavaClass _ | ObjcClass _ -> true | _ -> false let is_class = function
| CppClass _ | JavaClass _ | ObjcClass _ | CSharpClass _ ->
true
| _ ->
false
let is_union = function CUnion _ -> true | _ -> false let is_union = function CUnion _ -> true | _ -> false
@ -440,7 +450,8 @@ module Name = struct
| CppClass _, CppClass _ | CppClass _, CppClass _
| JavaClass _, JavaClass _ | JavaClass _, JavaClass _
| ObjcClass _, ObjcClass _ | ObjcClass _, ObjcClass _
| ObjcProtocol _, ObjcProtocol _ -> | ObjcProtocol _, ObjcProtocol _
| CSharpClass _, CSharpClass _ ->
true true
| _ -> | _ ->
false false
@ -454,6 +465,10 @@ module Name = struct
let union_from_qual_name qual_name = CUnion qual_name let union_from_qual_name qual_name = CUnion qual_name
end end
module CSharp = struct
let from_string name_str = CSharpClass (CSharpClassName.from_string name_str)
end
module Java = struct module Java = struct
let from_string name_str = JavaClass (JavaClassName.from_string name_str) let from_string name_str = JavaClass (JavaClassName.from_string name_str)
@ -549,6 +564,9 @@ module Name = struct
| JavaClass java_class_name -> | JavaClass java_class_name ->
let java_class_name' = JavaClassName.Normalizer.normalize java_class_name in let java_class_name' = JavaClassName.Normalizer.normalize java_class_name in
if phys_equal java_class_name java_class_name' then t else JavaClass java_class_name' if phys_equal java_class_name java_class_name' then t else JavaClass java_class_name'
| CSharpClass cs_class_name ->
let cs_class_name' = CSharpClassName.Normalizer.normalize cs_class_name in
if phys_equal cs_class_name cs_class_name' then t else CSharpClass cs_class_name'
end) end)
end end
@ -670,6 +688,73 @@ let rec pp_java ~verbose f {desc} =
L.die InternalError "pp_java rec" L.die InternalError "pp_java rec"
let rec pp_cs ~verbose f {desc} =
let string_of_int = function
| IInt ->
JConfig.int_st
| IBool ->
JConfig.boolean_st
| ISChar ->
JConfig.byte_st
| IUShort ->
JConfig.char_st
| ILong ->
JConfig.long_st
| IShort ->
JConfig.short_st
| _ ->
L.die InternalError "pp_cs int"
in
let string_of_float = function
| FFloat ->
JConfig.float_st
| FDouble ->
JConfig.double_st
| _ ->
L.die InternalError "pp_cs float"
in
match desc with
| Tint ik ->
F.pp_print_string f (string_of_int ik)
| Tfloat fk ->
F.pp_print_string f (string_of_float fk)
| Tvoid ->
F.pp_print_string f JConfig.void
| Tptr (typ, _) ->
pp_cs ~verbose f typ
| Tstruct (CSharpClass cs_class_name) ->
CSharpClassName.pp_with_verbosity ~verbose f cs_class_name
| Tarray {elt} ->
F.fprintf f "%a[]" (pp_cs ~verbose) elt
| _ ->
L.die InternalError "pp_cs rec"
let is_csharp_primitive_type {desc} =
let is_csharp_int = function
| IInt | IBool | ISChar | IUShort | ILong | IShort ->
true
| _ ->
false
in
let is_csharp_float = function FFloat | FDouble -> true | _ -> false in
match desc with Tint ik -> is_csharp_int ik | Tfloat fk -> is_csharp_float fk | _ -> false
let rec is_csharp_type t =
match t.desc with
| Tvoid ->
true
| Tint _ | Tfloat _ ->
is_csharp_primitive_type t
| Tptr ({desc= Tstruct (CSharpClass _)}, Pk_pointer) ->
true
| Tptr ({desc= Tarray {elt}}, Pk_pointer) ->
is_csharp_type elt
| _ ->
false
let is_java_primitive_type {desc} = let is_java_primitive_type {desc} =
let is_java_int = function let is_java_int = function
| IInt | IBool | ISChar | IUShort | ILong | IShort -> | IInt | IBool | ISChar | IUShort | ILong | IShort ->

@ -106,6 +106,7 @@ and name =
args of its parent classes, for example: MyClass<int>::InnerClass<int> will store args of its parent classes, for example: MyClass<int>::InnerClass<int> will store
"MyClass<int>", "InnerClass" *) "MyClass<int>", "InnerClass" *)
| CppClass of {name: QualifiedCppName.t; template_spec_info: template_spec_info; is_union: bool} | CppClass of {name: QualifiedCppName.t; template_spec_info: template_spec_info; is_union: bool}
| CSharpClass of CSharpClassName.t
| JavaClass of JavaClassName.t | JavaClass of JavaClassName.t
| ObjcClass of QualifiedCppName.t * name list | ObjcClass of QualifiedCppName.t * name list
(** ObjC class that conforms to a list of protocols, e.g. id<NSFastEnumeration, NSCopying> *) (** ObjC class that conforms to a list of protocols, e.g. id<NSFastEnumeration, NSCopying> *)
@ -193,6 +194,10 @@ module Name : sig
val union_from_qual_name : QualifiedCppName.t -> t val union_from_qual_name : QualifiedCppName.t -> t
end end
module CSharp : sig
val from_string : string -> t
end
module Java : sig module Java : sig
val from_string : string -> t val from_string : string -> t
(** Create a typename from a Java classname in the form "package.class" *) (** Create a typename from a Java classname in the form "package.class" *)
@ -261,6 +266,9 @@ val pp_desc : Pp.env -> F.formatter -> desc -> unit
val pp_java : verbose:bool -> F.formatter -> t -> unit val pp_java : verbose:bool -> F.formatter -> t -> unit
(** Pretty print a Java type. Raises if type isn't produced by the Java frontend *) (** Pretty print a Java type. Raises if type isn't produced by the Java frontend *)
val pp_cs : verbose:bool -> F.formatter -> t -> unit
(** Pretty print a Java type. Raises if type isn't produced by the CSharp frontend *)
val pp_protocols : Pp.env -> F.formatter -> name list -> unit val pp_protocols : Pp.env -> F.formatter -> name list -> unit
val to_string : t -> string val to_string : t -> string
@ -314,6 +322,9 @@ val is_unsigned_int : t -> bool
val is_char : t -> bool val is_char : t -> bool
val is_csharp_type : t -> bool
(** is [t] a type produced by the Java frontend? *)
val is_java_type : t -> bool val is_java_type : t -> bool
(** is [t] a type produced by the Java frontend? *) (** is [t] a type produced by the Java frontend? *)

@ -161,7 +161,7 @@ roots:=Infer
ifeq ($(IS_FACEBOOK_TREE),yes) ifeq ($(IS_FACEBOOK_TREE),yes)
roots += $(INFER_CREATE_TRACEVIEW_LINKS_MODULE) roots += $(INFER_CREATE_TRACEVIEW_LINKS_MODULE)
endif endif
clusters:=absint al atd backend base biabduction bufferoverrun checkers clang clang_stubs concurrency cost c_stubs deadcode integration IR istd java labs llvm nullsafe opensource pulse quandary scripts test_determinator topl unit clusters:=absint al atd backend base biabduction bufferoverrun checkers clang clang_stubs concurrency cost c_stubs deadcode integration IR istd java labs dotnet llvm nullsafe opensource pulse quandary scripts test_determinator topl unit
ml_src_files:=$(shell find . -not -path "./*stubs*" -regex '\./[a-zA-Z].*\.ml\(i\)*') ml_src_files:=$(shell find . -not -path "./*stubs*" -regex '\./[a-zA-Z].*\.ml\(i\)*')
inc_flags:=$(foreach dir,$(shell find . -not -path './_build*' -type d),-I $(dir)) inc_flags:=$(foreach dir,$(shell find . -not -path './_build*' -type d),-I $(dir))

@ -312,7 +312,7 @@ let is_android_lifecycle_method tenv pname =
Procname.get_class_type_name procname Procname.get_class_type_name procname
|> Option.exists ~f:(fun typename -> |> Option.exists ~f:(fun typename ->
match (typename : Typ.Name.t) with match (typename : Typ.Name.t) with
| CUnion _ | CStruct _ | CppClass _ | ObjcClass _ | ObjcProtocol _ -> | CUnion _ | CStruct _ | CppClass _ | CSharpClass _ | ObjcClass _ | ObjcProtocol _ ->
false false
| JavaClass java_class_name -> | JavaClass java_class_name ->
JavaClassName.package java_class_name JavaClassName.package java_class_name
@ -331,7 +331,7 @@ let is_android_lifecycle_method tenv pname =
false false
in in
match (pname : Procname.t) with match (pname : Procname.t) with
| C _ | Linters_dummy_method | Block _ | ObjC_Cpp _ | WithBlockParameters _ -> | C _ | Linters_dummy_method | Block _ | ObjC_Cpp _ | CSharp _ | WithBlockParameters _ ->
false false
| Java _ -> | Java _ ->
method_starts_with_on pname method_starts_with_on pname

@ -52,6 +52,8 @@ let templated_name_of_class_name class_name =
(qual_name, template_args_of_template_spec_info template_spec_info) (qual_name, template_args_of_template_spec_info template_spec_info)
| JavaClass mangled_name -> | JavaClass mangled_name ->
(QualifiedCppName.of_list [JavaClassName.to_string mangled_name], []) (QualifiedCppName.of_list [JavaClassName.to_string mangled_name], [])
| CSharpClass mangled_name ->
(QualifiedCppName.of_list [CSharpClassName.to_string mangled_name], [])
let templated_name_of_java java = let templated_name_of_java java =
@ -61,6 +63,14 @@ let templated_name_of_java java =
(qual_name, []) (qual_name, [])
let templated_name_of_csharp csharp =
let qual_name =
QualifiedCppName.of_list
[Procname.CSharp.get_class_name csharp; Procname.CSharp.get_method csharp]
in
(qual_name, [])
(* Intermediate matcher types *) (* Intermediate matcher types *)
type ('context, 'f_in, 'f_out, 'arg_payload) name_matcher = type ('context, 'f_in, 'f_out, 'arg_payload) name_matcher =
@ -943,6 +953,10 @@ module ProcName = struct
let templated_name = templated_name_of_java java in let templated_name = templated_name_of_java java in
on_templated_name context templated_name on_templated_name context templated_name
in in
let on_csharp context (csharp : Procname.CSharp.t) =
let templated_name = templated_name_of_csharp csharp in
on_templated_name context templated_name
in
let on_c context (c : c) = let on_c context (c : c) =
let template_args = template_args_of_template_spec_info c.template_args in let template_args = template_args_of_template_spec_info c.template_args in
let templated_name = (c.name, template_args) in let templated_name = (c.name, template_args) in
@ -956,6 +970,8 @@ module ProcName = struct
on_c context c on_c context c
| Java java -> | Java java ->
on_java context java on_java context java
| CSharp cs ->
on_csharp context cs
| _ -> | _ ->
None None
end end

@ -102,6 +102,8 @@ let log_issue_from_summary ?severity_override proc_desc err_log ~node ~session ~
match procname with match procname with
| Procname.Java java_pname -> | Procname.Java java_pname ->
Procname.Java.is_generated java_pname Procname.Java.is_generated java_pname
| Procname.CSharp csharp_pname ->
Procname.CSharp.is_generated csharp_pname
| _ -> | _ ->
false false
in in

@ -7,13 +7,24 @@
open Core open Core
(* NOTE: All variants must be also added to [command_to_string] below *) (* NOTE: All variants must be also added to [command_to_string] below *)
type t = Analyze | Capture | Compile | Debug | Explore | Help | Report | ReportDiff | Run type t =
| Analyze
| AnalyzeJson
| Capture
| Compile
| Debug
| Explore
| Help
| Report
| ReportDiff
| Run
[@@deriving compare] [@@deriving compare]
let equal = [%compare.equal: t] let equal = [%compare.equal: t]
let command_to_string = let command_to_string =
[ (Analyze, "analyze") [ (Analyze, "analyze")
; (AnalyzeJson, "analyzejson")
; (Capture, "capture") ; (Capture, "capture")
; (Compile, "compile") ; (Compile, "compile")
; (Debug, "debug") ; (Debug, "debug")

@ -8,6 +8,7 @@
(** Main modes of operation for infer *) (** Main modes of operation for infer *)
type t = type t =
| Analyze (** analyze previously captured source files *) | Analyze (** analyze previously captured source files *)
| AnalyzeJson (** analyze captured cfg and tenv json files *)
| Capture | 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 | Compile

@ -12,5 +12,7 @@ open! IStd
val main : changed_files:SourceFile.Set.t option -> unit val main : changed_files:SourceFile.Set.t option -> unit
val register_active_checkers : unit -> unit
val invalidate_changed_procedures : SourceFile.Set.t option -> unit val invalidate_changed_procedures : SourceFile.Set.t option -> unit
(** Invalidate specs files for procedures that have changed. Used for incremental analysis. *) (** Invalidate specs files for procedures that have changed. Used for incremental analysis. *)

@ -0,0 +1,693 @@
(*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
(** Main module for the analyzejson analysis after the capture phase *)
open! IStd
open Yojson
open Yojson.Safe.Util
(* We use Yojson.Safe to parse json so it handles long integers, which cannot be handled by OCaml's basic integers *)
module L = Logging
module Hashtbl = Caml.Hashtbl
module IntHash = struct
type t = int
let equal i j = Int.equal i j
let hash i = i land Int.max_value
end
module IntTbl = Hashtbl.Make (IntHash)
exception JsonParse_Error of string
let typename_of_classname cn = Typ.Name.CSharp.from_string cn
let parse_list (eleparse : Safe.t -> 'a) (json : Safe.t) = List.map ~f:eleparse (to_list json)
let parse_parameter (str : string) : Annot.parameter = Annot.{name= Some str; value= Annot.Str str}
let parse_list_parameters (eleparse : Safe.t -> 'a) (json : Safe.t) =
List.map ~f:eleparse (to_list json) |> List.map ~f:parse_parameter
let parse_cil_type_name (str : string) : Typ.t =
let r = Str.regexp "\\." in
try
let n = Str.search_backward r str (String.length str) in
let _namespace = Str.string_before str n in
let _name = Str.string_after str (n + 1) in
Typ.(
mk_ptr
(mk_struct
(CSharpClass (CSharpClassName.make ~namespace:(Some _namespace) ~classname:_name))))
with _ ->
Typ.(mk_ptr (mk_struct (CSharpClass (CSharpClassName.make ~namespace:None ~classname:str))))
let parse_cil_procname (json : Safe.t) : Procname.t =
let method_name = to_string (member "method_name" json) in
match method_name with
| "__new" ->
BuiltinDecl.__new
| _ ->
let return_type =
if String.equal Procname.CSharp.constructor_method_name method_name then None
else Some (to_string (member "return_type" json) |> parse_cil_type_name)
in
let class_name = to_string (member "class_name" json) |> Typ.Name.CSharp.from_string in
let param_types = parse_list to_string (member "parameters" json) in
let params = List.map ~f:parse_cil_type_name param_types in
let is_static = to_bool (member "is_static" json) in
let method_kind = if is_static then Procname.CSharp.Static else Procname.CSharp.Non_Static in
let proc_name_cs =
Procname.(
make_csharp ~class_name ~return_type ~method_name ~parameters:params ~kind:method_kind)
in
proc_name_cs ()
let parse_ikind (json : Safe.t) =
let ikind_map =
[ ("IChar", Typ.IChar)
; ("ISChar", Typ.ISChar)
; ("IUChar", Typ.IUChar)
; ("IBool", Typ.IBool)
; ("IInt", Typ.IInt)
; ("IUInt", Typ.IUInt)
; ("IShort", Typ.IShort)
; ("IUShort", Typ.IUShort)
; ("ILong", Typ.ILong)
; ("IULong", Typ.IULong)
; ("ILongLong", Typ.ILongLong)
; ("IULongLong", Typ.IULongLong)
; ("I128", Typ.I128)
; ("IU128", Typ.IU128) ]
in
List.Assoc.find_exn ~equal:String.equal ikind_map (to_string json)
let parse_fkind (json : Safe.t) =
let fkind_map =
[("FFloat", Typ.FFloat); ("FDouble", Typ.FDouble); ("FLongDouble", Typ.FLongDouble)]
in
List.Assoc.find_exn ~equal:String.equal fkind_map (to_string json)
let parse_ptr_kind (json : Safe.t) =
let ptr_kind_map =
[ ("Pk_pointer", Typ.Pk_pointer)
; ("Pk_reference", Typ.Pk_reference)
; ("Pk_objc_weak", Typ.Pk_objc_weak)
; ("Pk_objc_unsafe_unretained", Typ.Pk_objc_unsafe_unretained)
; ("Pk_objc_autoreleasing", Typ.Pk_objc_autoreleasing) ]
in
List.Assoc.find_exn ~equal:String.equal ptr_kind_map (to_string json)
let parse_if_kind (json : Safe.t) =
let ifkind_map =
[ ("Ik_bexp", Sil.Ik_bexp)
; ("Ik_dowhile", Sil.Ik_dowhile)
; ("Ik_for", Sil.Ik_for)
; ("Ik_if", Sil.Ik_if)
; ("Ik_land_lor", Sil.Ik_land_lor)
; ("Ik_while", Sil.Ik_while)
; ("Ik_switch", Sil.Ik_switch) ]
in
List.Assoc.find_exn ~equal:String.equal ifkind_map (to_string json)
let parse_csu (json : Safe.t) =
let csu = to_string (member "csu_kind" json) in
let name = to_string (member "name" json) in
match csu with
| "Class" ->
typename_of_classname name
| _ ->
raise (JsonParse_Error "JSON Parse Error: Can only parse Class types so far.")
let parse_unop (json : Safe.t) =
let unop_map = [("Neg", Unop.Neg); ("BNot", Unop.BNot); ("LNot", Unop.LNot)] in
List.Assoc.find_exn ~equal:String.equal unop_map (to_string json)
let parse_binop (json : Safe.t) =
(*TODO: need to check the usage of "None" *)
let binop_map =
[ ("PlusA", Binop.PlusA None)
; ("PlusPI", Binop.PlusPI)
; ("MinusA", Binop.MinusA None)
; ("MinusPI", Binop.MinusPI)
; ("MinusPP", Binop.MinusPP)
; ("Mult", Binop.Mult None)
; ("Div", Binop.Div)
; ("Mod", Binop.Mod)
; ("Shiftlt", Binop.Shiftlt)
; ("Shiftrt", Binop.Shiftrt)
; ("Lt", Binop.Lt)
; ("Gt", Binop.Gt)
; ("Le", Binop.Le)
; ("Ge", Binop.Ge)
; ("Eq", Binop.Eq)
; ("Ne", Binop.Ne)
; ("BAnd", Binop.BAnd)
; ("BXor", Binop.BXor)
; ("BOr", Binop.BOr)
; ("LAnd", Binop.LAnd)
; ("LOr", Binop.LOr) ]
in
List.Assoc.find_exn ~equal:String.equal binop_map (to_string json)
let parse_typename (json : Safe.t) =
let tname = to_string (member "type_name_kind" json) in
if String.equal tname "TN_typedef" then typename_of_classname (to_string (member "name" json))
else if String.equal tname "CsuTypeName" then parse_csu json
(*what about if the name is <Module>*)
else Logging.die InternalError "Can't parse typename"
let parse_long (json : Safe.t) = Int64.of_string (Yojson.Safe.to_string json)
let parse_intrep (json : Safe.t) =
let v = parse_long (member "value" json) in
let p = to_bool (member "is_pointer" json) in
match (p, v) with true, 0L -> IntLit.null | _ -> IntLit.of_int64 v
let parse_floatrep (json : Safe.t) = Float.of_string (Yojson.Safe.to_string json)
let parse_ident (json : Safe.t) =
let k = to_string (member "kind" json) in
let kind =
if String.equal k "Normal" then Ident.knormal
else if String.equal k "Primed" then Ident.kprimed
else if String.equal k "Footprint" then Ident.kfootprint
else if String.equal k "None" then Ident.knone
else Logging.die InternalError "Unsupported identifier kind: %s" k
in
Ident.create_with_stamp kind
(Ident.string_to_name (to_string (member "name" json)))
(to_int (member "stamp" json))
let parse_fieldident (json : Safe.t) =
Fieldname.make StdTyp.Name.CSharp.system_string (to_string (member "field_name" json))
let parse_source_file (json : Safe.t) =
let p = to_string (member "path" json) in
SourceFile.create ~warn_on_error:false p
let parse_location (json : Safe.t) =
{ Location.line= to_int (member "line" json)
; Location.col= to_int (member "col" json)
; Location.file= parse_source_file (member "source_file" json) }
let rec parse_pvar (json : Safe.t) =
let pvname = Mangled.from_string (to_string (member "pv_name" json)) in
let pvkind = to_string (member "pv_kind" json) in
if String.equal pvkind "LocalVariable" then
let pname = parse_cil_procname (member "proc_name" json) in
Pvar.mk pvname pname
else if String.equal pvkind "CalledVariable" then
let pname = parse_cil_procname (member "proc_name" json) in
Pvar.mk_callee pvname pname
else if String.equal pvkind "GlobalVariable" then Pvar.mk_global pvname
else Logging.die InternalError "Unknown program variable kind %s" pvkind
and parse_constant (json : Safe.t) =
let const_kind = to_string (member "kind" json) in
let const_value = member "const_value" json in
if String.equal const_kind "Int" then
let i = parse_intrep const_value in
Const.Cint i
else if String.equal const_kind "Float" then
try
let f = parse_floatrep const_value in
Const.Cfloat f
with _ -> Const.Cfloat Float.nan
else if String.equal const_kind "Fun" then
let pname = parse_cil_procname const_value in
Const.Cfun pname
else if String.equal const_kind "Str" then Const.Cstr (to_string const_value)
else if String.equal const_kind "Class" then
Const.Cclass (Ident.string_to_name (to_string const_value))
else Logging.die InternalError "Unknown constant kind %s" const_kind
and parse_exp (json : Safe.t) =
let ekind = to_string (member "expr_kind" json) in
if String.equal ekind "VarExpression" then Exp.Var (parse_ident (member "identifier" json))
else if String.equal ekind "UnopExpression" then
let op = parse_unop (member "operator" json) in
let e = parse_exp (member "expression" json) in
let t =
let t_nullable = member "type" json in
match t_nullable with `Null -> None | _ -> Some (parse_sil_type_name t_nullable)
in
Exp.UnOp (op, e, t)
else if String.equal ekind "BinopExpression" then
let op = parse_binop (member "operator" json) in
let e1 = parse_exp (member "left" json) in
let e2 = parse_exp (member "right" json) in
Exp.BinOp (op, e1, e2)
else if String.equal ekind "ConstExpression" then Exp.Const (parse_constant json)
else if String.equal ekind "CastExpression" then
let t = parse_sil_type_name (member "type" json) in
let e = parse_exp (member "expression" json) in
Exp.Cast (t, e)
else if String.equal ekind "LvarExpression" then Exp.Lvar (parse_pvar (member "pvar" json))
else if String.equal ekind "LfieldExpression" then
let e = parse_exp (member "expression" json) in
let fi = parse_fieldident (member "identifier" json) in
let t = parse_sil_type_name (member "type" json) in
Exp.Lfield (e, fi, t)
else if String.equal ekind "LindexExpression" then
let e1 = parse_exp (member "array" json) in
let e2 = parse_exp (member "index" json) in
Exp.Lindex (e1, e2)
else if String.equal ekind "SizeofExpression" then
let t = parse_sil_type_name (member "type" json) in
let s = to_string (member "kind" json) in
match s with
| "exact" ->
Exp.Sizeof {typ= t; nbytes= None; dynamic_length= None; subtype= Subtype.exact}
| _ ->
Logging.die InternalError "Subtype in Sizeof instruction is not 'exact'"
else Logging.die InternalError "Unknown expression kind %s" ekind
and parse_struct_field (json : Safe.t) =
let fi = parse_fieldident json in
let t = parse_sil_type_name (member "type" json) in
let annot = parse_item_annotation (member "annotation" json) in
(fi, t, annot)
and parse_sil_type_name (json : Safe.t) : Typ.t =
let type_kind = to_string (member "type_kind" json) in
if String.equal type_kind "Tarray" then
let t = parse_sil_type_name (member "content_type" json) in
Typ.mk_array t
else if String.equal type_kind "Tfloat" then
let fkind = parse_fkind (member "kind" json) in
Typ.mk (Typ.Tfloat fkind)
else if String.equal type_kind "Tint" then
let ikind = parse_ikind (member "kind" json) in
Typ.mk (Typ.Tint ikind)
else if String.equal type_kind "Tptr" then
let t = parse_sil_type_name (member "type" json) in
let pkind = parse_ptr_kind (member "kind" json) in
Typ.mk (Typ.Tptr (t, pkind))
else if String.equal type_kind "Tstruct" then
let tn = typename_of_classname (to_string (member "struct_name" json)) in
Typ.mk (Tstruct tn)
else if String.equal type_kind "Tvar" then
let tn = parse_typename (member "type_name" json) in
Typ.mk (Typ.TVar (Typ.Name.name tn))
else if String.equal type_kind "Tvoid" then StdTyp.void
else if String.equal type_kind "Tenum" then
(* Sil.Tenum (parse_list (parse_pair (fun n -> Mangled.from_string (to_string n)) parse_constant) value) *)
Logging.die InternalError "Enums are not supported yet"
else Logging.die InternalError "Unknown sil type kind %s" type_kind
and parse_item_annotation (json : Safe.t) : Annot.Item.t =
let parse_annotation (json : Safe.t) =
let class_name = to_string (member "class_name" json) in
let p = member "params" json in
let parameters = parse_list_parameters to_string p in
{Annot.class_name; Annot.parameters}
in
parse_list
(fun j ->
let a = member "annotation" j in
let v = member "visible" j in
(parse_annotation a, to_bool v) )
(member "annotations" json)
and parse_struct (json : Safe.t) =
let fields = parse_list parse_struct_field (member "instance_fields" json) in
let statics = parse_list parse_struct_field (member "static_fields" json) in
let supers = parse_list parse_csu (member "supers" json) in
let methods = parse_list parse_cil_procname (member "methods" json) in
let annots = parse_item_annotation json in
(fields, statics, supers, methods, annots)
let parse_method_annotation (json : Safe.t) : Annot.Method.t =
let return = parse_item_annotation (member "return_value" json) in
let params = parse_list parse_item_annotation (member "params" json) in
{return; params}
let parse_captured_var (json : Safe.t) =
let n = to_string (member "name" json) in
let t = parse_sil_type_name (member "type" json) in
CapturedVar.make ~name:(Mangled.from_string n) ~typ:t ~capture_mode:Pvar.ByValue
let parse_proc_attributes_var (json : Safe.t) =
let n = to_string (member "name" json) in
let t = parse_sil_type_name (member "type" json) in
(Mangled.from_string n, t, Pvar.ByValue)
let parse_proc_attributes_formals (json : Safe.t) =
let n, t, _ = parse_proc_attributes_var json in
(n, t)
let parse_proc_attributes_locals (json : Safe.t) : ProcAttributes.var_data =
let n, t, _ = parse_proc_attributes_var json in
let mib = to_bool (member "modify_in_block" json) in
let ice = to_bool (member "is_const_expr" json) in
{name= n; typ= t; modify_in_block= mib; is_constexpr= ice; is_declared_unused= false}
let parse_proc_attributes (json : Safe.t) =
let access =
match to_string (member "access" json) with
| "Default" ->
PredSymb.Default
| "Public" ->
PredSymb.Public
| "Private" ->
PredSymb.Private
| "Protected" ->
PredSymb.Protected
| atype ->
Logging.die InternalError "Unsupported access type %s" atype
in
let captured = parse_list parse_captured_var (member "captured" json) in
let formals = parse_list parse_proc_attributes_formals (member "formals" json) in
let locals = parse_list parse_proc_attributes_locals (member "locals" json) in
let loc = parse_location (member "loc" json) in
let file = loc.file in
let proc_name = parse_cil_procname (member "proc_name" json) in
{ (ProcAttributes.default file proc_name) with
access
; captured
; exceptions= parse_list to_string (member "exceptions" json)
; formals
; is_abstract= to_bool (member "is_abstract" json)
; is_bridge_method= to_bool (member "is_bridge_method" json)
; is_defined= to_bool (member "is_defined" json)
; is_synthetic_method= to_bool (member "is_synthetic_method" json)
; loc
; locals
; method_annotation= parse_method_annotation (member "method_annotations" json)
; ret_type= parse_sil_type_name (member "ret_type" json) }
let parse_call_flags (json : Safe.t) =
{ CallFlags.default with
CallFlags.cf_virtual= to_bool (member "cf_virtual" json)
; CallFlags.cf_is_objc_block= to_bool (member "cf_is_objc_block" json) }
let parse_call_args (json : Safe.t) =
let e = parse_exp (member "expression" json) in
let t = parse_sil_type_name (member "type" json) in
(e, t)
let parse_instr (json : Safe.t) =
let instr_kind = to_string (member "instruction_kind" json) in
let l = parse_location (member "location" json) in
if String.equal instr_kind "Load" then
let i = parse_ident (member "identifier" json) in
let e = parse_exp (member "expression" json) in
let t = parse_sil_type_name (member "type" json) in
Sil.Load {id= i; e; root_typ= t; typ= t; loc= l}
else if String.equal instr_kind "Store" then
let e1 = parse_exp (member "lvalue" json) in
let e2 = parse_exp (member "rvalue" json) in
let t = parse_sil_type_name (member "type" json) in
Sil.Store {e1; root_typ= t; typ= t; e2; loc= l}
else if String.equal instr_kind "Prune" then
let e = parse_exp (member "condition" json) in
let f = to_bool (member "true_branch" json) in
let k = parse_if_kind (member "if_kind" json) in
Sil.Prune (e, l, f, k)
else if String.equal instr_kind "Call" then
let rs =
(parse_ident (member "return_var" json), parse_sil_type_name (member "return_type" json))
in
let e = parse_exp (member "function_expression" json) in
let ps = parse_list parse_call_args (member "args" json) in
let fs = parse_call_flags (member "flags" json) in
Sil.Call (rs, e, ps, l, fs)
else Logging.die InternalError "Unknown instruction kind %s" instr_kind
(* This has the side-effect of inserting the procedure description into the CFG. *)
let parse_pdesc (cfg : Cfg.t) (pd_id_to_pd : Procdesc.t IntTbl.t) (start_nd_tbl : int IntTbl.t)
(exit_nd_tbl : int IntTbl.t) (json : Safe.t) =
let _attrs = parse_proc_attributes (member "pd_attributes" json) in
let _id = to_int (member "pd_id" json) in
(* Store away start/end node, to be filled in later *)
IntTbl.add start_nd_tbl _id (to_int (member "pd_start_node" json)) ;
IntTbl.add exit_nd_tbl _id (to_int (member "pd_exit_node" json)) ;
(* let open Procdesc in *)
let pd =
let pname = _attrs.proc_name in
match Procname.Hash.find_opt cfg pname with
| Some pdesc ->
pdesc
| None ->
Cfg.create_proc_desc cfg _attrs
in
IntTbl.add pd_id_to_pd _id pd
(* Expect the entire node json to be passed *)
let parse_stmt_nodekind (json : Safe.t) : Procdesc.Node.stmt_nodekind =
let nk_comment = member "stmt_node_comment" json in
match to_string (member "stmt_node_kind" json) with
| "AssertionFailure" ->
Procdesc.Node.AssertionFailure
| "BetweenJoinAndExit" ->
Procdesc.Node.BetweenJoinAndExit
| "BinaryConditionalStmtInit" ->
Procdesc.Node.BinaryConditionalStmtInit
| "BinaryOperatorStmt" ->
Procdesc.Node.BinaryOperatorStmt (to_string nk_comment)
| "Call" ->
Procdesc.Node.Call (to_string nk_comment)
| "CallObjCNew" ->
Procdesc.Node.CallObjCNew
| "ClassCastException" ->
Procdesc.Node.ClassCastException
| "ConditionalStmtBranch" ->
Procdesc.Node.ConditionalStmtBranch
| "ConstructorInit" ->
Procdesc.Node.ConstructorInit
| "CXXDynamicCast" ->
Procdesc.Node.CXXDynamicCast
| "CXXNewExpr" ->
Procdesc.Node.CXXNewExpr
| "CXXStdInitializerListExpr" ->
Procdesc.Node.CXXStdInitializerListExpr
| "CXXTypeidExpr" ->
Procdesc.Node.CXXTypeidExpr
| "DeclStmt" ->
Procdesc.Node.DeclStmt
| "DefineBody" ->
Procdesc.Node.DefineBody
| "ExceptionHandler" ->
Procdesc.Node.ExceptionHandler
| "ExceptionsSink" ->
Procdesc.Node.ExceptionsSink
| "FinallyBranch" ->
Procdesc.Node.FinallyBranch
| "GCCAsmStmt" ->
Procdesc.Node.GCCAsmStmt
| "GenericSelectionExpr" ->
Procdesc.Node.GenericSelectionExpr
| "IfStmtBranch" ->
Procdesc.Node.IfStmtBranch
| "InitializeDynamicArrayLength" ->
Procdesc.Node.InitializeDynamicArrayLength
| "InitListExp" ->
Procdesc.Node.InitListExp
| "MessageCall" ->
Procdesc.Node.MessageCall (to_string nk_comment)
| "MethodBody" ->
Procdesc.Node.MethodBody
| "MonitorEnter" ->
Procdesc.Node.MonitorEnter
| "MonitorExit" ->
Procdesc.Node.MonitorExit
| "ObjCCPPThrow" ->
Procdesc.Node.ObjCCPPThrow
| "OutOfBound" ->
Procdesc.Node.OutOfBound
| "ReturnStmt" ->
Procdesc.Node.ReturnStmt
| "Skip" ->
Procdesc.Node.Skip (to_string nk_comment)
| "SwitchStmt" ->
Procdesc.Node.SwitchStmt
| "ThisNotNull" ->
Procdesc.Node.ThisNotNull
| "Throw" ->
Procdesc.Node.Throw
| "ThrowNPE" ->
Procdesc.Node.ThrowNPE
| "UnaryOperator" ->
Procdesc.Node.UnaryOperator
| snk ->
Logging.die InternalError "Unknown stmt node kind %s" snk
let parse_prune_nodekind (json : Safe.t) : Procdesc.Node.prune_node_kind =
match to_string json with
| "ExceptionHandler" ->
PruneNodeKind_ExceptionHandler
| "FalseBranch" ->
PruneNodeKind_FalseBranch
| "InBound" ->
PruneNodeKind_InBound
| "IsInstance" ->
PruneNodeKind_IsInstance
| "MethodBody" ->
PruneNodeKind_MethodBody
| "NotNull" ->
PruneNodeKind_NotNull
| "TrueBranch" ->
PruneNodeKind_TrueBranch
| pnk ->
Logging.die InternalError "Unknown prune node kind %s" pnk
let parse_nodekind (_pd_id_to_pd : Procdesc.t IntTbl.t) (json : Safe.t) =
let nkname = to_string (member "nd_kind" json) in
if String.equal nkname "StartNode" then Procdesc.Node.Start_node
else if String.equal nkname "ExitNode" then Procdesc.Node.Exit_node
else if String.equal nkname "StatementNode" then
Procdesc.Node.Stmt_node (parse_stmt_nodekind json)
else if String.equal nkname "JoinNode" then Procdesc.Node.Join_node
else if String.equal nkname "PruneNode" then
let f = to_bool (member "true_branch" json) in
let ik = parse_if_kind (member "if_kind" json) in
let d = parse_prune_nodekind (member "prune_node_kind" json) in
Procdesc.Node.Prune_node (f, ik, d)
else if String.equal nkname "SkipNode" then
Procdesc.Node.Skip_node (to_string (member "skip_node_comment" json))
else Logging.die InternalError "Unknown nodekind: %s" nkname
let parse_node (pd_id_to_pd : Procdesc.t IntTbl.t) (nd_id_to_node : Procdesc.Node.t IntTbl.t)
(nd_id_to_exn_nodes : int list IntTbl.t) (nd_id_to_pred_nodes : int list IntTbl.t)
(nd_id_to_succ_nodes : int list IntTbl.t) (json : Safe.t) =
let nd_id = to_int (member "nd_id" json) in
IntTbl.add nd_id_to_exn_nodes nd_id (parse_list to_int (member "nd_exn_ids" json)) ;
let nd_instrs = parse_list parse_instr (member "nd_instrs" json) in
let nd_kind = parse_nodekind pd_id_to_pd json in
let nd_loc = parse_location (member "nd_loc" json) in
IntTbl.add nd_id_to_pred_nodes nd_id (parse_list to_int (member "nd_pred_ids" json)) ;
IntTbl.add nd_id_to_succ_nodes nd_id (parse_list to_int (member "nd_succ_ids" json)) ;
let nd_proc_desc = IntTbl.find pd_id_to_pd (to_int (member "nd_proc_id" json)) in
let node = Procdesc.create_node nd_proc_desc nd_loc nd_kind nd_instrs in
IntTbl.add nd_id_to_node nd_id node ;
node
let parse_cfg (json : Safe.t) =
let cfg = Cfg.create () in
(* These hold information that's in the procedure description or nodes, but can only be completed once we've parsed all nodes. *)
let pd_id_to_pd = IntTbl.create 1000 in
let pd_id_to_start_node = IntTbl.create 1000 in
let pd_id_to_exit_node = IntTbl.create 1000 in
let nd_id_to_node = IntTbl.create 1000 in
let nd_id_to_exn_nodes = IntTbl.create 1000 in
let nd_id_to_pred_nodes = IntTbl.create 1000 in
let nd_id_to_succ_nodes = IntTbl.create 1000 in
List.iter
~f:(fun (_, pdjson) -> parse_pdesc cfg pd_id_to_pd pd_id_to_start_node pd_id_to_exit_node pdjson)
(to_assoc (member "procs" json)) ;
let _ =
parse_list
(parse_node pd_id_to_pd nd_id_to_node nd_id_to_exn_nodes nd_id_to_pred_nodes
nd_id_to_succ_nodes)
(member "nodes" json)
in
(* Now fix up the dangling ends *)
IntTbl.iter
(fun pd_id pd ->
let start_node = IntTbl.find nd_id_to_node (IntTbl.find pd_id_to_start_node pd_id) in
let exit_node = IntTbl.find nd_id_to_node (IntTbl.find pd_id_to_exit_node pd_id) in
Procdesc.set_start_node pd start_node ;
Procdesc.set_exit_node pd exit_node )
pd_id_to_pd ;
IntTbl.iter
(fun (nd_id : int) (node : Procdesc.Node.t) ->
let exn_nodes =
List.map ~f:(IntTbl.find nd_id_to_node) (IntTbl.find nd_id_to_exn_nodes nd_id)
in
let succ_nodes =
List.map ~f:(IntTbl.find nd_id_to_node) (IntTbl.find nd_id_to_succ_nodes nd_id)
in
Procdesc.set_succs node ~normal:(Some succ_nodes) ~exn:(Some exn_nodes) )
nd_id_to_node ;
cfg
let parse_tenv_type (json : Safe.t) tenv =
let tn = parse_typename (member "type_name" json) in
let fields, statics, supers, methods, annots = parse_struct (member "type_struct" json) in
ignore (Tenv.mk_struct tenv ~fields ~statics ~methods ~supers ~annots tn)
let parse_tenv (json : Safe.t) =
let tenv = Tenv.create () in
List.iter ~f:(fun entry -> parse_tenv_type entry tenv) (to_list json) ;
tenv
let clear_caches () =
Summary.OnDisk.clear_cache () ;
Procname.SQLite.clear_cache ()
let analyze_json cfg_json tenv_json =
clear_caches () ;
InferAnalyze.register_active_checkers () ;
if not Config.continue_analysis then
if Config.reanalyze then (
L.progress "Invalidating procedures to be reanalyzed@." ;
Summary.OnDisk.reset_all ~filter:(Lazy.force Filtering.procedures_filter) () ;
L.progress "Done@." )
else if not Config.incremental_analysis then DBWriter.delete_all_specs () ;
Printexc.record_backtrace true ;
let tenv = parse_tenv (Yojson.Safe.from_file tenv_json) in
let cfg = parse_cfg (Yojson.Safe.from_file cfg_json) in
let source_file = SourceFile.create ~warn_on_error:false "./Program.cs" in
(* let source_dir = DB.source_dir_from_source_file source_file in
Utils.create_dir (DB.source_dir_to_string source_dir) ;
let tenv_file = DB.source_dir_get_internal_file source_dir ".tenv" in
let cfg_file = DB.source_dir_get_internal_file source_dir ".cfg" in
Tenv.store_to_filename tenv tenv_file ; *)
Tenv.store_global tenv ;
Cfg.store source_file cfg ;
SourceFiles.add source_file cfg Tenv.Global None ;
(*Cfg.print_cfg_procs cfg ;*)
Language.curr_language := Language.CIL ;
let exe_env = Exe_env.mk () in
Ondemand.analyze_file exe_env source_file ;
if Config.write_html then Printer.write_all_html_files source_file ;
()

@ -0,0 +1,12 @@
(*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open! IStd
(** Main module for the analyzejson analysis after the capture phase *)
val analyze_json : string -> string -> unit

@ -16,6 +16,7 @@ type t =
; config_checks_between_markers: ConfigChecksBetweenMarkers.Summary.t option ; config_checks_between_markers: ConfigChecksBetweenMarkers.Summary.t option
; cost: CostDomain.summary option ; cost: CostDomain.summary option
; lab_resource_leaks: ResourceLeakDomain.summary option ; lab_resource_leaks: ResourceLeakDomain.summary option
; dotnet_resource_leaks: ResourceLeakCSDomain.summary option
; litho_required_props: LithoDomain.summary option ; litho_required_props: LithoDomain.summary option
; pulse: PulseSummary.t option ; pulse: PulseSummary.t option
; purity: PurityDomain.summary option ; purity: PurityDomain.summary option
@ -52,6 +53,7 @@ let fields =
~quandary:(fun f -> mk f "Quandary" QuandarySummary.pp) ~quandary:(fun f -> mk f "Quandary" QuandarySummary.pp)
~racerd:(fun f -> mk f "RacerD" RacerDDomain.pp_summary) ~racerd:(fun f -> mk f "RacerD" RacerDDomain.pp_summary)
~lab_resource_leaks:(fun f -> mk f "Resource Leaks Lab" ResourceLeakDomain.pp) ~lab_resource_leaks:(fun f -> mk f "Resource Leaks Lab" ResourceLeakDomain.pp)
~dotnet_resource_leaks:(fun f -> mk f "DOTNET Resource Leaks" ResourceLeakCSDomain.Summary.pp)
~siof:(fun f -> mk f "Siof" SiofDomain.Summary.pp) ~siof:(fun f -> mk f "Siof" SiofDomain.Summary.pp)
~starvation:(fun f -> mk f "Starvation" StarvationDomain.pp_summary) ~starvation:(fun f -> mk f "Starvation" StarvationDomain.pp_summary)
~nullsafe:(fun f -> mk f "Nullsafe" NullsafeSummary.pp) ~nullsafe:(fun f -> mk f "Nullsafe" NullsafeSummary.pp)
@ -71,6 +73,7 @@ let empty =
; config_checks_between_markers= None ; config_checks_between_markers= None
; cost= None ; cost= None
; lab_resource_leaks= None ; lab_resource_leaks= None
; dotnet_resource_leaks= None
; litho_required_props= None ; litho_required_props= None
; pulse= None ; pulse= None
; purity= None ; purity= None

@ -20,6 +20,7 @@ include sig
; config_checks_between_markers: ConfigChecksBetweenMarkers.Summary.t option ; config_checks_between_markers: ConfigChecksBetweenMarkers.Summary.t option
; cost: CostDomain.summary option ; cost: CostDomain.summary option
; lab_resource_leaks: ResourceLeakDomain.summary option ; lab_resource_leaks: ResourceLeakDomain.summary option
; dotnet_resource_leaks: ResourceLeakCSDomain.summary option
; litho_required_props: LithoDomain.summary option ; litho_required_props: LithoDomain.summary option
; pulse: PulseSummary.t option ; pulse: PulseSummary.t option
; purity: PurityDomain.summary option ; purity: PurityDomain.summary option

@ -10,9 +10,9 @@
(:standard -open Core -open IStdlib -open IStd -open OpenSource -open (:standard -open Core -open IStdlib -open IStd -open OpenSource -open
ATDGenerated -open IBase -open IR -open Absint -open Biabduction -open BO ATDGenerated -open IBase -open IR -open Absint -open Biabduction -open BO
-open Nullsafe -open Pulselib -open Checkers -open Costlib -open Quandary -open Nullsafe -open Pulselib -open Checkers -open Costlib -open Quandary
-open TOPLlib -open Concurrency -open Labs)) -open TOPLlib -open Concurrency -open Labs -open Dotnet))
(libraries core memtrace IStdlib ATDGenerated IBase IR Absint Biabduction (libraries core memtrace IStdlib ATDGenerated IBase IR Absint Biabduction
Nullsafe BO Checkers Costlib Quandary TOPLlib Concurrency Labs) Nullsafe BO Checkers Costlib Quandary TOPLlib Concurrency Labs Dotnet)
(preprocess (preprocess
(pps ppx_compare ppx_fields_conv ppx_yojson_conv))) (pps ppx_compare ppx_fields_conv ppx_yojson_conv)))

@ -463,11 +463,14 @@ let do_preanalysis exe_env pdesc =
let summary = Summary.OnDisk.reset pdesc in let summary = Summary.OnDisk.reset pdesc in
let tenv = Exe_env.get_tenv exe_env (Procdesc.get_proc_name pdesc) in let tenv = Exe_env.get_tenv exe_env (Procdesc.get_proc_name pdesc) in
let proc_name = Procdesc.get_proc_name pdesc in let proc_name = Procdesc.get_proc_name pdesc in
if Procname.is_java proc_name then InlineJavaSyntheticMethods.process pdesc ; if Procname.is_java proc_name || Procname.is_csharp proc_name then
if Config.function_pointer_specialization && not (Procname.is_java proc_name) then InlineJavaSyntheticMethods.process pdesc ;
FunctionPointers.substitute pdesc ; if
Config.function_pointer_specialization
&& not (Procname.is_java proc_name || Procname.is_csharp proc_name)
then FunctionPointers.substitute pdesc ;
(* NOTE: It is important that this preanalysis stays before Liveness *) (* NOTE: It is important that this preanalysis stays before Liveness *)
if not (Procname.is_java proc_name) then ( if not (Procname.is_java proc_name || Procname.is_csharp proc_name) then (
ClosuresSubstitution.process_closure_call summary ; ClosuresSubstitution.process_closure_call summary ;
ClosureSubstSpecializedMethod.process summary ; ClosureSubstSpecializedMethod.process summary ;
ReplaceObjCMethodCall.process tenv pdesc proc_name ) ; ReplaceObjCMethodCall.process tenv pdesc proc_name ) ;

@ -134,6 +134,10 @@ let all_checkers =
interprocedural later on *) interprocedural later on *)
interprocedural Payloads.Fields.lab_resource_leaks ResourceLeaks.checker interprocedural Payloads.Fields.lab_resource_leaks ResourceLeaks.checker
, Java ) ] } , Java ) ] }
; (* .NET resource analysis, based on the toy resource analysis in the infer lab *)
{ checker= DOTNETResourceLeaks
; callbacks=
[(interprocedural Payloads.Fields.dotnet_resource_leaks ResourceLeaksCS.checker, CIL)] }
; { checker= RacerD ; { checker= RacerD
; callbacks= ; callbacks=
(let racerd_proc = interprocedural Payloads.Fields.racerd RacerD.analyze_procedure in (let racerd_proc = interprocedural Payloads.Fields.racerd RacerD.analyze_procedure in
@ -180,7 +184,7 @@ let all_checkers =
Topl.analyze_with_biabduction Interproc.analyze_procedure Topl.analyze_with_biabduction Interproc.analyze_procedure
else Interproc.analyze_procedure ) else Interproc.analyze_procedure )
in in
[(biabduction, Clang); (biabduction, Java)] ) } [(biabduction, Clang); (biabduction, Java); (biabduction, CIL)] ) }
; { checker= AnnotationReachability ; { checker= AnnotationReachability
; callbacks= ; callbacks=
(let annot_reach = (let annot_reach =

@ -32,6 +32,7 @@ type t =
| Quandary | Quandary
| RacerD | RacerD
| ResourceLeakLabExercise | ResourceLeakLabExercise
| DOTNETResourceLeaks
| SIOF | SIOF
| SelfInBlock | SelfInBlock
| Starvation | Starvation
@ -67,10 +68,10 @@ let config_unsafe checker =
let supports_clang_and_java _ = Support in let supports_clang_and_java _ = Support in
let supports_clang_and_java_experimental _ = ExperimentalSupport in let supports_clang_and_java_experimental _ = ExperimentalSupport in
let supports_clang (language : Language.t) = let supports_clang (language : Language.t) =
match language with Clang -> Support | Java -> NoSupport match language with Clang -> Support | Java -> NoSupport | CIL -> NoSupport
in in
let supports_java (language : Language.t) = let supports_java (language : Language.t) =
match language with Clang -> NoSupport | Java -> Support match language with Clang -> NoSupport | Java -> Support | CIL -> Support
in in
match checker with match checker with
| AnnotationReachability -> | AnnotationReachability ->
@ -281,7 +282,7 @@ let config_unsafe checker =
| Pulse -> | Pulse ->
{ id= "pulse" { id= "pulse"
; kind= UserFacing {title= "Pulse"; markdown_body= ""} ; kind= UserFacing {title= "Pulse"; markdown_body= ""}
; support= (function Clang -> Support | Java -> ExperimentalSupport) ; support= (function Clang -> Support | Java -> ExperimentalSupport | CIL -> NoSupport)
; short_documentation= "Memory and lifetime analysis." ; short_documentation= "Memory and lifetime analysis."
; cli_flags= Some {deprecated= ["-ownership"]; show_in_help= true} ; cli_flags= Some {deprecated= ["-ownership"]; show_in_help= true}
; enabled_by_default= false ; enabled_by_default= false
@ -338,12 +339,20 @@ let config_unsafe checker =
leaks! See the [lab \ leaks! See the [lab \
instructions](https://github.com/facebook/infer/blob/master/infer/src/labs/README.md)." instructions](https://github.com/facebook/infer/blob/master/infer/src/labs/README.md)."
} }
; support= (function Clang -> NoSupport | Java -> Support) ; support= (function Clang -> NoSupport | Java -> Support | CIL -> Support)
; short_documentation= ; short_documentation=
"Toy checker for the \"resource leak\" write-your-own-checker exercise." "Toy checker for the \"resource leak\" write-your-own-checker exercise."
; cli_flags= Some {deprecated= []; show_in_help= false} ; cli_flags= Some {deprecated= []; show_in_help= false}
; enabled_by_default= false ; enabled_by_default= false
; activates= [] } ; activates= [] }
| DOTNETResourceLeaks ->
{ id= "dotnet-resource-leak"
; kind= UserFacing {title= "Resource Leak checker for .NET"; markdown_body= ""}
; support= (function Clang -> NoSupport | Java -> NoSupport | CIL -> Support)
; short_documentation= "\"resource leak\" checker for .NET."
; cli_flags= Some {deprecated= []; show_in_help= false}
; enabled_by_default= true
; activates= [] }
| SIOF -> | SIOF ->
{ id= "siof" { id= "siof"
; kind= UserFacing {title= "Static Initialization Order Fiasco"; markdown_body= ""} ; kind= UserFacing {title= "Static Initialization Order Fiasco"; markdown_body= ""}

@ -31,6 +31,7 @@ type t =
| Quandary | Quandary
| RacerD | RacerD
| ResourceLeakLabExercise | ResourceLeakLabExercise
| DOTNETResourceLeaks
| SIOF | SIOF
| SelfInBlock | SelfInBlock
| Starvation | Starvation

@ -58,6 +58,28 @@ $(b,infer) $(i,[options])|}
~see_also:InferCommand.[Report; Run] ~see_also:InferCommand.[Report; Run]
let analyze_json =
mk_command_doc ~title:"Infer JSON Analysis"
~short_description:"analyze the cfg and tenv json files captured by infersharp"
~synopsis:
{|$(b,infer) $(b,analyzejson) $(b,--debug) $(b,--cfg-json) $(i,[options])
$(b,--tenv-json) $(i,[options]))|}
~description:
[ `P
"Analyze the cfg and tenv json files captured in the project results directory and \
report." ]
~examples:
[ `P
"To analyze cfg json and tenv json, one should start with configuring infer environment \
and then run analyzejson command, for instance:"
; `Pre
{| infer capture
mkdir infer-out/captured
infer analyzejson --debug --cfg-json [path_to_cfg.json] --tenv-json [path_to_tenv.json]|}
]
~see_also:InferCommand.[Report; Run]
let capture = let capture =
mk_command_doc ~title:"Infer Compilation Capture" mk_command_doc ~title:"Infer Compilation Capture"
~short_description:"capture source files for later analysis" ~short_description:"capture source files for later analysis"
@ -332,6 +354,7 @@ let command_to_data =
in in
let open InferCommand in let open InferCommand in
[ mk Analyze analyze [ mk Analyze analyze
; mk AnalyzeJson analyze_json
; mk Capture capture ; mk Capture capture
; mk Compile compile ; mk Compile compile
; mk Debug debug ; mk Debug debug

@ -485,7 +485,7 @@ let () =
match cmd with match cmd with
| Report -> | Report ->
`Add `Add
| Analyze | Capture | Compile | Debug | Explore | Help | ReportDiff | Run -> | Analyze | AnalyzeJson | Capture | Compile | Debug | Explore | Help | ReportDiff | Run ->
`Reject `Reject
in in
(* make sure we generate doc for all the commands we know about *) (* make sure we generate doc for all the commands we know about *)
@ -778,6 +778,12 @@ and capture_blacklist =
the javac integration for now)." the javac integration for now)."
and cfg_json =
CLOpt.mk_path_opt ~long:"cfg-json"
~in_help:InferCommand.[(AnalyzeJson, manual_generic)]
~meta:"file" "Path to CFG json file"
and censor_report = and censor_report =
CLOpt.mk_string_list ~long:"censor-report" ~deprecated:["-filter-report"] CLOpt.mk_string_list ~long:"censor-report" ~deprecated:["-filter-report"]
~in_help:InferCommand.[(Report, manual_generic); (Run, manual_generic)] ~in_help:InferCommand.[(Report, manual_generic); (Run, manual_generic)]
@ -1009,7 +1015,7 @@ and ( bo_debug
match command with match command with
| Debug | Explore | Help -> | Debug | Explore | Help ->
None None
| (Analyze | Capture | Compile | Report | ReportDiff | Run) as command -> | (Analyze | AnalyzeJson | Capture | Compile | Report | ReportDiff | Run) as command ->
Some (command, manual_generic) ) Some (command, manual_generic) )
in in
let bo_debug = let bo_debug =
@ -2361,6 +2367,12 @@ and starvation_strict_mode =
"During starvation analysis, report strict mode violations (Android only)" "During starvation analysis, report strict mode violations (Android only)"
and tenv_json =
CLOpt.mk_path_opt ~long:"tenv-json"
~in_help:InferCommand.[(AnalyzeJson, manual_generic)]
~meta:"file" "Path to TEnv json file"
and testing_mode = and testing_mode =
CLOpt.mk_bool CLOpt.mk_bool
~deprecated:["testing_mode"; "-testing_mode"; "tm"] ~deprecated:["testing_mode"; "-testing_mode"; "tm"]
@ -2775,6 +2787,8 @@ and capture = !capture
and capture_blacklist = !capture_blacklist and capture_blacklist = !capture_blacklist
and cfg_json = !cfg_json
and censor_report = and censor_report =
RevList.rev_map !censor_report ~f:(fun str -> RevList.rev_map !censor_report ~f:(fun str ->
match String.split str ~on:':' with match String.split str ~on:':' with
@ -3297,6 +3311,8 @@ and symops_per_iteration = !symops_per_iteration
and keep_going = !keep_going and keep_going = !keep_going
and tenv_json = !tenv_json
and test_determinator = !test_determinator and test_determinator = !test_determinator
and export_changed_functions = !export_changed_functions and export_changed_functions = !export_changed_functions

@ -198,6 +198,8 @@ val capture : bool
val capture_blacklist : string option val capture_blacklist : string option
val cfg_json : string option
val censor_report : ((bool * Str.regexp) * (bool * Str.regexp) * string) list val censor_report : ((bool * Str.regexp) * (bool * Str.regexp) * string) list
val changed_files_index : string option val changed_files_index : string option
@ -597,6 +599,8 @@ val suppress_lint_ignore_types : bool
val symops_per_iteration : int option val symops_per_iteration : int option
val tenv_json : string option
val test_determinator : bool val test_determinator : bool
val export_changed_functions : bool val export_changed_functions : bool

@ -702,6 +702,11 @@ let lab_resource_leak =
register ~id:"LAB_RESOURCE_LEAK" Error ResourceLeakLabExercise ~user_documentation:"Toy issue." register ~id:"LAB_RESOURCE_LEAK" Error ResourceLeakLabExercise ~user_documentation:"Toy issue."
let dotnet_resource_leak =
register ~id:"DOTNET_RESOURCE_LEAK" Error DOTNETResourceLeaks
~user_documentation:"Resource leak checker for .NET."
let leak_after_array_abstraction = let leak_after_array_abstraction =
register_hidden ~id:"Leak_after_array_abstraction" Error Biabduction register_hidden ~id:"Leak_after_array_abstraction" Error Biabduction

@ -238,6 +238,8 @@ val javascript_injection : t
val lab_resource_leak : t val lab_resource_leak : t
val dotnet_resource_leak : t
val leak_after_array_abstraction : t val leak_after_array_abstraction : t
val leak_in_footprint : t val leak_in_footprint : t

@ -6,11 +6,11 @@
*) *)
open! IStd open! IStd
type t = Clang | Java [@@deriving compare, enumerate] type t = Clang | Java | CIL [@@deriving compare, enumerate]
let equal = [%compare.equal: t] let equal = [%compare.equal: t]
let language_to_string = [(Clang, "C/C++/ObjC"); (Java, "Java")] let language_to_string = [(Clang, "C/C++/ObjC"); (Java, "Java"); (CIL, "C#/.Net")]
let to_string lang = List.Assoc.find_exn language_to_string ~equal lang let to_string lang = List.Assoc.find_exn language_to_string ~equal lang

@ -7,7 +7,7 @@
open! IStd open! IStd
type t = Clang | Java [@@deriving compare, enumerate] type t = Clang | Java | CIL [@@deriving compare, enumerate]
val equal : t -> t -> bool val equal : t -> t -> bool

@ -1108,7 +1108,7 @@ let check_junk {InterproceduralAnalysis.proc_desc; err_log; tenv} prop =
when Language.curr_language_is Clang -> when Language.curr_language_is Clang ->
(is_none ml_bucket_opt, exn_leak) (is_none ml_bucket_opt, exn_leak)
| Some _, Rmemory _ -> | Some _, Rmemory _ ->
(Language.curr_language_is Java, exn_leak) (Language.curr_language_is Java || Language.curr_language_is CIL, exn_leak)
| Some _, Rignore -> | Some _, Rignore ->
(true, exn_leak) (true, exn_leak)
| Some _, Rfile -> | Some _, Rfile ->
@ -1116,7 +1116,7 @@ let check_junk {InterproceduralAnalysis.proc_desc; err_log; tenv} prop =
| Some _, Rlock -> | Some _, Rlock ->
(false, exn_leak) (false, exn_leak)
| _ -> | _ ->
(Language.curr_language_is Java, exn_leak) (Language.curr_language_is Java || Language.curr_language_is CIL, exn_leak)
in in
let already_reported () = let already_reported () =
let attr_opt_equal ao1 ao2 = let attr_opt_equal ao1 ao2 =
@ -1137,7 +1137,8 @@ let check_junk {InterproceduralAnalysis.proc_desc; err_log; tenv} prop =
|| already_reported () || already_reported ()
in in
let report_and_continue = let report_and_continue =
Language.curr_language_is Java || !BiabductionConfig.footprint Language.curr_language_is Java || Language.curr_language_is CIL
|| !BiabductionConfig.footprint
in in
let report_leak () = let report_leak () =
if not report_and_continue then raise exn if not report_and_continue then raise exn

@ -77,7 +77,8 @@ let check_access access_opt de_opt =
let process_formal_letref = function let process_formal_letref = function
| Sil.Load {id; e= Exp.Lvar pvar} -> | Sil.Load {id; e= Exp.Lvar pvar} ->
let is_java_this = Language.curr_language_is Java && Pvar.is_this pvar in let is_java_this = Language.curr_language_is Java && Pvar.is_this pvar in
if (not is_java_this) && is_formal pvar then Some id else None let is_cil_this = Language.curr_language_is CIL && Pvar.is_this pvar in
if ((not is_java_this) && not is_cil_this) && is_formal pvar then Some id else None
| _ -> | _ ->
None None
in in

@ -2222,6 +2222,18 @@ and sigma_imply tenv calc_index_frame calc_missing subs prop1 sigma2 : subst2 *
in in
let fields = ["count"; "hash"; "offset"; "value"] in let fields = ["count"; "hash"; "offset"; "value"] in
Predicates.Estruct (List.map ~f:mk_fld_sexp fields, Predicates.inst_none) Predicates.Estruct (List.map ~f:mk_fld_sexp fields, Predicates.inst_none)
| CIL ->
let mk_fld_sexp field_name =
let fld = Fieldname.make StdTyp.Name.CSharp.system_string field_name in
let se =
Predicates.Eexp (Exp.Var (Ident.create_fresh Ident.kprimed), Predicates.Inone)
in
(fld, se)
in
let fields =
["System.String.Empty" (* ; "System.String.Chars" *); "System.String.Length"]
in
Predicates.Estruct (List.map ~f:mk_fld_sexp fields, Predicates.inst_none)
in in
let const_string_texp = let const_string_texp =
match !Language.curr_language with match !Language.curr_language with
@ -2238,6 +2250,15 @@ and sigma_imply tenv calc_index_frame calc_missing subs prop1 sigma2 : subst2 *
; nbytes= None ; nbytes= None
; dynamic_length= None ; dynamic_length= None
; subtype= Subtype.exact } ; subtype= Subtype.exact }
| CIL ->
(* cil todo *)
(* Logging.die Logging.InternalError "No string constant support for CIL yet." *)
let object_type = Typ.Name.CSharp.from_string "System.String" in
Exp.Sizeof
{ typ= Typ.mk (Tstruct object_type)
; nbytes= None
; dynamic_length= None
; subtype= Subtype.exact }
in in
Predicates.Hpointsto (root, sexp, const_string_texp) Predicates.Hpointsto (root, sexp, const_string_texp)
in in

@ -470,7 +470,13 @@ let mk_ptsto_exp_footprint analysis_data pname tenv orig_prop (lexp, typ) max_st
raise (Exceptions.Dangling_pointer_dereference (false, err_desc, __POS__)) ) ; raise (Exceptions.Dangling_pointer_dereference (false, err_desc, __POS__)) ) ;
let off_foot, eqs = laundry_offset_for_footprint max_stamp off in let off_foot, eqs = laundry_offset_for_footprint max_stamp off in
let subtype = let subtype =
match !Language.curr_language with Clang -> Subtype.exact | Java -> Subtype.subtypes match !Language.curr_language with
| Clang ->
Subtype.exact
| Java ->
Subtype.subtypes
| CIL ->
Subtype.subtypes
in in
let create_ptsto footprint_part off0 = let create_ptsto footprint_part off0 =
match (root, off0, typ.Typ.desc) with match (root, off0, typ.Typ.desc) with

@ -417,7 +417,7 @@ let check_arith_norm_exp {InterproceduralAnalysis.proc_desc; err_log; tenv} exp
let method_exists right_proc_name methods = let method_exists right_proc_name methods =
if Language.curr_language_is Java then if Language.curr_language_is Java || Language.curr_language_is CIL then
List.exists ~f:(fun meth_name -> Procname.equal right_proc_name meth_name) methods List.exists ~f:(fun meth_name -> Procname.equal right_proc_name meth_name) methods
else else
(* ObjC/C++ case : The attribute map will only exist when we have code for the method or (* ObjC/C++ case : The attribute map will only exist when we have code for the method or
@ -474,6 +474,13 @@ let resolve_virtual_pname tenv prop actuals callee_pname call_flags : Procname.t
Typ.mk (Typ.Tptr (Typ.mk (Tstruct name), Pk_pointer)) Typ.mk (Typ.Tptr (Typ.mk (Tstruct name), Pk_pointer))
| None -> | None ->
fallback_typ ) fallback_typ )
| Procname.CSharp pname_csharp -> (
let name = Procname.CSharp.get_class_type_name pname_csharp in
match Tenv.lookup tenv name with
| Some _ ->
Typ.mk (Typ.Tptr (Typ.mk (Tstruct name), Pk_pointer))
| None ->
fallback_typ )
| _ -> | _ ->
fallback_typ fallback_typ
in in
@ -492,7 +499,7 @@ let resolve_virtual_pname tenv prop actuals callee_pname call_flags : Procname.t
(* if this is not a virtual or interface call, there's no need for resolution *) (* if this is not a virtual or interface call, there's no need for resolution *)
[callee_pname] [callee_pname]
| (receiver_exp, actual_receiver_typ) :: _ -> | (receiver_exp, actual_receiver_typ) :: _ ->
if not (Language.curr_language_is Java) then if not (Language.curr_language_is Java || Language.curr_language_is CIL) then
(* default mode for Obj-C/C++/Java virtual calls: resolution only *) (* default mode for Obj-C/C++/Java virtual calls: resolution only *)
[do_resolve callee_pname receiver_exp actual_receiver_typ] [do_resolve callee_pname receiver_exp actual_receiver_typ]
else else
@ -840,7 +847,10 @@ let add_constraints_on_retval tenv pdesc prop ret_exp ~has_nonnull_annot typ cal
let prop_with_abduced_var = let prop_with_abduced_var =
let abduced_ret_pv = let abduced_ret_pv =
(* in Java, always re-use the same abduced ret var to prevent false alarms with repeated method calls *) (* in Java, always re-use the same abduced ret var to prevent false alarms with repeated method calls *)
let loc = if Procname.is_java callee_pname then Location.dummy else callee_loc in let loc =
if Procname.is_java callee_pname || Procname.is_csharp callee_pname then Location.dummy
else callee_loc
in
Pvar.mk_abduced_ret callee_pname loc Pvar.mk_abduced_ret callee_pname loc
in in
if !BiabductionConfig.footprint then if !BiabductionConfig.footprint then
@ -1205,6 +1215,33 @@ let rec sym_exec
exec_skip_call ~reason ret_annots proc_attrs.ProcAttributes.ret_type ) exec_skip_call ~reason ret_annots proc_attrs.ProcAttributes.ret_type )
in in
List.fold ~f:(fun acc pname -> exec_one_pname pname @ acc) ~init:[] resolved_pnames List.fold ~f:(fun acc pname -> exec_one_pname pname @ acc) ~init:[] resolved_pnames
| CSharp callee_pname_csharp ->
let norm_prop, norm_args = normalize_params analysis_data prop_ actual_params in
let url_handled_args = call_constructor_url_update_args callee_pname norm_args in
let resolved_pnames =
resolve_virtual_pname tenv norm_prop url_handled_args callee_pname call_flags
in
let exec_one_pname pname =
let exec_skip_call ~reason ret_annots ret_type =
skip_call ~reason norm_prop path pname ret_annots loc ret_id_typ ret_type
url_handled_args
in
match analyze_dependency pname with
| None ->
let ret_typ = Procname.CSharp.get_return_typ callee_pname_csharp in
let ret_annots = load_ret_annots callee_pname in
exec_skip_call ~reason:"unknown method" ret_annots ret_typ
| Some ((callee_proc_desc, _) as callee_summary) -> (
match reason_to_skip ~callee_desc:(`Summary callee_summary) with
| None ->
let handled_args = call_args norm_prop pname url_handled_args ret_id_typ loc in
proc_call callee_summary handled_args
| Some reason ->
let proc_attrs = Procdesc.get_attributes callee_proc_desc in
let ret_annots = proc_attrs.ProcAttributes.method_annotation.return in
exec_skip_call ~reason ret_annots proc_attrs.ProcAttributes.ret_type )
in
List.fold ~f:(fun acc pname -> exec_one_pname pname @ acc) ~init:[] resolved_pnames
| _ -> | _ ->
(* Generic fun call with known name *) (* Generic fun call with known name *)
let prop_r, n_actual_params = normalize_params analysis_data prop_ actual_params in let prop_r, n_actual_params = normalize_params analysis_data prop_ actual_params in
@ -1471,6 +1508,9 @@ and unknown_or_scan_call ~is_scan ~reason ret_typ ret_annots
| Java _ -> | Java _ ->
(* FIXME (T19882766): we need to disable this for Java because it breaks too many tests *) (* FIXME (T19882766): we need to disable this for Java because it breaks too many tests *)
false false
| CSharp _ ->
(* FIXME (T19882766): we need to disable this for Java because it breaks too many tests *)
false
| ObjC_Cpp cpp_name -> | ObjC_Cpp cpp_name ->
(* FIXME: we need to work around a frontend hack for std::shared_ptr (* FIXME: we need to work around a frontend hack for std::shared_ptr
* to silent some of the uninitialization warnings *) * to silent some of the uninitialization warnings *)
@ -1498,7 +1538,11 @@ and unknown_or_scan_call ~is_scan ~reason ret_typ ret_annots
let has_nonnull_annot = Annotations.ia_is_nonnull ret_annots in let has_nonnull_annot = Annotations.ia_is_nonnull ret_annots in
let pre_final = let pre_final =
(* in Java, assume that skip functions close resources passed as params *) (* in Java, assume that skip functions close resources passed as params *)
let pre_1 = if Procname.is_java callee_pname then remove_file_attribute pre else pre in let pre_1 =
if Procname.is_java callee_pname || Procname.is_csharp callee_pname then
remove_file_attribute pre
else pre
in
let pre_2 = let pre_2 =
(* TODO(jjb): Should this use the type of ret_id, or ret_type from the procedure type? *) (* TODO(jjb): Should this use the type of ret_id, or ret_type from the procedure type? *)
add_constraints_on_retval tenv proc_desc pre_1 add_constraints_on_retval tenv proc_desc pre_1

@ -626,7 +626,7 @@ let prop_init_formals_seed tenv new_formals (prop : 'a Prop.t) : Prop.exposed Pr
match !Language.curr_language with match !Language.curr_language with
| Clang -> | Clang ->
Exp.Sizeof {typ; nbytes= None; dynamic_length= None; subtype= Subtype.exact} Exp.Sizeof {typ; nbytes= None; dynamic_length= None; subtype= Subtype.exact}
| Java -> | Java | CIL ->
Exp.Sizeof {typ; nbytes= None; dynamic_length= None; subtype= Subtype.subtypes} Exp.Sizeof {typ; nbytes= None; dynamic_length= None; subtype= Subtype.subtypes}
in in
Prop.mk_ptsto_lvar tenv Prop.Fld_init Predicates.inst_formal (pv, texp, None) Prop.mk_ptsto_lvar tenv Prop.Fld_init Predicates.inst_formal (pv, texp, None)
@ -867,7 +867,7 @@ let custom_error_preconditions summary =
let remove_this_not_null tenv prop = let remove_this_not_null tenv prop =
let collect_hpred (var_option, hpreds) = function let collect_hpred (var_option, hpreds) = function
| Predicates.Hpointsto (Exp.Lvar pvar, Eexp (Exp.Var var, _), _) | Predicates.Hpointsto (Exp.Lvar pvar, Eexp (Exp.Var var, _), _)
when Language.curr_language_is Java && Pvar.is_this pvar -> when (Language.curr_language_is Java || Language.curr_language_is CIL) && Pvar.is_this pvar ->
(Some var, hpreds) (Some var, hpreds)
| hpred -> | hpred ->
(var_option, hpred :: hpreds) (var_option, hpred :: hpreds)

@ -86,6 +86,9 @@ module Exec = struct
Dom.Val.of_c_array_alloc allocsite ~stride ~offset ~size ~traces Dom.Val.of_c_array_alloc allocsite ~stride ~offset ~size ~traces
| Language.Java -> | Language.Java ->
Dom.Val.of_java_array_alloc allocsite ~length:size ~traces Dom.Val.of_java_array_alloc allocsite ~length:size ~traces
| Language.CIL ->
(* cil todo *)
Dom.Val.of_java_array_alloc allocsite ~length:size ~traces
in in
if Int.equal dimension 1 then Dom.Mem.add_stack ~represents_multiple_values loc arr mem if Int.equal dimension 1 then Dom.Mem.add_stack ~represents_multiple_values loc arr mem
else Dom.Mem.add_heap ~represents_multiple_values loc arr mem else Dom.Mem.add_heap ~represents_multiple_values loc arr mem

@ -58,7 +58,7 @@ let pp_with_base pp_base fmt (base, accesses) =
| ArrayAccess _ :: rest, _ -> | ArrayAccess _ :: rest, _ ->
F.fprintf fmt "%a[]" pp_rev_accesses rest F.fprintf fmt "%a[]" pp_rev_accesses rest
| FieldAccess field_name :: Dereference :: rest, _ -> | FieldAccess field_name :: Dereference :: rest, _ ->
let op = match !Language.curr_language with Clang -> "->" | Java -> "." in let op = match !Language.curr_language with Clang -> "->" | Java -> "." | CIL -> "." in
F.fprintf fmt "%a%s%a" pp_rev_accesses rest op Fieldname.pp field_name F.fprintf fmt "%a%s%a" pp_rev_accesses rest op Fieldname.pp field_name
| FieldAccess field_name :: rest, _ -> | FieldAccess field_name :: rest, _ ->
(* Java is allowed here only because the frontend is broken and generates (* Java is allowed here only because the frontend is broken and generates
@ -68,6 +68,10 @@ let pp_with_base pp_base fmt (base, accesses) =
F.fprintf fmt "*(%a)" pp_rev_accesses rest F.fprintf fmt "*(%a)" pp_rev_accesses rest
| TakeAddress :: rest, Clang -> | TakeAddress :: rest, Clang ->
F.fprintf fmt "&(%a)" pp_rev_accesses rest F.fprintf fmt "&(%a)" pp_rev_accesses rest
| Dereference :: rest, CIL ->
F.fprintf fmt "*(%a)" pp_rev_accesses rest
| TakeAddress :: rest, CIL ->
F.fprintf fmt "&(%a)" pp_rev_accesses rest
| access :: rest, Java -> | access :: rest, Java ->
L.internal_error "Asked to print %a in Java mode@\n" L.internal_error "Asked to print %a in Java mode@\n"
(HilExp.Access.pp (fun _ _ -> ())) (HilExp.Access.pp (fun _ _ -> ()))

@ -908,7 +908,7 @@ let class_has_concurrent_method class_summaries =
let should_report_on_class (classname : Typ.Name.t) class_summaries = let should_report_on_class (classname : Typ.Name.t) class_summaries =
match classname with match classname with
| JavaClass _ -> | JavaClass _ | CSharpClass _ ->
true true
| CppClass _ | ObjcClass _ | ObjcProtocol _ | CStruct _ -> | CppClass _ | ObjcClass _ | ObjcProtocol _ | CStruct _ ->
class_has_concurrent_method class_summaries class_has_concurrent_method class_summaries

@ -33,6 +33,8 @@ let pp_exp fmt exp =
AccessExpression.pp fmt exp AccessExpression.pp fmt exp
| Java -> | Java ->
AccessPath.pp fmt (AccessExpression.to_access_path exp) AccessPath.pp fmt (AccessExpression.to_access_path exp)
| CIL ->
AccessPath.pp fmt (AccessExpression.to_access_path exp)
let rec should_keep_exp formals (exp : AccessExpression.t) = let rec should_keep_exp formals (exp : AccessExpression.t) =

@ -409,7 +409,7 @@ let is_java_main_method (pname : Procname.t) =
match args with [arg] -> Typ.equal pointer_to_array_of_java_lang_string arg | _ -> false match args with [arg] -> Typ.equal pointer_to_array_of_java_lang_string arg | _ -> false
in in
match pname with match pname with
| C _ | Linters_dummy_method | Block _ | ObjC_Cpp _ | WithBlockParameters _ -> | C _ | Linters_dummy_method | Block _ | ObjC_Cpp _ | CSharp _ | WithBlockParameters _ ->
false false
| Java java_pname -> | Java java_pname ->
Procname.Java.is_static java_pname Procname.Java.is_static java_pname

@ -13,7 +13,7 @@ module BasicCost = struct
(* NOTE: Increment the version number if you changed the [t] type. This is for avoiding (* NOTE: Increment the version number if you changed the [t] type. This is for avoiding
demarshalling failure of cost analysis results in running infer-reportdiff. *) demarshalling failure of cost analysis results in running infer-reportdiff. *)
let version = 9 let version = 10
end end
module BasicCostWithReason = struct module BasicCostWithReason = struct

@ -50,8 +50,8 @@ endif
# Note that we run find under _build directory. Since we copy some # Note that we run find under _build directory. Since we copy some
# sources from subfolders to src/ folder to avoid duplicates we use # sources from subfolders to src/ folder to avoid duplicates we use
# $(DEPTH_ONE) and iteration over main and library folders. # $(DEPTH_ONE) and iteration over main and library folders.
LIBRARY_FOLDERS = . ./IR ./absint ./al ./atd ./backend ./base ./biabduction ./bufferoverrun ./c_stubs ./checkers ./clang ./clang/unit ./concurrency ./cost ./integration ./istd ./java ./labs ./nullsafe ./nullsafe/unit ./pulse ./quandary ./scripts ./test_determinator ./topl ./unit LIBRARY_FOLDERS = . ./IR ./absint ./al ./atd ./backend ./base ./biabduction ./bufferoverrun ./c_stubs ./checkers ./clang ./clang/unit ./concurrency ./cost ./integration ./istd ./java ./labs ./dotnet ./nullsafe ./nullsafe/unit ./pulse ./quandary ./scripts ./test_determinator ./topl ./unit
INCLUDE_FOLDERS = -I IR -I absint -I al -I atd -I backend -I base -I biabduction -I bufferoverrun -I c_stubs -I checkers -I clang -I clang/unit -I concurrency -I cost -I integration -I istd -I java -I labs -I nullsafe -I nullsafe/unit -I pulse -I quandary -I scripts -I test_determinator -I topl -I unit INCLUDE_FOLDERS = -I IR -I absint -I al -I atd -I backend -I base -I biabduction -I bufferoverrun -I c_stubs -I checkers -I clang -I clang/unit -I concurrency -I cost -I integration -I istd -I java -I labs -I dotnet -I nullsafe -I nullsafe/unit -I pulse -I quandary -I scripts -I test_determinator -I topl -I unit
ml_src_files = $(shell \ ml_src_files = $(shell \
cd $(INFER_BUILD_DIR); \ cd $(INFER_BUILD_DIR); \

@ -0,0 +1,266 @@
(*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open! IStd
module F = Format
module Hashtbl = Caml.Hashtbl
module LeakList = struct
include Base.List
let append_one leakList typeWithLeak = leakList @ [typeWithLeak]
end
(** Resource analysis in loop and branch*)
module FiniteBounds = struct
type t = int
let leq ~lhs ~rhs = lhs <= rhs
let join a b = max a b
let widen ~prev ~next ~num_iters:_ = join prev next
let pp fmt astate = F.fprintf fmt "%d" astate
end
(** Resource analysis in loop *)
module BoundsWithTop = struct
open AbstractDomain.Types
include AbstractDomain.TopLifted (FiniteBounds)
let widening_threshold = 5
let widen ~prev ~next ~num_iters =
match (prev, next) with
| Top, _ | _, Top ->
Top
| NonTop prev, NonTop next when num_iters < widening_threshold ->
NonTop (FiniteBounds.join prev next)
| NonTop _, NonTop _ (* num_iters >= widening_threshold *) ->
Top
end
module ResourcesHeld = AbstractDomain.Map (AccessPath) (BoundsWithTop)
open AbstractDomain.Types
(** Initializes resources to type map *)
let type_map = ref (Hashtbl.create 100)
(** Initializes resources to count map *)
let initial = ResourcesHeld.empty
(** Updates the count of a specific resource *)
let update_count count n = match count with Top -> Top | NonTop held -> NonTop (held + n)
(** Increments the count of a specific resource *)
let incr_count count = update_count count 1
(** Decrements the count of a specific resource *)
let decr_count count = update_count count (-1)
(** Checks the count of a specific resource *)
let find_count access_path held =
match ResourcesHeld.find_opt access_path held with Some count -> count | None -> NonTop 0
(** Checks the count of resources held if resource exists, otherwise returns false*)
let check_count access_path held =
let old_count = find_count access_path held in
match old_count with NonTop count when count > 0 -> true | _ -> false
let get_type_map = !type_map
let reset_type_map = Hashtbl.reset !type_map
(** Adds resources acquired to records *)
let acquire_resource access_path class_name held =
let add_resource_to_hash =
match ResourcesHeld.find_opt access_path held with
| Some _ ->
()
| None ->
Hashtbl.add !type_map access_path class_name
in
add_resource_to_hash ;
let old_count = find_count access_path held in
ResourcesHeld.add access_path (incr_count old_count) held
(** Releases acquired resources from records when release function is called*)
let release_resource access_path held =
let old_count = find_count access_path held in
let remove_resource_from_hash =
match old_count with
| NonTop count when count < 2 ->
Hashtbl.remove !type_map access_path
| _ ->
()
in
remove_resource_from_hash ;
ResourcesHeld.add access_path (decr_count old_count) held
(** Re-assigns resources when transferred to other objects*)
let assign lhs_access_path rhs_access_path held =
let add_type_map search_access_path access_path =
match Hashtbl.find !type_map search_access_path with
| class_name ->
Hashtbl.add !type_map access_path class_name ;
Hashtbl.remove !type_map search_access_path
| exception Caml.Not_found ->
()
in
let one_binding access_path count held =
match
AccessPath.replace_prefix ~prefix:rhs_access_path ~replace_with:access_path lhs_access_path
with
| Some base_access_path ->
add_type_map access_path base_access_path ;
ResourcesHeld.add base_access_path count held
| None ->
if AccessPath.equal rhs_access_path access_path then (
add_type_map access_path lhs_access_path ;
ResourcesHeld.add lhs_access_path count held )
else ResourcesHeld.add access_path count held
in
ResourcesHeld.fold one_binding held ResourcesHeld.empty
(** Checks if there is a resource leak*)
let has_leak formal_map held =
(* test if we acquired resources that we do not return to the caller *)
let is_local_leak access_path count =
let base, _ = access_path in
match (count, base) with
| Top, _ ->
false
| NonTop count, _ when count > 1 ->
true
| NonTop count, _ when count <= 0 ->
false
(* count = 1 *)
| _, (var, _) when Var.is_global var ->
false
| _, (ret, _) when Var.is_return ret ->
false
| _, base when FormalMap.is_formal base formal_map ->
false
| _ ->
true
in
ResourcesHeld.exists is_local_leak held
(** module for resource leak summary *)
module Summary = struct
module InterfaceAccessPath = struct
type base = Return | Formal of int [@@deriving compare]
let pp_base f = function
| Return ->
F.pp_print_string f "Return"
| Formal i ->
F.fprintf f "Formal(%d)" i
type t = base * AccessPath.access list [@@deriving compare]
let pp f = function
| base, [] ->
pp_base f base
| base, accesses ->
F.fprintf f "%a.%a" pp_base base AccessPath.pp_access_list accesses
end
module ResourcesFromFormals = PrettyPrintable.MakePPMap (InterfaceAccessPath)
let interface_type_map = ref (Hashtbl.create 100)
let reset_interface_type_map = Hashtbl.reset !interface_type_map
type t = BoundsWithTop.t ResourcesFromFormals.t
let pp = ResourcesFromFormals.pp ~pp_value:BoundsWithTop.pp
let make formal_map held =
let to_interface access_path =
let base, accesses = access_path in
match FormalMap.get_formal_index base formal_map with
| Some i ->
Some (InterfaceAccessPath.Formal i, accesses)
| None ->
if Var.is_return (fst base) then Some (InterfaceAccessPath.Return, accesses) else None
in
let add_resource_to_hash interface_access_path class_name =
Hashtbl.add !interface_type_map interface_access_path class_name
in
let add_to_type_map search_access_path interface_access_path =
match Hashtbl.find !type_map search_access_path with
| class_name ->
add_resource_to_hash interface_access_path class_name
| exception Caml.Not_found ->
()
in
ResourcesHeld.fold
(fun access_path count acquired ->
match to_interface access_path with
| Some interface_access_path ->
add_to_type_map access_path interface_access_path ;
ResourcesFromFormals.add interface_access_path count acquired
| None ->
acquired )
held ResourcesFromFormals.empty
let apply ~callee:summary ~return ~actuals held =
let apply_one (base, accesses) callee_count held =
let access_path_opt =
match (base : InterfaceAccessPath.base) with
| Return ->
Some (return, accesses)
| Formal i -> (
match List.nth actuals i with
| Some (HilExp.AccessExpression actual_expr) ->
Some (AccessPath.append (HilExp.AccessExpression.to_access_path actual_expr) accesses)
| _ ->
None )
in
let add_type_map search_access_path =
match Hashtbl.find !interface_type_map (base, accesses) with
| class_name ->
Hashtbl.add !type_map search_access_path class_name
| exception Caml.Not_found ->
()
in
match access_path_opt with
| None ->
held
| Some access_path ->
let new_count =
match callee_count with
| Top ->
Top
| NonTop callee_count ->
let old_count =
ResourcesHeld.find_opt access_path held |> Option.value ~default:(NonTop 0)
in
update_count old_count callee_count
in
let add_resource_to_hash =
match new_count with NonTop count when count > 0 -> add_type_map access_path | _ -> ()
in
add_resource_to_hash ;
ResourcesHeld.add access_path new_count held
in
ResourcesFromFormals.fold apply_one summary held
end
type summary = Summary.t
include ResourcesHeld

@ -0,0 +1,46 @@
(*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open! IStd
include AbstractDomain.S
val initial : t
module LeakList : sig
include module type of Base.List
val append_one : 'a list -> 'a -> 'a list
end
val check_count : AccessPath.t -> t -> bool
val get_type_map : (AccessPath.t, string) Caml.Hashtbl.t
val reset_type_map : unit
val acquire_resource : AccessPath.t -> string -> t -> t
val release_resource : AccessPath.t -> t -> t
val assign : AccessPath.t -> AccessPath.t -> t -> t
val has_leak : FormalMap.t -> t -> bool
type summary
module Summary : sig
val apply : callee:summary -> return:AccessPath.base -> actuals:HilExp.t list -> t -> t
val reset_interface_type_map : unit
val make : FormalMap.t -> t -> summary
val pp : Format.formatter -> summary -> unit
type t = summary
end

@ -0,0 +1,200 @@
(*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open! IStd
module F = Format
module L = Logging
module Hashtbl = Caml.Hashtbl
let leak_list = ref []
let type_map = ref (Hashtbl.create 100)
module TransferFunctions (CFG : ProcCfg.S) = struct
module CFG = CFG
module Domain = ResourceLeakCSDomain
type analysis_data = ResourceLeakCSDomain.Summary.t InterproceduralAnalysis.t
let is_closeable_typename tenv typename =
let is_closable_interface typename _ =
match Typ.Name.name typename with
| "java.io.AutoCloseable" | "java.io.Closeable" ->
true
| "System.IDisposable" ->
true
| _ ->
false
in
PatternMatch.supertype_exists tenv is_closable_interface typename
let is_closeable_procname tenv procname =
match procname with
| Procname.Java java_procname ->
is_closeable_typename tenv (Procname.Java.get_class_type_name java_procname)
| Procname.CSharp csharp_procname ->
is_closeable_typename tenv (Procname.CSharp.get_class_type_name csharp_procname)
| _ ->
false
let acquires_resource tenv procname =
(* We assume all constructors of a subclass of Closeable acquire a resource *)
Procname.is_constructor procname && is_closeable_procname tenv procname
let releases_resource tenv procname =
(* We assume the close method of a Closeable releases all of its resources *)
match procname with
| Procname.CSharp _ ->
( String.equal "Close" (Procname.get_method procname)
|| String.equal "Dispose" (Procname.get_method procname) )
&& is_closeable_procname tenv procname
| _ ->
String.equal "close" (Procname.get_method procname) && is_closeable_procname tenv procname
(** Take an abstract state and instruction, produce a new abstract state *)
let exec_instr (astate : ResourceLeakCSDomain.t)
{InterproceduralAnalysis.proc_desc; tenv; analyze_dependency; _} _ (instr : HilInstr.t) =
let assign_type_map = type_map := ResourceLeakCSDomain.get_type_map in
assign_type_map ;
let is_not_enumerable =
let contains s1 s2 =
let re = Str.regexp_string s2 in
try
ignore (Str.search_forward re s1 0) ;
false
with Not_found_s _ | Caml.Not_found -> true
in
contains (Procname.to_string (Procdesc.get_proc_name proc_desc)) "IEnumerable"
&& contains (Procname.to_string (Procdesc.get_proc_name proc_desc)) "Enumerator"
in
match instr with
| Call (_return, Direct callee_procname, HilExp.AccessExpression allocated :: _, _, _loc)
when acquires_resource tenv callee_procname && is_not_enumerable ->
let get_class_name =
match callee_procname with
| Procname.Java java_procname ->
Procname.Java.get_class_name java_procname
| Procname.CSharp csharp_procname ->
Procname.CSharp.get_class_name csharp_procname
| _ ->
L.die InternalError "Unsupported procname kind! Only Java and .NET is supported"
in
ResourceLeakCSDomain.acquire_resource
(HilExp.AccessExpression.to_access_path allocated)
get_class_name astate
| Call (_, Direct callee_procname, [actual], _, _loc)
when releases_resource tenv callee_procname -> (
match actual with
| HilExp.AccessExpression access_expr ->
ResourceLeakCSDomain.release_resource
(HilExp.AccessExpression.to_access_path access_expr)
astate
| _ ->
astate )
| Call (return, Direct callee_procname, actuals, _, _loc) -> (
match analyze_dependency callee_procname with
| Some (_callee_proc_desc, callee_summary) ->
(* interprocedural analysis produced a summary: use it *)
ResourceLeakCSDomain.Summary.apply ~callee:callee_summary ~return ~actuals astate
| None ->
(* No summary for [callee_procname]; it's native code or missing for some reason *)
astate )
| Assign (access_expr, AccessExpression rhs_access_expr, _loc) ->
ResourceLeakCSDomain.assign
(HilExp.AccessExpression.to_access_path access_expr)
(HilExp.AccessExpression.to_access_path rhs_access_expr)
astate
| Assign (lhs_access_path, rhs_exp, _loc) -> (
match rhs_exp with
| HilExp.AccessExpression access_expr ->
ResourceLeakCSDomain.assign
(HilExp.AccessExpression.to_access_path lhs_access_path)
(HilExp.AccessExpression.to_access_path access_expr)
astate
| _ ->
astate )
| Assume (assume_exp, _, _, _loc) -> (
(* a conditional assume([assume_exp]). blocks if [assume_exp] evaluates to false *)
let rec extract_null_compare_expr expr =
match expr with
| HilExp.Cast (_, e) ->
extract_null_compare_expr e
| HilExp.BinaryOperator (Binop.Eq, HilExp.AccessExpression access_expr, exp)
| HilExp.BinaryOperator (Binop.Eq, exp, HilExp.AccessExpression access_expr)
| HilExp.UnaryOperator
( Unop.LNot
, HilExp.BinaryOperator (Binop.Ne, HilExp.AccessExpression access_expr, exp)
, _ )
| HilExp.UnaryOperator
( Unop.LNot
, HilExp.BinaryOperator (Binop.Ne, exp, HilExp.AccessExpression access_expr)
, _ ) ->
Option.some_if (HilExp.is_null_literal exp)
(HilExp.AccessExpression.to_access_path access_expr)
| _ ->
None
in
match extract_null_compare_expr assume_exp with
| Some ap ->
ResourceLeakCSDomain.release_resource ap astate
| _ ->
astate )
| Call (_, Indirect _, _, _, _) ->
(* This should never happen in Java. Fail if it does. *)
L.(die InternalError) "Unexpected indirect call %a" HilInstr.pp instr
| Metadata _ ->
astate
let pp_session_name _node fmt = F.pp_print_string fmt "resource leaks"
end
(** 5(a) Type of CFG to analyze--Exceptional to follow exceptional control-flow edges, Normal to
ignore them *)
module CFG = ProcCfg.Normal
(* Create an intraprocedural abstract interpreter from the transfer functions we defined *)
module Analyzer = LowerHil.MakeAbstractInterpreter (TransferFunctions (CFG))
(** Report an error when we have acquired more resources than we have released *)
let report_if_leak {InterproceduralAnalysis.proc_desc; err_log; _} formal_map post =
if ResourceLeakCSDomain.has_leak formal_map post then (
let last_loc = Procdesc.Node.get_loc (Procdesc.get_exit_node proc_desc) in
let message =
let concat_types =
Hashtbl.iter
(fun x y ->
if ResourceLeakCSDomain.check_count x post then
leak_list := ResourceLeakCSDomain.LeakList.append_one !leak_list y )
!type_map
in
concat_types ;
let concat_leak_list = String.concat ~sep:", " !leak_list in
F.asprintf "Leaked %a resource(s) at type(s) %s" ResourceLeakCSDomain.pp post concat_leak_list
in
ResourceLeakCSDomain.reset_type_map ;
ResourceLeakCSDomain.Summary.reset_interface_type_map ;
leak_list := [] ;
Reporting.log_issue proc_desc err_log ~loc:last_loc DOTNETResourceLeaks
IssueType.dotnet_resource_leak message )
else ResourceLeakCSDomain.reset_type_map ;
ResourceLeakCSDomain.Summary.reset_interface_type_map
(* Callback for invoking the checker from the outside--registered in RegisterCheckers *)
let checker ({InterproceduralAnalysis.proc_desc} as analysis_data) =
let result =
Analyzer.compute_post analysis_data ~initial:ResourceLeakCSDomain.initial proc_desc
in
Option.map result ~f:(fun post ->
let formal_map = FormalMap.make proc_desc in
report_if_leak analysis_data formal_map post ;
ResourceLeakCSDomain.Summary.make formal_map post )

@ -0,0 +1,11 @@
(*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open! IStd
val checker :
ResourceLeakCSDomain.summary InterproceduralAnalysis.t -> ResourceLeakCSDomain.summary option

@ -0,0 +1,14 @@
; Copyright (c) Facebook, Inc. and its affiliates.
;
; This source code is licensed under the MIT license found in the
; LICENSE file in the root directory of this source tree.
(library
(name Dotnet)
(public_name infer.Dotnet)
(flags
(:standard -open Core -open IR -open IStdlib -open IStd -open ATDGenerated
-open IBase -open Absint))
(libraries core IStdlib ATDGenerated IBase IR Absint)
(preprocess
(pps ppx_compare)))

@ -99,7 +99,7 @@ let infertop_stanza =
(modes byte_complete) (modes byte_complete)
(modules Infertop) (modules Infertop)
(flags (:standard -open Core -open IStdlib -open IStd)) (flags (:standard -open Core -open IStdlib -open IStd))
(libraries %s utop Absint ASTLanguage ATDGenerated Backend IBase Biabduction BO Checkers Concurrency Costlib CStubs IR IStdlib Labs Nullsafe Pulselib Quandary Integration TestDeterminators TOPLlib UnitTests) (libraries %s utop Absint ASTLanguage ATDGenerated Backend IBase Biabduction BO Checkers Concurrency Costlib CStubs IR IStdlib Labs Dotnet Nullsafe Pulselib Quandary Integration TestDeterminators TOPLlib UnitTests)
(link_flags (-linkall -warn-error -31)) (link_flags (-linkall -warn-error -31))
(preprocess (pps ppx_compare)) (preprocess (pps ppx_compare))
(promote (until-clean) (into ../bin)) (promote (until-clean) (into ../bin))

@ -34,7 +34,7 @@ let setup () =
already_started := true ) already_started := true )
in in
( match Config.command with ( match Config.command with
| Analyze -> | Analyze | AnalyzeJson ->
ResultsDir.assert_results_dir "have you run capture before?" ResultsDir.assert_results_dir "have you run capture before?"
| Report | ReportDiff -> | Report | ReportDiff ->
ResultsDir.create_results_dir () ResultsDir.create_results_dir ()
@ -67,7 +67,7 @@ let setup () =
() ) ; () ) ;
let has_result_dir = let has_result_dir =
match Config.command with match Config.command with
| Analyze | Capture | Compile | Debug | Explore | Report | ReportDiff | Run -> | Analyze | AnalyzeJson | Capture | Compile | Debug | Explore | Report | ReportDiff | Run ->
true true
| Help -> | Help ->
false false
@ -162,6 +162,8 @@ let () =
else JSourceFileInfo.debug_on_file (Option.value_exn Config.java_debug_source_file_info) else JSourceFileInfo.debug_on_file (Option.value_exn Config.java_debug_source_file_info)
| Analyze -> | Analyze ->
run Driver.Analyze run Driver.Analyze
| AnalyzeJson ->
run Driver.AnalyzeJson
| Capture | Compile | Run -> | Capture | Compile | Run ->
run (Lazy.force Driver.mode_from_command_line) run (Lazy.force Driver.mode_from_command_line)
| Help -> | Help ->

@ -16,6 +16,7 @@ module F = Format
(* based on the build_system and options passed to infer, we run in different driver modes *) (* based on the build_system and options passed to infer, we run in different driver modes *)
type mode = type mode =
| Analyze | Analyze
| AnalyzeJson
| Ant of {prog: string; args: string list} | Ant of {prog: string; args: string list}
| BuckClangFlavor of {build_cmd: string list} | BuckClangFlavor of {build_cmd: string list}
| BuckCompilationDB of {deps: BuckMode.clang_compilation_db_deps; prog: string; args: string list} | BuckCompilationDB of {deps: BuckMode.clang_compilation_db_deps; prog: string; args: string list}
@ -35,6 +36,8 @@ let is_analyze_mode = function Analyze -> true | _ -> false
let pp_mode fmt = function let pp_mode fmt = function
| Analyze -> | Analyze ->
F.fprintf fmt "Analyze driver mode" F.fprintf fmt "Analyze driver mode"
| AnalyzeJson ->
F.fprintf fmt "Analyze json mode"
| Ant {prog; args} -> | Ant {prog; args} ->
F.fprintf fmt "Ant driver mode:@\nprog = '%s'@\nargs = %a" prog Pp.cli_args args F.fprintf fmt "Ant driver mode:@\nprog = '%s'@\nargs = %a" prog Pp.cli_args args
| BuckClangFlavor {build_cmd} -> | BuckClangFlavor {build_cmd} ->
@ -99,7 +102,7 @@ let check_xcpretty () =
let capture ~changed_files = function let capture ~changed_files = function
| Analyze -> | Analyze | AnalyzeJson ->
() ()
| Ant {prog; args} -> | Ant {prog; args} ->
L.progress "Capturing in ant mode...@." ; L.progress "Capturing in ant mode...@." ;
@ -168,6 +171,17 @@ let execute_analyze ~changed_files =
PerfEvent.(log (fun logger -> log_end_event logger ())) PerfEvent.(log (fun logger -> log_end_event logger ()))
let execute_analyze_json () =
match (Config.cfg_json, Config.tenv_json) with
| Some cfg_json, Some tenv_json ->
InferAnalyzeJson.analyze_json cfg_json tenv_json
| _, _ ->
L.user_warning
"** Missing cfg or tenv json files. Provide them as arguments throught '--cfg-json' and \
'--tenv-json' **\n" ;
()
let report ?(suppress_console = false) () = let report ?(suppress_console = false) () =
let issues_json = ResultsDir.get_path ReportJson in let issues_json = ResultsDir.get_path ReportJson in
JsonReports.write_reports ~issues_json ~costs_json:(ResultsDir.get_path ReportCostsJson) ; JsonReports.write_reports ~issues_json ~costs_json:(ResultsDir.get_path ReportCostsJson) ;
@ -226,7 +240,7 @@ let analyze_and_report ?suppress_console_report ~changed_files mode =
(false, false) (false, false)
| (Capture | Compile | Debug | Explore | Help | Report | ReportDiff), _ -> | (Capture | Compile | Debug | Explore | Help | Report | ReportDiff), _ ->
(false, false) (false, false)
| (Analyze | Run), _ -> | (Analyze | AnalyzeJson | Run), _ ->
(true, true) (true, true)
in in
let should_analyze = should_analyze && Config.capture in let should_analyze = should_analyze && Config.capture in
@ -242,15 +256,19 @@ let analyze_and_report ?suppress_console_report ~changed_files mode =
true true
| Analyze | BuckJavaFlavor _ | Gradle _ -> | Analyze | BuckJavaFlavor _ | Gradle _ ->
ResultsDir.RunState.get_merge_capture () ResultsDir.RunState.get_merge_capture ()
| AnalyzeJson ->
false
| _ -> | _ ->
false false
in in
let analyze_json = match mode with AnalyzeJson -> true | _ -> false in
if should_merge then ( if should_merge then (
if Config.export_changed_functions then MergeCapture.merge_changed_functions () ; if Config.export_changed_functions then MergeCapture.merge_changed_functions () ;
MergeCapture.merge_captured_targets () ; MergeCapture.merge_captured_targets () ;
ResultsDir.RunState.set_merge_capture false ) ; ResultsDir.RunState.set_merge_capture false ) ;
if should_analyze then if should_analyze then
if SourceFiles.is_empty () && Config.capture then error_nothing_to_analyze mode if analyze_json then execute_analyze_json ()
else if SourceFiles.is_empty () && Config.capture then error_nothing_to_analyze mode
else ( else (
execute_analyze ~changed_files ; execute_analyze ~changed_files ;
if Config.starvation_whole_program then StarvationGlobalAnalysis.whole_program_analysis () ) ; if Config.starvation_whole_program then StarvationGlobalAnalysis.whole_program_analysis () ) ;

@ -13,6 +13,7 @@ open! IStd
(** based on the build_system and options passed to infer, we run in different driver modes *) (** based on the build_system and options passed to infer, we run in different driver modes *)
type mode = type mode =
| Analyze | Analyze
| AnalyzeJson
| Ant of {prog: string; args: string list} | Ant of {prog: string; args: string list}
| BuckClangFlavor of {build_cmd: string list} | BuckClangFlavor of {build_cmd: string list}
| BuckCompilationDB of {deps: BuckMode.clang_compilation_db_deps; prog: string; args: string list} | BuckCompilationDB of {deps: BuckMode.clang_compilation_db_deps; prog: string; args: string list}

@ -72,7 +72,7 @@ let loc_trace_to_jsonbug_record trace_list ekind =
let should_report issue_type error_desc = let should_report issue_type error_desc =
if not Config.filtering then true if (not Config.filtering) || Language.curr_language_is CIL then true
else else
let issue_type_is_null_deref = let issue_type_is_null_deref =
let null_deref_issue_types = let null_deref_issue_types =

@ -0,0 +1,19 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# Makefiles that include this one must define TESTS_DIR and then include
# $(TESTS_DIR)/analyzejson.make.
include $(TESTS_DIR)/infer.make
cfg.json: jsons.tar.xz
$(QUIET)tar xf $<
infer-out/report.json: $(MAKEFILE_LIST) cfg.json
$(QUIET)$(call silent_on_success,Testing infer/dotnet in $(TEST_REL_DIR),\
$(INFER_BIN) capture && \
$(INFER_BIN) analyzejson --project-root $(TESTS_DIR) \
$(INFER_OPTIONS) && \
sed -i -e 's#/app/infernew/infer/tests/##g' $@ )

@ -0,0 +1,14 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
TESTS_DIR = ../../..
INFER_OPTIONS = \
--debug --cfg-json cfg.json \
--tenv-json tenv.json \
INFERPRINT_OPTIONS = --issues-tests-fields "file,procedure,line_offset,bug_type,bucket" --issues-tests
include $(TESTS_DIR)/analyzejson.make

@ -0,0 +1,13 @@
codetoanalyze/dotnet/nullderef-arithmetic/NullDerefArithmetic.cs, Void TestCodeArithmetic.NullExceptionANDBad(), 8, NULL_DEREFERENCE, B5
codetoanalyze/dotnet/nullderef-arithmetic/NullDerefArithmetic.cs, Void TestCodeArithmetic.NullExceptionBitwiseComp1Bad(), 6, NULL_DEREFERENCE, B5
codetoanalyze/dotnet/nullderef-arithmetic/NullDerefArithmetic.cs, Void TestCodeArithmetic.NullExceptionDivBad(), 8, NULL_DEREFERENCE, B5
codetoanalyze/dotnet/nullderef-arithmetic/NullDerefArithmetic.cs, Void TestCodeArithmetic.NullExceptionLeftShftBad(), 8, NULL_DEREFERENCE, B5
codetoanalyze/dotnet/nullderef-arithmetic/NullDerefArithmetic.cs, Void TestCodeArithmetic.NullExceptionMinusBad(), 8, NULL_DEREFERENCE, B5
codetoanalyze/dotnet/nullderef-arithmetic/NullDerefArithmetic.cs, Void TestCodeArithmetic.NullExceptionModBad(), 8, NULL_DEREFERENCE, B5
codetoanalyze/dotnet/nullderef-arithmetic/NullDerefArithmetic.cs, Void TestCodeArithmetic.NullExceptionMultBad(), 8, NULL_DEREFERENCE, B5
codetoanalyze/dotnet/nullderef-arithmetic/NullDerefArithmetic.cs, Void TestCodeArithmetic.NullExceptionORBad(), 8, NULL_DEREFERENCE, B5
codetoanalyze/dotnet/nullderef-arithmetic/NullDerefArithmetic.cs, Void TestCodeArithmetic.NullExceptionPlusBad(), 8, NULL_DEREFERENCE, B5
codetoanalyze/dotnet/nullderef-arithmetic/NullDerefArithmetic.cs, Void TestCodeArithmetic.NullExceptionRightShftBad(), 8, NULL_DEREFERENCE, B5
codetoanalyze/dotnet/nullderef-arithmetic/NullDerefArithmetic.cs, Void TestCodeArithmetic.NullExceptionUnsignedDivBad(), 8, NULL_DEREFERENCE, B5
codetoanalyze/dotnet/nullderef-arithmetic/NullDerefArithmetic.cs, Void TestCodeArithmetic.NullExceptionUnsignedModBad(), 8, NULL_DEREFERENCE, B5
codetoanalyze/dotnet/nullderef-arithmetic/NullDerefArithmetic.cs, Void TestCodeArithmetic.NullExceptionXORBad(), 8, NULL_DEREFERENCE, B5

@ -0,0 +1,14 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
TESTS_DIR = ../../..
INFER_OPTIONS = \
--debug --cfg-json cfg.json \
--tenv-json tenv.json \
INFERPRINT_OPTIONS = --issues-tests-fields "file,procedure,line_offset,bug_type,bucket" --issues-tests
include $(TESTS_DIR)/analyzejson.make

@ -0,0 +1,3 @@
codetoanalyze/dotnet/array/NullDerefArray.cs, Void TestCodeArray.NullExceptionArrayOneDim1Bad(), 5, NULL_DEREFERENCE, B5
codetoanalyze/dotnet/array/NullDerefArray.cs, Void TestCodeArray.NullExceptionArrayOneDim2Bad(), 5, NULL_DEREFERENCE, B5
codetoanalyze/dotnet/array/NullDerefArray.cs, Void TestCodeArray.NullExceptionArrayTwoDimBad(), 5, NULL_DEREFERENCE, B5

@ -0,0 +1,14 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
TESTS_DIR = ../../..
INFER_OPTIONS = \
--debug --cfg-json cfg.json \
--tenv-json tenv.json \
INFERPRINT_OPTIONS = --issues-tests-fields "file,procedure,line_offset,bug_type,bucket" --issues-tests
include $(TESTS_DIR)/analyzejson.make

@ -0,0 +1,2 @@
codetoanalyze/dotnet/bgeble/NullDerefBgeBle.cs, Void TestCodeBgeBle.NullExceptionBgeBad(), 8, NULL_DEREFERENCE, B5
codetoanalyze/dotnet/bgeble/NullDerefBgeBle.cs, Void TestCodeBgeBle.NullExceptionBleBad(), 8, NULL_DEREFERENCE, B5

@ -0,0 +1,14 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
TESTS_DIR = ../../..
INFER_OPTIONS = \
--debug --cfg-json cfg.json \
--tenv-json tenv.json \
INFERPRINT_OPTIONS = --issues-tests-fields "file,procedure,line_offset,bug_type,bucket" --issues-tests
include $(TESTS_DIR)/analyzejson.make

@ -0,0 +1 @@
codetoanalyze/dotnet/box/NullDerefBox.cs, Void TestCodeBox.NullExceptionBoxIntegersBad(), 5, NULL_DEREFERENCE, B5

@ -0,0 +1,14 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
TESTS_DIR = ../../..
INFER_OPTIONS = \
--debug --cfg-json cfg.json \
--tenv-json tenv.json \
INFERPRINT_OPTIONS = --issues-tests-fields "file,procedure,line_offset,bug_type,bucket" --issues-tests
include $(TESTS_DIR)/analyzejson.make

@ -0,0 +1,4 @@
codetoanalyze/dotnet/fieldderef/NullDerefFieldDeref.cs, Void TestCodeFieldDeref.NullExceptionFieldDeref1Bad(), 4, NULL_DEREFERENCE, B5
codetoanalyze/dotnet/fieldderef/NullDerefFieldDeref.cs, Void TestCodeFieldDeref.NullExceptionFieldDeref2Bad(), 4, NULL_DEREFERENCE, B5
codetoanalyze/dotnet/fieldderef/NullDerefFieldDeref.cs, Void TestCodeFieldDeref.NullExceptionStaticDeref1Bad(), 2, NULL_DEREFERENCE, B1
codetoanalyze/dotnet/fieldderef/NullDerefFieldDeref.cs, Void TestCodeFieldDeref.NullExceptionStaticDeref2Bad(), 2, NULL_DEREFERENCE, B1

@ -0,0 +1,14 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
TESTS_DIR = ../../..
INFER_OPTIONS = \
--debug --cfg-json cfg.json \
--tenv-json tenv.json \
INFERPRINT_OPTIONS = --issues-tests-fields "file,procedure,line_offset,bug_type,bucket" --issues-tests
include $(TESTS_DIR)/analyzejson.make

@ -0,0 +1,2 @@
codetoanalyze/dotnet/isinst/NullDerefIsInst.cs, Void TestCodeIsInst.NullExceptionIsInst1Bad(), 3, NULL_DEREFERENCE, B5
codetoanalyze/dotnet/isinst/NullDerefIsInst.cs, Void TestCodeIsInst.NullExceptionIsInst2Bad(), 3, NULL_DEREFERENCE, B5

@ -0,0 +1,14 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
TESTS_DIR = ../../..
INFER_OPTIONS = \
--debug --cfg-json cfg.json \
--tenv-json tenv.json \
INFERPRINT_OPTIONS = --issues-tests-fields "file,procedure,line_offset,bug_type,bucket" --issues-tests
include $(TESTS_DIR)/analyzejson.make

@ -0,0 +1 @@
codetoanalyze/dotnet/ldstr/NullExceptionLdstr.cs, Void TestCodeLdstr.NullExceptionLdstrBad(), 3, NULL_DEREFERENCE, B5

@ -0,0 +1,14 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
TESTS_DIR = ../../..
INFER_OPTIONS = \
--debug --cfg-json cfg.json \
--tenv-json tenv.json \
INFERPRINT_OPTIONS = --issues-tests-fields "file,procedure,line_offset,bug_type,bucket" --issues-tests
include $(TESTS_DIR)/analyzejson.make

@ -0,0 +1,6 @@
codetoanalyze/dotnet/logical/NullExceptionLogical.cs, Void TestCodeLogical.NullExceptionANDBad(), 8, NULL_DEREFERENCE, B5
codetoanalyze/dotnet/logical/NullExceptionLogical.cs, Void TestCodeLogical.NullExceptionOR1Bad(), 8, NULL_DEREFERENCE, B5
codetoanalyze/dotnet/logical/NullExceptionLogical.cs, Void TestCodeLogical.NullExceptionOR2Bad(), 8, NULL_DEREFERENCE, B5
codetoanalyze/dotnet/logical/NullExceptionLogical.cs, Void TestCodeLogical.NullExceptionOR3Bad(), 8, NULL_DEREFERENCE, B5
codetoanalyze/dotnet/logical/NullExceptionLogical.cs, Void TestCodeLogical.NullExceptionXOR1Bad(), 8, NULL_DEREFERENCE, B5
codetoanalyze/dotnet/logical/NullExceptionLogical.cs, Void TestCodeLogical.NullExceptionXOR2Bad(), 8, NULL_DEREFERENCE, B5

@ -0,0 +1,14 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
TESTS_DIR = ../../..
INFER_OPTIONS = \
--debug --cfg-json cfg.json \
--tenv-json tenv.json \
INFERPRINT_OPTIONS = --issues-tests-fields "file,procedure,line_offset,bug_type,bucket" --issues-tests
include $(TESTS_DIR)/analyzejson.make

@ -0,0 +1 @@
codetoanalyze/dotnet/nullderef-interproc/NullDerefInterproc.cs, Void TestCodeNullDerefInterproc.NullExceptionInterprocBad(), 3, NULL_DEREFERENCE, B5

@ -0,0 +1,14 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
TESTS_DIR = ../../..
INFER_OPTIONS = \
--debug --cfg-json cfg.json \
--tenv-json tenv.json \
INFERPRINT_OPTIONS = --issues-tests-fields "file,procedure,line_offset,bug_type,bucket" --issues-tests
include $(TESTS_DIR)/analyzejson.make

@ -0,0 +1 @@
codetoanalyze/dotnet/nullderef-simple/NullExceptionSimple.cs, Void TestCodeSimple.NullExceptionSimpleBad(), 3, NULL_DEREFERENCE, B5

@ -0,0 +1,14 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
TESTS_DIR = ../../..
INFER_OPTIONS = \
--debug --cfg-json cfg.json \
--tenv-json tenv.json \
INFERPRINT_OPTIONS = --issues-tests-fields "file,procedure,line_offset,bug_type,bucket" --issues-tests
include $(TESTS_DIR)/analyzejson.make

@ -0,0 +1 @@
codetoanalyze/dotnet/nullparam/NullExceptionNullParam.cs, Void TestCodeNullParam.NullExceptionNullParamBad(), 3, NULL_DEREFERENCE, B5

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

Loading…
Cancel
Save