[Infer][filtering] Adding support for filtering by procname using .inferconfig

Summary:
This is a prereq for being able to support @SuppressWarnings
annotations on Java methods and classes
master
Sam Blackshear 10 years ago
parent 09eb65f4e2
commit a9b6f33940

@ -24,20 +24,24 @@ let lookup_string_list key json =
type path_filter = DB.source_file -> bool type path_filter = DB.source_file -> bool
type error_filter = Localise.t -> bool type error_filter = Localise.t -> bool
type proc_filter = Procname.t -> bool
type filters = type filters =
{ {
path_filter : path_filter; path_filter : path_filter;
error_filter: error_filter; error_filter : error_filter;
proc_filter : proc_filter;
} }
let default_path_filter : path_filter = function path -> true let default_path_filter : path_filter = function path -> true
let default_error_filter : error_filter = function error_name -> true let default_error_filter : error_filter = function error_name -> true
let default_proc_filter : proc_filter = function proc_name -> true
let do_not_filter : filters = let do_not_filter : filters =
{ {
path_filter = default_path_filter; path_filter = default_path_filter;
error_filter = default_error_filter; error_filter = default_error_filter;
proc_filter = default_proc_filter;
} }
type filter_config = type filter_config =
@ -48,24 +52,6 @@ type filter_config =
suppress_errors: string list; suppress_errors: string list;
} }
let load_filters analyzer =
try
let json =
match !inferconfig_home with
| Some dir ->
Yojson.Basic.from_file (Filename.concat dir inferconfig_file)
| None -> Yojson.Basic.from_file inferconfig_file in
let inferconfig =
{
whitelist = lookup_string_list (analyzer ^ "_whitelist") json;
blacklist = lookup_string_list (analyzer ^ "_blacklist") json;
blacklist_files_containing =
lookup_string_list (analyzer ^ "_blacklist_files_containing") json;
suppress_errors = lookup_string_list (analyzer ^ "_suppress_errors") json;
} in
Some inferconfig
with Sys_error _ -> None
let is_matching patterns = let is_matching patterns =
fun source_file -> fun source_file ->
let path = DB.source_file_to_rel_path source_file in let path = DB.source_file_to_rel_path source_file in
@ -110,43 +96,12 @@ module FileContainsStringMatcher = struct
with Sys_error _ -> false with Sys_error _ -> false
end end
let filters_from_inferconfig inferconfig : filters = module type MATCHABLE_JSON = sig
let path_filter = val json_key : string
let whitelist_filter : path_filter = end
if inferconfig.whitelist = [] then default_path_filter
else is_matching (list_map Str.regexp inferconfig.whitelist) in
let blacklist_filter : path_filter =
is_matching (list_map Str.regexp inferconfig.blacklist) in
let blacklist_files_containing_filter : path_filter =
FileContainsStringMatcher.create_matcher inferconfig.blacklist_files_containing in
function source_file ->
whitelist_filter source_file &&
not (blacklist_filter source_file) &&
not (blacklist_files_containing_filter source_file) in
let error_filter =
function error_name ->
let error_str = Localise.to_string error_name in
not (list_exists (string_equal error_str) inferconfig.suppress_errors) in
{
path_filter = path_filter;
error_filter = error_filter;
}
(* Create filters based on .inferconfig.*)
(* The environment varialble NO_PATH_FILTERING disables path filtering. *)
let create_filters analyzer =
Config.project_root := Some (Sys.getcwd ());
if Config.from_env_variable "NO_PATH_FILTERING" then
do_not_filter
else
match load_filters (Utils.string_of_analyzer analyzer) with
| None -> do_not_filter
| Some inferconfig ->
filters_from_inferconfig inferconfig
module NeverReturnNull = struct
let never_returning_null_key = "never_returning_null" module FileOrProcMatcher = functor (M : MATCHABLE_JSON) ->
struct
type matcher = DB.source_file -> Procname.t -> bool type matcher = DB.source_file -> Procname.t -> bool
@ -173,8 +128,7 @@ module NeverReturnNull = struct
let language_of_string = function let language_of_string = function
| "Java" -> Config.Java | "Java" -> Config.Java
| "C_CPP" -> Config.C_CPP | l -> failwith ("Inferconfig JSON key " ^ M.json_key ^ " not supported for language " ^ l)
| _ -> failwith ("Unknown language found in " ^ inferconfig_file)
let pp_pattern fmt pattern = let pp_pattern fmt pattern =
let pp_string fmt s = let pp_string fmt s =
@ -206,7 +160,7 @@ module NeverReturnNull = struct
let rec loop = function let rec loop = function
| [] -> | [] ->
failwith failwith
("No language found for " ^ never_returning_null_key ^ " in " ^ inferconfig_file) ("No language found for " ^ M.json_key ^ " in " ^ inferconfig_file)
| (key, `String s) :: _ when key = "language" -> | (key, `String s) :: _ when key = "language" ->
language_of_string s language_of_string s
| _:: tl -> loop tl in | _:: tl -> loop tl in
@ -218,7 +172,7 @@ module NeverReturnNull = struct
and is_source_contains key = list_exists (string_equal key) ["source_contains"] in and is_source_contains key = list_exists (string_equal key) ["source_contains"] in
let rec loop = function let rec loop = function
| [] -> | [] ->
failwith ("Unknown pattern for " ^ never_returning_null_key ^ " in " ^ inferconfig_file) failwith ("Unknown pattern for " ^ M.json_key ^ " in " ^ inferconfig_file)
| (key, _) :: _ when is_method_pattern key -> | (key, _) :: _ when is_method_pattern key ->
Method_pattern (language, default_method_pattern) Method_pattern (language, default_method_pattern)
| (key, _) :: _ when is_source_contains key -> | (key, _) :: _ when is_source_contains key ->
@ -261,9 +215,7 @@ module NeverReturnNull = struct
| `List l -> list_fold_left translate accu l | `List l -> list_fold_left translate accu l
| _ -> assert false | _ -> assert false
let create_method_matcher language m_patterns = let create_method_matcher m_patterns =
if language <> Config.Java then assert false
else
if m_patterns = [] then if m_patterns = [] then
default_matcher default_matcher
else else
@ -290,34 +242,103 @@ module NeverReturnNull = struct
class_patterns class_patterns
with Not_found -> false with Not_found -> false
let create_file_matcher language patterns = let create_file_matcher patterns =
let s_patterns, m_patterns = let s_patterns, m_patterns =
let collect (s_patterns, m_patterns) = function let collect (s_patterns, m_patterns) = function
| Source_contains (lang, s) when lang = language -> (s:: s_patterns, m_patterns) | Source_contains (lang, s) -> (s:: s_patterns, m_patterns)
| Method_pattern (lang, mp) when lang = language -> | Method_pattern (lang, mp) -> (s_patterns, mp :: m_patterns) in
(s_patterns, mp :: m_patterns)
| _ -> (s_patterns, m_patterns) in
list_fold_left collect ([], []) patterns in list_fold_left collect ([], []) patterns in
let s_matcher = let s_matcher =
let matcher = FileContainsStringMatcher.create_matcher s_patterns in let matcher = FileContainsStringMatcher.create_matcher s_patterns in
fun source_file proc_name -> matcher source_file fun source_file proc_name -> matcher source_file
and m_matcher = create_method_matcher language m_patterns in and m_matcher = create_method_matcher m_patterns in
fun source_file proc_name -> fun source_file proc_name ->
m_matcher source_file proc_name || s_matcher source_file proc_name m_matcher source_file proc_name || s_matcher source_file proc_name
let load_matcher language = let load_matcher inferconfig =
try if Sys.file_exists inferconfig then
let patterns = try
let found = let patterns =
Yojson.Basic.Util.filter_member let found =
never_returning_null_key Yojson.Basic.Util.filter_member
[Yojson.Basic.from_file inferconfig_file] in M.json_key
list_fold_left translate [] found in [Yojson.Basic.from_file inferconfig] in
create_file_matcher language patterns list_fold_left translate [] found in
with Sys_error _ -> create_file_matcher patterns
default_matcher with Sys_error _ ->
default_matcher
else default_matcher
end (* of module FileOrProcMatcher *)
module NeverReturnNull = FileOrProcMatcher(struct
let json_key = "never_returning_null"
end)
module ProcMatcher = FileOrProcMatcher(struct
let json_key = "suppress_procedures"
end)
let inferconfig () = match !inferconfig_home with
| Some dir -> Filename.concat dir inferconfig_file
| None -> inferconfig_file
let load_filters analyzer =
let inferconfig_file = inferconfig () in
if Sys.file_exists inferconfig_file then
try
let json = Yojson.Basic.from_file inferconfig_file in
let inferconfig =
{
whitelist = lookup_string_list (analyzer ^ "_whitelist") json;
blacklist = lookup_string_list (analyzer ^ "_blacklist") json;
blacklist_files_containing =
lookup_string_list (analyzer ^ "_blacklist_files_containing") json;
suppress_errors = lookup_string_list (analyzer ^ "_suppress_errors") json;
} in
Some inferconfig
with Sys_error _ -> None
else None
let filters_from_inferconfig inferconfig : filters =
let path_filter =
let whitelist_filter : path_filter =
if inferconfig.whitelist = [] then default_path_filter
else is_matching (list_map Str.regexp inferconfig.whitelist) in
let blacklist_filter : path_filter =
is_matching (list_map Str.regexp inferconfig.blacklist) in
let blacklist_files_containing_filter : path_filter =
FileContainsStringMatcher.create_matcher inferconfig.blacklist_files_containing in
function source_file ->
whitelist_filter source_file &&
not (blacklist_filter source_file) &&
not (blacklist_files_containing_filter source_file) in
let error_filter =
function error_name ->
let error_str = Localise.to_string error_name in
not (list_exists (string_equal error_str) inferconfig.suppress_errors) in
let proc_filter =
let filter = match !local_config with
| Some f -> ProcMatcher.load_matcher f
| None -> ProcMatcher.default_matcher in
fun pname -> not (filter DB.source_file_empty pname) in
{
path_filter = path_filter;
error_filter = error_filter;
proc_filter = proc_filter;
}
end (* of module NeverReturnNull *) (* Create filters based on .inferconfig.*)
(* The environment varialble NO_PATH_FILTERING disables path filtering. *)
let create_filters analyzer =
Config.project_root := Some (Sys.getcwd ());
if Config.from_env_variable "NO_PATH_FILTERING" then
do_not_filter
else
match load_filters (Utils.string_of_analyzer analyzer) with
| None -> do_not_filter
| Some inferconfig ->
filters_from_inferconfig inferconfig
(* 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 *)

@ -7,20 +7,27 @@
* of patent rights can be found in the PATENTS file in the same directory. * of patent rights can be found in the PATENTS file in the same directory.
*) *)
(** Filter type for a source file *)
type path_filter = DB.source_file -> bool
val inferconfig_home : string option ref val inferconfig_home : string option ref
val local_config : string option ref val local_config : string option ref
(** get the path to the .inferconfig file *)
val inferconfig : unit -> string
(** Filter type for a source file *)
type path_filter = DB.source_file -> bool
(** Filter type for an error name. *) (** Filter type for an error name. *)
type error_filter = Localise.t -> bool type error_filter = Localise.t -> bool
(** Filter type for a procedure name *)
type proc_filter = Procname.t -> bool
type filters = type filters =
{ {
path_filter : path_filter; path_filter : path_filter;
error_filter: error_filter; error_filter : error_filter;
proc_filter : proc_filter;
} }
(** Filters that accept everything. *) (** Filters that accept everything. *)
@ -31,7 +38,7 @@ val create_filters : Utils.analyzer -> filters
module NeverReturnNull : sig module NeverReturnNull : sig
type matcher = DB.source_file -> Procname.t -> bool type matcher = DB.source_file -> Procname.t -> bool
val load_matcher : Config.language -> matcher val load_matcher : string -> matcher
end end
(** Load the config file and list the files to report on *) (** Load the config file and list the files to report on *)

@ -808,19 +808,18 @@ end
(** Process a summary *) (** Process a summary *)
let process_summary filters linereader stats (top_proc_set: Procname.Set.t) (fname, summary) = let process_summary filters linereader stats (top_proc_set: Procname.Set.t) (fname, summary) =
let proc_name = Specs.get_proc_name summary in let proc_name = Specs.get_proc_name summary in
let base = DB.chop_extension (DB.filename_from_string fname) in let base = DB.chop_extension (DB.filename_from_string fname) in
let pp_simple_saved = !Config.pp_simple in let pp_simple_saved = !Config.pp_simple in
Config.pp_simple := true; Config.pp_simple := true;
if !quiet then () (* L.err "File: %s@." fname *) if !quiet then ()
else L.out "Procedure: %a@\n%a@." Procname.pp proc_name (Specs.pp_summary pe_text !whole_seconds) summary; else L.out "Procedure: %a@\n%a@." Procname.pp proc_name (Specs.pp_summary pe_text !whole_seconds) summary;
let error_filter error_desc error_name = let error_filter error_desc error_name =
let always_report () = let always_report () =
Localise.error_desc_extract_tag_value error_desc "always_report" = "true" in Localise.error_desc_extract_tag_value error_desc "always_report" = "true" in
(filters.Inferconfig.path_filter summary.Specs.attributes.Sil.loc.Location.file (filters.Inferconfig.path_filter summary.Specs.attributes.Sil.loc.Location.file
|| always_report ()) && || always_report ()) &&
filters.Inferconfig.error_filter error_name in filters.Inferconfig.error_filter error_name && filters.Inferconfig.proc_filter proc_name in
do_outf procs_csv (fun outf -> F.fprintf outf.fmt "%a" (ProcsCsv.pp_summary fname top_proc_set) summary); do_outf procs_csv (fun outf -> F.fprintf outf.fmt "%a" (ProcsCsv.pp_summary fname top_proc_set) summary);
do_outf calls_csv (fun outf -> F.fprintf outf.fmt "%a" (CallsCsv.pp_calls fname) summary); do_outf calls_csv (fun outf -> F.fprintf outf.fmt "%a" (CallsCsv.pp_calls fname) summary);
do_outf procs_xml (fun outf -> ProcsXml.pp_proc top_proc_set outf.fmt summary); do_outf procs_xml (fun outf -> ProcsXml.pp_proc top_proc_set outf.fmt summary);

@ -146,7 +146,7 @@ let do_all_files classpath sources classes =
let program = JClasspath.load_program classpath classes sources in let program = JClasspath.load_program classpath classes sources in
let tenv = load_tenv program in let tenv = load_tenv program in
let linereader = Printer.LineReader.create () in let linereader = Printer.LineReader.create () in
let never_null_matcher = Inferconfig.NeverReturnNull.load_matcher Config.Java in let never_null_matcher = Inferconfig.NeverReturnNull.load_matcher (Inferconfig.inferconfig ()) in
let proc_file_map = let proc_file_map =
StringMap.fold StringMap.fold
(do_source_file never_null_matcher linereader classes program tenv) (do_source_file never_null_matcher linereader classes program tenv)

Loading…
Cancel
Save