[infer][java] translate the annotations of the callees

Summary:
This allows Eradicate to lookup the annotations from the classpath and without requiring the code in the classpath to have been previously analyzed. The benefit is that source files can be analyzed independently of each other as long as the classpath is known.

The main goal is to run be able to run Eradicate as a linter without losing warnings.

We may have to add some more models of the standard libraries as no `Nullable` on a parameter does not necessarily mean that the method does not accept `null`.

Reviewed By: sblackshear

Differential Revision: D6921720

fbshipit-source-id: f525269
master
Jeremy Dubreil 7 years ago committed by Facebook Github Bot
parent b4d046a290
commit b903b2d932

@ -464,7 +464,7 @@ end = struct
trace := Errlog.make_trace_element level curr_loc descr node_tags :: !trace ; trace := Errlog.make_trace_element level curr_loc descr node_tags :: !trace ;
Option.iter Option.iter
~f:(fun loc -> ~f:(fun loc ->
if Typ.Procname.is_java pname then if Typ.Procname.is_java pname && not (SourceFile.is_invalid loc.Location.file) then
let definition_descr = let definition_descr =
Format.sprintf "Definition of %s" (Typ.Procname.to_simplified_string pname) Format.sprintf "Definition of %s" (Typ.Procname.to_simplified_string pname)
in in

@ -226,9 +226,15 @@ let load_from_arguments classes_out_path =
(classpath, search_sources (), classes) (classpath, search_sources (), classes)
type callee_status = Translated | Missing of JBasics.class_name * JBasics.method_signature
type classmap = JCode.jcode Javalib.interface_or_class JBasics.ClassMap.t type classmap = JCode.jcode Javalib.interface_or_class JBasics.ClassMap.t
type program = {classpath: Javalib.class_path; models: classmap; mutable classmap: classmap} type program =
{ classpath: Javalib.class_path
; models: classmap
; mutable classmap: classmap
; callees: callee_status Typ.Procname.Hash.t }
let get_classmap program = program.classmap let get_classmap program = program.classmap
@ -240,6 +246,20 @@ let add_class cn jclass program =
program.classmap <- JBasics.ClassMap.add cn jclass program.classmap program.classmap <- JBasics.ClassMap.add cn jclass program.classmap
let set_callee_translated program pname =
Typ.Procname.Hash.replace program.callees pname Translated
let add_missing_callee program pname cn ms =
if not (Typ.Procname.Hash.mem program.callees pname) then
Typ.Procname.Hash.add program.callees pname (Missing (cn, ms))
let iter_missing_callees program ~f =
let select proc_name = function Translated -> () | Missing (cn, ms) -> f proc_name cn ms in
Typ.Procname.Hash.iter select program.callees
let cleanup program = Javalib.close_class_path program.classpath let cleanup program = Javalib.close_class_path program.classpath
let lookup_node cn program = let lookup_node cn program =
@ -270,7 +290,10 @@ let load_program classpath classes =
else collect_classes JBasics.ClassMap.empty !models_jar else collect_classes JBasics.ClassMap.empty !models_jar
in in
let program = let program =
{classpath= Javalib.class_path classpath; models; classmap= JBasics.ClassMap.empty} { classpath= Javalib.class_path classpath
; models
; classmap= JBasics.ClassMap.empty
; callees= Typ.Procname.Hash.create 128 }
in in
JBasics.ClassSet.iter (fun cn -> ignore (lookup_node cn program)) classes ; JBasics.ClassSet.iter (fun cn -> ignore (lookup_node cn program)) classes ;
L.(debug Capture Medium) "done@." ; L.(debug Capture Medium) "done@." ;

@ -44,3 +44,13 @@ val load_program : string -> JBasics.ClassSet.t -> program
val lookup_node : JBasics.class_name -> program -> JCode.jcode Javalib.interface_or_class option val lookup_node : JBasics.class_name -> program -> JCode.jcode Javalib.interface_or_class option
(** retrive a Java node from the classname *) (** retrive a Java node from the classname *)
val add_missing_callee :
program -> Typ.Procname.t -> JBasics.class_name -> JBasics.method_signature -> unit
(** add the class name of method signature to the list of callees *)
val set_callee_translated : program -> Typ.Procname.t -> unit
(** set that the CFG for the procedure has been created *)
val iter_missing_callees :
program -> f:(Typ.Procname.t -> JBasics.class_name -> JBasics.method_signature -> unit) -> unit

@ -141,6 +141,7 @@ let create_icfg source_file linereader program icfg cn node =
if Config.dependency_mode && not (is_classname_cached cn) then cache_classname cn ; if Config.dependency_mode && not (is_classname_cached cn) then cache_classname cn ;
let translate m = let translate m =
let proc_name = JTransType.translate_method_name m in let proc_name = JTransType.translate_method_name m in
JClasspath.set_callee_translated program proc_name ;
if JClasspath.is_model proc_name then if JClasspath.is_model proc_name then
(* do not translate the method if there is a model for it *) (* do not translate the method if there is a model for it *)
L.(debug Capture Verbose) L.(debug Capture Verbose)

@ -79,6 +79,13 @@ let save_tenv tenv =
Tenv.store_global tenv Tenv.store_global tenv
let store_callee_attributes tenv program =
let f proc_name cn ms =
Option.iter ~f:Attributes.store (JTrans.create_callee_attributes tenv program cn ms proc_name)
in
JClasspath.iter_missing_callees program ~f
(* The program is loaded and translated *) (* The program is loaded and translated *)
let do_all_files classpath sources classes = let do_all_files classpath sources classes =
L.(debug Capture Quiet) L.(debug Capture Quiet)
@ -113,6 +120,7 @@ let do_all_files classpath sources classes =
source_files ) source_files )
sources ; sources ;
if Config.dependency_mode then capture_libs linereader program tenv ; if Config.dependency_mode then capture_libs linereader program tenv ;
store_callee_attributes tenv program ;
save_tenv tenv ; save_tenv tenv ;
JClasspath.cleanup program ; JClasspath.cleanup program ;
L.(debug Capture Quiet) "done capturing all files@." L.(debug Capture Quiet) "done capturing all files@."

@ -284,6 +284,35 @@ let trans_access = function
PredSymb.Protected PredSymb.Protected
let create_callee_attributes tenv program cn ms procname =
let f jclass =
try
let jmethod = Javalib.get_method jclass ms in
let formals =
formals_from_signature program tenv cn ms (JTransType.get_method_kind jmethod)
in
let ret_type = JTransType.return_type program tenv ms in
let access, method_annotation, exceptions, is_abstract =
match jmethod with
| Javalib.AbstractMethod am ->
( trans_access am.Javalib.am_access
, JAnnotation.translate_method am.Javalib.am_annotations
, List.map ~f:JBasics.cn_name am.Javalib.am_exceptions
, true )
| Javalib.ConcreteMethod cm ->
( trans_access cm.Javalib.cm_access
, JAnnotation.translate_method cm.Javalib.cm_annotations
, List.map ~f:JBasics.cn_name cm.Javalib.cm_exceptions
, false )
in
Some
{ (ProcAttributes.default procname) with
ProcAttributes.access; exceptions; method_annotation; formals; ret_type; is_abstract }
with Not_found -> None
in
Option.bind ~f (JClasspath.lookup_node cn program)
let create_empty_cfg proc_name source_file procdesc = let create_empty_cfg proc_name source_file procdesc =
let start_kind = Procdesc.Node.Start_node proc_name in let start_kind = Procdesc.Node.Start_node proc_name in
let start_node = Procdesc.create_node procdesc (Location.none source_file) start_kind [] in let start_node = Procdesc.create_node procdesc (Location.none source_file) start_kind [] in
@ -625,6 +654,7 @@ let method_invocation (context: JContext.t) loc pc var_opt cn ms sil_obj_opt exp
then proc then proc
else JTransType.get_method_procname cn' ms method_kind else JTransType.get_method_procname cn' ms method_kind
in in
JClasspath.add_missing_callee program callee_procname cn' ms ;
let call_instrs = let call_instrs =
let callee_fun = Exp.Const (Const.Cfun callee_procname) in let callee_fun = Exp.Const (Const.Cfun callee_procname) in
let return_type = let return_type =

@ -21,6 +21,10 @@ type translation =
val is_java_native : JCode.jcode Javalib.concrete_method -> bool val is_java_native : JCode.jcode Javalib.concrete_method -> bool
val create_callee_attributes :
Tenv.t -> JClasspath.program -> JBasics.class_name -> JBasics.method_signature -> Typ.Procname.t
-> ProcAttributes.t option
val create_am_procdesc : val create_am_procdesc :
SourceFile.t -> JClasspath.program -> JContext.icfg -> Javalib.abstract_method -> Typ.Procname.t SourceFile.t -> JClasspath.program -> JContext.icfg -> Javalib.abstract_method -> Typ.Procname.t
-> Procdesc.t -> Procdesc.t

@ -1,3 +1,7 @@
build_systems/genrule/module2/Class2.java, void Class2.dereferenceInterTargetFieldBad(Class1), 1, ERADICATE_NULL_METHOD_CALL, [origin,The value of `class1.field1` in the call to `toString()` could be null. (Origin: field Class1.field1 at line 55)] build_systems/genrule/module2/Class2.java, void Class2.dereferenceInterTargetFieldBad(Class1), 1, ERADICATE_NULL_METHOD_CALL, [origin,The value of `class1.field1` in the call to `toString()` could be null. (Origin: field Class1.field1 at line 55)]
build_systems/genrule/module2/Class2.java, void Class2.dereferenceLocalNullableFieldBad(), 1, ERADICATE_NULL_METHOD_CALL, [origin,The value of `Class2.field2` in the call to `toString()` could be null. (Origin: field Class2.field2 at line 51)] build_systems/genrule/module2/Class2.java, void Class2.dereferenceLocalNullableFieldBad(), 1, ERADICATE_NULL_METHOD_CALL, [origin,The value of `Class2.field2` in the call to `toString()` could be null. (Origin: field Class2.field2 at line 51)]
build_systems/genrule/module2/Class2.java, void Class2.followMethodDeclarationOnlyBad(SkipImplementationClass1), 2, ERADICATE_NULL_METHOD_CALL, [origin,The value of `obj2` in the call to `toString()` could be null. (Origin: call to annotatedNullable() at line 41)]
build_systems/genrule/module2/Class2.java, void Class2.interTargetAbstractNPEBad(Class1), 2, ERADICATE_NULL_METHOD_CALL, [origin,The value of `obj` in the call to `toString()` could be null. (Origin: call to abstractMayReturnNull() at line 31)]
build_systems/genrule/module2/Class2.java, void Class2.interTargetNPEBad(), 2, ERADICATE_NULL_METHOD_CALL, [origin,The value of `obj` in the call to `toString()` could be null. (Origin: call to returnsNull() at line 26)]
build_systems/genrule/module2/Class2.java, void Class2.interTargetNativeNPEBad(Class1), 2, ERADICATE_NULL_METHOD_CALL, [origin,The value of `obj` in the call to `toString()` could be null. (Origin: call to nativeMayReturnNull() at line 36)]
build_systems/genrule/module2/Class2.java, void Class2.localNPE2Bad(), 2, ERADICATE_NULL_METHOD_CALL, [origin,The value of `obj` in the call to `toString()` could be null. (Origin: null constant at line 21)] build_systems/genrule/module2/Class2.java, void Class2.localNPE2Bad(), 2, ERADICATE_NULL_METHOD_CALL, [origin,The value of `obj` in the call to `toString()` could be null. (Origin: null constant at line 21)]

Loading…
Cancel
Save