[toplevel] do not read inferconfig location from the command line

Summary:
Try to read .inferconfig in the current directory, then in .., then in ../..,
etc. This can be overriden with the `INFERCONFIG` environment variable.

This removes the need for two-phase parsing, so clean up that code too.

Paths in .inferconfig are interpreted relative to where .inferconfig is located.
This does not apply to other path-sensitive things like regexpes... this is not
a show stopper because regexpes can account for the fact that infer may be
called from different project roots.

Make sure we fail when .inferconfig exists but cannot be read.

Reviewed By: jberdine

Differential Revision: D4843142

fbshipit-source-id: 340a63f
master
Jules Villard 8 years ago committed by Facebook Github Bot
parent 652cf81dab
commit 724a592c34

@ -36,7 +36,7 @@ ifneq ($(BUCK),no)
BUILD_SYSTEMS_TESTS += buck-clang-db BUILD_SYSTEMS_TESTS += buck-clang-db
endif endif
ifneq ($(CMAKE),no) ifneq ($(CMAKE),no)
BUILD_SYSTEMS_TESTS += clang_compilation_db cmake BUILD_SYSTEMS_TESTS += clang_compilation_db cmake inferconfig
endif endif
ifneq ($(NDKBUILD),no) ifneq ($(NDKBUILD),no)
BUILD_SYSTEMS_TESTS += ndk_build BUILD_SYSTEMS_TESTS += ndk_build

@ -217,11 +217,11 @@ let patterns_of_json_with_key (json_key, json) =
| "Java" -> | "Java" ->
Ok Config.Java Ok Config.Java
| l -> | l ->
Error ("Inferconfig JSON key " ^ json_key ^ " not supported for language " ^ l) in Error ("JSON key " ^ json_key ^ " not supported for language " ^ l) in
let rec detect_language = function let rec detect_language = function
| [] -> | [] ->
Error ("No language found for " ^ json_key ^ " in " ^ Config.inferconfig_file) Error ("No language found for " ^ json_key)
| ("language", `String s) :: _ -> | ("language", `String s) :: _ ->
language_of_string s language_of_string s
| _:: tl -> | _:: tl ->
@ -237,7 +237,7 @@ let patterns_of_json_with_key (json_key, json) =
and is_source_contains key = List.exists ~f:(String.equal key) ["source_contains"] in and is_source_contains key = List.exists ~f:(String.equal key) ["source_contains"] in
let rec loop = function let rec loop = function
| [] -> | [] ->
Error ("Unknown pattern for " ^ json_key ^ " in " ^ Config.inferconfig_file) Error ("Unknown pattern for " ^ json_key)
| (key, _) :: _ when is_method_pattern key -> | (key, _) :: _ when is_method_pattern key ->
Ok (Method_pattern (language, default_method_pattern)) Ok (Method_pattern (language, default_method_pattern))
| (key, _) :: _ when is_source_contains key -> | (key, _) :: _ when is_source_contains key ->
@ -280,8 +280,7 @@ let patterns_of_json_with_key (json_key, json) =
error in error in
let warn_user msg = let warn_user msg =
F.eprintf "WARNING: in file %s: error parsing option %s@\n%s@." CLOpt.warnf "WARNING: error parsing option %s@\n%s@." json_key msg in
Config.inferconfig_file json_key msg in
(* Translate all the JSON entries into matching patterns *) (* Translate all the JSON entries into matching patterns *)
let rec translate accu = function let rec translate accu = function
@ -358,7 +357,8 @@ let is_checker_enabled checker_name =
| false, true -> (* if it is not blacklisted and it is whitelisted then it should be enabled *) | false, true -> (* if it is not blacklisted and it is whitelisted then it should be enabled *)
true true
| true, true -> (* if it's both blacklisted and whitelisted then we flag error *) | true, true -> (* if it's both blacklisted and whitelisted then we flag error *)
failwith ("Inconsistent setting in .inferconfig: checker" ^ checker_name ^ " is both blacklisted and whitelisted.") failwithf "Inconsistent settings: checker %s is both blacklisted and whitelisted."
checker_name
(* This function loads and list the path that are being filtered by the analyzer. The results *) (* This function loads and list the path that are being filtered by the analyzer. The results *)
(* are of the form: path/to/file.java -> {infer, eradicate} meaning that analysis results will *) (* are of the form: path/to/file.java -> {infer, eradicate} meaning that analysis results will *)

@ -16,6 +16,27 @@ module YBU = Yojson.Basic.Util
let (=) = String.equal let (=) = String.equal
let is_env_var_set v =
Option.value (Option.map (Sys.getenv v) ~f:((=) "1")) ~default:false
(** The working directory of the initial invocation of infer, to which paths passed as command line
options are relative. *)
let init_work_dir, is_originator =
match Sys.getenv "INFER_CWD" with
| Some dir ->
(dir, false)
| None ->
let real_cwd = Utils.realpath (Sys.getcwd ()) in
Unix.putenv ~key:"INFER_CWD" ~data:real_cwd;
(real_cwd, true)
let strict_mode = is_env_var_set "INFER_STRICT_MODE"
let warnf =
if strict_mode then failwithf
else if not is_originator then fun fmt -> F.ifprintf F.err_formatter fmt
else F.eprintf
let terminal_width = lazy ( let terminal_width = lazy (
let open Ctypes in let open Ctypes in
let module T = IOCtl.Types(IOCtl_types) in let module T = IOCtl.Types(IOCtl_types) in
@ -47,27 +68,6 @@ let to_arg_spec = function
let to_arg_spec_triple (x, spec, y) = (x, to_arg_spec spec, y) let to_arg_spec_triple (x, spec, y) = (x, to_arg_spec spec, y)
let to_arg_speclist = List.map ~f:to_arg_spec_triple let to_arg_speclist = List.map ~f:to_arg_spec_triple
let is_env_var_set v =
Option.value (Option.map (Sys.getenv v) ~f:((=) "1")) ~default:false
(** The working directory of the initial invocation of infer, to which paths passed as command line
options are relative. *)
let init_work_dir, is_originator =
match Sys.getenv "INFER_CWD" with
| Some dir ->
(dir, false)
| None ->
let real_cwd = Utils.realpath (Sys.getcwd ()) in
Unix.putenv ~key:"INFER_CWD" ~data:real_cwd;
(real_cwd, true)
let strict_mode = is_env_var_set "INFER_STRICT_MODE"
let warnf =
if strict_mode then failwithf
else if not is_originator then fun fmt -> F.ifprintf F.err_formatter fmt
else F.eprintf
type section = type section =
Analysis | BufferOverrun | Checkers | Clang | Crashcontext | Driver | Java | Print | Quandary Analysis | BufferOverrun | Checkers | Clang | Crashcontext | Driver | Java | Print | Quandary
[@@deriving compare] [@@deriving compare]
@ -108,7 +108,7 @@ let accept_unknown_args = function
type desc = { type desc = {
long: string; short: string; meta: string; doc: string; spec: spec; long: string; short: string; meta: string; doc: string; spec: spec;
(** how to go from an option in the json config file to a list of command-line options *) (** how to go from an option in the json config file to a list of command-line options *)
decode_json: Yojson.Basic.json -> string list ; decode_json: inferconfig_dir:string -> Yojson.Basic.json -> string list ;
} }
let dashdash long = let dashdash long =
@ -288,9 +288,9 @@ let deprecate_desc parse_mode ~long ~short ~deprecated desc =
| String f -> String (warn_then_f f) | String f -> String (warn_then_f f)
| Symbol (symbols, f) -> Symbol (symbols, warn_then_f f) | Symbol (symbols, f) -> Symbol (symbols, warn_then_f f)
| Rest _ as spec -> spec in | Rest _ as spec -> spec in
let deprecated_decode_json j = let deprecated_decode_json ~inferconfig_dir j =
warnf "WARNING: in .inferconfig: '%s' is deprecated. Use '%s' instead.@." deprecated long; warnf "WARNING: in .inferconfig: '%s' is deprecated. Use '%s' instead.@." deprecated long;
desc.decode_json j in desc.decode_json ~inferconfig_dir j in
{ long = ""; short = deprecated; meta = ""; doc = ""; { long = ""; short = deprecated; meta = ""; doc = "";
spec = deprecated_spec; decode_json = deprecated_decode_json } spec = deprecated_spec; decode_json = deprecated_decode_json }
@ -356,9 +356,18 @@ type 'a t =
?parse_mode:parse_mode -> ?meta:string -> Arg.doc -> ?parse_mode:parse_mode -> ?meta:string -> Arg.doc ->
'a 'a
let string_json_decoder ~long json = [dashdash long; YBU.to_string json] let string_json_decoder ~long ~inferconfig_dir:_ json =
[dashdash long; YBU.to_string json]
let list_json_decoder json_decoder json = List.concat (YBU.convert_each json_decoder json) let path_json_decoder ~long ~inferconfig_dir json =
let abs_path =
let path = YBU.to_string json in
if Filename.is_relative path then inferconfig_dir ^/ path
else path in
[dashdash long; abs_path]
let list_json_decoder json_decoder ~inferconfig_dir json =
List.concat (YBU.convert_each (json_decoder ~inferconfig_dir) json)
let mk_set var value ?(deprecated=[]) ~long ?short ?parse_mode ?(meta="") doc = let mk_set var value ?(deprecated=[]) ~long ?short ?parse_mode ?(meta="") doc =
let setter () = var := value in let setter () = var := value in
@ -406,13 +415,13 @@ let mk_bool ?(deprecated_no=[]) ?(default=false) ?(f=fun b -> b)
let var = let var =
mk ~long ?short ~deprecated ~default ?parse_mode mk ~long ?short ~deprecated ~default ?parse_mode
~meta doc ~default_to_string ~mk_setter:(fun var _ -> var := f true) ~meta doc ~default_to_string ~mk_setter:(fun var _ -> var := f true)
~decode_json:(fun json -> ~decode_json:(fun ~inferconfig_dir:_ json ->
[dashdash (if YBU.to_bool json then long else nolong)]) [dashdash (if YBU.to_bool json then long else nolong)])
~mk_spec in ~mk_spec in
ignore( ignore(
mk ~long:nolong ?short:noshort ~deprecated:deprecated_no ~default:(not default) ?parse_mode mk ~long:nolong ?short:noshort ~deprecated:deprecated_no ~default:(not default) ?parse_mode
~meta nodoc ~default_to_string ~mk_setter:(fun _ _ -> var := f false) ~meta nodoc ~default_to_string ~mk_setter:(fun _ _ -> var := f false)
~decode_json:(fun json -> ~decode_json:(fun ~inferconfig_dir:_ json ->
[dashdash (if YBU.to_bool json then nolong else long)]) [dashdash (if YBU.to_bool json then nolong else long)])
~mk_spec ); ~mk_spec );
var var
@ -495,21 +504,21 @@ let mk_path_helper ~setter ~default_to_string
let mk_path ~default ?(deprecated=[]) ~long ?short ?parse_mode ?(meta="path") = let mk_path ~default ?(deprecated=[]) ~long ?short ?parse_mode ?(meta="path") =
mk_path_helper mk_path_helper
~setter:(fun var x -> var := x) ~setter:(fun var x -> var := x)
~decode_json:(string_json_decoder ~long) ~decode_json:(path_json_decoder ~long)
~default_to_string:(fun s -> s) ~default_to_string:(fun s -> s)
~default ~deprecated ~long ~short ~parse_mode ~meta ~default ~deprecated ~long ~short ~parse_mode ~meta
let mk_path_opt ?default ?(deprecated=[]) ~long ?short ?parse_mode ?(meta="path") = let mk_path_opt ?default ?(deprecated=[]) ~long ?short ?parse_mode ?(meta="path") =
mk_path_helper mk_path_helper
~setter:(fun var x -> var := Some x) ~setter:(fun var x -> var := Some x)
~decode_json:(string_json_decoder ~long) ~decode_json:(path_json_decoder ~long)
~default_to_string:(function Some s -> s | None -> "") ~default_to_string:(function Some s -> s | None -> "")
~default ~deprecated ~long ~short ~parse_mode ~meta ~default ~deprecated ~long ~short ~parse_mode ~meta
let mk_path_list ?(default=[]) ?(deprecated=[]) ~long ?short ?parse_mode ?(meta="path") = let mk_path_list ?(default=[]) ?(deprecated=[]) ~long ?short ?parse_mode ?(meta="path") =
mk_path_helper mk_path_helper
~setter:(fun var x -> var := x :: !var) ~setter:(fun var x -> var := x :: !var)
~decode_json:(list_json_decoder (string_json_decoder ~long)) ~decode_json:(list_json_decoder (path_json_decoder ~long))
~default_to_string:(String.concat ~sep:", ") ~default_to_string:(String.concat ~sep:", ")
~default ~deprecated ~long ~short ~parse_mode ~meta ~default ~deprecated ~long ~short ~parse_mode ~meta
@ -542,7 +551,7 @@ let mk_symbol_seq ?(default=[]) ~symbols ~eq ?(deprecated=[]) ~long ?short ?pars
~default_to_string:(fun syms -> String.concat ~sep:" " (List.map ~f:to_string syms)) ~default_to_string:(fun syms -> String.concat ~sep:" " (List.map ~f:to_string syms))
~mk_setter:(fun var str_seq -> ~mk_setter:(fun var str_seq ->
var := List.map ~f:of_string (Str.split (Str.regexp_string ",") str_seq)) var := List.map ~f:of_string (Str.split (Str.regexp_string ",") str_seq))
~decode_json:(fun json -> ~decode_json:(fun ~inferconfig_dir:_ json ->
[dashdash long; [dashdash long;
String.concat ~sep:"," (YBU.convert_each YBU.to_string json)]) String.concat ~sep:"," (YBU.convert_each YBU.to_string json)])
~mk_spec:(fun set -> String set) ~mk_spec:(fun set -> String set)
@ -552,14 +561,14 @@ let mk_set_from_json ~default ~default_to_string ~f
mk ~deprecated ~long ?short ?parse_mode ~meta doc mk ~deprecated ~long ?short ?parse_mode ~meta doc
~default ~default_to_string ~default ~default_to_string
~mk_setter:(fun var json -> var := f (Yojson.Basic.from_string json)) ~mk_setter:(fun var json -> var := f (Yojson.Basic.from_string json))
~decode_json:(fun json -> [dashdash long; Yojson.Basic.to_string json]) ~decode_json:(fun ~inferconfig_dir:_ json -> [dashdash long; Yojson.Basic.to_string json])
~mk_spec:(fun set -> String set) ~mk_spec:(fun set -> String set)
let mk_json ?(deprecated=[]) ~long ?short ?parse_mode ?(meta="json") doc = let mk_json ?(deprecated=[]) ~long ?short ?parse_mode ?(meta="json") doc =
mk ~deprecated ~long ?short ?parse_mode ~meta doc mk ~deprecated ~long ?short ?parse_mode ~meta doc
~default:(`List []) ~default_to_string:Yojson.Basic.to_string ~default:(`List []) ~default_to_string:Yojson.Basic.to_string
~mk_setter:(fun var json -> var := Yojson.Basic.from_string json) ~mk_setter:(fun var json -> var := Yojson.Basic.from_string json)
~decode_json:(fun json -> [dashdash long; Yojson.Basic.to_string json]) ~decode_json:(fun ~inferconfig_dir:_ json -> [dashdash long; Yojson.Basic.to_string json])
~mk_spec:(fun set -> String set) ~mk_spec:(fun set -> String set)
(** [mk_anon] always return the same ref. Anonymous arguments are only accepted if (** [mk_anon] always return the same ref. Anonymous arguments are only accepted if
@ -569,10 +578,11 @@ let mk_anon () = rev_anon_args
let mk_rest ?(parse_mode=Infer []) doc = let mk_rest ?(parse_mode=Infer []) doc =
let rest = ref [] in let rest = ref [] in
let spec = Rest (fun arg -> rest := arg :: !rest) in let spec = Rest (fun arg -> rest := arg :: !rest) in
add parse_mode {long = "--"; short = ""; meta = ""; doc; spec; decode_json = fun _ -> []} ; add parse_mode {long = "--"; short = ""; meta = ""; doc; spec;
decode_json = fun ~inferconfig_dir:_ _ -> []} ;
rest rest
let set_curr_speclist_for_parse_action ~incomplete ~usage ?(parse_all=false) parse_action = let set_curr_speclist_for_parse_action ~usage ?(parse_all=false) parse_action =
let full_speclist = ref [] in let full_speclist = ref [] in
let curr_usage status = let curr_usage status =
@ -589,23 +599,19 @@ let set_curr_speclist_for_parse_action ~incomplete ~usage ?(parse_all=false) par
let unknown opt = let unknown opt =
(opt, Unit (fun () -> raise (Arg.Bad ("unknown option '" ^ opt ^ "'"))), "") in (opt, Unit (fun () -> raise (Arg.Bad ("unknown option '" ^ opt ^ "'"))), "") in
let mk_spec ~long ?(short="") spec doc = let mk_spec ~long ?(short="") spec doc =
pad_and_xform doc_width left_width { long; short; meta=""; spec; doc; pad_and_xform doc_width left_width {
decode_json=fun _ -> raise (Arg.Bad long)} in long; short; meta=""; spec; doc;
if incomplete then decode_json=fun ~inferconfig_dir:_ _ -> raise (Arg.Bad long);
speclist @ [ } in
(unknown "--help") ; speclist @ [
(unknown "-help") mk_spec ~long:"help" ~short:"h"
] (Unit (fun () -> curr_usage 0))
else "Display this list of options";
speclist @ [ mk_spec ~long:"help-full"
mk_spec ~long:"help" ~short:"h" (Unit (fun () -> full_usage 0))
(Unit (fun () -> curr_usage 0)) "Display the full list of options, including internal and experimental options";
"Display this list of options"; (unknown "-help")
mk_spec ~long:"help-full" ]
(Unit (fun () -> full_usage 0))
"Display the full list of options, including internal and experimental options";
(unknown "-help")
]
in in
let normalize speclist = let normalize speclist =
let norm k = let norm k =
@ -680,8 +686,8 @@ let set_curr_speclist_for_parse_action ~incomplete ~usage ?(parse_all=false) par
curr_usage curr_usage
let select_parse_action ~incomplete ~usage ?parse_all action = let select_parse_action ~usage ?parse_all action =
let usage = set_curr_speclist_for_parse_action ~incomplete ~usage ?parse_all action in let usage = set_curr_speclist_for_parse_action ~usage ?parse_all action in
unknown_args_action := if accept_unknown_args action then `Add else `Reject; unknown_args_action := if accept_unknown_args action then `Add else `Reject;
final_parse_action := action; final_parse_action := action;
usage usage
@ -690,16 +696,17 @@ let mk_rest_actions ?(parse_mode=Infer []) doc ~usage decode_action =
let rest = ref [] in let rest = ref [] in
let spec = String (fun arg -> let spec = String (fun arg ->
rest := List.rev (Array.to_list (Array.slice !args_to_parse (!arg_being_parsed + 1) 0)) ; rest := List.rev (Array.to_list (Array.slice !args_to_parse (!arg_being_parsed + 1) 0)) ;
select_parse_action ~incomplete:false ~usage (decode_action arg) |> ignore; select_parse_action ~usage (decode_action arg) |> ignore;
(* stop accepting new anonymous arguments *) (* stop accepting new anonymous arguments *)
unknown_args_action := `Skip) in unknown_args_action := `Skip) in
add parse_mode {long = "--"; short = ""; meta = ""; doc; spec; decode_json = fun _ -> []} ; add parse_mode {long = "--"; short = ""; meta = ""; doc; spec;
decode_json = fun ~inferconfig_dir:_ _ -> []} ;
rest rest
let mk_switch_parse_action let mk_switch_parse_action
parse_action ~usage ?(deprecated=[]) ~long ?short ?parse_mode ?(meta="") doc = parse_action ~usage ?(deprecated=[]) ~long ?short ?parse_mode ?(meta="") doc =
let switch () = let switch () =
select_parse_action ~incomplete:false ~usage parse_action |> ignore in select_parse_action ~usage parse_action |> ignore in
ignore( ignore(
mk ~deprecated ~long ?short ~default:() ?parse_mode ~meta doc mk ~deprecated ~long ?short ~default:() ?parse_mode ~meta doc
~default_to_string:(fun () -> "") ~default_to_string:(fun () -> "")
@ -708,7 +715,7 @@ let mk_switch_parse_action
~mk_spec:(fun _ -> Unit switch)) ~mk_spec:(fun _ -> Unit switch))
let decode_inferconfig_to_argv path = let decode_inferconfig_to_argv path =
let json = match Utils.read_optional_json_file path with let json = match Utils.read_json_file path with
| Ok json -> | Ok json ->
json json
| Error msg -> | Error msg ->
@ -716,6 +723,7 @@ let decode_inferconfig_to_argv path =
`Assoc [] in `Assoc [] in
let desc_list = List.Assoc.find_exn ~equal:equal_parse_tag parse_tag_desc_lists AllInferTags in let desc_list = List.Assoc.find_exn ~equal:equal_parse_tag parse_tag_desc_lists AllInferTags in
let json_config = YBU.to_assoc json in let json_config = YBU.to_assoc json in
let inferconfig_dir = Filename.dirname path in
let one_config_item result (key, json_val) = let one_config_item result (key, json_val) =
try try
let {decode_json} = let {decode_json} =
@ -724,7 +732,7 @@ let decode_inferconfig_to_argv path =
String.equal key long String.equal key long
|| (* for deprecated options *) String.equal key short) || (* for deprecated options *) String.equal key short)
!desc_list in !desc_list in
decode_json json_val @ result decode_json ~inferconfig_dir json_val @ result
with with
| Not_found -> | Not_found ->
warnf "WARNING: while reading config file %s:@\nUnknown option %s@." path key ; warnf "WARNING: while reading config file %s:@\nUnknown option %s@." path key ;
@ -769,11 +777,11 @@ let extra_env_args = ref []
let extend_env_args args = let extend_env_args args =
extra_env_args := List.rev_append args !extra_env_args extra_env_args := List.rev_append args !extra_env_args
let parse_args ~incomplete ~usage ?parse_all action args = let parse_args ~usage ?parse_all action args =
let exe_name = Sys.executable_name in let exe_name = Sys.executable_name in
args_to_parse := Array.of_list (exe_name :: args); args_to_parse := Array.of_list (exe_name :: args);
arg_being_parsed := 0; arg_being_parsed := 0;
let curr_usage = select_parse_action ~incomplete ~usage ?parse_all action in let curr_usage = select_parse_action ~usage ?parse_all action in
(* tests if msg indicates an unknown option, as opposed to a known option with bad argument *) (* tests if msg indicates an unknown option, as opposed to a known option with bad argument *)
let is_unknown msg = String.is_substring msg ~substring:": unknown option" in let is_unknown msg = String.is_substring msg ~substring:": unknown option" in
let rec parse_loop () = let rec parse_loop () =
@ -781,7 +789,6 @@ let parse_args ~incomplete ~usage ?parse_all action args =
Arg.parse_argv_dynamic ~current:arg_being_parsed !args_to_parse curr_speclist Arg.parse_argv_dynamic ~current:arg_being_parsed !args_to_parse curr_speclist
anon_fun usage anon_fun usage
with with
| Arg.Bad _ when incomplete -> parse_loop ()
| Arg.Bad usage_msg -> | Arg.Bad usage_msg ->
if !unknown_args_action <> `Reject && is_unknown usage_msg then ( if !unknown_args_action <> `Reject && is_unknown usage_msg then (
anon_fun !args_to_parse.(!arg_being_parsed); anon_fun !args_to_parse.(!arg_being_parsed);
@ -795,7 +802,7 @@ let parse_args ~incomplete ~usage ?parse_all action args =
parse_loop (); parse_loop ();
curr_usage curr_usage
let parse ?(incomplete=false) ?config_file ~usage action = let parse ?config_file ~usage action =
let env_args = decode_env_to_argv (Option.value (Sys.getenv args_env_var) ~default:"") in let env_args = decode_env_to_argv (Option.value (Sys.getenv args_env_var) ~default:"") in
let inferconfig_args = let inferconfig_args =
Option.map ~f:decode_inferconfig_to_argv config_file |> Option.value ~default:[] in Option.map ~f:decode_inferconfig_to_argv config_file |> Option.value ~default:[] in
@ -814,15 +821,15 @@ let parse ?(incomplete=false) ?config_file ~usage action =
else !args_to_export ^ String.of_char env_var_sep ^ encode_argv_to_env args in else !args_to_export ^ String.of_char env_var_sep ^ encode_argv_to_env args in
args_to_export := arg_string in args_to_export := arg_string in
(* read .inferconfig first, then env vars, then command-line options *) (* read .inferconfig first, then env vars, then command-line options *)
parse_args ~incomplete ~usage ~parse_all:true (Infer Driver) inferconfig_args |> ignore; parse_args ~usage ~parse_all:true (Infer Driver) inferconfig_args |> ignore;
(* NOTE: do not add the contents of .inferconfig to INFER_ARGS. This helps avoid hitting the (* NOTE: do not add the contents of .inferconfig to INFER_ARGS. This helps avoid hitting the
command line size limit. *) command line size limit. *)
parse_args ~incomplete ~usage ~parse_all:true (Infer Driver) env_args |> ignore; parse_args ~usage ~parse_all:true (Infer Driver) env_args |> ignore;
if not incomplete then add_parsed_args_to_args_to_export (); add_parsed_args_to_args_to_export ();
let curr_usage = let curr_usage =
let cl_args = match Array.to_list Sys.argv with _ :: tl -> tl | [] -> [] in let cl_args = match Array.to_list Sys.argv with _ :: tl -> tl | [] -> [] in
let curr_usage = parse_args ~incomplete ~usage action cl_args in let curr_usage = parse_args ~usage action cl_args in
if not incomplete then add_parsed_args_to_args_to_export (); add_parsed_args_to_args_to_export ();
curr_usage in curr_usage in
if not incomplete then Unix.putenv ~key:args_env_var ~data:!args_to_export; Unix.putenv ~key:args_env_var ~data:!args_to_export;
!final_parse_action, curr_usage !final_parse_action, curr_usage

@ -11,6 +11,9 @@
open! IStd open! IStd
(** Print to stderr in case of error, fails in strict mode *)
val warnf : ('a, Format.formatter, unit) format -> 'a
(** a section is a part of infer that can be affected by an infer option *) (** a section is a part of infer that can be affected by an infer option *)
type section = type section =
Analysis | BufferOverrun | Checkers | Clang | Crashcontext | Driver | Java | Print | Quandary Analysis | BufferOverrun | Checkers | Clang | Crashcontext | Driver | Java | Print | Quandary
@ -164,13 +167,10 @@ val extend_env_args : string list -> unit
specified in the environment variable, which themselves supersede those passed via the config specified in the environment variable, which themselves supersede those passed via the config
file. file.
If [incomplete] is set, unknown options are ignored, and [args_env_var] is not set.
WARNING: An argument will be interpreted as many times as it appears in all of the config file, WARNING: An argument will be interpreted as many times as it appears in all of the config file,
the environment variable, and the command line. The [args_env_var] is set to the set of options the environment variable, and the command line. The [args_env_var] is set to the set of options
parsed in [args_env_var] and on the command line. *) parsed in [args_env_var] and on the command line. *)
val parse : ?incomplete:bool -> ?config_file:string -> val parse : ?config_file:string -> usage:Arg.usage_msg -> parse_action -> parse_action * (int -> 'a)
usage:Arg.usage_msg -> parse_action -> parse_action * (int -> 'a)
(** [is_env_var_set var] is true if $[var]=1 *) (** [is_env_var_set var] is true if $[var]=1 *)
val is_env_var_set : string -> bool val is_env_var_set : string -> bool

@ -163,9 +163,6 @@ let incremental_procs = true
(** Our Python script does its own argument parsing and will fail with this error on failure *) (** Our Python script does its own argument parsing and will fail with this error on failure *)
let infer_py_argparse_error_exit_code = 22 let infer_py_argparse_error_exit_code = 22
(** Name of the infer configuration file *)
let inferconfig_file = ".inferconfig"
let ivar_attributes = "ivar_attributes" let ivar_attributes = "ivar_attributes"
let lint_dotty_dir_name = "lint_dotty" let lint_dotty_dir_name = "lint_dotty"
@ -375,34 +372,6 @@ let exe_usage = match current_exe with
(** Command Line options *) (** Command Line options *)
(* Declare the phase 1 options *)
let inferconfig_home =
CLOpt.mk_path_opt ~long:"inferconfig-home"
~parse_mode:CLOpt.(Infer all_sections) ~meta:"dir" "Path to the .inferconfig file"
and project_root =
CLOpt.mk_path ~deprecated:["project_root"; "-project_root"; "pr"] ~long:"project-root" ~short:'C'
~default:CLOpt.init_work_dir
~parse_mode:CLOpt.(Infer [Analysis;Clang;Driver;Print])
~meta:"dir" "Specify the root directory of the project"
(* Parse the phase 1 options, ignoring the rest *)
let _ : CLOpt.parse_action * (int -> 'a) = CLOpt.parse ~incomplete:true startup_action ~usage:""
(* Define the values that depend on phase 1 options *)
let inferconfig_home = !inferconfig_home
and project_root = !project_root
let inferconfig_path =
match inferconfig_home with
| Some dir -> dir ^/ inferconfig_file
| None -> project_root ^/ inferconfig_file
(* Proceed to declare and parse the remaining options *)
(* HOWTO define a new command line and config file option. (* HOWTO define a new command line and config file option.
1. Add an entry in the following let...and...and... binding. See the documentation in 1. Add an entry in the following let...and...and... binding. See the documentation in
@ -1171,6 +1140,12 @@ and progress_bar =
~parse_mode:CLOpt.(Infer [Driver]) ~parse_mode:CLOpt.(Infer [Driver])
"Show a progress bar" "Show a progress bar"
and project_root =
CLOpt.mk_path ~deprecated:["project_root"; "-project_root"; "pr"] ~long:"project-root" ~short:'C'
~default:CLOpt.init_work_dir
~parse_mode:CLOpt.(Infer [Analysis;Clang;Driver;Print])
~meta:"dir" "Specify the root directory of the project"
and quandary_sources = and quandary_sources =
CLOpt.mk_json ~long:"quandary-sources" CLOpt.mk_json ~long:"quandary-sources"
~parse_mode:CLOpt.(Infer [Quandary]) ~parse_mode:CLOpt.(Infer [Quandary])
@ -1570,9 +1545,37 @@ let post_parsing_initialization () =
| Some (Capture | Compile | Infer | Linters) | None -> () | Some (Capture | Compile | Infer | Linters) | None -> ()
let inferconfig_env_var = "INFERCONFIG"
(** Name of the infer configuration file *)
let inferconfig_file = ".inferconfig"
let inferconfig_path () =
let rec find dir =
match Sys.file_exists ~follow_symlinks:false (dir ^/ inferconfig_file) with
| `Yes ->
Some dir
| `No | `Unknown ->
let parent = Filename.dirname dir in
let is_root = String.equal dir parent in
if is_root then None
else find parent in
match Sys.getenv "INFERCONFIG" with
| Some env_path ->
(* make sure the path makes sense in children infer processes *)
Some (
if Filename.is_relative env_path then
Utils.filename_to_absolute ~root:CLOpt.init_work_dir env_path
else
env_path
)
| None ->
find (Sys.getcwd ())
|> Option.map ~f:(fun dir -> dir ^/ inferconfig_file)
let parse_action, parse_args_and_return_usage_exit = let parse_action, parse_args_and_return_usage_exit =
let parse_action, usage_exit = let config_file = inferconfig_path () in
CLOpt.parse ~config_file:inferconfig_path ~usage:exe_usage startup_action in let parse_action, usage_exit = CLOpt.parse ?config_file ~usage:exe_usage startup_action in
post_parsing_initialization () ; post_parsing_initialization () ;
parse_action, usage_exit parse_action, usage_exit
@ -1708,6 +1711,7 @@ and print_using_diff = !print_using_diff
and procedures_per_process = !procedures_per_process and procedures_per_process = !procedures_per_process
and procs_csv = !procs_csv and procs_csv = !procs_csv
and procs_xml = !procs_xml and procs_xml = !procs_xml
and project_root = !project_root
and quandary = !quandary and quandary = !quandary
and quandary_sources = !quandary_sources and quandary_sources = !quandary_sources
and quandary_sinks = !quandary_sinks and quandary_sinks = !quandary_sinks

@ -101,7 +101,6 @@ val global_tenv_filename : string
val idempotent_getters : bool val idempotent_getters : bool
val incremental_procs : bool val incremental_procs : bool
val infer_py_argparse_error_exit_code : int val infer_py_argparse_error_exit_code : int
val inferconfig_file : string
val initial_analysis_time : float val initial_analysis_time : float
val ivar_attributes : string val ivar_attributes : string
val lib_dir : string val lib_dir : string

@ -192,13 +192,11 @@ let dir_is_empty path =
let string_crc_hex32 s = Digest.to_hex (Digest.string s) let string_crc_hex32 s = Digest.to_hex (Digest.string s)
let read_optional_json_file path = let read_json_file path =
if Sys.file_exists path = `Yes then try
try Ok (Yojson.Basic.from_file path)
Ok (Yojson.Basic.from_file path) with Sys_error msg | Yojson.Json_error msg ->
with Sys_error msg | Yojson.Json_error msg -> Error msg
Error msg
else Ok (`Assoc [])
let do_finally f g = let do_finally f g =
let res = try f () with exc -> g () |> ignore; raise exc in let res = try f () with exc -> g () |> ignore; raise exc in

@ -55,7 +55,7 @@ val directory_iter : (string -> unit) -> string -> unit
(** Returns true if a given directory is empty. The directory is assumed to exist. *) (** Returns true if a given directory is empty. The directory is assumed to exist. *)
val dir_is_empty : string -> bool val dir_is_empty : string -> bool
val read_optional_json_file : string -> (Yojson.Basic.json, string) Result.t val read_json_file : string -> (Yojson.Basic.json, string) Result.t
val with_file_in : string -> f:(In_channel.t -> 'a) -> 'a val with_file_in : string -> f:(In_channel.t -> 'a) -> 'a
val with_file_out : string -> f:(Out_channel.t -> 'a) -> 'a val with_file_out : string -> f:(Out_channel.t -> 'a) -> 'a

@ -20,7 +20,7 @@ $(OBJECTS): $(SOURCES)
infer-out/report.json: $(JAVA_DEPS) $(SOURCES) infer-out/report.json: $(JAVA_DEPS) $(SOURCES)
$(QUIET)$(call silent_on_success,Testing ant integration,\ $(QUIET)$(call silent_on_success,Testing ant integration,\
$(INFER_BIN) -a $(ANALYZER) --project-root $(TESTS_DIR) --inferconfig-home . -- $(ANT)) $(INFER_BIN) -a $(ANALYZER) --project-root $(TESTS_DIR) -- $(ANT))
clean: clean:
$(ANT) clean $(ANT) clean

@ -0,0 +1,35 @@
# Copyright (c) 2017 - present Facebook, Inc.
# All rights reserved.
#
# This source code is licensed under the BSD style license found in the
# LICENSE file in the root directory of this source tree. An additional grant
# of patent rights can be found in the PATENTS file in the same directory.
TESTS_DIR = ../..
CMAKE_DIR = $(CURDIR)/../codetoanalyze/clang_compilation_database
CMAKE_BUILD_DIR = $(CURDIR)/_build_infer
ANALYZER = infer
CLEAN_EXTRA = $(CMAKE_BUILD_DIR)
INFER_OPTIONS = --report-custom-error --developer-mode --project-root $(CMAKE_DIR) --no-failures-allowed
SOURCES = $(CMAKE_DIR)/hello.cpp
OBJECTS = $(CMAKE_BUILD_DIR)/compile_commands.json
INFERPRINT_OPTIONS = --issues-tests
include $(TESTS_DIR)/infer.make
$(CMAKE_BUILD_DIR):
$(QUIET)$(MKDIR_P) $@
$(CMAKE_BUILD_DIR)/compile_commands.json: $(SOURCES) $(CMAKE_DIR)/CMakeLists.txt $(CMAKE_BUILD_DIR)
$(QUIET)cd $(CMAKE_BUILD_DIR) && \
$(call silent_on_success,Running cmake to generate Makefiles,\
$(CMAKE) -DCMAKE_EXPORT_COMPILE_COMMANDS=1 $(CMAKE_DIR))
infer-out/report.json: $(CMAKE_BUILD_DIR)/compile_commands.json $(CLANG_DEPS) $(SOURCES)
# test that config/infer.conf is read and paths inside it are interpreted relative to its
# location
$(QUIET)$(call silent_on_success,Testing Clang compilation database with index integration,\
INFERCONFIG=config/infer.conf $(INFER_BIN) -a $(ANALYZER) $(INFER_OPTIONS) -o $(@D) \
--compilation-database $<)

@ -0,0 +1,3 @@
{
"changed-files-index": "index.txt"
}

@ -0,0 +1,3 @@
hello.cpp, test0, 2, NULL_DEREFERENCE, [start of procedure test0()]
hello.cpp, test1, 2, NULL_DEREFERENCE, [start of procedure test1(),start of procedure deref1()]
lib3.h, test, 1, NULL_DEREFERENCE, [start of procedure test(),start of procedure deref3()]

@ -24,5 +24,5 @@ $(OBJECTS): $(SOURCES)
infer-out/report.json: $(JAVA_DEPS) $(SOURCES) infer-out/report.json: $(JAVA_DEPS) $(SOURCES)
$(QUIET)$(call silent_on_success,Testing infer/java$(ANALYZER_STRING) in $(TEST_REL_DIR),\ $(QUIET)$(call silent_on_success,Testing infer/java$(ANALYZER_STRING) in $(TEST_REL_DIR),\
$(INFER_BIN) -a $(ANALYZER) --inferconfig-home . --project-root $(PROJECT_ROOT) $(INFER_OPTIONS) -- \ $(INFER_BIN) -a $(ANALYZER) --project-root $(PROJECT_ROOT) $(INFER_OPTIONS) -- \
$(JAVAC) -cp $(CLASSPATH) $(SOURCES)) $(JAVAC) -cp $(CLASSPATH) $(SOURCES))

Loading…
Cancel
Save