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",