From a7b947f9714eec0ff0b5c6340661ef02bb878449 Mon Sep 17 00:00:00 2001 From: Martino Luca Date: Mon, 20 Mar 2017 09:37:59 -0700 Subject: [PATCH] Add support to format reports natively Reviewed By: jvillard Differential Revision: D4715013 fbshipit-source-id: 8894299 --- infer/lib/linter_rules/linters.al | 2 +- infer/src/IR/Localise.ml | 176 ++++++++++-------- infer/src/base/Config.ml | 11 ++ infer/src/base/Config.mli | 1 + infer/src/base/MarkupFormatter.ml | 68 +++++++ infer/src/base/MarkupFormatter.mli | 26 +++ .../src/bufferoverrun/bufferOverrunDomain.ml | 4 +- infer/src/checkers/ThreadSafety.ml | 17 +- infer/src/checkers/annotationReachability.ml | 39 ++-- infer/src/clang/ComponentKit.ml | 7 +- infer/src/clang/cFrontend_errors.ml | 14 +- infer/src/eradicate/typeErr.ml | 134 +++++++------ .../codetoanalyze/c/bufferoverrun/issues.exp | 8 +- .../codetoanalyze/java/eradicate/issues.exp | 2 +- 14 files changed, 326 insertions(+), 183 deletions(-) create mode 100644 infer/src/base/MarkupFormatter.ml create mode 100644 infer/src/base/MarkupFormatter.mli diff --git a/infer/lib/linter_rules/linters.al b/infer/lib/linter_rules/linters.al index 102a58453..80aa4230a 100644 --- a/infer/lib/linter_rules/linters.al +++ b/infer/lib/linter_rules/linters.al @@ -31,7 +31,7 @@ DEFINE-CHECKER ASSIGN_POINTER_WARNING = { is_assign_property() AND is_property_pointer_type() HOLDS-IN-NODE ObjCPropertyDecl; - SET message = "Property `%decl_name%` is a pointer type marked with the `assign` attribute"; + SET message = "Property %decl_name% is a pointer type marked with the `assign` attribute"; SET suggestion = "Use a different attribute like `strong` or `weak`."; SET severity = "WARNING"; }; diff --git a/infer/src/IR/Localise.ml b/infer/src/IR/Localise.ml index 46528a444..4311960e2 100644 --- a/infer/src/IR/Localise.ml +++ b/infer/src/IR/Localise.ml @@ -13,6 +13,7 @@ open! IStd (** Support for localisation *) module F = Format +module MF = MarkupFormatter type t = string * string [@@deriving compare] (* issue_id, human_readable *) @@ -289,7 +290,7 @@ let at_line tags loc = let call_to tags proc_name = let proc_name_str = Typ.Procname.to_simplified_string proc_name in Tags.add tags Tags.call_procedure proc_name_str; - "call to " ^ proc_name_str + "call to " ^ MF.monospaced_to_string proc_name_str let call_to_at_line tags proc_name loc = (call_to tags proc_name) ^ " " ^ at_line_tag tags Tags.call_line loc @@ -464,51 +465,54 @@ let deref_str_uninitialized alloc_att_opt = (** Java unchecked exceptions errors *) let java_unchecked_exn_desc proc_name exn_name pre_str : error_desc = { no_desc with descriptions = [ - Typ.Procname.to_string proc_name; - "can throw " ^ (Typ.Name.name exn_name); + MF.monospaced_to_string (Typ.Procname.to_string proc_name); + "can throw " ^ MF.monospaced_to_string (Typ.Name.name exn_name); "whenever " ^ pre_str]; } let desc_context_leak pname context_typ fieldname leak_path : error_desc = let fld_str = Ident.fieldname_to_string fieldname in - let leak_root = " Static field " ^ fld_str ^ " |->\n " in + let leak_root = "Static field " ^ fld_str ^ " |->\n" in let leak_path_entry_to_str acc entry = let entry_str = match entry with | (Some fld, _) -> Ident.fieldname_to_string fld | (None, typ) -> Typ.to_string typ in (* intentionally omit space; [typ_to_string] adds an extra space *) - acc ^ entry_str ^ " |->\n " in + acc ^ entry_str ^ " |->\n" in let context_str = Typ.to_string context_typ in let path_str = let path_prefix = if List.is_empty leak_path then "Leaked " - else (List.fold ~f:leak_path_entry_to_str ~init:"" leak_path) ^ " Leaked " in + else (List.fold ~f:leak_path_entry_to_str ~init:"" leak_path) ^ "Leaked " in path_prefix ^ context_str in let preamble = let pname_str = match pname with | Typ.Procname.Java pname_java -> - Printf.sprintf "%s.%s" - (Typ.Procname.java_get_class_name pname_java) - (Typ.Procname.java_get_method pname_java) + MF.monospaced_to_string + (Printf.sprintf "%s.%s" + (Typ.Procname.java_get_class_name pname_java) + (Typ.Procname.java_get_method pname_java)) | _ -> "" in "Context " ^ context_str ^ " may leak during method " ^ pname_str ^ ":\n" in - { no_desc with descriptions = [preamble; leak_root; path_str] } + { no_desc with descriptions = [preamble ^ MF.code_to_string (leak_root ^ path_str)] } let desc_unsafe_guarded_by_access pname accessed_fld guarded_by_str loc = let line_info = at_line (Tags.create ()) loc in let accessed_fld_str = Ident.fieldname_to_string accessed_fld in - let annot_str = Printf.sprintf "`@GuardedBy(\"%s\")`" guarded_by_str in + let annot_str = Printf.sprintf "@GuardedBy(\"%s\")" guarded_by_str in + let syncronized_str = + MF.monospaced_to_string (Printf.sprintf "synchronized(%s)" guarded_by_str) in let msg = - Printf.sprintf - "The field `%s` is annotated with %s, but the lock `%s` is not held during the access to the field `%s`. Consider wrapping the access in a `synchronized(%s)` block or annotating %s with %s" - accessed_fld_str - annot_str - guarded_by_str + Format.asprintf + "The field %a is annotated with %a, but the lock %a is not held during the access to the field %s. Consider wrapping the access in a %s block or annotating %s with %a" + MF.pp_monospaced accessed_fld_str + MF.pp_monospaced annot_str + MF.pp_monospaced guarded_by_str line_info - guarded_by_str + syncronized_str (Typ.Procname.to_string pname) - annot_str in + MF.pp_monospaced annot_str in { no_desc with descriptions = [msg]; } @@ -546,8 +550,8 @@ let dereference_string deref_str value_str access_opt loc = String.concat ~sep:"" [ (match deref_str.value_pre with Some s -> s ^ " " | _ -> ""); (if is_call_access then "returned by " else ""); - value_str; - (match deref_str.value_post with Some s -> " " ^ s | _ -> "")] in + MF.monospaced_to_string value_str; + (match deref_str.value_post with Some s -> " " ^ (MF.monospaced_to_string s) | _ -> "")] in let access_desc = match access_opt with | None -> [] @@ -564,19 +568,22 @@ let dereference_string deref_str value_str access_opt loc = ["initialized automatically"] in let problem_desc = let nullable_text = - if Config.curr_language_is Config.Java - then "@Nullable" - else "__nullable" in + MF.monospaced_to_string + (if Config.curr_language_is Config.Java + then "@Nullable" + else "__nullable") in let problem_str = match Tags.get !tags Tags.nullable_src, Tags.get !tags Tags.weak_captured_var_src with | Some nullable_src, _ -> if String.equal nullable_src value_str then "is annotated with " ^ nullable_text ^ " and is dereferenced without a null check" - else "is indirectly marked " ^ nullable_text ^ " (source: " ^ nullable_src ^ ") and is dereferenced without a null check" + else "is indirectly marked " ^ nullable_text ^ + " (source: " ^ MF.monospaced_to_string nullable_src ^ + ") and is dereferenced without a null check" | None, Some weak_var_str -> if String.equal weak_var_str value_str then "is a weak pointer captured in the block and is dereferenced without a null check" - else "is equal to the variable " ^ weak_var_str ^ + else "is equal to the variable " ^ (MF.monospaced_to_string weak_var_str) ^ ", a weak pointer captured in the block, and is dereferenced without a null check" | None, None -> deref_str.problem_str in [(problem_str ^ " " ^ at_line tags loc)] in @@ -586,7 +593,8 @@ let parameter_field_not_null_checked_desc (desc : error_desc) exp = let parameter_not_nullable_desc var = let var_s = Pvar.to_string var in let param_not_null_desc = - "Parameter "^var_s^" is not checked for null, there could be a null pointer dereference:" in + "Parameter " ^ (MF.monospaced_to_string var_s) ^ + " is not checked for null, there could be a null pointer dereference:" in { desc with descriptions = param_not_null_desc :: desc.descriptions; tags = (Tags.parameter_not_null_checked, var_s) :: desc.tags; } in let field_not_nullable_desc exp = @@ -597,7 +605,8 @@ let parameter_field_not_null_checked_desc (desc : error_desc) exp = | _ -> "" in let var_s = exp_to_string exp in let field_not_null_desc = - "Instance variable "^var_s^" is not checked for null, there could be a null pointer dereference:" in + "Instance variable " ^ (MF.monospaced_to_string var_s) ^ + " is not checked for null, there could be a null pointer dereference:" in { desc with descriptions = field_not_null_desc :: desc.descriptions; tags = (Tags.field_not_null_checked, var_s) :: desc.tags; } in match exp with @@ -627,8 +636,10 @@ let desc_allocation_mismatch alloc dealloc = Tags.add tags tag_line (string_of_int loc.Location.line); let by_call = if Typ.Procname.equal primitive_pname called_pname then "" - else " by call to " ^ Typ.Procname.to_simplified_string called_pname in - "using " ^ Typ.Procname.to_simplified_string primitive_pname ^ by_call ^ " " ^ at_line (Tags.create ()) (* ignore the tag *) loc in + else " by call to " ^ + (MF.monospaced_to_string (Typ.Procname.to_simplified_string called_pname)) in + "using " ^ (MF.monospaced_to_string (Typ.Procname.to_simplified_string primitive_pname)) ^ + by_call ^ " " ^ at_line (Tags.create ()) (* ignore the tag *) loc in let description = Format.sprintf "%s %s is deallocated %s" mem_dyn_allocated @@ -655,7 +666,7 @@ let desc_condition_always_true_false i cond_str_opt loc = Tags.add tags Tags.value value; let description = Format.sprintf "Boolean condition %s is always %s %s" - (if String.equal value "" then "" else " " ^ value) + (if String.equal value "" then "" else " " ^ (MF.monospaced_to_string value)) tt_ff (at_line tags loc) in { no_desc with descriptions = [description]; tags = !tags } @@ -663,18 +674,18 @@ let desc_condition_always_true_false i cond_str_opt loc = let desc_deallocate_stack_variable var_str proc_name loc = let tags = Tags.create () in Tags.add tags Tags.value var_str; - let description = Format.sprintf - "Stack variable %s is freed by a %s" - var_str + let description = Format.asprintf + "Stack variable %a is freed by a %s" + MF.pp_monospaced var_str (call_to_at_line tags proc_name loc) in { no_desc with descriptions = [description]; tags = !tags } let desc_deallocate_static_memory const_str proc_name loc = let tags = Tags.create () in Tags.add tags Tags.value const_str; - let description = Format.sprintf - "Constant string %s is freed by a %s" - const_str + let description = Format.asprintf + "Constant string %a is freed by a %s" + MF.pp_monospaced const_str (call_to_at_line tags proc_name loc) in { no_desc with descriptions = [description]; tags = !tags } @@ -685,15 +696,15 @@ let desc_class_cast_exception pname_opt typ_str1 typ_str2 exp_str_opt loc = let in_expression = match exp_str_opt with | Some exp_str -> Tags.add tags Tags.value exp_str; - " in expression " ^ exp_str ^ " " + " in expression " ^ (MF.monospaced_to_string exp_str) ^ " " | None -> " " in let at_line' () = match pname_opt with | Some proc_name -> "in " ^ call_to_at_line tags proc_name loc | None -> at_line tags loc in - let description = Format.sprintf - "%s cannot be cast to %s %s %s" - typ_str1 - typ_str2 + let description = Format.asprintf + "%a cannot be cast to %a %s %s" + MF.pp_monospaced typ_str1 + MF.pp_monospaced typ_str2 in_expression (at_line' ()) in { no_desc with descriptions = [description]; tags = !tags } @@ -701,14 +712,14 @@ let desc_class_cast_exception pname_opt typ_str1 typ_str2 exp_str_opt loc = let desc_divide_by_zero expr_str loc = let tags = Tags.create () in Tags.add tags Tags.value expr_str; - let description = Format.sprintf - "Expression %s could be zero %s" - expr_str + let description = Format.asprintf + "Expression %a could be zero %s" + MF.pp_monospaced expr_str (at_line tags loc) in { no_desc with descriptions = [description]; tags = !tags } let desc_empty_vector_access pname_opt object_str loc = - let vector_str = Format.sprintf "Vector %s" object_str in + let vector_str = Format.asprintf "Vector %a" MF.pp_monospaced object_str in let desc = access_str_empty pname_opt in let tags = desc.tags in Tags.add tags Tags.empty_vector_access object_str; @@ -741,11 +752,11 @@ let desc_leak hpred_type_opt value_str_opt resource_opt resource_action_opt loc | None -> "", "", "" | Some s -> Tags.add tags Tags.value s; - s, " to ", " on " in + MF.monospaced_to_string s, " to ", " on " in let typ_str = match hpred_type_opt with | Some (Exp.Sizeof (Tstruct (TN_csu (Class _, _, _) as name), _, _)) -> - " of type " ^ Typ.Name.name name ^ " " + " of type " ^ MF.monospaced_to_string (Typ.Name.name name) ^ " " | _ -> " " in let desc_str = match resource_opt with @@ -795,9 +806,9 @@ let desc_null_test_after_dereference expr_str line loc = let tags = Tags.create () in Tags.add tags Tags.dereferenced_line (string_of_int line); Tags.add tags Tags.value expr_str; - let description = Format.sprintf - "Pointer %s was dereferenced at line %d and is tested for null %s" - expr_str + let description = Format.asprintf + "Pointer %a was dereferenced at line %d and is tested for null %s" + MF.pp_monospaced expr_str line (at_line tags loc) in { no_desc with descriptions = [description]; tags = !tags } @@ -823,20 +834,23 @@ let desc_retain_cycle cycle loc cycle_dotty = let do_edge ((se, _), f, _) = match se with | Sil.Eexp(Exp.Lvar pvar, _) when Pvar.equal pvar Sil.block_pvar -> - str_cycle:=!str_cycle^" ("^(string_of_int !ct)^") a block capturing "^(Ident.fieldname_to_string f)^"; "; + str_cycle:=!str_cycle^" ("^(string_of_int !ct)^") a block capturing " ^ + MF.monospaced_to_string (Ident.fieldname_to_string f)^"; "; ct:=!ct +1; | Sil.Eexp(Exp.Lvar pvar as e, _) -> let e_str = Exp.to_string e in let e_str = if Pvar.is_seed pvar then remove_old e_str else e_str in - str_cycle:=!str_cycle^" ("^(string_of_int !ct)^") object "^e_str^" retaining "^e_str^"."^(Ident.fieldname_to_string f)^", "; + str_cycle:=!str_cycle^" ("^(string_of_int !ct)^") object "^e_str^" retaining " ^ + MF.monospaced_to_string (e_str^"."^(Ident.fieldname_to_string f))^", "; ct:=!ct +1 | Sil.Eexp (Exp.Sizeof (typ, _, _), _) -> let step = - " (" ^ (string_of_int !ct) ^ ") an object of " - ^ (Typ.to_string typ) ^ " retaining another object via instance variable " - ^ (Ident.fieldname_to_string f) ^ ", " in + " (" ^ (string_of_int !ct) ^ ") an object of " ^ + MF.monospaced_to_string (Typ.to_string typ) ^ + " retaining another object via instance variable " ^ + MF.monospaced_to_string (Ident.fieldname_to_string f) ^ ", " in str_cycle := !str_cycle ^ step; ct:=!ct +1 | _ -> () in @@ -850,7 +864,7 @@ let registered_observer_being_deallocated_str obj_str = let desc_registered_observer_being_deallocated pvar loc = let tags = Tags.create () in - let obj_str = Pvar.to_string pvar in + let obj_str = MF.monospaced_to_string (Pvar.to_string pvar) in { no_desc with descriptions = [ registered_observer_being_deallocated_str obj_str ^ at_line tags loc ^ ". Being still registered as observer of the notification " ^ "center, the deallocated object " @@ -871,9 +885,9 @@ let desc_unary_minus_applied_to_unsigned_expression expr_str_opt typ_str loc = Tags.add tags Tags.value s; "expression " ^ s | None -> "an expression" in - let description = Format.sprintf - "A unary minus is applied to %s of type %s %s" - expression + let description = Format.asprintf + "A unary minus is applied to %a of type %s %s" + MF.pp_monospaced expression typ_str (at_line tags loc) in { no_desc with descriptions = [description]; tags = !tags } @@ -888,7 +902,7 @@ let desc_inherently_dangerous_function proc_name = let proc_name_str = Typ.Procname.to_string proc_name in let tags = Tags.create () in Tags.add tags Tags.value proc_name_str; - { no_desc with descriptions = [proc_name_str]; tags = !tags } + { no_desc with descriptions = [MF.monospaced_to_string proc_name_str]; tags = !tags } let desc_stack_variable_address_escape expr_str addr_dexp_str loc = let tags = Tags.create () in @@ -898,9 +912,9 @@ let desc_stack_variable_address_escape expr_str addr_dexp_str loc = Tags.add tags Tags.escape_to s; "to " ^ s ^ " " | None -> "" in - let description = Format.sprintf - "Address of stack variable %s escapes %s%s" - expr_str + let description = Format.asprintf + "Address of stack variable %a escapes %s%s" + MF.pp_monospaced expr_str escape_to_str (at_line tags loc) in { no_desc with descriptions = [description]; tags = !tags } @@ -912,37 +926,37 @@ let desc_tainted_value_reaching_sensitive_function let description = match taint_kind with | PredSymb.Tk_unverified_SSL_socket -> - F.sprintf - "The hostname of SSL socket `%s` (returned from %s) has not been verified! Reading from the socket via the call to %s %s is dangerous. You should verify the hostname of the socket using a HostnameVerifier before reading; otherwise, you may be vulnerable to a man-in-the-middle attack." - expr_str + F.asprintf + "The hostname of SSL socket %a (returned from %s) has not been verified! Reading from the socket via the call to %s %s is dangerous. You should verify the hostname of the socket using a HostnameVerifier before reading; otherwise, you may be vulnerable to a man-in-the-middle attack." + MF.pp_monospaced expr_str (format_method tainting_fun) (format_method sensitive_fun) (at_line tags loc) | PredSymb.Tk_shared_preferences_data -> - F.sprintf - "`%s` holds sensitive data read from a SharedPreferences object (via call to %s). This data may leak via the call to %s %s." - expr_str + F.asprintf + "%a holds sensitive data read from a SharedPreferences object (via call to %s). This data may leak via the call to %s %s." + MF.pp_monospaced expr_str (format_method tainting_fun) (format_method sensitive_fun) (at_line tags loc) | PredSymb.Tk_privacy_annotation -> - F.sprintf - "`%s` holds privacy-sensitive data (source: call to %s). This data may leak via the call to %s %s." - expr_str + F.asprintf + "%a holds privacy-sensitive data (source: call to %s). This data may leak via the call to %s %s." + MF.pp_monospaced expr_str (format_method tainting_fun) (format_method sensitive_fun) (at_line tags loc) | PredSymb.Tk_integrity_annotation -> - F.sprintf - "`%s` holds untrusted user-controlled data (source: call to %s). This data may flow into a security-sensitive sink via the call to %s %s." - expr_str + F.asprintf + "%a holds untrusted user-controlled data (source: call to %s). This data may flow into a security-sensitive sink via the call to %s %s." + MF.pp_monospaced expr_str (format_method tainting_fun) (format_method sensitive_fun) (at_line tags loc) | PredSymb.Tk_unknown -> - F.sprintf - "Value `%s` could be insecure (tainted) due to call to function %s %s %s %s. Function %s %s" - expr_str + F.asprintf + "Value %a could be insecure (tainted) due to call to function %s %s %s %s. Function %s %s" + MF.pp_monospaced expr_str (format_method tainting_fun) "and is reaching sensitive function" (format_method sensitive_fun) @@ -958,10 +972,10 @@ let desc_uninitialized_dangling_pointer_deref deref expr_str loc = | Some s -> s | _ -> "" in let description = - Format.sprintf - "%s %s %s %s" + Format.asprintf + "%s %a %s %s" prefix - expr_str + MF.pp_monospaced expr_str deref.problem_str (at_line tags loc) in { no_desc with descriptions = [description]; tags = !tags } diff --git a/infer/src/base/Config.ml b/infer/src/base/Config.ml index edb737dc6..0d451f0cb 100644 --- a/infer/src/base/Config.ml +++ b/infer/src/base/Config.ml @@ -1178,6 +1178,16 @@ and report_custom_error = CLOpt.mk_bool ~long:"report-custom-error" "" +and report_formatter = + CLOpt.mk_symbol ~long:"report-formatter" + ~parse_mode:CLOpt.(Infer [Driver; Print]) + ~default:`Phabricator_formatter + ~symbols:[ + ("none", `No_formatter); + ("phabricator", `Phabricator_formatter); + ] ~eq:PVariant.(=) + "Which formatter to use when emitting the report" + and report_hook = CLOpt.mk_string_opt ~long:"report-hook" ~default:(lib_dir ^/ "python" ^/ "report.py") @@ -1669,6 +1679,7 @@ and reactive_capture = !reactive_capture and report = !report and report_current = !report_current and report_custom_error = !report_custom_error +and report_formatter = !report_formatter and report_hook = !report_hook and report_previous = !report_previous and report_runtime_exceptions = !tracing diff --git a/infer/src/base/Config.mli b/infer/src/base/Config.mli index 2d858a02d..4446083f0 100644 --- a/infer/src/base/Config.mli +++ b/infer/src/base/Config.mli @@ -297,6 +297,7 @@ val reactive_mode : bool val reactive_capture : bool val report : string option val report_current : string option +val report_formatter : [`No_formatter | `Phabricator_formatter] val report_hook : string option val report_previous : string option val report_runtime_exceptions : bool diff --git a/infer/src/base/MarkupFormatter.ml b/infer/src/base/MarkupFormatter.ml new file mode 100644 index 000000000..fb076df10 --- /dev/null +++ b/infer/src/base/MarkupFormatter.ml @@ -0,0 +1,68 @@ +(* + * 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. + *) + +open! IStd + +type 'a formatter = { + wrap_monospaced : (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a -> unit; + pp_monospaced : Format.formatter -> string -> unit; + monospaced_to_string : string -> string; + + wrap_code : (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a -> unit; + pp_code : Format.formatter -> string -> unit; + code_to_string : string -> string; +} + +module NoFormatter : sig + val formatter : 'a formatter +end = struct + let wrap_simple pp fmt x = pp fmt x + let pp_simple = wrap_simple Format.pp_print_string + let formatter = { + wrap_monospaced = wrap_simple; + pp_monospaced = pp_simple; + monospaced_to_string = Fn.id; + wrap_code = wrap_simple; + pp_code = pp_simple; + code_to_string = Fn.id + } +end + +module PhabricatorFormatter : sig + val formatter : 'a formatter +end = struct + (* https://secure.phabricator.com/book/phabricator/article/remarkup/ *) + let wrap_monospaced pp fmt x = Format.fprintf fmt "`%a`" pp x + let pp_monospaced fmt s = wrap_monospaced Format.pp_print_string fmt s + let monospaced_to_string s = Format.asprintf "%a" pp_monospaced s + + let wrap_code pp fmt x = Format.fprintf fmt "```%a```" pp x + let pp_code fmt s = wrap_code Format.pp_print_string fmt s + let code_to_string s = Format.asprintf "%a" pp_code s + + let formatter = { + wrap_monospaced; + pp_monospaced; + monospaced_to_string; + wrap_code; + pp_code; + code_to_string; + } +end + +let formatter = match Config.report_formatter with + | `No_formatter -> NoFormatter.formatter + | `Phabricator_formatter -> PhabricatorFormatter.formatter + +let wrap_monospaced = formatter.wrap_monospaced +let pp_monospaced = formatter.pp_monospaced +let monospaced_to_string = formatter.monospaced_to_string +let wrap_code = formatter.wrap_code +let pp_code = formatter.pp_code +let code_to_string = formatter.code_to_string diff --git a/infer/src/base/MarkupFormatter.mli b/infer/src/base/MarkupFormatter.mli new file mode 100644 index 000000000..81a0454be --- /dev/null +++ b/infer/src/base/MarkupFormatter.mli @@ -0,0 +1,26 @@ +(* + * 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. + *) + +(** used to combine pp together, wrap content into a monospaced block *) +val wrap_monospaced : (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a -> unit + +(** pp to wrap into a monospaced block *) +val pp_monospaced : Format.formatter -> string -> unit + +(* wrap into a monospaced block *) +val monospaced_to_string : string -> string + +(** used to combine pp together, wrap content into a code block *) +val wrap_code : (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a -> unit + +(** pp to wrap into a code block *) +val pp_code : Format.formatter -> string -> unit + +(* wrap into a code block *) +val code_to_string : string -> string diff --git a/infer/src/bufferoverrun/bufferOverrunDomain.ml b/infer/src/bufferoverrun/bufferOverrunDomain.ml index b8771a616..0e654d9ca 100644 --- a/infer/src/bufferoverrun/bufferOverrunDomain.ml +++ b/infer/src/bufferoverrun/bufferOverrunDomain.ml @@ -15,6 +15,7 @@ open AbsLoc module F = Format module L = Logging +module MF = MarkupFormatter module Condition = struct @@ -129,8 +130,7 @@ let to_string : t -> string ^ (match c.trace with Inter (_, pname, _) -> " by call " - ^ Typ.Procname.to_string pname - ^ "() " + ^ MF.monospaced_to_string (Typ.Procname.to_string pname ^ "()") ^ " " | Intra _ -> "") let subst : t -> Itv.Bound.t Itv.SubstMap.t -> Typ.Procname.t -> Typ.Procname.t -> Location.t -> t diff --git a/infer/src/checkers/ThreadSafety.ml b/infer/src/checkers/ThreadSafety.ml index 5dcbdb702..74627798b 100644 --- a/infer/src/checkers/ThreadSafety.ml +++ b/infer/src/checkers/ThreadSafety.ml @@ -11,7 +11,7 @@ open! IStd module F = Format module L = Logging - +module MF = MarkupFormatter module Summary = Summary.Make (struct type summary = ThreadSafetyDomain.summary @@ -944,7 +944,10 @@ let calculate_addendum_message tenv pname = | Some (current_class,thread_safe_annotated_classes) -> if not (List.mem ~equal:Typ.Name.equal thread_safe_annotated_classes current_class) then match thread_safe_annotated_classes with - | hd::_ -> F.asprintf "\n Note: Superclass %a is marked @ThreadSafe." Typ.Name.pp hd + | hd::_ -> + F.asprintf "\n Note: Superclass %a is marked %a." + (MF.wrap_monospaced Typ.Name.pp) hd + MF.pp_monospaced "@ThreadSafe" | [] -> "" else "" | _ -> "" @@ -1126,10 +1129,10 @@ let make_unprotected_write_description tenv pname final_sink_site initial_sink_site final_sink _ _ = Format.asprintf "Unprotected write. Public method %a%s %s %a outside of synchronization.%s" - Typ.Procname.pp pname + (MF.wrap_monospaced Typ.Procname.pp) pname (if CallSite.equal final_sink_site initial_sink_site then "" else " indirectly") (if is_container_write_sink final_sink then "mutates" else "writes to field") - (pp_accesses_sink ~is_write_access:true) final_sink + (MF.wrap_monospaced (pp_accesses_sink ~is_write_access:true)) final_sink (calculate_addendum_message tenv pname) let make_read_write_race_description @@ -1146,11 +1149,11 @@ let make_read_write_race_description let conflicts_description = Format.asprintf "Potentially races with writes in method%s %a." (if List.length conflicting_proc_names > 1 then "s" else "") - pp_proc_name_list conflicting_proc_names in + (MF.wrap_monospaced pp_proc_name_list) conflicting_proc_names in Format.asprintf "Read/Write race. Public method %a%s reads from field %a. %s %s" - Typ.Procname.pp pname + (MF.wrap_monospaced Typ.Procname.pp) pname (if CallSite.equal final_sink_site initial_sink_site then "" else " indirectly") - (pp_accesses_sink ~is_write_access:false) final_sink + (MF.wrap_monospaced (pp_accesses_sink ~is_write_access:false)) final_sink conflicts_description (calculate_addendum_message tenv pname) diff --git a/infer/src/checkers/annotationReachability.ml b/infer/src/checkers/annotationReachability.ml index ea3406b08..d21ab71de 100644 --- a/infer/src/checkers/annotationReachability.ml +++ b/infer/src/checkers/annotationReachability.ml @@ -11,6 +11,7 @@ open! IStd module F = Format module L = Logging +module MF = MarkupFormatter module CallSiteSet = AbstractDomain.FiniteSet (CallSite.Set) module CallsDomain = AbstractDomain.Map (Annot.Map) (CallSiteSet) @@ -178,13 +179,12 @@ let report_allocation_stack let final_trace = List.rev (update_trace call_loc trace) in let constr_str = string_of_pname constructor_pname in let description = - Printf.sprintf - "Method `%s` annotated with `@%s` allocates `%s` via `%s%s`" - (Typ.Procname.to_simplified_string pname) - src_annot - constr_str - stack_str - ("new "^constr_str) in + Format.asprintf + "Method %a annotated with %a allocates %a via %a" + MF.pp_monospaced (Typ.Procname.to_simplified_string pname) + MF.pp_monospaced ("@" ^ src_annot) + MF.pp_monospaced constr_str + MF.pp_monospaced (stack_str ^ ("new "^constr_str)) in let exn = Exceptions.Checkers (allocates_memory, Localise.verbatim_desc description) in Reporting.log_error pname ~loc:fst_call_loc ~ltr:final_trace exn @@ -196,14 +196,13 @@ let report_annotation_stack src_annot snk_annot src_pname loc trace stack_str sn let final_trace = List.rev (update_trace call_loc trace) in let exp_pname_str = string_of_pname snk_pname in let description = - Printf.sprintf - "Method `%s` annotated with `@%s` calls `%s%s` where `%s` is annotated with `@%s`" - (Typ.Procname.to_simplified_string src_pname) - src_annot - stack_str - exp_pname_str - exp_pname_str - snk_annot in + Format.asprintf + "Method %a annotated with %a calls %a where %a is annotated with %a" + MF.pp_monospaced (Typ.Procname.to_simplified_string src_pname) + MF.pp_monospaced ("@" ^ src_annot) + MF.pp_monospaced (stack_str ^ exp_pname_str) + MF.pp_monospaced exp_pname_str + MF.pp_monospaced ("@" ^ snk_annot) in let msg = if String.equal src_annot Annotations.performance_critical then calls_expensive_method @@ -355,11 +354,11 @@ module Interprocedural = struct let check_expensive_subtyping_rules overridden_pname = if not (method_is_expensive tenv overridden_pname) then let description = - Printf.sprintf - "Method `%s` overrides unannotated method `%s` and cannot be annotated with `@%s`" - (Typ.Procname.to_string proc_name) - (Typ.Procname.to_string overridden_pname) - Annotations.expensive in + Format.asprintf + "Method %a overrides unannotated method %a and cannot be annotated with %a" + MF.pp_monospaced (Typ.Procname.to_string proc_name) + MF.pp_monospaced (Typ.Procname.to_string overridden_pname) + MF.pp_monospaced ("@" ^ Annotations.expensive) in let exn = Exceptions.Checkers (expensive_overrides_unexpensive, Localise.verbatim_desc description) in diff --git a/infer/src/clang/ComponentKit.ml b/infer/src/clang/ComponentKit.ml index 3ea251c44..1ba3f122e 100644 --- a/infer/src/clang/ComponentKit.ml +++ b/infer/src/clang/ComponentKit.ml @@ -10,6 +10,8 @@ open! IStd open! PVariant +module MF = MarkupFormatter + let get_source_range an = match an with | Ctl_parser_types.Decl decl -> @@ -119,8 +121,9 @@ let mutable_local_vars_advice context an = CIssue.name = "MUTABLE_LOCAL_VARIABLE_IN_COMPONENT_FILE"; severity = Exceptions.Kadvice; mode = CIssue.On; - description = "Local variable '" ^ named_decl_info.ni_name - ^ "' should be const to avoid reassignment"; + description = + "Local variable " ^ MF.monospaced_to_string named_decl_info.ni_name ^ + " should be const to avoid reassignment"; suggestion = Some "Add a const (after the asterisk for pointer types)."; loc = CFrontend_checkers.location_from_dinfo context decl_info } diff --git a/infer/src/clang/cFrontend_errors.ml b/infer/src/clang/cFrontend_errors.ml index 62250fa4f..3100f6253 100644 --- a/infer/src/clang/cFrontend_errors.ml +++ b/infer/src/clang/cFrontend_errors.ml @@ -9,6 +9,8 @@ open! IStd +module MF = MarkupFormatter + type linter = { condition : CTL.t; issue_desc : CIssue.issue_desc; @@ -47,15 +49,15 @@ let parsed_linters = ref [] let evaluate_place_holder ph an = match ph with - | "%ivar_name%" -> CFrontend_checkers.ivar_name an - | "%decl_name%" -> CFrontend_checkers.decl_name an + | "%ivar_name%" -> MF.monospaced_to_string (CFrontend_checkers.ivar_name an) + | "%decl_name%" -> MF.monospaced_to_string (CFrontend_checkers.decl_name an) | "%cxx_ref_captured_in_block%" -> - CFrontend_checkers.cxx_ref_captured_in_block an + MF.monospaced_to_string (CFrontend_checkers.cxx_ref_captured_in_block an) | "%decl_ref_or_selector_name%" -> - CFrontend_checkers.decl_ref_or_selector_name an + MF.monospaced_to_string (CFrontend_checkers.decl_ref_or_selector_name an) | "%iphoneos_target_sdk_version%" -> - CFrontend_checkers.iphoneos_target_sdk_version an - | "%available_ios_sdk%" -> CFrontend_checkers.available_ios_sdk an + MF.monospaced_to_string (CFrontend_checkers.iphoneos_target_sdk_version an) + | "%available_ios_sdk%" -> MF.monospaced_to_string (CFrontend_checkers.available_ios_sdk an) | _ -> (Logging.err "ERROR: helper function %s is unknown. Stop.\n" ph; assert false) diff --git a/infer/src/eradicate/typeErr.ml b/infer/src/eradicate/typeErr.ml index 1124957fe..a3974b83b 100644 --- a/infer/src/eradicate/typeErr.ml +++ b/infer/src/eradicate/typeErr.ml @@ -11,6 +11,7 @@ open! IStd module Hashtbl = Caml.Hashtbl module L = Logging +module MF = MarkupFormatter module P = Printf (** Module for Type Error messages. *) @@ -262,7 +263,9 @@ let report_error_now tenv "The condition %s is always %b according to the existing annotations." (Option.value s_opt ~default:"") b, - Some "Consider adding a `@Nullable` annotation or removing the redundant check.", + Some ("Consider adding a " ^ + MF.monospaced_to_string "@Nullable" ^ + " annotation or removing the redundant check."), None, None | Field_not_initialized (fn, pn) -> @@ -272,24 +275,26 @@ let report_error_now tenv else match pn with | Typ.Procname.Java pn_java -> - Typ.Procname.java_get_method pn_java + MF.monospaced_to_string (Typ.Procname.java_get_method pn_java) | _ -> - Typ.Procname.to_simplified_string pn in + MF.monospaced_to_string (Typ.Procname.to_simplified_string pn) in true, Localise.eradicate_field_not_initialized, - P.sprintf - "Field `%s` is not initialized in %s and is not declared `@Nullable`" - (Ident.fieldname_to_simplified_string fn) - constructor_name, + Format.asprintf + "Field %a is not initialized in %s and is not declared %a" + MF.pp_monospaced (Ident.fieldname_to_simplified_string fn) + constructor_name + MF.pp_monospaced "@Nullable", None, Some fn, None | Field_not_mutable (fn, (origin_description, origin_loc, _)) -> true, Localise.eradicate_field_not_mutable, - P.sprintf - "Field `%s` is modified but is not declared `@Mutable`. %s" - (Ident.fieldname_to_simplified_string fn) + Format.asprintf + "Field %a is modified but is not declared %a. %s" + MF.pp_monospaced (Ident.fieldname_to_simplified_string fn) + MF.pp_monospaced "@Mutable" origin_description, None, None, @@ -298,15 +303,17 @@ let report_error_now tenv let kind_s, description = match ann with | AnnotatedSignature.Nullable -> Localise.eradicate_field_not_nullable, - P.sprintf - "Field `%s` can be null but is not declared `@Nullable`. %s" - (Ident.fieldname_to_simplified_string fn) + Format.asprintf + "Field %a can be null but is not declared %a. %s" + MF.pp_monospaced (Ident.fieldname_to_simplified_string fn) + MF.pp_monospaced "@Nullable" origin_description | AnnotatedSignature.Present -> Localise.eradicate_field_value_absent, - P.sprintf - "Field `%s` is assigned a possibly absent value but is declared `@Present`. %s" - (Ident.fieldname_to_simplified_string fn) + Format.asprintf + "Field %a is assigned a possibly absent value but is declared %a. %s" + MF.pp_monospaced (Ident.fieldname_to_simplified_string fn) + MF.pp_monospaced "@Present" origin_description in true, kind_s, @@ -326,10 +333,11 @@ let report_error_now tenv Typ.Procname.to_simplified_string pn in true, Localise.eradicate_field_over_annotated, - P.sprintf - "Field `%s` is always initialized in %s but is declared `@Nullable`" - (Ident.fieldname_to_simplified_string fn) - constructor_name, + Format.asprintf + "Field %a is always initialized in %s but is declared %a" + MF.pp_monospaced (Ident.fieldname_to_simplified_string fn) + constructor_name + MF.pp_monospaced "@Nullable", None, Some fn, None @@ -337,11 +345,11 @@ let report_error_now tenv let at_index = if indexed then "element at index" else "field" in true, Localise.eradicate_null_field_access, - P.sprintf - "Object `%s` could be null when accessing %s `%s`. %s" - (Option.value s_opt ~default:"") + Format.asprintf + "Object %a could be null when accessing %s %a. %s" + MF.pp_monospaced (Option.value s_opt ~default:"") at_index - (Ident.fieldname_to_simplified_string fn) + MF.pp_monospaced (Ident.fieldname_to_simplified_string fn) origin_description, None, None, @@ -350,17 +358,18 @@ let report_error_now tenv let kind_s, description = match ann with | AnnotatedSignature.Nullable -> Localise.eradicate_null_method_call, - P.sprintf - "The value of `%s` in the call to `%s` could be null. %s" - (Option.value s_opt ~default:"") - (Typ.Procname.to_simplified_string pn) + Format.asprintf + "The value of %a in the call to %a could be null. %s" + MF.pp_monospaced (Option.value s_opt ~default:"") + MF.pp_monospaced (Typ.Procname.to_simplified_string pn) origin_description | AnnotatedSignature.Present -> Localise.eradicate_value_not_present, - P.sprintf - "The value of `%s` in the call to `%s` is not @Present. %s" - (Option.value s_opt ~default:"") - (Typ.Procname.to_simplified_string pn) + Format.asprintf + "The value of %a in the call to %a is not %a. %s" + MF.pp_monospaced (Option.value s_opt ~default:"") + MF.pp_monospaced (Typ.Procname.to_simplified_string pn) + MF.pp_monospaced "@Present" origin_description in true, kind_s, @@ -372,19 +381,19 @@ let report_error_now tenv let kind_s, description = match ann with | AnnotatedSignature.Nullable -> Localise.eradicate_parameter_not_nullable, - P.sprintf - "`%s` needs a non-null value in parameter %d but argument `%s` can be null. %s" - (Typ.Procname.to_simplified_string pn) + Format.asprintf + "%a needs a non-null value in parameter %d but argument %a can be null. %s" + MF.pp_monospaced (Typ.Procname.to_simplified_string pn) n - s + MF.pp_monospaced s origin_desc | AnnotatedSignature.Present -> Localise.eradicate_parameter_value_absent, - P.sprintf - "`%s` needs a present value in parameter %d but argument `%s` can be absent. %s" - (Typ.Procname.to_simplified_string pn) + Format.asprintf + "%a needs a present value in parameter %d but argument %a can be absent. %s" + MF.pp_monospaced (Typ.Procname.to_simplified_string pn) n - s + MF.pp_monospaced s origin_desc in true, kind_s, @@ -396,15 +405,17 @@ let report_error_now tenv let kind_s, description = match ann with | AnnotatedSignature.Nullable -> Localise.eradicate_return_not_nullable, - P.sprintf - "Method `%s` may return null but it is not annotated with `@Nullable`. %s" - (Typ.Procname.to_simplified_string pn) + Format.asprintf + "Method %a may return null but it is not annotated with %a. %s" + MF.pp_monospaced (Typ.Procname.to_simplified_string pn) + MF.pp_monospaced "@Nullable" origin_description | AnnotatedSignature.Present -> Localise.eradicate_return_value_not_present, - P.sprintf - "Method `%s` may return an absent value but it is annotated with `@Present`. %s" - (Typ.Procname.to_simplified_string pn) + Format.asprintf + "Method %a may return an absent value but it is annotated with %a. %s" + MF.pp_monospaced (Typ.Procname.to_simplified_string pn) + MF.pp_monospaced "@Present" origin_description in true, kind_s, @@ -415,19 +426,21 @@ let report_error_now tenv | Return_over_annotated pn -> false, Localise.eradicate_return_over_annotated, - P.sprintf - "Method `%s` is annotated with `@Nullable` but never returns null." - (Typ.Procname.to_simplified_string pn), + Format.asprintf + "Method %a is annotated with %a but never returns null." + MF.pp_monospaced (Typ.Procname.to_simplified_string pn) + MF.pp_monospaced "@Nullable", None, None, None | Inconsistent_subclass_return_annotation (pn, opn) -> false, Localise.eradicate_inconsistent_subclass_return_annotation, - P.sprintf - "Method `%s` is annotated with `@Nullable` but overrides unannotated method `%s`." - (Typ.Procname.to_simplified_string ~withclass: true pn) - (Typ.Procname.to_simplified_string ~withclass: true opn), + Format.asprintf + "Method %a is annotated with %a but overrides unannotated method %a." + MF.pp_monospaced (Typ.Procname.to_simplified_string ~withclass: true pn) + MF.pp_monospaced "@Nullable" + MF.pp_monospaced (Typ.Procname.to_simplified_string ~withclass: true opn), None, None, None @@ -439,12 +452,15 @@ let report_error_now tenv | n -> (string_of_int n)^"th" in false, Localise.eradicate_inconsistent_subclass_parameter_annotation, - P.sprintf - "%s parameter `%s` of method `%s` is not `@Nullable` but is declared `@Nullable`\ - in the parent class method `%s`." - (translate_position pos) param_name - (Typ.Procname.to_simplified_string ~withclass: true pn) - (Typ.Procname.to_simplified_string ~withclass: true opn), + Format.asprintf + "%s parameter %a of method %a is not %a but is declared %a\ + in the parent class method %a." + (translate_position pos) + MF.pp_monospaced param_name + MF.pp_monospaced (Typ.Procname.to_simplified_string ~withclass: true pn) + MF.pp_monospaced "@Nullable" + MF.pp_monospaced "@Nullable" + MF.pp_monospaced (Typ.Procname.to_simplified_string ~withclass: true opn), None, None, None in diff --git a/infer/tests/codetoanalyze/c/bufferoverrun/issues.exp b/infer/tests/codetoanalyze/c/bufferoverrun/issues.exp index a1ff4b301..6c4c966d0 100644 --- a/infer/tests/codetoanalyze/c/bufferoverrun/issues.exp +++ b/infer/tests/codetoanalyze/c/bufferoverrun/issues.exp @@ -1,9 +1,9 @@ codetoanalyze/c/bufferoverrun/break_continue_return.c, break_continue_return, 16, BUFFER_OVERRUN, [Offset : [0, 10] Size : [10, 10] @ codetoanalyze/c/bufferoverrun/break_continue_return.c:29:5] -codetoanalyze/c/bufferoverrun/do_while.c, do_while, 2, BUFFER_OVERRUN, [Offset : [0, +oo] Size : [10, 10] @ codetoanalyze/c/bufferoverrun/do_while.c:18:5 by call do_while_sub() ] -codetoanalyze/c/bufferoverrun/do_while.c, do_while, 3, BUFFER_OVERRUN, [Offset : [0, +oo] Size : [10, 10] @ codetoanalyze/c/bufferoverrun/do_while.c:18:5 by call do_while_sub() ] +codetoanalyze/c/bufferoverrun/do_while.c, do_while, 2, BUFFER_OVERRUN, [Offset : [0, +oo] Size : [10, 10] @ codetoanalyze/c/bufferoverrun/do_while.c:18:5 by call `do_while_sub()` ] +codetoanalyze/c/bufferoverrun/do_while.c, do_while, 3, BUFFER_OVERRUN, [Offset : [0, +oo] Size : [10, 10] @ codetoanalyze/c/bufferoverrun/do_while.c:18:5 by call `do_while_sub()` ] codetoanalyze/c/bufferoverrun/for_loop.c, for_loop, 10, BUFFER_OVERRUN, [Offset : [0, 9] Size : [5, 10] @ codetoanalyze/c/bufferoverrun/for_loop.c:38:5] -codetoanalyze/c/bufferoverrun/function_call.c, function_call, 4, BUFFER_OVERRUN, [Offset : [100, 100] Size : [10, 10] @ codetoanalyze/c/bufferoverrun/function_call.c:17:3 by call arr_access() ] -codetoanalyze/c/bufferoverrun/function_call.c, function_call, 4, BUFFER_OVERRUN, [Offset : [20, 20] Size : [10, 10] @ codetoanalyze/c/bufferoverrun/function_call.c:18:3 by call arr_access() ] +codetoanalyze/c/bufferoverrun/function_call.c, function_call, 4, BUFFER_OVERRUN, [Offset : [20, 20] Size : [10, 10] @ codetoanalyze/c/bufferoverrun/function_call.c:18:3 by call `arr_access()` ] +codetoanalyze/c/bufferoverrun/function_call.c, function_call, 4, BUFFER_OVERRUN, [Offset : [100, 100] Size : [10, 10] @ codetoanalyze/c/bufferoverrun/function_call.c:17:3 by call `arr_access()` ] codetoanalyze/c/bufferoverrun/goto_loop.c, goto_loop, 11, BUFFER_OVERRUN, [Offset : [10, +oo] Size : [10, 10] @ codetoanalyze/c/bufferoverrun/goto_loop.c:24:3] codetoanalyze/c/bufferoverrun/nested_loop.c, nested_loop, 7, BUFFER_OVERRUN, [Offset : [0, 10] Size : [10, 10] @ codetoanalyze/c/bufferoverrun/nested_loop.c:20:7] codetoanalyze/c/bufferoverrun/nested_loop_with_label.c, nested_loop_with_label, 6, BUFFER_OVERRUN, [Offset : [0, +oo] Size : [10, 10] @ codetoanalyze/c/bufferoverrun/nested_loop_with_label.c:19:5] diff --git a/infer/tests/codetoanalyze/java/eradicate/issues.exp b/infer/tests/codetoanalyze/java/eradicate/issues.exp index 1efb68963..c6af37bbf 100644 --- a/infer/tests/codetoanalyze/java/eradicate/issues.exp +++ b/infer/tests/codetoanalyze/java/eradicate/issues.exp @@ -52,7 +52,7 @@ codetoanalyze/java/eradicate/ParameterNotNullable.java, void ParameterNotNullabl codetoanalyze/java/eradicate/ParameterNotNullable.java, void ParameterNotNullable.testThreeParameters(), 3, ERADICATE_PARAMETER_NOT_NULLABLE, [origin,`threeParameters(...)` needs a non-null value in parameter 2 but argument `null` can be null. (Origin: null constant at line 85)] codetoanalyze/java/eradicate/ParameterNotNullable.java, void ParameterNotNullable.testThreeParameters(), 4, ERADICATE_PARAMETER_NOT_NULLABLE, [origin,`threeParameters(...)` needs a non-null value in parameter 3 but argument `null` can be null. (Origin: null constant at line 86)] codetoanalyze/java/eradicate/PresentTest.java, Optional PresentTest$TestPresentAnnotationBasic.returnPresentBad(), 0, ERADICATE_RETURN_VALUE_NOT_PRESENT, [origin,Method `returnPresentBad()` may return an absent value but it is annotated with `@Present`. (Origin: field PresentTest$TestPresentAnnotationBasic.absent at line 47)] -codetoanalyze/java/eradicate/PresentTest.java, Optional PresentTest$TestPresentAnnotationBasic.returnPresentBad(), 1, ERADICATE_VALUE_NOT_PRESENT, [origin,The value of `PresentTest$TestPresentAnnotationBasic.absent` in the call to `get()` is not @Present. (Origin: field PresentTest$TestPresentAnnotationBasic.absent at line 47)] +codetoanalyze/java/eradicate/PresentTest.java, Optional PresentTest$TestPresentAnnotationBasic.returnPresentBad(), 1, ERADICATE_VALUE_NOT_PRESENT, [origin,The value of `PresentTest$TestPresentAnnotationBasic.absent` in the call to `get()` is not `@Present`. (Origin: field PresentTest$TestPresentAnnotationBasic.absent at line 47)] codetoanalyze/java/eradicate/PresentTest.java, void PresentTest$TestPresentAnnotationBasic.testOptionalAbsent(), 1, ERADICATE_PARAMETER_VALUE_ABSENT, [origin,`expectPresent(...)` needs a present value in parameter 1 but argument `absent()` can be absent. (Origin: call to absent() at line 65)] codetoanalyze/java/eradicate/PresentTest.java, void PresentTest.testPresent(Optional,Optional), 4, ERADICATE_PARAMETER_VALUE_ABSENT, [`argPresent(...)` needs a present value in parameter 1 but argument `absent` can be absent. (Origin: method parameter absent)] codetoanalyze/java/eradicate/ReturnNotNullable.java, Object ReturnNotNullable$ConditionalAssignment.test(boolean), 0, ERADICATE_RETURN_NOT_NULLABLE, [origin,Method `test(...)` may return null but it is not annotated with `@Nullable`. (Origin: field ReturnNotNullable$ConditionalAssignment.f1 at line 146)]