|
|
|
@ -9,6 +9,10 @@ open! IStd
|
|
|
|
|
|
|
|
|
|
(** Make sure callbacks are always unregistered. drive the point home by reporting possible NPE's *)
|
|
|
|
|
|
|
|
|
|
let on_create_view = "onCreateView"
|
|
|
|
|
|
|
|
|
|
let on_destroy_view = "onDestroyView"
|
|
|
|
|
|
|
|
|
|
let rec format_typ typ =
|
|
|
|
|
match typ.Typ.desc with
|
|
|
|
|
| Typ.Tptr (t, _) when Language.curr_language_is Java ->
|
|
|
|
@ -19,11 +23,6 @@ let rec format_typ typ =
|
|
|
|
|
Typ.to_string typ
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let format_field f =
|
|
|
|
|
if Language.curr_language_is Java then Typ.Fieldname.Java.get_field f
|
|
|
|
|
else Typ.Fieldname.to_string f
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let format_method pname =
|
|
|
|
|
match pname with
|
|
|
|
|
| Typ.Procname.Java pname_java ->
|
|
|
|
@ -32,24 +31,27 @@ let format_method pname =
|
|
|
|
|
Typ.Procname.to_string pname
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let report_warning fragment_typ fld fld_typ summary pdesc =
|
|
|
|
|
let pname = Procdesc.get_proc_name pdesc in
|
|
|
|
|
let report_warning class_name fld fld_typ summary =
|
|
|
|
|
let pname = Summary.get_proc_name summary in
|
|
|
|
|
let loc = Summary.get_loc summary in
|
|
|
|
|
let pp_m = MarkupFormatter.pp_monospaced in
|
|
|
|
|
let description =
|
|
|
|
|
Printf.sprintf
|
|
|
|
|
"Fragment %s does not nullify View field %s (type %s) in %s. If this Fragment is placed on \
|
|
|
|
|
Format.asprintf
|
|
|
|
|
"Fragment %a does not nullify View field %a (type %a) in %a. If this Fragment is placed on \
|
|
|
|
|
the back stack, a reference to this (probably dead) View will be retained. In general, it \
|
|
|
|
|
is a good idea to initialize View's in onCreateView, then nullify them in onDestroyView."
|
|
|
|
|
(format_typ fragment_typ) (format_field fld) (format_typ fld_typ) (format_method pname)
|
|
|
|
|
is a good idea to initialize View's in %a, then nullify them in %a."
|
|
|
|
|
pp_m (Typ.Name.name class_name) pp_m
|
|
|
|
|
(Typ.Fieldname.to_flat_string fld)
|
|
|
|
|
pp_m (format_typ fld_typ) pp_m (format_method pname) pp_m on_create_view pp_m on_destroy_view
|
|
|
|
|
in
|
|
|
|
|
let loc = Procdesc.get_loc pdesc in
|
|
|
|
|
Reporting.log_warning summary ~loc IssueType.checkers_fragment_retain_view description
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let callback_fragment_retains_view_java pname_java {Callbacks.proc_desc; summary; tenv} =
|
|
|
|
|
let callback_fragment_retains_view_java java_pname {Callbacks.proc_desc; summary; tenv} =
|
|
|
|
|
(* TODO: complain if onDestroyView is not defined, yet the Fragment has View fields *)
|
|
|
|
|
(* TODO: handle fields nullified in callees in the same file *)
|
|
|
|
|
let is_on_destroy_view =
|
|
|
|
|
String.equal (Typ.Procname.Java.get_method pname_java) "onDestroyView"
|
|
|
|
|
String.equal (Typ.Procname.Java.get_method java_pname) on_destroy_view
|
|
|
|
|
in
|
|
|
|
|
let fld_typ_is_view typ =
|
|
|
|
|
match typ.Typ.desc with
|
|
|
|
@ -64,10 +66,10 @@ let callback_fragment_retains_view_java pname_java {Callbacks.proc_desc; summary
|
|
|
|
|
Typ.Name.equal fld_classname class_typename && fld_typ_is_view fld_typ
|
|
|
|
|
in
|
|
|
|
|
if is_on_destroy_view then
|
|
|
|
|
let class_typename = Typ.Name.Java.from_string (Typ.Procname.Java.get_class_name pname_java) in
|
|
|
|
|
match Tenv.lookup tenv class_typename with
|
|
|
|
|
| Some {fields} when AndroidFramework.is_fragment tenv class_typename ->
|
|
|
|
|
let declared_view_fields = List.filter ~f:(is_declared_view_typ class_typename) fields in
|
|
|
|
|
let class_name = Typ.Name.Java.from_string (Typ.Procname.Java.get_class_name java_pname) in
|
|
|
|
|
match Tenv.lookup tenv class_name with
|
|
|
|
|
| Some {fields} when AndroidFramework.is_fragment tenv class_name ->
|
|
|
|
|
let declared_view_fields = List.filter ~f:(is_declared_view_typ class_name) fields in
|
|
|
|
|
let fields_nullified = PatternMatch.get_fields_nullified proc_desc in
|
|
|
|
|
(* report if a field is declared by C, but not nulled out in C.onDestroyView *)
|
|
|
|
|
List.iter
|
|
|
|
@ -76,8 +78,7 @@ let callback_fragment_retains_view_java pname_java {Callbacks.proc_desc; summary
|
|
|
|
|
not
|
|
|
|
|
( Annotations.ia_ends_with ia Annotations.auto_cleanup
|
|
|
|
|
|| Typ.Fieldname.Set.mem fname fields_nullified )
|
|
|
|
|
then report_warning (Typ.mk (Tstruct class_typename)) fname fld_typ summary proc_desc
|
|
|
|
|
)
|
|
|
|
|
then report_warning class_name fname fld_typ summary )
|
|
|
|
|
declared_view_fields
|
|
|
|
|
| _ ->
|
|
|
|
|
()
|
|
|
|
@ -86,8 +87,8 @@ let callback_fragment_retains_view_java pname_java {Callbacks.proc_desc; summary
|
|
|
|
|
let callback_fragment_retains_view ({Callbacks.summary} as args) : Summary.t =
|
|
|
|
|
let proc_name = Summary.get_proc_name summary in
|
|
|
|
|
( match proc_name with
|
|
|
|
|
| Typ.Procname.Java pname_java ->
|
|
|
|
|
callback_fragment_retains_view_java pname_java args
|
|
|
|
|
| Typ.Procname.Java java_pname ->
|
|
|
|
|
callback_fragment_retains_view_java java_pname args
|
|
|
|
|
| _ ->
|
|
|
|
|
() ) ;
|
|
|
|
|
summary
|
|
|
|
|