[nullsafe] Functionality to load 3rd party info from the storage

Summary:
In this diff, we just load the info from the storage. Next diff will be
actually using this information to infer nullability.

`ThirdPartyAnnotationGlobalRepo.get_repo` will be used in the next diff,
hence #skipdeadcode

Reviewed By: artempyanykh

Differential Revision: D18347647

fbshipit-source-id: 82a9223c6
master
Mitya Lyubarskiy 5 years ago committed by Facebook Github Bot
parent 7ea42938fe
commit 3d2df4cc3c

@ -1538,6 +1538,14 @@ INTERNAL OPTIONS
Activates: Warn when containers are used with nullable keys or
values (Conversely: --no-nullsafe-strict-containers)
--nullsafe-third-party-signatures string
Path to a folder with annotated signatures of third-party methods
to be taken into account by nullsafe. Path is relative to
.inferconfig folder.
--nullsafe-third-party-signatures-reset
Cancel the effect of --nullsafe-third-party-signatures.
--no-only-cheap-debug
Deactivates: Disable expensive debugging output (Conversely:
--only-cheap-debug)

@ -1758,6 +1758,12 @@ and nullable_annotation =
CLOpt.mk_string_opt ~long:"nullable-annotation-name" "Specify custom nullable annotation name"
and nullsafe_third_party_signatures =
CLOpt.mk_string_opt ~long:"nullsafe-third-party-signatures"
"Path to a folder with annotated signatures of third-party methods to be taken into account \
by nullsafe. Path is relative to .inferconfig folder."
and nullsafe_strict_containers =
CLOpt.mk_bool ~long:"nullsafe-strict-containers" ~default:false
"Warn when containers are used with nullable keys or values"
@ -2516,7 +2522,7 @@ and (_ : bool ref) =
(** Parse Command Line Args *)
let inferconfig_file =
let inferconfig_dir =
let rec find dir =
match Sys.file_exists ~follow_symlinks:false (dir ^/ CommandDoc.inferconfig_file) with
| `Yes ->
@ -2526,11 +2532,15 @@ let inferconfig_file =
let is_root = String.equal dir parent in
if is_root then None else find parent
in
find (Sys.getcwd ())
let inferconfig_file =
match Sys.getenv CommandDoc.inferconfig_env_var with
| Some _ as env_path ->
env_path
| None ->
find (Sys.getcwd ()) |> Option.map ~f:(fun dir -> dir ^/ CommandDoc.inferconfig_file)
Option.map inferconfig_dir ~f:(fun dir -> dir ^/ CommandDoc.inferconfig_file)
let post_parsing_initialization command_opt =
@ -2999,6 +3009,8 @@ and nelseg = !nelseg
and nullable_annotation = !nullable_annotation
and nullsafe_third_party_signatures = !nullsafe_third_party_signatures
and nullsafe_strict_containers = !nullsafe_strict_containers
and no_translate_libs = not !headers

@ -404,6 +404,8 @@ val implicit_sdk_root : string option
val inferconfig_file : string option
val inferconfig_dir : string option
val iphoneos_target_sdk_version : string option
val iphoneos_target_sdk_version_path_regex : iphoneos_target_sdk_version_path_regex list
@ -496,6 +498,8 @@ val nullable_annotation : string option
val nullsafe : bool
val nullsafe_third_party_signatures : string option
val nullsafe_strict_containers : bool
val oom_threshold : int option

@ -63,6 +63,7 @@ let setup () =
| Events ->
ResultsDir.assert_results_dir "have you run infer before?" ) ;
db_start () ;
NullsafeInit.init () ;
if CLOpt.is_originator then ( RunState.add_run_to_sequence () ; RunState.store () ) ;
()

@ -0,0 +1,45 @@
(*
* 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
let load_third_party_repo ~absolute_path_to_repo_dir =
match Sys.is_directory absolute_path_to_repo_dir with
| `Yes -> (
match ThirdPartyAnnotationInfoLoader.load ~path_to_repo_dir:absolute_path_to_repo_dir with
| Ok storage ->
storage
| Error error ->
Logging.die Logging.InternalError "Error while reading 3rd party annotation repo: %a"
ThirdPartyAnnotationInfoLoader.pp_load_error error )
| _ ->
Logging.die Logging.InternalError
"Could not locate 3rd party annotation repository: expected location %s"
absolute_path_to_repo_dir
let get_absolute_path_to_repo_dir relative_path_to_repo_dir =
match Config.inferconfig_dir with
| None ->
Logging.die Logging.InternalError
"Could not locate .inferconfig directory, which is required for resolving the path to \
third party annotation repository"
| Some inferconfig_dir ->
inferconfig_dir ^/ relative_path_to_repo_dir
let create_global_storage () =
match Config.nullsafe_third_party_signatures with
| Some dir ->
load_third_party_repo ~absolute_path_to_repo_dir:(get_absolute_path_to_repo_dir dir)
(* Create empty *)
| None ->
ThirdPartyAnnotationInfo.create_storage ()
let init () =
let global_storage = create_global_storage () in
ThirdPartyAnnotationGlobalRepo.initialize global_storage

@ -0,0 +1,10 @@
(*
* 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 init : unit -> unit
(** This function should be called once before nullsafe checker is called *)

@ -0,0 +1,25 @@
(*
* 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
let repo = ref None
let initialize storage =
match !repo with
| None ->
repo := Some storage
| Some _ ->
Logging.die Logging.InternalError "Attempt to initialize global 3rd party repository twice"
let get_repo () =
match !repo with
| None ->
Logging.die Logging.InternalError "Attempt to access not initialized 3rd party repository"
| Some repo ->
repo

@ -0,0 +1,16 @@
(*
* 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
(** A singleton. Should be initialized once prior to start of nullsafe. *)
val initialize : ThirdPartyAnnotationInfo.storage -> unit
(** Should be initialized exactly once before access. *)
val get_repo : unit -> ThirdPartyAnnotationInfo.storage
(** Can be accessed only when initialization was done *)

@ -0,0 +1,29 @@
(*
* 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
type load_error = {filename: string; parsing_error: ThirdPartyAnnotationInfo.file_parsing_error}
let pp_load_error fmt {filename; parsing_error} =
Format.fprintf fmt "Could not parse %s: %a" filename ThirdPartyAnnotationInfo.pp_parsing_error
parsing_error
let add_from_file storage ~path_to_repo_dir ~sig_file =
let lines = In_channel.read_lines (path_to_repo_dir ^ "/" ^ sig_file) in
ThirdPartyAnnotationInfo.add_from_signature_file storage ~lines
|> Result.map_error ~f:(fun parsing_error -> {filename= sig_file; parsing_error})
|> Result.bind ~f:(fun _ -> Ok storage)
let load ~path_to_repo_dir =
(* Sequentally load information from all .sig files *)
Sys.ls_dir path_to_repo_dir
|> List.filter ~f:(String.is_suffix ~suffix:".sig")
|> List.fold_result ~init:(ThirdPartyAnnotationInfo.create_storage ())
~f:(fun accumulated_storage sig_file ->
add_from_file accumulated_storage ~path_to_repo_dir ~sig_file )

@ -0,0 +1,17 @@
(*
* 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
type load_error
val pp_load_error : Format.formatter -> load_error -> unit
val load : path_to_repo_dir:string -> (ThirdPartyAnnotationInfo.storage, load_error) result
(** Given a path to a repo with 3rd annotation info, loads it from a disk to
in-memory representation.
After this is done, information can be requested via [ThirdPartyAnnotationInfo].
*)

@ -2,5 +2,6 @@
"external-java-packages": [
"external."
],
"nullsafe-strict-containers": true
"nullsafe-strict-containers": true,
"nullsafe-third-party-signatures": "third-party-annots"
}

@ -0,0 +1,2 @@
some.test.pckg.SomeClass#getNullable() @Nullable
some.test.pckg.SomeClass#getNonnull()
Loading…
Cancel
Save