From e123635122c1798a8987ec4f59cb19087c471acf Mon Sep 17 00:00:00 2001 From: jrm Date: Fri, 4 Mar 2016 14:07:23 -0800 Subject: [PATCH] Add support for @SuppressWarnings for Gradle and Ant projects. Summary:public Instead of using the collection of suppress warnings annotations to filter out the errors while generating the error reports, we just add this SuppressWarnings at translation time, like any other annotations, and the reporting functions in the Reporting module will just skip the errors when the method is annotated with SuppressWarnings. This allows us to have a suppress warnings mechanism that is independant from the integration with the build system. Reviewed By: sblackshear Differential Revision: D3012395 fb-gh-sync-id: 35f5f9b shipit-source-id: 35f5f9b --- .../main/java/infer/other/MainActivity.java | 5 +++ infer/lib/python/inferlib/analyze.py | 3 -- infer/lib/python/inferlib/jwlib.py | 3 ++ infer/src/backend/inferconfig.ml | 26 ++++-------- infer/src/backend/inferconfig.mli | 11 ++--- infer/src/backend/inferprint.ml | 5 --- infer/src/backend/reporting.ml | 8 ++++ infer/src/backend/sil.mli | 2 +- infer/src/checkers/annotations.ml | 5 +++ infer/src/checkers/annotations.mli | 2 + infer/src/java/jAnnotation.ml | 42 ++++++++++++++++++- infer/src/java/jAnnotation.mli | 2 +- infer/src/java/jMain.ml | 5 +++ infer/src/java/jTrans.ml | 12 ++++-- infer/tests/ant_report.json | 5 --- 15 files changed, 93 insertions(+), 43 deletions(-) diff --git a/examples/android_hello/app/src/main/java/infer/other/MainActivity.java b/examples/android_hello/app/src/main/java/infer/other/MainActivity.java index 9251c998c..7382fd133 100644 --- a/examples/android_hello/app/src/main/java/infer/other/MainActivity.java +++ b/examples/android_hello/app/src/main/java/infer/other/MainActivity.java @@ -23,4 +23,9 @@ public class MainActivity extends ActionBarActivity { source().toString(); } + @SuppressWarnings("infer") + void shouldNotBeReported() { + source().toString(); + } + } diff --git a/infer/lib/python/inferlib/analyze.py b/infer/lib/python/inferlib/analyze.py index 221ef6a86..37d45b918 100644 --- a/infer/lib/python/inferlib/analyze.py +++ b/infer/lib/python/inferlib/analyze.py @@ -419,9 +419,6 @@ class AnalyzerWrapper(object): '-procs', procs_report, '-analyzer', self.args.analyzer ] - if self.javac is not None and self.javac.annotations_out is not None: - infer_print_options += [ - '-local_config', self.javac.annotations_out] if self.args.debug or self.args.debug_exceptions: infer_print_options.append('-with_infer_src_loc') exit_status = subprocess.check_call( diff --git a/infer/lib/python/inferlib/jwlib.py b/infer/lib/python/inferlib/jwlib.py index a62d53533..74daff9fb 100644 --- a/infer/lib/python/inferlib/jwlib.py +++ b/infer/lib/python/inferlib/jwlib.py @@ -246,6 +246,9 @@ class AnalyzerWithFrontendWrapper(analyze.AnalyzerWrapper): if os.path.isfile(config.MODELS_JAR): infer_cmd += ['-models', config.MODELS_JAR] + if self.javac.annotations_out is not None: + infer_cmd += ['-local_config', self.javac.annotations_out] + infer_cmd.append('-no-static_final') if self.args.debug: diff --git a/infer/src/backend/inferconfig.ml b/infer/src/backend/inferconfig.ml index 3fdd99cbe..fb98cb43e 100644 --- a/infer/src/backend/inferconfig.ml +++ b/infer/src/backend/inferconfig.ml @@ -98,6 +98,11 @@ module type MATCHABLE_JSON = sig val json_key : string end +module type Matcher = sig + type matcher = DB.source_file -> Procname.t -> bool + val load_matcher : string -> matcher +end + module FileOrProcMatcher = functor (M : MATCHABLE_JSON) -> struct @@ -305,17 +310,6 @@ let load_filters analyzer = with Sys_error _ -> None else None -(** parse autogenerated list of procedures/classes with Java @SuppressWarnings annotations. This - list is generated by an annotation parser than runs with the javac compilation step and saved in - the [local_config] file *) -let make_proc_filter_from_local_config () = - let filter = match !local_config with - | Some f -> - (try ProcMatcher.load_matcher f - with Yojson.Json_error _ -> ProcMatcher.default_matcher) - | None -> ProcMatcher.default_matcher in - fun pname -> not (filter DB.source_file_empty pname) - let filters_from_inferconfig inferconfig : filters = let path_filter = @@ -340,17 +334,15 @@ let filters_from_inferconfig inferconfig : filters = proc_filter = default_proc_filter; } -(* Create filters based on .inferconfig.*) +(* 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 - let filters = - match load_filters (Utils.string_of_analyzer analyzer) with - | None -> do_not_filter - | Some inferconfig -> filters_from_inferconfig inferconfig in - { filters with proc_filter = make_proc_filter_from_local_config () } + 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 daa770746..76691e92e 100644 --- a/infer/src/backend/inferconfig.mli +++ b/infer/src/backend/inferconfig.mli @@ -36,15 +36,16 @@ val do_not_filter : filters (** Create filters based on the config file *) val create_filters : analyzer -> filters -module NeverReturnNull : sig +module type Matcher = sig type matcher = DB.source_file -> Procname.t -> bool val load_matcher : string -> matcher end -module SkipTranslationMatcher : sig - type matcher = DB.source_file -> Procname.t -> bool - val load_matcher : string -> matcher -end +module NeverReturnNull : Matcher + +module SkipTranslationMatcher : Matcher + +module ProcMatcher : Matcher (** Load the config file and list the files to report on *) val test: unit -> unit diff --git a/infer/src/backend/inferprint.ml b/infer/src/backend/inferprint.ml index 1a3bcfdbb..aa3c3d194 100644 --- a/infer/src/backend/inferprint.ml +++ b/infer/src/backend/inferprint.ml @@ -180,11 +180,6 @@ let arg_desc = Some "dir", "Path to the .inferconfig file" ; - "-local_config", - Arg.String (fun s -> Inferconfig.local_config := Some s), - Some "Path", - "Path to local config file" - ; "-with_infer_src_loc", Arg.Set reports_include_ml_loc, None, diff --git a/infer/src/backend/reporting.ml b/infer/src/backend/reporting.ml index fee1aeb03..1bef1d7a2 100644 --- a/infer/src/backend/reporting.ml +++ b/infer/src/backend/reporting.ml @@ -54,7 +54,15 @@ let log_issue ?(ltr = None) ?(pre = None) exn = + let should_suppress_warnings summary = + if !Config.curr_language = Config.C_CPP then false + else + let annotated_signature = + Annotations.get_annotated_signature summary.Specs.attributes in + let ret_annotation, _ = annotated_signature.Annotations.ret in + Annotations.ia_is_suppress_warnings ret_annotation in match Specs.get_summary proc_name with + | Some summary when should_suppress_warnings summary -> () | Some summary -> let err_log = summary.Specs.attributes.ProcAttributes.err_log in log_issue_from_errlog err_kind err_log ~loc:loc ~node_id:node_id diff --git a/infer/src/backend/sil.mli b/infer/src/backend/sil.mli index 561a65db7..d703b616a 100644 --- a/infer/src/backend/sil.mli +++ b/infer/src/backend/sil.mli @@ -805,7 +805,7 @@ val pp_const: printenv -> Format.formatter -> const -> unit (** Pretty print an item annotation. *) val pp_item_annotation : Format.formatter -> item_annotation -> unit -val item_annotation_to_string : item_annotation -> string +val item_annotation_to_string : item_annotation -> string (** Pretty print a method annotation. *) val pp_method_annotation : string -> Format.formatter -> method_annotation -> unit diff --git a/infer/src/checkers/annotations.ml b/infer/src/checkers/annotations.ml index b60891d86..3f16bbeda 100644 --- a/infer/src/checkers/annotations.ml +++ b/infer/src/checkers/annotations.ml @@ -112,6 +112,7 @@ let expensive = "Expensive" let performance_critical = "PerformanceCritical" let no_allocation = "NoAllocation" let ignore_allocations = "IgnoreAllocations" +let suppress_warnings = "SuppressWarnings" let ia_is_nullable ia = ia_ends_with ia nullable @@ -162,6 +163,10 @@ let ia_is_no_allocation ia = let ia_is_ignore_allocations ia = ia_ends_with ia ignore_allocations +let ia_is_suppress_warnings ia = + ia_ends_with ia suppress_warnings + + type annotation = | Nullable | Present diff --git a/infer/src/checkers/annotations.mli b/infer/src/checkers/annotations.mli index e2a697fb3..c8e304b2a 100644 --- a/infer/src/checkers/annotations.mli +++ b/infer/src/checkers/annotations.mli @@ -14,6 +14,7 @@ val suppressLint : string val expensive : string val performance_critical : string val no_allocation : string +val suppress_warnings : string type annotation = | Nullable @@ -75,6 +76,7 @@ val ia_is_expensive : Sil.item_annotation -> bool val ia_is_performance_critical : Sil.item_annotation -> bool val ia_is_no_allocation : Sil.item_annotation -> bool val ia_is_ignore_allocations : Sil.item_annotation -> bool +val ia_is_suppress_warnings : Sil.item_annotation -> bool val ia_iter : (Sil.annotation -> unit) -> Sil.item_annotation -> unit diff --git a/infer/src/java/jAnnotation.ml b/infer/src/java/jAnnotation.ml index 6cad80135..b94f3a6ba 100644 --- a/infer/src/java/jAnnotation.ml +++ b/infer/src/java/jAnnotation.ml @@ -10,6 +10,40 @@ open Javalib_pack + +let suppress_warnings_lookup = ref None + + +let load_suppress_warnings_lookup () = + let default_matcher = fun _ -> false in + let matcher = + match !Inferconfig.local_config with + | Some f -> + (try + let m = Inferconfig.ProcMatcher.load_matcher f in + (m DB.source_file_empty) + with Yojson.Json_error _ -> + default_matcher) + | None -> failwith "Local config expected!" in + suppress_warnings_lookup := Some matcher + + +let is_suppress_warnings_annotated proc_name = + let matcher = + let () = + match !suppress_warnings_lookup with + | None -> + load_suppress_warnings_lookup () + | Some _ -> () in + Option.get !suppress_warnings_lookup in + matcher proc_name + + +let suppress_warnings = + ({ Sil.class_name = Annotations.suppress_warnings; + Sil.parameters = ["infer"] }, + true) + (** Translate an annotation. *) let translate a : Sil.annotation = let class_name = JBasics.cn_name a.JBasics.kind in @@ -35,9 +69,13 @@ let translate_item avlist : Sil.item_annotation = (** Translate a method annotation. *) -let translate_method ann : Sil.method_annotation = +let translate_method proc_name ann : Sil.method_annotation = let global_ann = ann.Javalib.ma_global in let param_ann = ann.Javalib.ma_parameters in - let ret_item = translate_item global_ann in + let ret_item = + let base_annotations = translate_item global_ann in + if is_suppress_warnings_annotated proc_name then + suppress_warnings :: base_annotations + else base_annotations in let param_items = IList.map translate_item param_ann in ret_item, param_items diff --git a/infer/src/java/jAnnotation.mli b/infer/src/java/jAnnotation.mli index f76d8e8f3..0853e9072 100644 --- a/infer/src/java/jAnnotation.mli +++ b/infer/src/java/jAnnotation.mli @@ -15,4 +15,4 @@ open Javalib_pack val translate_item : (JBasics.annotation * Javalib.visibility) list -> Sil.item_annotation (** Translate a method annotation. *) -val translate_method : Javalib.method_annotations -> Sil.method_annotation +val translate_method : Procname.t -> Javalib.method_annotations -> Sil.method_annotation diff --git a/infer/src/java/jMain.ml b/infer/src/java/jMain.ml index f1fdff859..d4f2c883e 100644 --- a/infer/src/java/jMain.ml +++ b/infer/src/java/jMain.ml @@ -62,6 +62,11 @@ let arg_desc = None, "Set the path to the javac verbose output" ; + "-local_config", + Arg.String (fun s -> Inferconfig.local_config := Some s), + Some "Path", + "Path to local config file" + ; ] in Arg.create_options_desc false "Parsing Options" desc diff --git a/infer/src/java/jTrans.ml b/infer/src/java/jTrans.ml index 809e43d6f..f0fe3e7aa 100644 --- a/infer/src/java/jTrans.ml +++ b/infer/src/java/jTrans.ml @@ -275,8 +275,10 @@ let create_local_procdesc program linereader cfg tenv node m = try match m with | Javalib.AbstractMethod am -> (* create a procdesc with empty body *) - let formals = formals_from_signature program tenv cn ms (JTransType.get_method_kind m) in - let method_annotation = JAnnotation.translate_method am.Javalib.am_annotations in + let formals = + formals_from_signature program tenv cn ms (JTransType.get_method_kind m) in + let method_annotation = + JAnnotation.translate_method proc_name am.Javalib.am_annotations in let procdesc = let proc_attributes = { (ProcAttributes.default proc_name Config.Java) with @@ -300,7 +302,8 @@ let create_local_procdesc program linereader cfg tenv node m = Cfg.Procdesc.set_exit_node procdesc exit_node | Javalib.ConcreteMethod cm when is_java_native cm -> let formals = formals_from_signature program tenv cn ms (JTransType.get_method_kind m) in - let method_annotation = JAnnotation.translate_method cm.Javalib.cm_annotations in + let method_annotation = + JAnnotation.translate_method proc_name cm.Javalib.cm_annotations in let proc_attributes = { (ProcAttributes.default proc_name Config.Java) with ProcAttributes.access = trans_access cm.Javalib.cm_access; @@ -319,7 +322,8 @@ let create_local_procdesc program linereader cfg tenv node m = let loc = (get_location impl 0 JContext.Normal cn) in fix_method_definition_line linereader proc_name loc in let loc_exit = (get_location impl (Array.length (JBir.code impl) - 1) JContext.Normal cn) in - let method_annotation = JAnnotation.translate_method cm.Javalib.cm_annotations in + let method_annotation = + JAnnotation.translate_method proc_name cm.Javalib.cm_annotations in update_constr_loc cn ms loc_start; update_init_loc cn ms loc_exit; let procdesc = diff --git a/infer/tests/ant_report.json b/infer/tests/ant_report.json index 016cde79f..20401f563 100644 --- a/infer/tests/ant_report.json +++ b/infer/tests/ant_report.json @@ -659,11 +659,6 @@ "file": "codetoanalyze/java/infer/ResourceLeaks.java", "procedure": "void ResourceLeaks.serverSocketNotClosed()" }, - { - "bug_type": "NULL_DEREFERENCE", - "file": "codetoanalyze/java/infer/NullPointerExceptions.java", - "procedure": "void NullPointerExceptions.shouldNotReportNPE()" - }, { "bug_type": "NULL_DEREFERENCE", "file": "codetoanalyze/java/infer/NullPointerExceptions.java",