From 7d4bd403df86f43e7f6336d86085cf094580d8be Mon Sep 17 00:00:00 2001 From: Brandon Kieft Date: Fri, 4 May 2018 07:16:17 -0700 Subject: [PATCH] Add is_objc_method_exposed predicate Reviewed By: dulmarod Differential Revision: D7862233 fbshipit-source-id: da2ee1b --- infer/src/clang/CLintersContext.ml | 3 + infer/src/clang/CLintersContext.mli | 2 + infer/src/clang/cFrontend_checkers_main.ml | 5 ++ infer/src/clang/cPredicates.ml | 60 +++++++++++++++++++ infer/src/clang/cPredicates.mli | 12 ++++ infer/src/clang/cTL.ml | 2 + .../al_definitions/linters_example.al | 9 +++ .../objc/linters-for-test-only/issues.exp | 28 +++++++++ 8 files changed, 121 insertions(+) diff --git a/infer/src/clang/CLintersContext.ml b/infer/src/clang/CLintersContext.ml index fdc86ca84..324cbbf56 100644 --- a/infer/src/clang/CLintersContext.ml +++ b/infer/src/clang/CLintersContext.ml @@ -26,6 +26,8 @@ type context = (** If inside an objc class, contains the objc class (impl or interface) decl. *) ; current_objc_category: Clang_ast_t.decl option (** If inside an objc category, contains the objc category (impl or interface) decl. *) + ; current_objc_protocol: Clang_ast_t.decl option + (** If inside an objc protocol, contains the objc protocol decl. *) ; et_evaluation_node: string option ; if_context: if_context option ; in_for_loop_declaration: bool } @@ -38,6 +40,7 @@ let empty translation_unit_context = ; is_ck_translation_unit= false ; current_objc_class= None ; current_objc_category= None + ; current_objc_protocol= None ; et_evaluation_node= None ; if_context= None ; in_for_loop_declaration= false } diff --git a/infer/src/clang/CLintersContext.mli b/infer/src/clang/CLintersContext.mli index 19be97ba7..8418c60fb 100644 --- a/infer/src/clang/CLintersContext.mli +++ b/infer/src/clang/CLintersContext.mli @@ -26,6 +26,8 @@ type context = (** If inside an objc class, contains the objc class (impl or interface) decl. *) ; current_objc_category: Clang_ast_t.decl option (** If inside an objc category, contains the objc category (impl or interface) decl. *) + ; current_objc_protocol: Clang_ast_t.decl option + (** If inside an objc protocol, contains the objc protocol decl. *) ; et_evaluation_node: string option ; if_context: if_context option ; in_for_loop_declaration: bool } diff --git a/infer/src/clang/cFrontend_checkers_main.ml b/infer/src/clang/cFrontend_checkers_main.ml index 3069ad191..533acc600 100644 --- a/infer/src/clang/cFrontend_checkers_main.ml +++ b/infer/src/clang/cFrontend_checkers_main.ml @@ -343,6 +343,11 @@ and do_frontend_checks_decl (context: CLintersContext.context) let context' = {context with current_objc_category= Some decl} in List.iter ~f:(do_frontend_checks_decl context' map_active) decls ; call_tableaux context' an map_active + | ObjCProtocolDecl (_, _, decls, _, _) -> + CFrontend_errors.invoke_set_of_checkers_on_node context an ; + let context' = {context with current_objc_protocol= Some decl} in + List.iter ~f:(do_frontend_checks_decl context' map_active) decls ; + call_tableaux context' an map_active | _ -> CFrontend_errors.invoke_set_of_checkers_on_node context an ; ( match Clang_ast_proj.get_decl_context_tuple decl with diff --git a/infer/src/clang/cPredicates.ml b/infer/src/clang/cPredicates.ml index 599339ac1..116656384 100644 --- a/infer/src/clang/cPredicates.ml +++ b/infer/src/clang/cPredicates.ml @@ -297,6 +297,66 @@ let is_objc_method_overriding an = false +let decl_list_has_objc_method decl_list method_name is_instance_method = + List.exists + ~f:(fun decl -> + match decl with + | Clang_ast_t.ObjCMethodDecl (_, ni, omdi) -> + Bool.equal omdi.omdi_is_instance_method is_instance_method + && String.equal ni.ni_name method_name + | _ -> + false ) + decl_list + + +let current_objc_container context = + let open CLintersContext in + let current_objc_class = context.current_objc_class in + let current_objc_category = context.current_objc_category in + let current_objc_protocol = context.current_objc_protocol in + if not (Option.is_none current_objc_class) then current_objc_class + else if not (Option.is_none current_objc_category) then current_objc_category + else if not (Option.is_none current_objc_protocol) then current_objc_protocol + else None + + +let is_objc_method_exposed context an = + let open Clang_ast_t in + if is_objc_method_overriding an then true + else + match an with + | Ctl_parser_types.Decl (ObjCMethodDecl (_, ndi, mdi)) + -> ( + let method_name = ndi.ni_name in + let is_instance_method = mdi.omdi_is_instance_method in + match current_objc_container context with + | Some (ObjCImplementationDecl (_, _, _, _, oidi)) -> ( + match CAst_utils.get_decl_opt_with_decl_ref oidi.oidi_class_interface with + | Some (ObjCInterfaceDecl (_, _, decl_list, _, otdi)) -> + decl_list_has_objc_method decl_list method_name is_instance_method + || List.exists + ~f:(fun decl_ref -> + match CAst_utils.get_decl decl_ref.dr_decl_pointer with + | Some (ObjCCategoryDecl (_, ni, decl_list, _, _)) -> + String.equal ni.ni_name "" + && decl_list_has_objc_method decl_list method_name is_instance_method + | _ -> + false ) + otdi.otdi_known_categories + | _ -> + false ) + | Some (ObjCCategoryImplDecl (_, _, _, _, ocidi)) -> ( + match CAst_utils.get_decl_opt_with_decl_ref ocidi.ocidi_category_decl with + | Some (ObjCCategoryDecl (_, _, decl_list, _, _)) -> + decl_list_has_objc_method decl_list method_name is_instance_method + | _ -> + false ) + | _ -> + false ) + | _ -> + false + + (* checks whether an object is of a certain class *) let is_object_of_class_named receiver cname = let open Clang_ast_t in diff --git a/infer/src/clang/cPredicates.mli b/infer/src/clang/cPredicates.mli index ebdbd67ea..ca4950bee 100644 --- a/infer/src/clang/cPredicates.mli +++ b/infer/src/clang/cPredicates.mli @@ -248,6 +248,18 @@ val is_objc_method_overriding : Ctl_parser_types.ast_node -> bool * is not considered as overriding the same method in the interface or its categories. *) +val is_objc_method_exposed : CLintersContext.context -> Ctl_parser_types.ast_node -> bool +(** + * Checks if the current node is an ObjCMethodDecl node and is exposed in an interface. + * + * A method is said to be exposed if it's overriding a method or it's declared + * in a matching interface. For example, a method defined in a class's + * implementation is exposed if it's declared in the class's interface or + * interface extension, but not if it's declared in a category on the class. + * If the current node is a subnode of an ObjCInterfaceDecl, ObjCCategoryDecl, + * or ObjCProtocolDecl, this predicate returns false. + *) + val captures_cxx_references : Ctl_parser_types.ast_node -> bool (** 'captures_cxx_references an' is true iff the node an captures some CXX references *) diff --git a/infer/src/clang/cTL.ml b/infer/src/clang/cTL.ml index a4eddf549..2bd064186 100644 --- a/infer/src/clang/cTL.ml +++ b/infer/src/clang/cTL.ml @@ -1068,6 +1068,8 @@ let rec eval_Atomic pred_name_ args an lcxt = CPredicates.is_objc_method_named an name | "is_objc_method_overriding", [], an -> CPredicates.is_objc_method_overriding an + | "is_objc_method_exposed", [], an -> + CPredicates.is_objc_method_exposed lcxt an | "is_property_pointer_type", [], an -> CPredicates.is_property_pointer_type an | "is_strong_property", [], an -> diff --git a/infer/tests/codetoanalyze/objc/linters-for-test-only/al_definitions/linters_example.al b/infer/tests/codetoanalyze/objc/linters-for-test-only/al_definitions/linters_example.al index 021d47946..2ff16e9b5 100644 --- a/infer/tests/codetoanalyze/objc/linters-for-test-only/al_definitions/linters_example.al +++ b/infer/tests/codetoanalyze/objc/linters-for-test-only/al_definitions/linters_example.al @@ -594,3 +594,12 @@ DEFINE-CHECKER TEST_IS_OVERRIDING_METHOD = { SET message = "Method %name% is overriding a method in a superclass."; }; + +DEFINE-CHECKER TEST_IS_METHOD_EXPOSED = { + + SET report_when = + is_objc_method_exposed; + + SET message = "Method %name% is exposed in an interface."; + +}; diff --git a/infer/tests/codetoanalyze/objc/linters-for-test-only/issues.exp b/infer/tests/codetoanalyze/objc/linters-for-test-only/issues.exp index 974450949..201a65326 100644 --- a/infer/tests/codetoanalyze/objc/linters-for-test-only/issues.exp +++ b/infer/tests/codetoanalyze/objc/linters-for-test-only/issues.exp @@ -1,6 +1,10 @@ +codetoanalyze/objc/linters-for-test-only/CallingAMethodWithSelf.m, CallingAMethodWithSelfBase_testView, 16, TEST_IS_METHOD_EXPOSED, WARNING, [] codetoanalyze/objc/linters-for-test-only/CallingAMethodWithSelf.m, CallingAMethodWithSelfBase_testView, 17, TEST_VAR_TYPE_CHECK, WARNING, [] +codetoanalyze/objc/linters-for-test-only/CallingAMethodWithSelf.m, CallingAMethodWithSelf_methodThatShallComplain, 40, TEST_IS_METHOD_EXPOSED, WARNING, [] codetoanalyze/objc/linters-for-test-only/CallingAMethodWithSelf.m, CallingAMethodWithSelf_methodThatShallComplain, 41, TEST_IF_VIEW_METHOD_IS_NOT_CALLED_WITH_SUPER, WARNING, [] +codetoanalyze/objc/linters-for-test-only/CallingAMethodWithSelf.m, CallingAMethodWithSelf_testView, 27, TEST_IS_METHOD_EXPOSED, WARNING, [] codetoanalyze/objc/linters-for-test-only/CallingAMethodWithSelf.m, CallingAMethodWithSelf_testView, 27, TEST_IS_OVERRIDING_METHOD, WARNING, [] +codetoanalyze/objc/linters-for-test-only/CallingAMethodWithSelf.m, CallingAMethodWithSelf_testView, 44, TEST_IS_METHOD_EXPOSED, WARNING, [] codetoanalyze/objc/linters-for-test-only/CallingAMethodWithSelf.m, CallingAMethodWithSelf_testView, 44, TEST_IS_OVERRIDING_METHOD, WARNING, [] codetoanalyze/objc/linters-for-test-only/CallingAMethodWithSelf.m, CallingAMethodWithSelf_testView, 45, TEST_VAR_TYPE_CHECK, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, Linters_dummy_method, 14, TEST_IF_IS_CLASS_NAMED, WARNING, [] @@ -24,28 +28,50 @@ codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MyBaseClass_myBaseC codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MyBaseClass_myBaseClassCategoryMethod, 47, TEST_IF_METHOD_IS_IN_CATEGORY_ON_CLASS_NAMED, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MyBaseClass_myBaseClassCategoryMethod, 53, TEST_IF_METHOD_IS_IN_CATEGORY_NAMED, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MyBaseClass_myBaseClassCategoryMethod, 53, TEST_IF_METHOD_IS_IN_CATEGORY_ON_CLASS_NAMED, WARNING, [] +codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MyBaseClass_myBaseClassCategoryMethod, 53, TEST_IS_METHOD_EXPOSED, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MyBaseClass_myBaseClassInterfaceExtensionMethod, 23, TEST_IF_METHOD_IS_IN_CATEGORY_ON_CLASS_NAMED, WARNING, [] +codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MyBaseClass_myBaseClassInterfaceExtensionMethod, 31, TEST_IS_METHOD_EXPOSED, WARNING, [] +codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MyBaseClass_myBaseClassMethod, 29, TEST_IS_METHOD_EXPOSED, WARNING, [] +codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MyBaseClass_myBaseClassProperty, 37, TEST_IS_METHOD_EXPOSED, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MyBaseClass_myBaseClassProperty, 37, TEST_RETURN_METHOD, WARNING, [] +codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MyBaseClass_myBaseClassProtocolOptionalMethod, 35, TEST_IS_METHOD_EXPOSED, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MyBaseClass_myBaseClassProtocolOptionalMethod, 35, TEST_IS_OVERRIDING_METHOD, WARNING, [] +codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MyBaseClass_myBaseClassProtocolRequiredMethod, 33, TEST_IS_METHOD_EXPOSED, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MyBaseClass_myBaseClassProtocolRequiredMethod, 33, TEST_IS_OVERRIDING_METHOD, WARNING, [] +codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MyBaseClass_setMyBaseClassProperty, 40, TEST_IS_METHOD_EXPOSED, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MyBaseClass_setMyBaseClassProperty, 40, TEST_PARAM_TYPE_CHECK2, WARNING, [] +codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_myBaseClassCategoryMethod, 113, TEST_IS_METHOD_EXPOSED, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_myBaseClassCategoryMethod, 113, TEST_IS_OVERRIDING_METHOD, WARNING, [] +codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_myBaseClassInterfaceExtensionMethod, 93, TEST_IS_METHOD_EXPOSED, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_myBaseClassInterfaceExtensionMethod, 93, TEST_IS_OVERRIDING_METHOD, WARNING, [] +codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_myBaseClassMethod, 89, TEST_IS_METHOD_EXPOSED, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_myBaseClassMethod, 89, TEST_IS_OVERRIDING_METHOD, WARNING, [] +codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_myBaseClassProperty, 105, TEST_IS_METHOD_EXPOSED, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_myBaseClassProperty, 105, TEST_IS_OVERRIDING_METHOD, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_myBaseClassProperty, 105, TEST_RETURN_METHOD, WARNING, [] +codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_myBaseClassProtocolOptionalMethod, 101, TEST_IS_METHOD_EXPOSED, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_myBaseClassProtocolOptionalMethod, 101, TEST_IS_OVERRIDING_METHOD, WARNING, [] +codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_myBaseClassProtocolRequiredMethod, 97, TEST_IS_METHOD_EXPOSED, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_myBaseClassProtocolRequiredMethod, 97, TEST_IS_OVERRIDING_METHOD, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_mySubclassCategoryMethod, 136, TEST_IF_METHOD_IS_IN_CATEGORY_ON_SUBCLASS_OF, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_mySubclassCategoryMethod, 142, TEST_IF_METHOD_IS_IN_CATEGORY_ON_SUBCLASS_OF, WARNING, [] +codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_mySubclassCategoryMethod, 142, TEST_IS_METHOD_EXPOSED, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_mySubclassMethod, 83, TEST_IF_IS_METHOD_NAMED, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_mySubclassMethod, 117, TEST_IF_IS_METHOD_NAMED, WARNING, [] +codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_mySubclassMethod, 117, TEST_IS_METHOD_EXPOSED, WARNING, [] +codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_mySubclassProtocol2OptionalMethod, 125, TEST_IS_METHOD_EXPOSED, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_mySubclassProtocol2OptionalMethod, 125, TEST_IS_OVERRIDING_METHOD, WARNING, [] +codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_mySubclassProtocol2RequiredMethod, 123, TEST_IS_METHOD_EXPOSED, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_mySubclassProtocol2RequiredMethod, 123, TEST_IS_OVERRIDING_METHOD, WARNING, [] +codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_mySubclassProtocolOptionalMethod, 121, TEST_IS_METHOD_EXPOSED, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_mySubclassProtocolOptionalMethod, 121, TEST_IS_OVERRIDING_METHOD, WARNING, [] +codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_mySubclassProtocolRequiredMethod, 119, TEST_IS_METHOD_EXPOSED, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_mySubclassProtocolRequiredMethod, 119, TEST_IS_OVERRIDING_METHOD, WARNING, [] +codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_mySubclassSubprotocol2OptionalMethod, 129, TEST_IS_METHOD_EXPOSED, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_mySubclassSubprotocol2OptionalMethod, 129, TEST_IS_OVERRIDING_METHOD, WARNING, [] +codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_mySubclassSubprotocol2RequiredMethod, 127, TEST_IS_METHOD_EXPOSED, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_mySubclassSubprotocol2RequiredMethod, 127, TEST_IS_OVERRIDING_METHOD, WARNING, [] +codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_setMyBaseClassProperty, 109, TEST_IS_METHOD_EXPOSED, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_setMyBaseClassProperty, 109, TEST_IS_OVERRIDING_METHOD, WARNING, [] codetoanalyze/objc/linters-for-test-only/GenericTestClass.m, MySubclass_setMyBaseClassProperty, 109, TEST_PARAM_TYPE_CHECK2, WARNING, [] codetoanalyze/objc/linters-for-test-only/InContextOfMethodsTest.m, InContextOfMethodsTest_method, 16, TEST_IN_METHOD_CONTEXT, WARNING, [] @@ -89,9 +115,11 @@ codetoanalyze/objc/linters-for-test-only/sel.m, fooButtonComponent_newWithAction codetoanalyze/objc/linters-for-test-only/sel.m, fooButtonComponent_newWithAction, 12, TEST_PARAMETER_SEL_TYPE, WARNING, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, A_foo, 13, TEST_PARAM_TYPE_CHECK2, WARNING, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, A_foo, 13, TEST_RETURN_METHOD, WARNING, [] +codetoanalyze/objc/linters-for-test-only/subclassing.m, A_foo, 19, TEST_IS_METHOD_EXPOSED, WARNING, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, A_foo, 19, TEST_PARAM_TYPE_CHECK2, WARNING, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, A_foo, 19, TEST_RETURN_METHOD, WARNING, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, B_bar, 28, TEST_RETURN_METHOD, WARNING, [] +codetoanalyze/objc/linters-for-test-only/subclassing.m, B_bar, 34, TEST_IS_METHOD_EXPOSED, WARNING, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, B_bar, 34, TEST_RETURN_METHOD, WARNING, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, B_bar, 35, TEST_ALL_METHODS, WARNING, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, B_bar, 36, MACRO_TEST1, WARNING, []