diff --git a/infer/src/nullsafe/ThirdPartyAnnotationInfo.ml b/infer/src/nullsafe/ThirdPartyAnnotationInfo.ml index dd7279d90..0720c8500 100644 --- a/infer/src/nullsafe/ThirdPartyAnnotationInfo.ml +++ b/infer/src/nullsafe/ThirdPartyAnnotationInfo.ml @@ -20,23 +20,27 @@ let pp_parsing_error fmt {line_number; unparsable_method; parsing_error} = (* Consequtively evaluates results for all elements in a list, - returns Ok () if all succeeded or the first error. + returns Ok (folded results) if all succeeded, or the first error. The evaluator function [f] has access to element's index. *) -let bind_list_with_index list ~f = - List.foldi list ~init:(Ok ()) ~f:(fun index acc elem -> Result.bind acc ~f:(fun _ -> f index elem)) +let bind_list_with_index ~init list ~f = + List.foldi list ~init:(Ok init) ~f:(fun index acc elem -> + Result.bind acc ~f:(fun acc -> f acc index elem) ) -let parse_line_and_add_to_storage storage line = +let parse_line_and_add_to_storage storage _line_index line = let open Result in ThirdPartyMethod.parse line - >>= fun (signature, nullability) -> Ok (Hashtbl.add storage signature nullability) + >>= fun (signature, nullability) -> + Ok + ( Hashtbl.add storage signature nullability ; + storage ) let add_from_signature_file storage ~lines = (* each line in a file should represent a method signature *) - bind_list_with_index lines ~f:(fun index method_as_str -> - parse_line_and_add_to_storage storage method_as_str + bind_list_with_index lines ~init:storage ~f:(fun storage index method_as_str -> + parse_line_and_add_to_storage storage index method_as_str |> Result.map_error ~f:(fun parsing_error -> {line_number= index + 1; unparsable_method= method_as_str; parsing_error} ) ) diff --git a/infer/src/nullsafe/ThirdPartyAnnotationInfo.mli b/infer/src/nullsafe/ThirdPartyAnnotationInfo.mli index e95c820dd..793fb6ca9 100644 --- a/infer/src/nullsafe/ThirdPartyAnnotationInfo.mli +++ b/infer/src/nullsafe/ThirdPartyAnnotationInfo.mli @@ -17,7 +17,7 @@ type file_parsing_error = val pp_parsing_error : Format.formatter -> file_parsing_error -> unit -val add_from_signature_file : storage -> lines:string list -> (unit, file_parsing_error) result +val add_from_signature_file : storage -> lines:string list -> (storage, file_parsing_error) result (** Parse the information from the signature file, and add it to the storage *) val find_nullability_info : diff --git a/infer/src/unit/nullsafe/ThirdPartyAnnotationInfoTests.ml b/infer/src/unit/nullsafe/ThirdPartyAnnotationInfoTests.ml index c141568f9..094a47f91 100644 --- a/infer/src/unit/nullsafe/ThirdPartyAnnotationInfoTests.ml +++ b/infer/src/unit/nullsafe/ThirdPartyAnnotationInfoTests.ml @@ -34,16 +34,18 @@ let assert_no_info storage unique_repr = let add_from_annot_file_and_check_success storage ~lines = - ThirdPartyAnnotationInfo.add_from_signature_file storage ~lines - |> Result.iter_error ~f:(fun parsing_error -> - assert_failure - (F.asprintf "Expected to parse the file, but it was unparsable: %a" - ThirdPartyAnnotationInfo.pp_parsing_error parsing_error) ) + match ThirdPartyAnnotationInfo.add_from_signature_file storage ~lines with + | Ok storage -> + storage + | Error parsing_error -> + assert_failure + (F.asprintf "Expected to parse the file, but it was unparsable: %a" + ThirdPartyAnnotationInfo.pp_parsing_error parsing_error) let add_from_annot_file_and_check_failure storage ~lines ~expected_error_line_number = match ThirdPartyAnnotationInfo.add_from_signature_file storage ~lines with - | Ok () -> + | Ok _ -> assert_failure "Expected to not be able to parse the file, but it was successfully parsed instead" | Error {line_number} -> @@ -55,10 +57,11 @@ let basic_find = let open ThirdPartyMethod in "basic_find" >:: fun _ -> - let storage = ThirdPartyAnnotationInfo.create_storage () in let lines = ["a.A#foo(b.B)"; "b.B#bar(c.C, @Nullable d.D) @Nullable"] in (* Load some functions from the file *) - add_from_annot_file_and_check_success storage ~lines ; + let storage = + add_from_annot_file_and_check_success (ThirdPartyAnnotationInfo.create_storage ()) ~lines + in (* Make sure we can find what we just stored *) assert_has_nullability_info storage {class_name= "a.A"; method_name= Method "foo"; param_types= ["b.B"]} @@ -83,7 +86,6 @@ let overload_resolution = let open ThirdPartyMethod in "overload_resolution" >:: fun _ -> - let storage = ThirdPartyAnnotationInfo.create_storage () in let lines = [ "a.b.SomeClass#foo(@Nullable a.b.C1) @Nullable" ; "a.b.SomeClass#(a.b.C1)" @@ -94,7 +96,9 @@ let overload_resolution = ; "a.b.SomeClass#foo(@Nullable a.b.C2)" ] in (* Load some functions from the file *) - add_from_annot_file_and_check_success storage ~lines ; + let storage = + add_from_annot_file_and_check_success (ThirdPartyAnnotationInfo.create_storage ()) ~lines + in (* Make sure we can find what we just stored *) (* a.b.SomeClass.foo with 1 param *) assert_has_nullability_info storage @@ -146,16 +150,17 @@ let can_add_several_files = "can_add_several_files" >:: fun _ -> let open ThirdPartyMethod in - let storage = ThirdPartyAnnotationInfo.create_storage () in (* 1. Add file and check if we added info *) let file1 = ["a.A#foo(b.B)"; "b.B#bar(c.C, @Nullable d.D) @Nullable"] in - add_from_annot_file_and_check_success storage ~lines:file1 ; + let storage = + add_from_annot_file_and_check_success (ThirdPartyAnnotationInfo.create_storage ()) ~lines:file1 + in assert_has_nullability_info storage {class_name= "a.A"; method_name= Method "foo"; param_types= ["b.B"]} ~expected_nullability:{ret_nullability= Nonnull; param_nullability= [Nonnull]} ; (* 2. Add another file and check if we added info *) let file2 = ["e.E#baz(f.F)"; "g.G#(h.H, @Nullable i.I) @Nullable"] in - add_from_annot_file_and_check_success storage ~lines:file2 ; + let storage = add_from_annot_file_and_check_success storage ~lines:file2 in assert_has_nullability_info storage {class_name= "e.E"; method_name= Method "baz"; param_types= ["f.F"]} ~expected_nullability:{ret_nullability= Nonnull; param_nullability= [Nonnull]} ; @@ -176,7 +181,8 @@ let should_not_forgive_unparsable_strings = let file_ok = [line1; line2_ok; line3] in let file_bad = [line1; line2_bad; line3] in (* Ensure we can add the good file, but can not add the bad one *) - add_from_annot_file_and_check_success (ThirdPartyAnnotationInfo.create_storage ()) ~lines:file_ok ; + add_from_annot_file_and_check_success (ThirdPartyAnnotationInfo.create_storage ()) ~lines:file_ok + |> ignore ; add_from_annot_file_and_check_failure (ThirdPartyAnnotationInfo.create_storage ()) ~lines:file_bad ~expected_error_line_number:2