[linters] Take current os version into account in the unavailable api check

Reviewed By: jvillard, martinoluca

Differential Revision: D4508223

fbshipit-source-id: c44b75d
master
Dulma Churchill 8 years ago committed by Facebook Github Bot
parent 0b87ad2352
commit 9ac65a7b1d

@ -11,6 +11,7 @@ open! IStd
type if_context = {
within_responds_to_selector_block : string list;
ios_version_guard : string list
}
type context = {

@ -0,0 +1,74 @@
(*
* Copyright (c) 2017 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*)
open! IStd
(* version macros like kCFCoreFoundationVersionNumber_iOS_9_0 are
tied to specific float values, e.g. 1240.1.
To be found in CoreFoundation/CFBase.h *)
type machine_readable_version = float
type human_readable_version = string
type t = machine_readable_version * human_readable_version
let version_numbers : t list =
[(478.23, "2.0");
(478.26, "2.1");
(478.29, "2.2");
(478.47, "3.0");
(478.52, "3.1");
(478.61, "3.2");
(550.32, "4.0");
(550.38, "4.1");
(550.52, "4.3");
(675.00, "5.0");
(690.10, "5.1");
(793.00, "6.1");
(847.20, "7.0");
(847.24, "7.1");
(1140.1, "8.0");
(1141.14, "8.1");
(1142.16, "8.2");
(1144.17, "8.3");
(1145.15, "8.4");
(1240.1, "9.0");
(1241.11, "9.1");
(1242.13, "9.3");
(1280.38, "9.4");
(1348.0, "10.0");
(1348.22, "10.2");]
let sort_versions versions =
let compare (version_float1, _) (version_float2, _) =
Float.compare version_float1 version_float2 in
List.sort ~cmp:compare versions
let version_of number_s : human_readable_version option =
let epsilon = 0.001 in
let rec version_of_aux version_numbers number =
match version_numbers with
| (version_n, version_s) :: (next_version_n, next_version_s) :: rest ->
if (number -. version_n) < epsilon && (number -. version_n) > -. epsilon then Some version_s
else if (number >= (version_n +. epsilon) && number <= (next_version_n -. epsilon))
then Some next_version_s
else version_of_aux ((next_version_n, next_version_s) :: rest) number
| [version_n, version_s] ->
if number >= version_n then Some version_s
else None
| [] -> None in
let number_opt =
try Some (float_of_string number_s)
with Failure _ -> None in
match number_opt with
| None -> None
| Some number -> version_of_aux (sort_versions version_numbers) number
let pp_diff_of_version_opt fmt (expected, actual) =
let option_to_string opt = Option.value ~default:"" opt in
Format.fprintf fmt
"Expected: [%s] Found: [%s]" (option_to_string expected) (option_to_string actual)

@ -0,0 +1,17 @@
(*
* Copyright (c) 2017 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*)
type machine_readable_version = float
type human_readable_version = string
type t = machine_readable_version * human_readable_version
val version_of : string -> human_readable_version option
val pp_diff_of_version_opt : Format.formatter ->
(human_readable_version option * human_readable_version option) -> unit

@ -56,14 +56,63 @@ let rec get_responds_to_selector stmt =
get_responds_to_selector stmt
| _ -> []
let rec is_core_foundation_version_number stmt =
let open Clang_ast_t in
match stmt with
| DeclRefExpr (_, _, _, decl_ref_info) ->
(match decl_ref_info.drti_decl_ref with
| Some decl_ref_info ->
let name_info, _, _ = CAst_utils.get_info_from_decl_ref decl_ref_info in
String.equal name_info.ni_name "kCFCoreFoundationVersionNumber"
| None -> false)
| ImplicitCastExpr (_, [stmt], _, _) ->
is_core_foundation_version_number stmt
| _ -> false
let rec current_os_version_constant stmt =
let open Clang_ast_t in
match stmt with
| FloatingLiteral (_, _, _, number) ->
CiOSVersionNumbers.version_of number
| IntegerLiteral (_, _, _, info) ->
CiOSVersionNumbers.version_of info.ili_value
| ImplicitCastExpr (_, [stmt], _, _) ->
current_os_version_constant stmt
| _ -> None
let rec get_current_os_version stmt =
let open Clang_ast_t in
match stmt with
| BinaryOperator (_, [stmt1;stmt2], _, bo_info) when
PVariant.(=) bo_info.Clang_ast_t.boi_kind `GE &&
is_core_foundation_version_number stmt1 ->
Option.to_list (current_os_version_constant stmt2)
| BinaryOperator (_, [stmt1;stmt2], _, bo_info) when
PVariant.(=) bo_info.Clang_ast_t.boi_kind `LE &&
is_core_foundation_version_number stmt2 ->
Option.to_list (current_os_version_constant stmt1)
| BinaryOperator (_, [stmt1;stmt2], _, bo_info) when
PVariant.(=) bo_info.Clang_ast_t.boi_kind `LAnd ->
List.append (get_current_os_version stmt1) (get_current_os_version stmt2)
| ImplicitCastExpr (_, [stmt], _, _)
| ParenExpr (_, [stmt], _)
| ExprWithCleanups(_, [stmt], _, _) ->
get_current_os_version stmt
| _ -> []
let compute_if_context (context:CLintersContext.context) stmt =
let prev_responds_to_selector =
let selector = get_responds_to_selector stmt in
let os_version = get_current_os_version stmt in
let within_responds_to_selector_block, ios_version_guard =
match context.if_context with
| Some if_context -> if_context.within_responds_to_selector_block
| None -> [] in
let within_responds_to_selector_block =
List.append (get_responds_to_selector stmt) (prev_responds_to_selector) in
Some ({within_responds_to_selector_block} : CLintersContext.if_context)
| Some if_context ->
let within_responds_to_selector_block =
List.append selector if_context.within_responds_to_selector_block in
let ios_version_guard =
List.append os_version if_context.ios_version_guard in
within_responds_to_selector_block, ios_version_guard
| None -> selector, os_version in
Some ({within_responds_to_selector_block; ios_version_guard} : CLintersContext.if_context)
let is_factory_method (context: CLintersContext.context) decl =
let interface_decl_opt =

@ -232,11 +232,20 @@ let isa classname an =
| _ -> false)
| _ -> false
let decl_unavailable_in_supported_ios_sdk an =
let decl_unavailable_in_supported_ios_sdk (cxt : CLintersContext.context) an =
let allowed_os_versions =
match Config.iphoneos_target_sdk_version,
(cxt.if_context : CLintersContext.if_context option) with
| Some iphoneos_target_sdk_version, Some if_context ->
iphoneos_target_sdk_version :: if_context.ios_version_guard
| Some iphoneos_target_sdk_version, None -> [iphoneos_target_sdk_version]
| _ -> [] in
let max_allowed_version_opt = List.max_elt allowed_os_versions ~cmp:Utils.compare_versions in
let available_attr_ios_sdk = get_available_attr_ios_sdk an in
match available_attr_ios_sdk, Config.iphoneos_target_sdk_version with
| Some available_attr_ios_sdk, Some iphoneos_target_sdk_version ->
Int.equal (Utils.compare_versions available_attr_ios_sdk iphoneos_target_sdk_version) 1
match available_attr_ios_sdk, max_allowed_version_opt with
| Some available_attr_ios_sdk, Some max_allowed_version ->
(Utils.compare_versions available_attr_ios_sdk max_allowed_version) > 0
| _ -> false

@ -53,7 +53,8 @@ val is_node : string -> Ctl_parser_types.ast_node -> bool
val pp_predicate : Format.formatter -> t -> unit
val decl_unavailable_in_supported_ios_sdk : Ctl_parser_types.ast_node -> bool
val decl_unavailable_in_supported_ios_sdk : CLintersContext.context -> Ctl_parser_types.ast_node
-> bool
val get_available_attr_ios_sdk : Ctl_parser_types.ast_node -> string option

@ -433,7 +433,7 @@ let rec eval_Atomic pred_name args an lcxt =
| "in_node", [nodename], an -> CPredicates.is_node nodename an
| "isa", [classname], an -> CPredicates.isa classname an
| "decl_unavailable_in_supported_ios_sdk", [], an ->
CPredicates.decl_unavailable_in_supported_ios_sdk an
CPredicates.decl_unavailable_in_supported_ios_sdk lcxt an
| "within_responds_to_selector_block", [], an ->
CPredicates.within_responds_to_selector_block lcxt an
| _ -> failwith ("ERROR: Undefined Predicate or wrong set of arguments: " ^ pred_name)

@ -0,0 +1,55 @@
(*
* Copyright (c) 2017 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*)
open! IStd
open OUnit2
let test_correct_ios_version =
let create_test (version : string) (expected_version : string option) _ =
let output = CiOSVersionNumbers.version_of version in
let cmp = fun s1 s2 -> Option.equal String.equal s1 s2 in
assert_equal ~pp_diff:CiOSVersionNumbers.pp_diff_of_version_opt
~cmp:cmp expected_version output in
[
(
"test_correct_ios_version_some_version",
"847.20",
(Some "7.0")
);
(
"test_correct_ios_version_edge_version",
"1348.22",
(Some "10.2")
);
(
"test_correct_ios_version_ck",
"1223.1",
(Some "9.0")
);
(
"test_correct_ios_version_9",
"1240.0999",
(Some "9.0")
);
(
"test_correct_ios_version_2",
"478.230001",
(Some "2.0")
);
(
"test_correct_ios_version_smaller",
"1.49",
(None)
)
]
|> List.map
~f:(fun (name, test_input, expected_output) ->
name >:: create_test test_input expected_output)
let tests = "cios_version_numbers_suite" >::: test_correct_ios_version

@ -20,6 +20,7 @@ let () =
AddressTakenTests.tests;
BoundedCallTreeTests.tests;
CopyPropagationTests.tests;
CiOSVersionNumbersTests.tests;
ProcCfgTests.tests;
LivenessTests.tests;
SchedulerTests.tests;

@ -1,89 +0,0 @@
/*
* Copyright (c) 2017 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <UIKit/UIKit.h>
@interface Unavailable_api_allowed_cases : NSObject
- (void)m NS_AVAILABLE(10_12, 10_0);
- (void)n NS_AVAILABLE(10_12, 10_0);
@end
@implementation Unavailable_api_allowed_cases
- (void)m {
}
- (void)n {
}
// no bug
- (void)with_responds_to_selector:(Unavailable_api_allowed_cases*)a {
if ([a respondsToSelector:@selector(m)]) {
int x = 1;
[a m];
x = 3;
}
}
// no bug
- (void)with_responds_to_selector:(Unavailable_api_allowed_cases*)a
and:(BOOL)ok {
if ([a respondsToSelector:@selector(m)] && ok) {
[a m];
}
}
// bug
- (void)without_responds_to_selector:(Unavailable_api_allowed_cases*)a {
[a m];
}
// bug
- (void)with_responds_to_selector_in_else:(Unavailable_api_allowed_cases*)a {
if ([a respondsToSelector:@selector(m)]) {
} else {
[a m];
}
}
// no bug
- (void)with_responds_to_selector_nested_if:(Unavailable_api_allowed_cases*)a {
if ([a respondsToSelector:@selector(m)]) {
if ([a respondsToSelector:@selector(n)]) {
[a m];
[a n];
}
}
}
// no bug
- (void)with_responds_to_selector_two_selectors:
(Unavailable_api_allowed_cases*)a {
if ([a respondsToSelector:@selector(m)] &&
[a respondsToSelector:@selector(n)]) {
[a m];
[a n];
}
}
// no bug
- (void)uifont_with_respondstoselector:(CGFloat)size {
UIFont* font;
if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)]) {
font = [UIFont systemFontOfSize:size weight:0];
}
}
// bug
- (void)uifont_without_respondstoselector:(CGFloat)size {
UIFont* font = [UIFont systemFontOfSize:size weight:0];
}
@end

@ -1,5 +1,7 @@
codetoanalyze/objc/ioslinters/unavailable_api_allowed_cases.m, Unavailable_api_allowed_cases_uifont_without_respondstoselector:, 86, UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK, []
codetoanalyze/objc/ioslinters/unavailable_api_allowed_cases.m, Unavailable_api_allowed_cases_with_responds_to_selector_in_else:, 52, UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK, []
codetoanalyze/objc/ioslinters/unavailable_api_allowed_cases.m, Unavailable_api_allowed_cases_without_responds_to_selector:, 45, UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK, []
codetoanalyze/objc/ioslinters/unavailable_api_allowed_cases.m, Unavailable_api_allowed_cases_m2, 120, UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK, []
codetoanalyze/objc/ioslinters/unavailable_api_allowed_cases.m, Unavailable_api_allowed_cases_m3:, 128, UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK, []
codetoanalyze/objc/ioslinters/unavailable_api_allowed_cases.m, Unavailable_api_allowed_cases_uifont_without_respondstoselector:, 102, UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK, []
codetoanalyze/objc/ioslinters/unavailable_api_allowed_cases.m, Unavailable_api_allowed_cases_with_responds_to_selector_in_else:, 68, UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK, []
codetoanalyze/objc/ioslinters/unavailable_api_allowed_cases.m, Unavailable_api_allowed_cases_without_responds_to_selector:, 61, UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK, []
codetoanalyze/objc/ioslinters/unavailable_api_in_supported_ios_sdk.m, OpenURLOptionsFromSourceApplication, 18, UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK, []
codetoanalyze/objc/ioslinters/unavailable_api_in_supported_ios_sdk.m, Unavailable_api_in_supported_ios_sdk_test:and:, 11, UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK, []

@ -0,0 +1,188 @@
/*
* Copyright (c) 2017 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <UIKit/UIKit.h>
@interface Unavailable_api_allowed_cases : NSObject
- (void)m NS_AVAILABLE(10_12, 10_0);
- (void)n NS_AVAILABLE(10_12, 10_0);
@end
#define CK_AT_LEAST_IOS9 (kCFCoreFoundationVersionNumber >= 1223.1)
#define AT_LEAST_IOS9 \
(kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_9_0)
#ifndef kCFCoreFoundationVersionNumber_iOS_10_0
#define kCFCoreFoundationVersionNumber_iOS_10_0 1348
#endif
#ifndef kCFCoreFoundationVersionNumber_iOS_10_2
#define kCFCoreFoundationVersionNumber_iOS_10_2 1348.22
#endif
#define AT_LEAST_IOS10 \
(kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_10_0)
@implementation Unavailable_api_allowed_cases
- (void)m {
}
- (void)n {
}
// no bug
- (void)with_responds_to_selector:(Unavailable_api_allowed_cases*)a {
if ([a respondsToSelector:@selector(m)]) {
int x = 1;
[a m];
x = 3;
}
}
// no bug
- (void)with_responds_to_selector:(Unavailable_api_allowed_cases*)a
and:(BOOL)ok {
if ([a respondsToSelector:@selector(m)] && ok) {
[a m];
}
}
// bug
- (void)without_responds_to_selector:(Unavailable_api_allowed_cases*)a {
[a m];
}
// bug
- (void)with_responds_to_selector_in_else:(Unavailable_api_allowed_cases*)a {
if ([a respondsToSelector:@selector(m)]) {
} else {
[a m];
}
}
// no bug
- (void)with_responds_to_selector_nested_if:(Unavailable_api_allowed_cases*)a {
if ([a respondsToSelector:@selector(m)]) {
if ([a respondsToSelector:@selector(n)]) {
[a m];
[a n];
}
}
}
// no bug
- (void)with_responds_to_selector_two_selectors:
(Unavailable_api_allowed_cases*)a {
if ([a respondsToSelector:@selector(m)] &&
[a respondsToSelector:@selector(n)]) {
[a m];
[a n];
}
}
// no bug
- (void)uifont_with_respondstoselector:(CGFloat)size {
UIFont* font;
if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)]) {
font = [UIFont systemFontOfSize:size weight:0];
}
}
// bug
- (void)uifont_without_respondstoselector:(CGFloat)size {
UIFont* font = [UIFont systemFontOfSize:size weight:0];
}
// no bug
- (void)m1 {
NSDictionary* destinationPixelBufferAttributes;
if (kCFCoreFoundationVersionNumber >=
kCFCoreFoundationVersionNumber_iOS_9_0) {
destinationPixelBufferAttributes = @{
(NSString*)kCVPixelBufferOpenGLESTextureCacheCompatibilityKey : @YES
};
}
}
// bug
- (void)m2 {
NSDictionary* destinationPixelBufferAttributes;
destinationPixelBufferAttributes =
@{(NSString*)kCVPixelBufferOpenGLESTextureCacheCompatibilityKey : @YES };
}
// bug
- (void)m3:(BOOL)ok {
NSDictionary* destinationPixelBufferAttributes;
if (ok) {
destinationPixelBufferAttributes = @{
(NSString*)kCVPixelBufferOpenGLESTextureCacheCompatibilityKey : @YES
};
}
}
// no bug
- (void)m4:(BOOL)ok {
NSDictionary* destinationPixelBufferAttributes;
if (kCFCoreFoundationVersionNumber >=
kCFCoreFoundationVersionNumber_iOS_9_0 &&
ok) {
destinationPixelBufferAttributes = @{
(NSString*)kCVPixelBufferOpenGLESTextureCacheCompatibilityKey : @YES
};
}
}
// no bug
- (void)m5 {
NSDictionary* destinationPixelBufferAttributes;
if (kCFCoreFoundationVersionNumber >=
kCFCoreFoundationVersionNumber_iOS_9_0 &&
kCFCoreFoundationVersionNumber >=
kCFCoreFoundationVersionNumber_iOS_7_0) {
destinationPixelBufferAttributes = @{
(NSString*)kCVPixelBufferOpenGLESTextureCacheCompatibilityKey : @YES
};
}
}
// no bug
- (void)m6 {
NSDictionary* destinationPixelBufferAttributes;
if (AT_LEAST_IOS9) {
destinationPixelBufferAttributes = @{
(NSString*)kCVPixelBufferOpenGLESTextureCacheCompatibilityKey : @YES
};
}
}
// no bug
- (void)m7 {
NSDictionary* destinationPixelBufferAttributes;
if (CK_AT_LEAST_IOS9) {
destinationPixelBufferAttributes = @{
(NSString*)kCVPixelBufferOpenGLESTextureCacheCompatibilityKey : @YES
};
}
}
// no bug
- (void)m8 {
NSDictionary* destinationPixelBufferAttributes;
if (AT_LEAST_IOS10) {
destinationPixelBufferAttributes = @{
(NSString*)kCVPixelBufferOpenGLESTextureCacheCompatibilityKey : @YES
};
}
}
@end
Loading…
Cancel
Save