From c24ff02bb4bab54798067ae935738f6abead780b Mon Sep 17 00:00:00 2001 From: Dulma Churchill Date: Wed, 9 May 2018 02:36:28 -0700 Subject: [PATCH] [biabduction] Add to the skip reason when the method comes from a protocol Reviewed By: jvillard Differential Revision: D7877741 fbshipit-source-id: 5c5d8fd --- infer/src/IR/Typ.ml | 6 +++ infer/src/IR/Typ.mli | 2 + infer/src/biabduction/SymExec.ml | 38 ++++++++++++------- infer/src/clang/CType_decl.ml | 5 ++- .../codetoanalyze/objc/errors/issues.exp | 2 +- 5 files changed, 37 insertions(+), 16 deletions(-) diff --git a/infer/src/IR/Typ.ml b/infer/src/IR/Typ.ml index 06ef5904a..e3b25db65 100644 --- a/infer/src/IR/Typ.ml +++ b/infer/src/IR/Typ.ml @@ -359,6 +359,8 @@ module Name = struct let is_class = function CppClass _ | JavaClass _ | ObjcClass _ -> true | _ -> false + let is_objc_protocol name = match name with ObjcProtocol _ -> true | _ -> false + let is_same_type t1 t2 = match (t1, t2) with | CStruct _, CStruct _ @@ -878,6 +880,10 @@ module Procname = struct t + let is_method_in_objc_protocol t = + match t with ObjC_Cpp osig -> Name.is_objc_protocol osig.class_name | _ -> false + + let rec objc_cpp_replace_method_name t (new_method_name: string) = match t with | ObjC_Cpp osig -> diff --git a/infer/src/IR/Typ.mli b/infer/src/IR/Typ.mli index 943e9aa1e..81f6f72c7 100644 --- a/infer/src/IR/Typ.mli +++ b/infer/src/IR/Typ.mli @@ -508,6 +508,8 @@ module Procname : sig (** Replace the class name component of a procedure name. In case of Java, replace package and class name. *) + val is_method_in_objc_protocol : t -> bool + val to_string : t -> string (** Convert a proc name to a string for the user to see. *) diff --git a/infer/src/biabduction/SymExec.ml b/infer/src/biabduction/SymExec.ml index f98da7ed0..098e0a47a 100644 --- a/infer/src/biabduction/SymExec.ml +++ b/infer/src/biabduction/SymExec.ml @@ -358,13 +358,22 @@ let check_inherently_dangerous_function caller_pname callee_pname = Reporting.log_warning_deprecated caller_pname exn -let reason_to_skip callee_summary : string option = - let attributes = Specs.get_attributes callee_summary in - if attributes.ProcAttributes.is_abstract then Some "abstract method" - else if not attributes.ProcAttributes.is_defined then Some "method has no implementation" - else if List.is_empty (Specs.get_specs_from_payload callee_summary) then - Some "empty list of specs" - else None +let reason_to_skip ~callee_desc : string option = + match callee_desc with + | Some (`Summary callee_summary) -> + let attributes = Specs.get_attributes callee_summary in + if attributes.ProcAttributes.is_abstract then Some "abstract method" + else if not attributes.ProcAttributes.is_defined then Some "method has no implementation" + else if List.is_empty (Specs.get_specs_from_payload callee_summary) then + Some "empty list of specs" + else (* we are not skipping *) None + | Some (`ProcName callee_pname) -> + (* no summary, so we are skipping, determining reasons *) + if Typ.Procname.is_method_in_objc_protocol callee_pname then + Some "no implementation found for method declared in Objective-C protocol" + else Some "function or method not found" + | None -> + Some "function or method not found" (** In case of constant string dereference, return the result immediately *) @@ -1175,7 +1184,7 @@ let rec sym_exec exe_env tenv current_pdesc instr_ (prop_: Prop.normal Prop.t) p let ret_annots = load_ret_annots callee_pname in exec_skip_call ~reason:"unknown method" resolved_pname ret_annots ret_typ | Some resolved_summary -> - match reason_to_skip resolved_summary with + match reason_to_skip ~callee_desc:(Some (`Summary resolved_summary)) with | None -> proc_call exe_env resolved_summary (call_args prop_ callee_pname norm_args ret_id_typ loc) @@ -1201,7 +1210,7 @@ let rec sym_exec exe_env tenv current_pdesc instr_ (prop_: Prop.normal Prop.t) p let ret_annots = load_ret_annots callee_pname in exec_skip_call ~reason:"unknown method" ret_annots ret_typ | Some callee_summary -> - match reason_to_skip callee_summary with + match reason_to_skip ~callee_desc:(Some (`Summary callee_summary)) with | None -> let handled_args = call_args norm_prop pname url_handled_args ret_id_typ loc in proc_call exe_env callee_summary handled_args @@ -1249,11 +1258,14 @@ let rec sym_exec exe_env tenv current_pdesc instr_ (prop_: Prop.normal Prop.t) p else [(prop_r, path)] in let do_call (prop, path) = - let reason_to_skip_opt = - Option.value_map ~f:reason_to_skip ~default:(Some "function or method not found") - resolved_summary_opt + let callee_desc = + match resolved_summary_opt with + | Some summary -> + Some (`Summary summary) + | None -> + Some (`ProcName resolved_pname) in - match reason_to_skip_opt with + match reason_to_skip ~callee_desc with | Some reason -> ( let ret_annots = diff --git a/infer/src/clang/CType_decl.ml b/infer/src/clang/CType_decl.ml index 7849b4b8d..6f4367752 100644 --- a/infer/src/clang/CType_decl.ml +++ b/infer/src/clang/CType_decl.ml @@ -284,9 +284,10 @@ and get_record_typename ?tenv decl = Typ.Name.Cpp.from_qual_name Typ.NoTemplate (CAst_utils.get_qualified_name ~linters_mode name_info) | ObjCInterfaceDecl (_, name_info, _, _, _), _ - | ObjCImplementationDecl (_, name_info, _, _, _), _ - | ObjCProtocolDecl (_, name_info, _, _, _), _ -> + | ObjCImplementationDecl (_, name_info, _, _, _), _ -> CAst_utils.get_qualified_name name_info |> Typ.Name.Objc.from_qual_name + | ObjCProtocolDecl (_, name_info, _, _, _), _ -> + CAst_utils.get_qualified_name name_info |> Typ.Name.Objc.protocol_from_qual_name | ObjCCategoryDecl (_, _, _, _, {odi_class_interface= Some {dr_name}}), _ | ObjCCategoryImplDecl (_, _, _, _, {ocidi_class_interface= Some {dr_name}}), _ -> ( match dr_name with diff --git a/infer/tests/codetoanalyze/objc/errors/issues.exp b/infer/tests/codetoanalyze/objc/errors/issues.exp index aca634098..2d9976013 100644 --- a/infer/tests/codetoanalyze/objc/errors/issues.exp +++ b/infer/tests/codetoanalyze/objc/errors/issues.exp @@ -87,7 +87,7 @@ codetoanalyze/objc/errors/npe/skip_method_with_nil_object.m, SkipMethodNilA_test codetoanalyze/objc/errors/property/main.c, property_main, 3, MEMORY_LEAK, ERROR, [start of procedure property_main(),Skipping aProperty: function or method not found] codetoanalyze/objc/errors/resource_leaks/Dispatch_sources.m, ProcessContentsOfFile, 35, RESOURCE_LEAK, ERROR, [start of procedure ProcessContentsOfFile(),Taking false branch,Skipping dispatch_get_global_queue(): function or method not found,Skipping dispatch_source_create(): function or method not found,Taking false branch] codetoanalyze/objc/errors/resource_leaks/Dispatch_sources.m, objc_blockProcessContentsOfFile_2, 6, MEMORY_LEAK, ERROR, [start of procedure block,Skipping dispatch_source_get_data(): function or method not found,Taking true branch,Skipping MyProcessFileData(): function or method not found] -codetoanalyze/objc/errors/resource_leaks/ResourceLeakExample.m, NSFileHandle_fileHandleForLoggingAtPath:mode:, 9, RESOURCE_LEAK, ERROR, [start of procedure fileHandleForLoggingAtPath:mode:,Taking true branch,Skipping fileSystemRepresentation: function or method not found,Taking false branch,Taking true branch,Skipping autorelease: function or method not found] +codetoanalyze/objc/errors/resource_leaks/ResourceLeakExample.m, NSFileHandle_fileHandleForLoggingAtPath:mode:, 9, RESOURCE_LEAK, ERROR, [start of procedure fileHandleForLoggingAtPath:mode:,Taking true branch,Skipping fileSystemRepresentation: function or method not found,Taking false branch,Taking true branch,Skipping autorelease: no implementation found for method declared in Objective-C protocol] codetoanalyze/objc/shared/annotations/nonnull_annotations.m, A_test1:, 2, PARAMETER_NOT_NULL_CHECKED, WARNING, [start of procedure test1:,Message child with receiver nil returns nil.] codetoanalyze/objc/shared/annotations/nonnull_annotations.m, A_test3:, 1, PARAMETER_NOT_NULL_CHECKED, WARNING, [start of procedure test3:] codetoanalyze/objc/shared/annotations/nullable_annotations.m, User_otherUserName, 2, NULL_DEREFERENCE, ERROR, [start of procedure otherUserName,Skipping otherUser: function or method not found]