diff --git a/infer/src/backend/paths.ml b/infer/src/backend/paths.ml index f6731f38f..ce7f759ae 100644 --- a/infer/src/backend/paths.ml +++ b/infer/src/backend/paths.ml @@ -464,7 +464,7 @@ end = struct trace := Errlog.make_trace_element level curr_loc descr node_tags :: !trace ; Option.iter ~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 = Format.sprintf "Definition of %s" (Typ.Procname.to_simplified_string pname) in diff --git a/infer/src/java/jClasspath.ml b/infer/src/java/jClasspath.ml index e82b6ef86..b11f6c2a1 100644 --- a/infer/src/java/jClasspath.ml +++ b/infer/src/java/jClasspath.ml @@ -226,9 +226,15 @@ let load_from_arguments classes_out_path = (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 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 @@ -240,6 +246,20 @@ let add_class cn jclass program = 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 lookup_node cn program = @@ -270,7 +290,10 @@ let load_program classpath classes = else collect_classes JBasics.ClassMap.empty !models_jar in 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 JBasics.ClassSet.iter (fun cn -> ignore (lookup_node cn program)) classes ; L.(debug Capture Medium) "done@." ; diff --git a/infer/src/java/jClasspath.mli b/infer/src/java/jClasspath.mli index 43bdea897..41426c359 100644 --- a/infer/src/java/jClasspath.mli +++ b/infer/src/java/jClasspath.mli @@ -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 (** 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 diff --git a/infer/src/java/jFrontend.ml b/infer/src/java/jFrontend.ml index 800074d4e..10108347e 100644 --- a/infer/src/java/jFrontend.ml +++ b/infer/src/java/jFrontend.ml @@ -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 ; let translate m = let proc_name = JTransType.translate_method_name m in + JClasspath.set_callee_translated program proc_name ; if JClasspath.is_model proc_name then (* do not translate the method if there is a model for it *) L.(debug Capture Verbose) diff --git a/infer/src/java/jMain.ml b/infer/src/java/jMain.ml index abd5e4aac..a8bab5ecf 100644 --- a/infer/src/java/jMain.ml +++ b/infer/src/java/jMain.ml @@ -79,6 +79,13 @@ let save_tenv 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 *) let do_all_files classpath sources classes = L.(debug Capture Quiet) @@ -113,6 +120,7 @@ let do_all_files classpath sources classes = source_files ) sources ; if Config.dependency_mode then capture_libs linereader program tenv ; + store_callee_attributes tenv program ; save_tenv tenv ; JClasspath.cleanup program ; L.(debug Capture Quiet) "done capturing all files@." diff --git a/infer/src/java/jTrans.ml b/infer/src/java/jTrans.ml index ae52d5609..5c04b654c 100644 --- a/infer/src/java/jTrans.ml +++ b/infer/src/java/jTrans.ml @@ -284,6 +284,35 @@ let trans_access = function 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 start_kind = Procdesc.Node.Start_node proc_name 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 else JTransType.get_method_procname cn' ms method_kind in + JClasspath.add_missing_callee program callee_procname cn' ms ; let call_instrs = let callee_fun = Exp.Const (Const.Cfun callee_procname) in let return_type = diff --git a/infer/src/java/jTrans.mli b/infer/src/java/jTrans.mli index b8cbb0cfb..a3aa0e7ba 100644 --- a/infer/src/java/jTrans.mli +++ b/infer/src/java/jTrans.mli @@ -21,6 +21,10 @@ type translation = 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 : SourceFile.t -> JClasspath.program -> JContext.icfg -> Javalib.abstract_method -> Typ.Procname.t -> Procdesc.t diff --git a/infer/tests/build_systems/genrule/issues.exp b/infer/tests/build_systems/genrule/issues.exp index 9b5412472..60cf4a3b5 100644 --- a/infer/tests/build_systems/genrule/issues.exp +++ b/infer/tests/build_systems/genrule/issues.exp @@ -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.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)]