Reviewed By: jeremydubreil Differential Revision: D5144831 fbshipit-source-id: edc9cf4master
parent
1d9d7a3ee0
commit
07c6fbef81
@ -0,0 +1,98 @@
|
||||
(*
|
||||
* 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
|
||||
|
||||
module F = Format
|
||||
module L = Logging
|
||||
|
||||
module Domain =
|
||||
AbstractDomain.FiniteSet(struct
|
||||
type t = AccessPath.Raw.t * Location.t
|
||||
|
||||
(* Ignore location while comparing access paths.
|
||||
* They are there just for error reporting purpose, and are pretty much irrelevant
|
||||
* from the analysis' perspective *)
|
||||
let compare (ap0, _) (ap1, _) = [%compare: AccessPath.Raw.t] ap0 ap1
|
||||
|
||||
let pp fmt (ap, loc) = F.fprintf fmt "(%a, %a)" AccessPath.Raw.pp ap Location.pp loc
|
||||
end)
|
||||
|
||||
(* Right now this is just a placeholder. We'll change it to something useful when necessary *)
|
||||
module Summary = Summary.Make(struct
|
||||
type payload = unit
|
||||
|
||||
let update_payload _ summary = summary
|
||||
let read_payload _ = None
|
||||
end)
|
||||
|
||||
type extras = unit
|
||||
|
||||
module TransferFunctions (CFG : ProcCfg.S) = struct
|
||||
module CFG = CFG
|
||||
module Domain = Domain
|
||||
type nonrec extras = extras
|
||||
|
||||
let is_null = HilExp.is_null_literal
|
||||
|
||||
let is_access_nullable ap proc_data =
|
||||
match AccessPath.Raw.get_field_and_annotation ap proc_data.ProcData.tenv with
|
||||
| Some (_, annot_item) ->
|
||||
Annotations.ia_is_nullable annot_item
|
||||
| _ -> false
|
||||
|
||||
let exec_instr (astate : Domain.astate) proc_data _ (instr : HilInstr.t) =
|
||||
match instr with
|
||||
| Assume _ ->
|
||||
(* For now we just assume that conditionals does not have any implications on nullability *)
|
||||
astate
|
||||
| Call _ ->
|
||||
(* For now we just assume the callee always return non-null *)
|
||||
astate
|
||||
| Assign (lhs, rhs, loc) ->
|
||||
if is_null rhs && not (is_access_nullable lhs proc_data) then
|
||||
Domain.add (lhs, loc) astate
|
||||
else
|
||||
astate
|
||||
end
|
||||
|
||||
module Analyzer =
|
||||
AbstractInterpreter.Make
|
||||
(ProcCfg.Exceptional)
|
||||
(LowerHil.Make (TransferFunctions))
|
||||
module Interprocedural = AbstractInterpreter.Interprocedural (Summary)
|
||||
|
||||
let checker callback =
|
||||
let report nullable_aps (proc_data : extras ProcData.t) =
|
||||
let report_access_path (ap, loc) =
|
||||
let pname = Procdesc.get_proc_name proc_data.pdesc in
|
||||
let issue_kind = Localise.to_issue_id Localise.field_should_be_nullable in
|
||||
match AccessPath.Raw.get_field_and_annotation ap proc_data.tenv with
|
||||
| Some (field_name, _) ->
|
||||
let message =
|
||||
F.asprintf "Field %s should be annotated with @Nullable"
|
||||
(Fieldname.to_string field_name) in
|
||||
let exn = Exceptions.Checkers (issue_kind, Localise.verbatim_desc message) in
|
||||
Reporting.log_warning pname ~loc exn
|
||||
| _ -> ()
|
||||
in
|
||||
Domain.iter report_access_path nullable_aps
|
||||
in
|
||||
let compute_post (proc_data : extras ProcData.t) =
|
||||
(* Assume all fields are not null in the beginning *)
|
||||
let initial = Domain.empty, IdAccessPathMapDomain.empty in
|
||||
match Analyzer.compute_post proc_data ~initial ~debug:false with
|
||||
| Some (post, _) ->
|
||||
report post proc_data;
|
||||
Some ()
|
||||
| None ->
|
||||
failwithf
|
||||
"Analyzer failed to compute post for %a"
|
||||
Typ.Procname.pp (Procdesc.get_proc_name proc_data.pdesc)
|
||||
in
|
||||
Interprocedural.compute_and_store_post ~compute_post ~make_extras:(fun _ -> ()) callback
|
@ -0,0 +1,13 @@
|
||||
(*
|
||||
* 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.
|
||||
*)
|
||||
|
||||
(* Module that suggest adding nullability annotations *)
|
||||
|
||||
open! IStd
|
||||
val checker: Callbacks.proc_callback_t
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package codetoanalyze.java.checkers;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class NullableSuggest {
|
||||
private Object obj0;
|
||||
@Nullable private Object obj1;
|
||||
|
||||
private static class OtherClass {
|
||||
private Object obj2;
|
||||
@Nullable private Object obj3;
|
||||
}
|
||||
|
||||
public void noAssignNullOk() {
|
||||
}
|
||||
|
||||
public void assignNotNullOk() {
|
||||
obj0 = new Object();
|
||||
obj1 = new Object();
|
||||
}
|
||||
|
||||
public void assignNullBad() {
|
||||
obj0 = null;
|
||||
}
|
||||
|
||||
public void assignNullToNullableOk() {
|
||||
obj1 = null;
|
||||
}
|
||||
|
||||
public void assignNullToFieldInOtherClassBad() {
|
||||
OtherClass oc = new OtherClass();
|
||||
oc.obj2 = null;
|
||||
}
|
||||
|
||||
public void assignNullToNullableFieldInOtherClassOk() {
|
||||
OtherClass oc = new OtherClass();
|
||||
oc.obj3 = null;
|
||||
}
|
||||
}
|
Loading…
Reference in new issue