diff --git a/infer/src/backend/inferconfig.ml b/infer/src/backend/inferconfig.ml index 9383714d7..8b9a9e86b 100644 --- a/infer/src/backend/inferconfig.ml +++ b/infer/src/backend/inferconfig.ml @@ -24,20 +24,24 @@ let lookup_string_list key json = type path_filter = DB.source_file -> bool type error_filter = Localise.t -> bool +type proc_filter = Procname.t -> bool type filters = { 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_error_filter : error_filter = function error_name -> true +let default_proc_filter : proc_filter = function proc_name -> true let do_not_filter : filters = { path_filter = default_path_filter; error_filter = default_error_filter; + proc_filter = default_proc_filter; } type filter_config = @@ -48,24 +52,6 @@ type filter_config = 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 = fun source_file -> let path = DB.source_file_to_rel_path source_file in @@ -110,43 +96,12 @@ module FileContainsStringMatcher = struct with Sys_error _ -> false end -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 - { - 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 +module type MATCHABLE_JSON = sig + val json_key : string +end - let never_returning_null_key = "never_returning_null" +module FileOrProcMatcher = functor (M : MATCHABLE_JSON) -> +struct type matcher = DB.source_file -> Procname.t -> bool @@ -173,8 +128,7 @@ module NeverReturnNull = struct let language_of_string = function | "Java" -> Config.Java - | "C_CPP" -> Config.C_CPP - | _ -> failwith ("Unknown language found in " ^ inferconfig_file) + | l -> failwith ("Inferconfig JSON key " ^ M.json_key ^ " not supported for language " ^ l) let pp_pattern fmt pattern = let pp_string fmt s = @@ -206,7 +160,7 @@ module NeverReturnNull = struct let rec loop = function | [] -> 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" -> language_of_string s | _:: tl -> loop tl in @@ -218,7 +172,7 @@ module NeverReturnNull = struct and is_source_contains key = list_exists (string_equal key) ["source_contains"] in 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 -> Method_pattern (language, default_method_pattern) | (key, _) :: _ when is_source_contains key -> @@ -261,9 +215,7 @@ module NeverReturnNull = struct | `List l -> list_fold_left translate accu l | _ -> assert false - let create_method_matcher language m_patterns = - if language <> Config.Java then assert false - else + let create_method_matcher m_patterns = if m_patterns = [] then default_matcher else @@ -290,34 +242,103 @@ module NeverReturnNull = struct class_patterns with Not_found -> false - let create_file_matcher language patterns = + let create_file_matcher patterns = let s_patterns, m_patterns = let collect (s_patterns, m_patterns) = function - | Source_contains (lang, s) when lang = language -> (s:: s_patterns, m_patterns) - | Method_pattern (lang, mp) when lang = language -> - (s_patterns, mp :: m_patterns) - | _ -> (s_patterns, m_patterns) in + | Source_contains (lang, s) -> (s:: s_patterns, m_patterns) + | Method_pattern (lang, mp) -> (s_patterns, mp :: m_patterns) in list_fold_left collect ([], []) patterns in let s_matcher = let matcher = FileContainsStringMatcher.create_matcher s_patterns in 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 -> m_matcher source_file proc_name || s_matcher source_file proc_name - let load_matcher language = - try - let patterns = - let found = - Yojson.Basic.Util.filter_member - never_returning_null_key - [Yojson.Basic.from_file inferconfig_file] in - list_fold_left translate [] found in - create_file_matcher language patterns - with Sys_error _ -> - default_matcher + let load_matcher inferconfig = + if Sys.file_exists inferconfig then + try + let patterns = + let found = + Yojson.Basic.Util.filter_member + M.json_key + [Yojson.Basic.from_file inferconfig] in + list_fold_left translate [] found in + create_file_matcher patterns + 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 *) (* are of the form: path/to/file.java -> {infer, eradicate} meaning that analysis results will *) diff --git a/infer/src/backend/inferconfig.mli b/infer/src/backend/inferconfig.mli index 5e440b128..29dd96b4f 100644 --- a/infer/src/backend/inferconfig.mli +++ b/infer/src/backend/inferconfig.mli @@ -7,20 +7,27 @@ * 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 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. *) type error_filter = Localise.t -> bool +(** Filter type for a procedure name *) +type proc_filter = Procname.t -> bool + type filters = { path_filter : path_filter; - error_filter: error_filter; + error_filter : error_filter; + proc_filter : proc_filter; } (** Filters that accept everything. *) @@ -31,7 +38,7 @@ val create_filters : Utils.analyzer -> filters module NeverReturnNull : sig type matcher = DB.source_file -> Procname.t -> bool - val load_matcher : Config.language -> matcher + val load_matcher : string -> matcher end (** Load the config file and list the files to report on *) diff --git a/infer/src/backend/inferprint.ml b/infer/src/backend/inferprint.ml index d8cf14799..1f2b45419 100644 --- a/infer/src/backend/inferprint.ml +++ b/infer/src/backend/inferprint.ml @@ -808,19 +808,18 @@ end (** Process a 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 base = DB.chop_extension (DB.filename_from_string fname) in let pp_simple_saved = !Config.pp_simple in 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; let error_filter error_desc error_name = let always_report () = Localise.error_desc_extract_tag_value error_desc "always_report" = "true" in (filters.Inferconfig.path_filter summary.Specs.attributes.Sil.loc.Location.file || 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 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); diff --git a/infer/src/java/jMain.ml b/infer/src/java/jMain.ml index 892610a00..3fca7177a 100644 --- a/infer/src/java/jMain.ml +++ b/infer/src/java/jMain.ml @@ -146,7 +146,7 @@ let do_all_files classpath sources classes = let program = JClasspath.load_program classpath classes sources in let tenv = load_tenv program 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 = StringMap.fold (do_source_file never_null_matcher linereader classes program tenv)