diff --git a/infer/src/IR/Struct.ml b/infer/src/IR/Struct.ml index 7990a5268..217061f1c 100644 --- a/infer/src/IR/Struct.ml +++ b/infer/src/IR/Struct.ml @@ -12,7 +12,13 @@ type field = Fieldname.t * Typ.t * Annot.Item.t [@@deriving compare] type fields = field list -type java_class_kind = Interface | AbstractClass | NormalClass +type java_class_kind = Interface | AbstractClass | NormalClass [@@deriving equal] + +type java_class_info = + { kind: java_class_kind (** class kind in Java *) + ; loc: Location.t option + (** None should correspond to rare cases when it was impossible to fetch the location in + source file *) } (** Type for a structured value. *) type t = @@ -23,7 +29,7 @@ type t = ; methods: Procname.t list (** methods defined *) ; exported_objc_methods: Procname.t list (** methods in ObjC interface, subset of [methods] *) ; annots: Annot.Item.t (** annotations *) - ; java_class_kind: java_class_kind option (** class kind in Java *) + ; java_class_info: java_class_info option (** present if and only if the class is Java *) ; dummy: bool (** dummy struct for class including static method *) } type lookup = Typ.Name.t -> t option @@ -60,7 +66,7 @@ let pp pe name f {fields; supers; methods; exported_objc_methods; annots} = let internal_mk_struct ?default ?fields ?statics ?methods ?exported_objc_methods ?supers ?annots - ?java_class_kind ?dummy () = + ?java_class_info ?dummy () = let default_ = { fields= [] ; statics= [] @@ -69,14 +75,14 @@ let internal_mk_struct ?default ?fields ?statics ?methods ?exported_objc_methods ; supers= [] ; subs= Typ.Name.Set.empty ; annots= Annot.Item.empty - ; java_class_kind= None + ; java_class_info= None ; dummy= false } in let mk_struct_ ?(default = default_) ?(fields = default.fields) ?(statics = default.statics) ?(methods = default.methods) ?(exported_objc_methods = default.exported_objc_methods) ?(supers = default.supers) ?(subs = default.subs) ?(annots = default.annots) ?(dummy = default.dummy) () = - {fields; statics; methods; exported_objc_methods; supers; subs; annots; java_class_kind; dummy} + {fields; statics; methods; exported_objc_methods; supers; subs; annots; java_class_info; dummy} in mk_struct_ ?default ?fields ?statics ?methods ?exported_objc_methods ?supers ?annots ?dummy () diff --git a/infer/src/IR/Struct.mli b/infer/src/IR/Struct.mli index 286c489c5..08487f388 100644 --- a/infer/src/IR/Struct.mli +++ b/infer/src/IR/Struct.mli @@ -13,18 +13,24 @@ type field = Fieldname.t * Typ.t * Annot.Item.t [@@deriving compare] type fields = field list -type java_class_kind = Interface | AbstractClass | NormalClass +type java_class_kind = Interface | AbstractClass | NormalClass [@@deriving equal] + +type java_class_info = + { kind: java_class_kind (** class kind in Java *) + ; loc: Location.t option + (** None should correspond to rare cases when it was impossible to fetch the location in + source file *) } (** Type for a structured value. *) -type t = private +type t = { fields: fields (** non-static fields *) ; statics: fields (** static fields *) - ; supers: Typ.Name.t list (** supers *) + ; supers: Typ.Name.t list (** superclasses *) ; subs: Typ.Name.Set.t (** subclasses, initialized after merging type environments *) ; methods: Procname.t list (** methods defined *) ; exported_objc_methods: Procname.t list (** methods in ObjC interface, subset of [methods] *) ; annots: Annot.Item.t (** annotations *) - ; java_class_kind: java_class_kind option (** class kind in Java *) + ; java_class_info: java_class_info option (** present if and only if the class is Java *) ; dummy: bool (** dummy struct for class including static method *) } type lookup = Typ.Name.t -> t option @@ -42,7 +48,7 @@ val internal_mk_struct : -> ?exported_objc_methods:Procname.t list -> ?supers:Typ.Name.t list -> ?annots:Annot.Item.t - -> ?java_class_kind:java_class_kind + -> ?java_class_info:java_class_info -> ?dummy:bool -> unit -> t diff --git a/infer/src/IR/Tenv.ml b/infer/src/IR/Tenv.ml index f2916a51e..7bd313d70 100644 --- a/infer/src/IR/Tenv.ml +++ b/infer/src/IR/Tenv.ml @@ -30,10 +30,10 @@ let create () = TypenameHash.create 1000 (** Construct a struct type in a type environment *) let mk_struct tenv ?default ?fields ?statics ?methods ?exported_objc_methods ?supers ?annots - ?java_class_kind ?dummy name = + ?java_class_info ?dummy name = let struct_typ = Struct.internal_mk_struct ?default ?fields ?statics ?methods ?exported_objc_methods ?supers - ?annots ?java_class_kind ?dummy () + ?annots ?java_class_info ?dummy () in TypenameHash.replace tenv name struct_typ ; struct_typ @@ -224,13 +224,15 @@ let get_summary_formals tenv ~get_summary ~get_formals = `NotFound | Some class_name -> ( match lookup tenv class_name with - | Some {Struct.java_class_kind= Some Interface; subs} - when Int.equal (Typ.Name.Set.cardinal subs) 1 -> + | Some {Struct.java_class_info= Some info; subs} + when Struct.equal_java_class_kind info.kind Interface + && Int.equal (Typ.Name.Set.cardinal subs) 1 -> let unique_sub = Typ.Name.Set.choose subs in Logging.d_printfln_escaped "Found a unique sub-class %a" Typ.Name.pp unique_sub ; let sub_pname = Procname.replace_class pname unique_sub in get_summary_formals_aux sub_pname |> found_from_subclass sub_pname - | Some {Struct.java_class_kind= Some AbstractClass; subs} -> + | Some {Struct.java_class_info= Some info; subs} + when Struct.equal_java_class_kind info.kind AbstractClass -> Option.value_map (Typ.Name.Set.min_elt_opt subs) ~default:`NotFound ~f:(fun sub -> Logging.d_printfln_escaped "Found an arbitrary sub-class %a" Typ.Name.pp sub ; let sub_pname = Procname.replace_class pname sub in diff --git a/infer/src/IR/Tenv.mli b/infer/src/IR/Tenv.mli index 436690b89..02ce81dea 100644 --- a/infer/src/IR/Tenv.mli +++ b/infer/src/IR/Tenv.mli @@ -41,7 +41,7 @@ val mk_struct : -> ?exported_objc_methods:Procname.t list -> ?supers:Typ.Name.t list -> ?annots:Annot.Item.t - -> ?java_class_kind:Struct.java_class_kind + -> ?java_class_info:Struct.java_class_info -> ?dummy:bool -> Typ.Name.t -> Struct.t diff --git a/infer/src/checkers/NullabilityPreanalysis.ml b/infer/src/checkers/NullabilityPreanalysis.ml index 113c3e292..f3287d8e7 100644 --- a/infer/src/checkers/NullabilityPreanalysis.ml +++ b/infer/src/checkers/NullabilityPreanalysis.ml @@ -78,11 +78,9 @@ let add_nonnull_to_fields fields tenv = match Typ.name typ with | Some typ_name -> ( match Tenv.lookup tenv typ_name with - | Some {fields; statics; supers; methods; annots} -> + | Some ({fields} as struct_typ) -> let fields_with_annot = List.map ~f:(add_nonnull_to_selected_field field) fields in - ignore - (Tenv.mk_struct tenv ~fields:fields_with_annot ~statics ~supers ~methods ~annots - typ_name) + ignore (Tenv.mk_struct tenv ~default:struct_typ ~fields:fields_with_annot typ_name) | None -> () ) | None -> diff --git a/infer/src/java/jClasspath.ml b/infer/src/java/jClasspath.ml index 1b83abdbe..f6d013d1c 100644 --- a/infer/src/java/jClasspath.ml +++ b/infer/src/java/jClasspath.ml @@ -199,13 +199,28 @@ type callee_status = Translated | Missing of JBasics.class_name * JBasics.method type classmap = JCode.jcode Javalib.interface_or_class JBasics.ClassMap.t +(** We store for each classname the location of its declaration. This map is filled during + JFrontend.compute_source_icfg and then it is used in JTransType.get_class_struct_typ before we + lose access to JClasspath.program. At the end, the information seats in each Struct.t (stored in + Tenv.t) *) +type java_location_map = Location.t JBasics.ClassMap.t + type program = { classpath_channel: Javalib.class_path ; mutable classmap: classmap + ; mutable java_location_map: java_location_map ; callees: callee_status Procname.Hash.t } let get_classmap program = program.classmap +let set_java_location program cn loc = + program.java_location_map <- JBasics.ClassMap.add cn loc program.java_location_map + + +let get_java_location program cn = + try Some (JBasics.ClassMap.find cn program.java_location_map) with Caml.Not_found -> None + + let mem_classmap cn program = JBasics.ClassMap.mem cn program.classmap let get_classpath_channel program = program.classpath_channel @@ -258,6 +273,7 @@ let load_program ~classpath classes = let program = { classpath_channel= Javalib.class_path classpath ; classmap= JBasics.ClassMap.empty + ; java_location_map= JBasics.ClassMap.empty ; callees= Procname.Hash.create 128 } in JBasics.ClassSet.iter (fun cn -> ignore (lookup_node cn program)) classes ; diff --git a/infer/src/java/jClasspath.mli b/infer/src/java/jClasspath.mli index 90b811fb4..be7373497 100644 --- a/infer/src/java/jClasspath.mli +++ b/infer/src/java/jClasspath.mli @@ -26,6 +26,10 @@ type program val get_classmap : program -> classmap +val set_java_location : program -> JBasics.class_name -> Location.t -> unit + +val get_java_location : program -> JBasics.class_name -> Location.t option + val mem_classmap : JBasics.class_name -> program -> bool val cleanup : program -> unit diff --git a/infer/src/java/jFrontend.ml b/infer/src/java/jFrontend.ml index 7b6b6b1eb..169f38d46 100644 --- a/infer/src/java/jFrontend.ml +++ b/infer/src/java/jFrontend.ml @@ -163,7 +163,7 @@ let create_icfg source_file program tenv icfg cn node = (* returns true for the set of classes that are selected to be translated in the given - progam *) + program *) let should_capture program package_opt source_basename node = let classname = Javalib.get_name node in let match_package pkg cn = @@ -194,6 +194,19 @@ let compute_source_icfg program tenv source_basename package_opt source_file = let select test procedure cn node = if test node then try procedure cn node with Bir.Subroutine -> () in + let set_java_location cn _node = + let cn_name = JBasics.cn_name cn in + let loc = JSourceFileInfo.class_name_location source_file cn_name in + L.debug Capture Verbose "set_java_location %s with location %a@." cn_name Location.pp_file_pos + loc ; + JClasspath.set_java_location program cn loc + in + (* we must set the java location for all classes in the source file before translation *) + let () = + JBasics.ClassMap.iter + (select (should_capture program package_opt source_basename) set_java_location) + (JClasspath.get_classmap program) + in let () = JBasics.ClassMap.iter (select diff --git a/infer/src/java/jSourceFileInfo.ml b/infer/src/java/jSourceFileInfo.ml new file mode 100644 index 000000000..56a5a1c35 --- /dev/null +++ b/infer/src/java/jSourceFileInfo.ml @@ -0,0 +1,13 @@ +(* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) + +open! IStd + +(** this is a naive/temporary implementation in a near diff, we will 1) parse the source file to + collect location datas for all class names 2) cache the result for later uses but we may have to + adapt a litle some signatures *) +let class_name_location file _cn : Location.t = {line= 0; col= 0; file} diff --git a/infer/src/java/jSourceFileInfo.mli b/infer/src/java/jSourceFileInfo.mli new file mode 100644 index 000000000..b05a009ae --- /dev/null +++ b/infer/src/java/jSourceFileInfo.mli @@ -0,0 +1,12 @@ +(* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) + +open! IStd + +val class_name_location : SourceFile.t -> string -> Location.t +(** [class_name_location source class_name] searches in file [source] the declaration location for + class name [class_name] *) diff --git a/infer/src/java/jTransType.ml b/infer/src/java/jTransType.ml index 0d326e57b..c1f83bb53 100644 --- a/infer/src/java/jTransType.ml +++ b/infer/src/java/jTransType.ml @@ -324,7 +324,16 @@ and get_class_struct_typ = let methods = Javalib.m_fold (fun m procnames -> translate_method_name program tenv m :: procnames) node [] in - Tenv.mk_struct tenv ~fields ~statics ~methods ~supers ~annots ~java_class_kind name + let node_name = Javalib.get_name node in + let java_location : Location.t option = JClasspath.get_java_location program node_name in + ( match java_location with + | Some loc -> + L.debug Capture Verbose "Java location %s -> %a@." (JBasics.cn_name node_name) + Location.pp_file_pos loc + | None -> + () ) ; + let java_class_info : Struct.java_class_info = {kind= java_class_kind; loc= java_location} in + Tenv.mk_struct tenv ~fields ~statics ~methods ~supers ~annots ~java_class_info name in fun program tenv cn -> let name = typename_of_classname cn in