diff --git a/infer/lib/linter_rules/linters.al b/infer/lib/linter_rules/linters.al index bcd4ad546..16398d367 100644 --- a/infer/lib/linter_rules/linters.al +++ b/infer/lib/linter_rules/linters.al @@ -223,6 +223,19 @@ DEFINE-CHECKER CXX_REFERENCE_CAPTURED_IN_OBJC_BLOCK = { SET severity = "ERROR"; }; + DEFINE-CHECKER UNAVAILABLE_CLASS_IN_SUPPORTED_IOS_SDK = { + SET report_when = + WHEN (class_unavailable_in_supported_ios_sdk()) + HOLDS-IN-NODE ObjCMessageExpr; + + SET message = + "The receiver %receiver_method_call% of %name% is not available in the required iOS SDK version + %iphoneos_target_sdk_version% (only available from version %class_available_ios_sdk%)"; + SET name = "Unavailable API In Supported iOS SDK"; + SET severity = "ERROR"; + SET mode = "OFF"; + }; + DEFINE-CHECKER POINTER_TO_INTEGRAL_IMPLICIT_CAST = { SET report_when = diff --git a/infer/src/clang/cFrontend_checkers.ml b/infer/src/clang/cFrontend_checkers.ml index e87fe7ae9..eeeb0e7ef 100644 --- a/infer/src/clang/cFrontend_checkers.ml +++ b/infer/src/clang/cFrontend_checkers.ml @@ -66,6 +66,27 @@ let available_ios_sdk an = ( "available_ios_sdk must be called with a DeclRefExpr or an ObjCMessageExpr, but got " ^ tag_name_of_node an ) +let class_available_ios_sdk an = + match CPredicates.receiver_method_call an with + | Some decl -> ( + match CPredicates.get_available_attr_ios_sdk (Decl decl) with + | Some version + -> version + | None + -> "" ) + | None + -> failwith + ( "class_available_ios_sdk must be called with ObjCMessageExpr, but got " + ^ tag_name_of_node an ) + +let receiver_method_call an = + match CPredicates.receiver_method_call an with + | Some decl + -> Ctl_parser_types.ast_node_name (Ctl_parser_types.Decl decl) + | _ + -> failwith + ("receiver_method_call must be called with ObjCMessageExpr, but got " ^ tag_name_of_node an) + let ivar_name an = let open Clang_ast_t in match an with diff --git a/infer/src/clang/cFrontend_checkers.mli b/infer/src/clang/cFrontend_checkers.mli index 677fef3e6..255ccb4de 100644 --- a/infer/src/clang/cFrontend_checkers.mli +++ b/infer/src/clang/cFrontend_checkers.mli @@ -27,4 +27,8 @@ val iphoneos_target_sdk_version : Ctl_parser_types.ast_node -> string val available_ios_sdk : Ctl_parser_types.ast_node -> string +val class_available_ios_sdk : Ctl_parser_types.ast_node -> string + +val receiver_method_call : Ctl_parser_types.ast_node -> string + val tag_name_of_node : Ctl_parser_types.ast_node -> string diff --git a/infer/src/clang/cFrontend_errors.ml b/infer/src/clang/cFrontend_errors.ml index a1cbb2660..1bf1b447f 100644 --- a/infer/src/clang/cFrontend_errors.ml +++ b/infer/src/clang/cFrontend_errors.ml @@ -104,10 +104,14 @@ let evaluate_place_holder ph an = -> MF.monospaced_to_string (CFrontend_checkers.cxx_ref_captured_in_block an) | "%decl_ref_or_selector_name%" -> MF.monospaced_to_string (CFrontend_checkers.decl_ref_or_selector_name an) + | "%receiver_method_call%" + -> MF.monospaced_to_string (CFrontend_checkers.receiver_method_call an) | "%iphoneos_target_sdk_version%" -> MF.monospaced_to_string (CFrontend_checkers.iphoneos_target_sdk_version an) | "%available_ios_sdk%" -> MF.monospaced_to_string (CFrontend_checkers.available_ios_sdk an) + | "%class_available_ios_sdk%" + -> MF.monospaced_to_string (CFrontend_checkers.class_available_ios_sdk an) | "%type%" -> MF.monospaced_to_string (Ctl_parser_types.ast_node_type an) | "%child_type%" diff --git a/infer/src/clang/cPredicates.ml b/infer/src/clang/cPredicates.ml index 7c6b6b214..60841f3c5 100644 --- a/infer/src/clang/cPredicates.ml +++ b/infer/src/clang/cPredicates.ml @@ -14,6 +14,42 @@ module L = Logging let parsed_type_map : Ctl_parser_types.abs_ctype String.Map.t ref = ref String.Map.empty +let rec objc_class_of_pointer_type type_ptr = + match CAst_utils.get_type type_ptr with + | Some ObjCInterfaceType (_, decl_ptr) + -> CAst_utils.get_decl decl_ptr + | Some ObjCObjectPointerType (_, inner_qual_type) + -> objc_class_of_pointer_type inner_qual_type.qt_type_ptr + | Some AttributedType (type_info, _) -> ( + match type_info.ti_desugared_type with + | Some type_ptr + -> objc_class_of_pointer_type type_ptr + | None + -> None ) + | _ + -> None + +let receiver_method_call an = + match an with + | Ctl_parser_types.Stmt ObjCMessageExpr (_, args, _, obj_c_message_expr_info) -> ( + match obj_c_message_expr_info.omei_receiver_kind with + | `Class qt + -> CAst_utils.get_decl_from_typ_ptr qt.qt_type_ptr + | `Instance -> ( + match args with + | receiver :: _ -> ( + match Clang_ast_proj.get_expr_tuple receiver with + | Some (_, _, expr_info) + -> objc_class_of_pointer_type expr_info.ei_qual_type.qt_type_ptr + | None + -> None ) + | [] + -> None ) + | _ + -> None ) + | _ + -> None + let get_available_attr_ios_sdk an = let open Clang_ast_t in let rec get_available_attr attrs = @@ -411,6 +447,13 @@ let decl_unavailable_in_supported_ios_sdk (cxt: CLintersContext.context) an = | _ -> false +let class_unavailable_in_supported_ios_sdk (cxt: CLintersContext.context) an = + match receiver_method_call an with + | Some decl + -> decl_unavailable_in_supported_ios_sdk cxt (Ctl_parser_types.Decl decl) + | None + -> false + (* Check whether a type_ptr and a string denote the same type *) let type_ptr_equal_type type_ptr type_str = let pos_str lexbuf = diff --git a/infer/src/clang/cPredicates.mli b/infer/src/clang/cPredicates.mli index 840e7ac48..9693d43d9 100644 --- a/infer/src/clang/cPredicates.mli +++ b/infer/src/clang/cPredicates.mli @@ -72,6 +72,9 @@ val pp_predicate : Format.formatter -> t -> unit val decl_unavailable_in_supported_ios_sdk : CLintersContext.context -> Ctl_parser_types.ast_node -> bool +val class_unavailable_in_supported_ios_sdk : + CLintersContext.context -> Ctl_parser_types.ast_node -> bool + val has_type : Ctl_parser_types.ast_node -> ALVar.alexp -> bool val method_return_type : Ctl_parser_types.ast_node -> ALVar.alexp -> bool @@ -87,3 +90,5 @@ val objc_method_has_nth_parameter_of_type : Ctl_parser_types.ast_node -> ALVar.alexp -> ALVar.alexp -> bool val using_namespace : Ctl_parser_types.ast_node -> ALVar.alexp -> bool + +val receiver_method_call : Ctl_parser_types.ast_node -> Clang_ast_t.decl option diff --git a/infer/src/clang/cTL.ml b/infer/src/clang/cTL.ml index ea3e2fa70..1bcf433cb 100644 --- a/infer/src/clang/cTL.ml +++ b/infer/src/clang/cTL.ml @@ -744,6 +744,8 @@ let rec eval_Atomic _pred_name args an lcxt = -> CPredicates.declaration_ref_name an decl_name | "decl_unavailable_in_supported_ios_sdk", [], an -> CPredicates.decl_unavailable_in_supported_ios_sdk lcxt an + | "class_unavailable_in_supported_ios_sdk", [], an + -> CPredicates.class_unavailable_in_supported_ios_sdk lcxt an | "has_cast_kind", [name], an -> CPredicates.has_cast_kind an name | "has_type", [typ], an diff --git a/infer/tests/codetoanalyze/objc/ioslints/issues.exp b/infer/tests/codetoanalyze/objc/ioslints/issues.exp index fa980e1f5..4b461cc77 100644 --- a/infer/tests/codetoanalyze/objc/ioslints/issues.exp +++ b/infer/tests/codetoanalyze/objc/ioslints/issues.exp @@ -4,6 +4,9 @@ codetoanalyze/objc/ioslints/unavailable_api_allowed_cases.m, Unavailable_api_all codetoanalyze/objc/ioslints/unavailable_api_allowed_cases.m, Unavailable_api_allowed_cases_with_responds_to_selector_in_else:, 71, UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK, [] codetoanalyze/objc/ioslints/unavailable_api_allowed_cases.m, Unavailable_api_allowed_cases_without_instances_responds_to_selector, 95, UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK, [] codetoanalyze/objc/ioslints/unavailable_api_allowed_cases.m, Unavailable_api_allowed_cases_without_responds_to_selector:, 64, UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK, [] -codetoanalyze/objc/ioslints/unavailable_api_in_supported_ios_sdk.m, OpenURLOptionsFromSourceApplication, 26, UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK, [] -codetoanalyze/objc/ioslints/unavailable_api_in_supported_ios_sdk.m, Unavailable_api_in_supported_ios_sdk_test:and:, 19, UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK, [] +codetoanalyze/objc/ioslints/unavailable_api_in_supported_ios_sdk.m, OpenURLOptionsFromSourceApplication, 51, UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK, [] +codetoanalyze/objc/ioslints/unavailable_api_in_supported_ios_sdk.m, Unavailable_api_in_supported_ios_sdk_test:and:, 28, UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK, [] +codetoanalyze/objc/ioslints/unavailable_api_in_supported_ios_sdk.m, Unavailable_api_in_supported_ios_sdk_unsupported_class, 34, UNAVAILABLE_CLASS_IN_SUPPORTED_IOS_SDK, [] +codetoanalyze/objc/ioslints/unavailable_api_in_supported_ios_sdk.m, Unavailable_api_in_supported_ios_sdk_unsupported_class, 34, UNAVAILABLE_CLASS_IN_SUPPORTED_IOS_SDK, [] +codetoanalyze/objc/ioslints/unavailable_api_in_supported_ios_sdk.m, Unavailable_api_in_supported_ios_sdk_unsupported_class_with_attributes:, 44, UNAVAILABLE_CLASS_IN_SUPPORTED_IOS_SDK, [] codetoanalyze/objc/ioslints/unavailable_property_ios.m, FNFPlayerLayer_initWithConfigs:, 22, UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK, [] diff --git a/infer/tests/codetoanalyze/objc/ioslints/unavailable_api_in_supported_ios_sdk.m b/infer/tests/codetoanalyze/objc/ioslints/unavailable_api_in_supported_ios_sdk.m index 997c5acb6..b484db3f0 100644 --- a/infer/tests/codetoanalyze/objc/ioslints/unavailable_api_in_supported_ios_sdk.m +++ b/infer/tests/codetoanalyze/objc/ioslints/unavailable_api_in_supported_ios_sdk.m @@ -7,6 +7,15 @@ * of patent rights can be found in the PATENTS file in the same directory. */ #import +#import + +NS_CLASS_AVAILABLE(10_12, 10_0) + +@interface Unav_class : NSObject + +- (void)m; + +@end @interface Unavailable_api_in_supported_ios_sdk : NSObject @@ -18,6 +27,22 @@ NSDictionary* cacheData = [NSKeyedUnarchiver unarchiveTopLevelObjectWithData:data error:nil]; } + +// bug +- (void)unsupported_class { + AVPlayerLooper* looper = + [[AVPlayerLooper alloc] initWithPlayer:nil + templateItem:nil + timeRange:kCMTimeRangeInvalid]; + if (!looper) { + NSLog(@""); + } +} + +// bug +- (void)unsupported_class_with_attributes:(nonnull Unav_class*)c { + [c m]; +} @end static NSDictionary* OpenURLOptionsFromSourceApplication(