Adding ability to express properties on the hierachy of protocols

Reviewed By: dulmarod

Differential Revision: D5227505

fbshipit-source-id: f4f9774
master
Dino Distefano 8 years ago committed by Facebook Github Bot
parent cd83a24381
commit 4a5d0e0b55

@ -361,6 +361,7 @@ let type_ptr_equal_type type_ptr type_str =
(string_of_int (pos.pos_cnum - pos.pos_bol + 1)) in (string_of_int (pos.pos_cnum - pos.pos_bol + 1)) in
let parse_type_string str = let parse_type_string str =
L.(debug Linters Medium) "Starting parsing type string '%s'@\n" str;
let lexbuf = Lexing.from_string str in let lexbuf = Lexing.from_string str in
try try
(Types_parser.abs_ctype token lexbuf) (Types_parser.abs_ctype token lexbuf)
@ -383,18 +384,17 @@ let type_ptr_equal_type type_ptr type_str =
Ctl_parser_types.c_type_equal c_type' abs_ctype Ctl_parser_types.c_type_equal c_type' abs_ctype
| _ -> L.(debug Linters Medium) "Couldn't find type....@\n"; false | _ -> L.(debug Linters Medium) "Couldn't find type....@\n"; false
let has_type an _typ = let get_ast_node_type_ptr an =
match an, _typ with match an with
| Ctl_parser_types.Stmt stmt, ALVar.Const typ -> | Ctl_parser_types.Stmt stmt ->
(match Clang_ast_proj.get_expr_tuple stmt with (match Clang_ast_proj.get_expr_tuple stmt with
| Some (_, _, expr_info) -> | Some (_, _, expr_info) -> Some expr_info.ei_qual_type.qt_type_ptr
type_ptr_equal_type expr_info.ei_qual_type.qt_type_ptr typ | _ -> None)
| _ -> false) | Ctl_parser_types.Decl decl -> CAst_utils.type_of_decl decl
| Ctl_parser_types.Decl decl, ALVar.Const typ ->
(match CAst_utils.type_of_decl decl with let has_type an _typ =
| Some type_ptr -> match get_ast_node_type_ptr an, _typ with
type_ptr_equal_type type_ptr typ | Some pt, ALVar.Const typ -> type_ptr_equal_type pt typ
| _ -> false)
| _ -> false | _ -> false
let method_return_type an _typ = let method_return_type an _typ =
@ -406,6 +406,45 @@ let method_return_type an _typ =
type_ptr_equal_type qual_type.Clang_ast_t.qt_type_ptr typ type_ptr_equal_type qual_type.Clang_ast_t.qt_type_ptr typ
| _ -> false | _ -> false
let rec check_protocol_hiearachy decls_ptr _prot_name =
let open Clang_ast_t in
let is_this_protocol di_opt =
match di_opt with
| Some di -> ALVar.compare_str_with_alexp di.ni_name _prot_name
| _ -> false in
match decls_ptr with
| [] -> false
| pt :: decls' ->
let di, protocols = (match CAst_utils.get_decl pt with
| Some ObjCProtocolDecl (_, di, _, _, opcdi) ->
Some di, opcdi.opcdi_protocols
| _ -> None, []) in
if (is_this_protocol di)
|| List.exists ~f:(fun dr -> is_this_protocol dr.dr_name) protocols then
true
else
let super_prot = List.map ~f:(fun dr -> dr.dr_decl_pointer) protocols in
check_protocol_hiearachy (super_prot @ decls') _prot_name
let has_type_subprotocol_of an _prot_name =
let open Clang_ast_t in
let rec check_subprotocol t =
match t with
| Some ObjCObjectPointerType (_, qt) ->
check_subprotocol (CAst_utils.get_type qt.qt_type_ptr)
| Some ObjCObjectType (_, ooti) ->
if List.length ooti.protocol_decls_ptr > 0 then
check_protocol_hiearachy ooti.protocol_decls_ptr _prot_name
else
List.exists
~f:(fun qt -> check_subprotocol (CAst_utils.get_type qt.qt_type_ptr)) ooti.type_args
| Some ObjCInterfaceType (_, pt) ->
check_protocol_hiearachy [pt] _prot_name
| _ -> false in
match get_ast_node_type_ptr an with
| Some tp -> check_subprotocol (CAst_utils.get_type tp)
| _ -> false
let within_responds_to_selector_block (cxt:CLintersContext.context) an = let within_responds_to_selector_block (cxt:CLintersContext.context) an =
let open Clang_ast_t in let open Clang_ast_t in
match an with match an with

@ -74,6 +74,8 @@ val has_type : Ctl_parser_types.ast_node -> ALVar.alexp -> bool
val method_return_type : Ctl_parser_types.ast_node -> ALVar.alexp -> bool val method_return_type : Ctl_parser_types.ast_node -> ALVar.alexp -> bool
val has_type_subprotocol_of : Ctl_parser_types.ast_node -> ALVar.alexp -> bool
val get_available_attr_ios_sdk : Ctl_parser_types.ast_node -> string option val get_available_attr_ios_sdk : Ctl_parser_types.ast_node -> string option
val within_responds_to_selector_block : val within_responds_to_selector_block :

@ -25,6 +25,7 @@ type transitions =
| Parameters (** decl to decl *) | Parameters (** decl to decl *)
| Cond | Cond
| PointerToDecl (** stmt to decl *) | PointerToDecl (** stmt to decl *)
| Protocol (** decl to decl *)
(* In formulas below prefix (* In formulas below prefix
"E" means "exists a path" "E" means "exists a path"
@ -106,6 +107,7 @@ module Debug = struct
| Super -> Format.pp_print_string fmt "Super" | Super -> Format.pp_print_string fmt "Super"
| Parameters -> Format.pp_print_string fmt "Parameters" | Parameters -> Format.pp_print_string fmt "Parameters"
| Cond -> Format.pp_print_string fmt "Cond" | Cond -> Format.pp_print_string fmt "Cond"
| Protocol -> Format.pp_print_string fmt "Protocol"
| PointerToDecl -> Format.pp_print_string fmt "PointerToDecl" in | PointerToDecl -> Format.pp_print_string fmt "PointerToDecl" in
match trans_opt with match trans_opt with
| Some trans -> pp_aux fmt trans | Some trans -> pp_aux fmt trans
@ -576,6 +578,18 @@ let transition_decl_to_decl_via_super d =
decl_opt_to_ast_node_opt (CAst_utils.get_decl_opt_with_decl_ref idi.otdi_super) decl_opt_to_ast_node_opt (CAst_utils.get_decl_opt_with_decl_ref idi.otdi_super)
| _ -> [] | _ -> []
let transition_decl_to_decl_via_protocol d =
let open Clang_ast_t in
let get_nodes dr =
match CAst_utils.get_decl dr.dr_decl_pointer with
| Some d -> Some (Decl d)
| None -> None in
match d with
| Clang_ast_t.ObjCProtocolDecl (_, _, _, _, opdi) ->
List.filter_map ~f:get_nodes opdi.opcdi_protocols
| _ -> []
let transition_stmt_to_stmt_via_condition st = let transition_stmt_to_stmt_via_condition st =
let open Clang_ast_t in let open Clang_ast_t in
match st with match st with
@ -612,6 +626,7 @@ let next_state_via_transition an trans =
| Decl d, Parameters -> transition_decl_to_decl_via_parameters d | Decl d, Parameters -> transition_decl_to_decl_via_parameters d
| Decl d, InitExpr | Decl d, InitExpr
| Decl d, Body -> transition_decl_to_stmt d trans | Decl d, Body -> transition_decl_to_stmt d trans
| Decl d, Protocol -> transition_decl_to_decl_via_protocol d
| Stmt st, Cond -> transition_stmt_to_stmt_via_condition st | Stmt st, Cond -> transition_stmt_to_stmt_via_condition st
| Stmt st, PointerToDecl -> transition_stmt_to_decl_via_pointer st | Stmt st, PointerToDecl -> transition_stmt_to_decl_via_pointer st
| _, _ -> [] | _, _ -> []
@ -659,6 +674,8 @@ let rec eval_Atomic _pred_name args an lcxt =
CPredicates.objc_method_has_nth_parameter_of_type an num typ CPredicates.objc_method_has_nth_parameter_of_type an num typ
| "using_namespace", [namespace], an -> | "using_namespace", [namespace], an ->
CPredicates.using_namespace an namespace CPredicates.using_namespace an namespace
| "has_type_subprotocol_of", [protname], an ->
CPredicates.has_type_subprotocol_of an protname
| _ -> failwith | _ -> failwith
("ERROR: Undefined Predicate or wrong set of arguments: '" ("ERROR: Undefined Predicate or wrong set of arguments: '"
^ pred_name ^ "'") ^ pred_name ^ "'")

@ -23,6 +23,7 @@ type transitions =
| Parameters (* decl to decl *) | Parameters (* decl to decl *)
| Cond | Cond
| PointerToDecl (* stmt to decl *) | PointerToDecl (* stmt to decl *)
| Protocol (** decl to decl *)
(* In formulas below prefix (* In formulas below prefix
"E" means "exists a path" "E" means "exists a path"

@ -222,6 +222,7 @@ actual_params:
transition_label: transition_label:
| identifier { match $1 with | identifier { match $1 with
| "Body" | "body" -> "Body", Some CTL.Body | "Body" | "body" -> "Body", Some CTL.Body
| "Protocol" | "protocol" -> "Protocol", Some CTL.Protocol
| "InitExpr" | "initexpr" -> "InitExpr", Some CTL.InitExpr | "InitExpr" | "initexpr" -> "InitExpr", Some CTL.InitExpr
| "Cond" | "cond" -> "Cond", Some CTL.Cond | "Cond" | "cond" -> "Cond", Some CTL.Cond
| "Parameters" | "parameters" -> "Parameters", Some CTL.Parameters | "Parameters" | "parameters" -> "Parameters", Some CTL.Parameters
@ -245,11 +246,20 @@ formula:
| formula AU formula { L.(debug Linters Verbose) "\tParsed AU@\n"; CTL.AU (None,$1, $3) } | formula AU formula { L.(debug Linters Verbose) "\tParsed AU@\n"; CTL.AU (None,$1, $3) }
| formula AF { L.(debug Linters Verbose) "\tParsed AF@\n"; CTL.AF (None,$1) } | formula AF { L.(debug Linters Verbose) "\tParsed AF@\n"; CTL.AF (None,$1) }
| formula EX { L.(debug Linters Verbose) "\tParsed EX@\n"; CTL.EX (None, $1) } | formula EX { L.(debug Linters Verbose) "\tParsed EX@\n"; CTL.EX (None, $1) }
| formula EX WITH_TRANSITION transition_label
{ L.(debug Linters Verbose) "\tParsed EX WITH-TRANSITION '%s'@\n" (fst $4);
CTL.EX (snd $4, $1) }
| formula AX { L.(debug Linters Verbose) "\tParsed AX@\n"; CTL.AX (None, $1) } | formula AX { L.(debug Linters Verbose) "\tParsed AX@\n"; CTL.AX (None, $1) }
| formula AX WITH_TRANSITION transition_label
{ L.(debug Linters Verbose) "\tParsed AX WITH-TRANSITION '%s'@\n" (fst $4);
CTL.AX (snd $4, $1) }
| formula EG { L.(debug Linters Verbose) "\tParsed EG@\n"; CTL.EG (None, $1) } | formula EG { L.(debug Linters Verbose) "\tParsed EG@\n"; CTL.EG (None, $1) }
| formula AG { L.(debug Linters Verbose) "\tParsed AG@\n"; CTL.AG (None, $1) } | formula AG { L.(debug Linters Verbose) "\tParsed AG@\n"; CTL.AG (None, $1) }
| formula EH node_list { L.(debug Linters Verbose) "\tParsed EH@\n"; CTL.EH ($3, $1) } | formula EH node_list { L.(debug Linters Verbose) "\tParsed EH@\n"; CTL.EH ($3, $1) }
| formula EF { L.(debug Linters Verbose) "\tParsed EF@\n"; CTL.EF (None, $1) } | formula EF { L.(debug Linters Verbose) "\tParsed EF@\n"; CTL.EF (None, $1) }
| formula EF WITH_TRANSITION transition_label
{ L.(debug Linters Verbose) "\tParsed EF WITH-TRANSITION '%s'@\n" (fst $4);
CTL.EF(snd $4, $1) }
| WHEN formula HOLDS_IN_NODE node_list | WHEN formula HOLDS_IN_NODE node_list
{ L.(debug Linters Verbose) "\tParsed InNode@\n"; CTL.InNode ($4, $2)} { L.(debug Linters Verbose) "\tParsed InNode@\n"; CTL.InNode ($4, $2)}
| ET node_list WITH_TRANSITION transition_label formula_EF | ET node_list WITH_TRANSITION transition_label formula_EF

@ -145,6 +145,7 @@ type abs_ctype =
| BuiltIn of builtin_kind | BuiltIn of builtin_kind
| Pointer of abs_ctype | Pointer of abs_ctype
| TypeName of ALVar.alexp | TypeName of ALVar.alexp
| ObjCGenProt of abs_ctype * abs_ctype (* Objective-C Protocol or Generics *)
let display_equality_warning () = let display_equality_warning () =
L.(debug Linters Medium) L.(debug Linters Medium)
@ -157,6 +158,8 @@ let rec abs_ctype_to_string t =
| BuiltIn t' -> "BuiltIn (" ^ (builtin_kind_to_string t') ^ ")" | BuiltIn t' -> "BuiltIn (" ^ (builtin_kind_to_string t') ^ ")"
| Pointer t' -> "Pointer (" ^ (abs_ctype_to_string t') ^ ")" | Pointer t' -> "Pointer (" ^ (abs_ctype_to_string t') ^ ")"
| TypeName ae -> "TypeName (" ^ (ALVar.alexp_to_string ae) ^ ")" | TypeName ae -> "TypeName (" ^ (ALVar.alexp_to_string ae) ^ ")"
| ObjCGenProt (b,p) ->
"ObjCGenProt (" ^ (abs_ctype_to_string b) ^ "," ^ (abs_ctype_to_string p) ^")"
let builtin_type_kind_assoc = let builtin_type_kind_assoc =
[ [
@ -209,25 +212,51 @@ let rec pointer_type_equal p ap =
match p, ap with match p, ap with
| PointerType (_, qt), Pointer abs_ctype' | PointerType (_, qt), Pointer abs_ctype'
| ObjCObjectPointerType (_, qt), Pointer abs_ctype' -> | ObjCObjectPointerType (_, qt), Pointer abs_ctype' ->
(match CAst_utils.get_type qt.qt_type_ptr with check_type_ptr qt.qt_type_ptr abs_ctype'
| Some c_type' ->
c_type_equal c_type' abs_ctype'
| None -> false)
| _, _ -> display_equality_warning (); | _, _ -> display_equality_warning ();
false false
and objc_object_type_equal c_type abs_ctype =
let open Clang_ast_t in
let check_type_args abs_arg_type qt =
check_type_ptr qt.qt_type_ptr abs_arg_type in
let check_prot prot_name pointer =
match prot_name with
| TypeName ae -> typename_equal pointer ae
| _ -> false in
match c_type, abs_ctype with
| ObjCObjectType (_, ooti), ObjCGenProt (base, args) ->
(match (CAst_utils.get_type ooti.base_type), ooti.protocol_decls_ptr, ooti.type_args with
| Some base_type, _::_, [] ->
c_type_equal base_type base &&
(List.for_all ~f:(check_prot args) ooti.protocol_decls_ptr)
| Some base_type, [], _::_ ->
c_type_equal base_type base &&
(List.for_all ~f:(check_type_args args) ooti.type_args)
| _ -> false)
| _ -> false
and typename_equal pointer typename = and typename_equal pointer typename =
match typename_to_string pointer with match typename_to_string pointer with
| Some name -> | Some name ->
L.(debug Linters Medium)
"Comparing typename '%s' and pointer '%s' for equality...@\n"
(ALVar.alexp_to_string typename) name;
ALVar.compare_str_with_alexp name typename ALVar.compare_str_with_alexp name typename
| None -> false | None -> false
and check_type_ptr type_ptr abs_ctype =
match CAst_utils.get_type type_ptr with
| Some c_type' -> c_type_equal c_type' abs_ctype
| None -> false
(* Temporary, partial equality function. Cover only what's covered (* Temporary, partial equality function. Cover only what's covered
by the types_parser. It needs to be replaced by a real by the types_parser. It needs to be replaced by a real
comparison function for Clang_ast_t.c_type *) comparison function for Clang_ast_t.c_type *)
and c_type_equal c_type abs_ctype = and c_type_equal c_type abs_ctype =
L.(debug Linters Medium) L.(debug Linters Medium)
"Comparing c_type/abs_ctype for equality... \ "@\nComparing c_type/abs_ctype for equality... \
Type compared: @\nc_type = `%s` @\nabs_ctype =`%s`@\n" Type compared: @\nc_type = `%s` @\nabs_ctype =`%s`@\n"
(Clang_ast_j.string_of_c_type c_type) (Clang_ast_j.string_of_c_type c_type)
(abs_ctype_to_string abs_ctype); (abs_ctype_to_string abs_ctype);
@ -238,10 +267,18 @@ and c_type_equal c_type abs_ctype =
| PointerType _, Pointer _ | PointerType _, Pointer _
| ObjCObjectPointerType _, Pointer _ -> | ObjCObjectPointerType _, Pointer _ ->
pointer_type_equal c_type abs_ctype pointer_type_equal c_type abs_ctype
| ObjCObjectPointerType (_, qt), ObjCGenProt _ ->
check_type_ptr qt.qt_type_ptr abs_ctype
| ObjCObjectType _, ObjCGenProt _ ->
objc_object_type_equal c_type abs_ctype
| ObjCInterfaceType (_, pointer), TypeName ae -> | ObjCInterfaceType (_, pointer), TypeName ae ->
typename_equal pointer ae typename_equal pointer ae
| TypedefType (_, tdi), TypeName ae -> | TypedefType (_, tdi), TypeName ae ->
typename_equal tdi.tti_decl_ptr ae typename_equal tdi.tti_decl_ptr ae
| TypedefType (ti, _), ObjCGenProt _ ->
(match ti.ti_desugared_type with
| Some dt -> check_type_ptr dt abs_ctype
| None -> false)
| _, _ -> display_equality_warning (); | _, _ -> display_equality_warning ();
false false

@ -66,6 +66,7 @@ type abs_ctype =
| BuiltIn of builtin_kind | BuiltIn of builtin_kind
| Pointer of abs_ctype | Pointer of abs_ctype
| TypeName of ALVar.alexp | TypeName of ALVar.alexp
| ObjCGenProt of abs_ctype * abs_ctype (* Objective-C Protocol or Generics *)
val c_type_equal : Clang_ast_t.c_type -> abs_ctype -> bool val c_type_equal : Clang_ast_t.c_type -> abs_ctype -> bool

@ -59,6 +59,8 @@ rule token = parse
| "REGEXP" { REGEXP } | "REGEXP" { REGEXP }
| "(" { LEFT_PAREN } | "(" { LEFT_PAREN }
| ")" { RIGHT_PAREN } | ")" { RIGHT_PAREN }
| "<" { LEFT_ANGLE }
| ">" { RIGHT_ANGLE }
| id { IDENTIFIER (Lexing.lexeme lexbuf) } | id { IDENTIFIER (Lexing.lexeme lexbuf) }
| ''' { read_string (Buffer.create 80) lexbuf } | ''' { read_string (Buffer.create 80) lexbuf }
| _ { raise (SyntaxError ("Unexpected char: '" ^ (Lexing.lexeme lexbuf) ^"'")) } | _ { raise (SyntaxError ("Unexpected char: '" ^ (Lexing.lexeme lexbuf) ^"'")) }

@ -61,6 +61,8 @@
%token REGEXP %token REGEXP
%token LEFT_PAREN %token LEFT_PAREN
%token RIGHT_PAREN %token RIGHT_PAREN
%token LEFT_ANGLE
%token RIGHT_ANGLE
%token <string> IDENTIFIER %token <string> IDENTIFIER
%token <string> STRING %token <string> STRING
%token <string> REARG %token <string> REARG
@ -76,6 +78,7 @@ abs_ctype:
; ;
ctype_specifier_seq: ctype_specifier_seq:
| protocol_or_generics_type_spec { $1 }
| noptr_type_spec { $1 } | noptr_type_spec { $1 }
| ptr_type_spec { $1 } | ptr_type_spec { $1 }
| type_name { $1 } | type_name { $1 }
@ -85,10 +88,30 @@ ptr_type_spec:
| noptr_type_spec STAR { Pointer $1 } | noptr_type_spec STAR { Pointer $1 }
| ptr_type_spec STAR { Pointer $1 } | ptr_type_spec STAR { Pointer $1 }
| type_name STAR { Pointer $1 } | type_name STAR { Pointer $1 }
| protocol_or_generics_type_spec STAR { Pointer $1 }
;
protocol_or_generics_type_spec:
| type_name_or_objid LEFT_ANGLE ctype_specifier_seq RIGHT_ANGLE {
let tname = $1 in
L.(debug Linters Verbose) "\tProtocol or Generics parsed: `%s<%s>`@\n"
(Ctl_parser_types.abs_ctype_to_string tname)
(Ctl_parser_types.abs_ctype_to_string $3);
ObjCGenProt (tname, $3)
}
;
type_name_or_objid:
| OBJCID { BuiltIn ObjCId}
| type_name { $1 }
; ;
type_name: type_name:
| alexp { TypeName $1 } | alexp {
L.(debug Linters Verbose) "\tType_name parsed: `%s`@\n"
(ALVar.alexp_to_string $1);
TypeName $1 }
noptr_type_spec: noptr_type_spec:
| trailing_type_specifier_seq | trailing_type_specifier_seq

@ -10,6 +10,19 @@ codetoanalyze/objc/linters-for-test-only/implicit_cast.c, main, 11, TEST_IMPLICI
codetoanalyze/objc/linters-for-test-only/implicit_cast.c, main, 11, TEST_VAR_TYPE_CHECK, [] codetoanalyze/objc/linters-for-test-only/implicit_cast.c, main, 11, TEST_VAR_TYPE_CHECK, []
codetoanalyze/objc/linters-for-test-only/namespace.mm, Linters_dummy_method, 9, TEST_DEFINE_NAMESPACE, [] codetoanalyze/objc/linters-for-test-only/namespace.mm, Linters_dummy_method, 9, TEST_DEFINE_NAMESPACE, []
codetoanalyze/objc/linters-for-test-only/namespace.mm, Linters_dummy_method, 17, TEST_USING_NAMESPACE, [] codetoanalyze/objc/linters-for-test-only/namespace.mm, Linters_dummy_method, 17, TEST_USING_NAMESPACE, []
codetoanalyze/objc/linters-for-test-only/protocols.m, Foo_newWithA:, 24, TEST_INSTANCE_TYPE, []
codetoanalyze/objc/linters-for-test-only/protocols.m, Foo_newWithA:, 24, TEST_PROTOCOL_TYPE_INHERITANCE, []
codetoanalyze/objc/linters-for-test-only/protocols.m, Foo_newWithB:, 25, TEST_INSTANCE_TYPE, []
codetoanalyze/objc/linters-for-test-only/protocols.m, Foo_newWithB:, 25, TEST_PROTOCOL_TYPE_INHERITANCE, []
codetoanalyze/objc/linters-for-test-only/protocols.m, Foo_newWithC:, 26, TEST_INSTANCE_TYPE, []
codetoanalyze/objc/linters-for-test-only/protocols.m, Foo_newWithC:, 26, TEST_PROTOCOL_TYPE_INHERITANCE, []
codetoanalyze/objc/linters-for-test-only/protocols.m, Foo_newWithCs:, 27, TEST_GENERICS_TYPE, []
codetoanalyze/objc/linters-for-test-only/protocols.m, Foo_newWithCs:, 27, TEST_INSTANCE_TYPE, []
codetoanalyze/objc/linters-for-test-only/protocols.m, Foo_newWithCs:, 27, TEST_PROTOCOL_TYPE_INHERITANCE, []
codetoanalyze/objc/linters-for-test-only/protocols.m, Foo_newWithD:, 28, TEST_BUILTIN_TYPE, []
codetoanalyze/objc/linters-for-test-only/protocols.m, Linters_dummy_method, 11, TEST_PROTOCOL_DEF_INHERITANCE, []
codetoanalyze/objc/linters-for-test-only/protocols.m, Linters_dummy_method, 14, TEST_PROTOCOL_DEF_INHERITANCE, []
codetoanalyze/objc/linters-for-test-only/protocols.m, Linters_dummy_method, 17, TEST_PROTOCOL_DEF_INHERITANCE, []
codetoanalyze/objc/linters-for-test-only/subclassing.m, A_foo:, 13, TEST_BUILTIN_TYPE, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, A_foo:, 13, TEST_BUILTIN_TYPE, []
codetoanalyze/objc/linters-for-test-only/subclassing.m, A_foo:, 13, TEST_PARAM_TYPE_CHECK2, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, A_foo:, 13, TEST_PARAM_TYPE_CHECK2, []
codetoanalyze/objc/linters-for-test-only/subclassing.m, A_foo:, 13, TEST_RETURN_METHOD, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, A_foo:, 13, TEST_RETURN_METHOD, []

@ -221,6 +221,63 @@ DEFINE-CHECKER TEST_NTH_PARAM_TYPE_CHECK = {
SET severity = "LIKE"; SET severity = "LIKE";
}; };
DEFINE-CHECKER TEST_PROTOCOL_DEF_INHERITANCE = {
LET is_subprotocol_of(x) =
declaration_has_name(x) HOLDS-EVENTUALLY WITH-TRANSITION Protocol;
SET report_when =
WHEN
is_subprotocol_of("A")
HOLDS-IN-NODE ObjCProtocolDecl;
SET message = "Protocol inherit from A";
};
DEFINE-CHECKER TEST_PROTOCOL_TYPE_INHERITANCE = {
LET method_has_parameter_subprotocol_of(x) =
WHEN
HOLDS-NEXT WITH-TRANSITION Parameters
(has_type_subprotocol_of(x))
HOLDS-IN-NODE ObjCMethodDecl;
SET report_when =
WHEN
declaration_has_name(REGEXP("^newWith.*:$")) AND
method_has_parameter_subprotocol_of("A")
HOLDS-IN-NODE ObjCMethodDecl;
SET message = "Method declared with parameter whose type inherit from protocol A";
};
DEFINE-CHECKER TEST_GENERICS_TYPE = {
LET method_has_parameter_type(x) =
WHEN
HOLDS-NEXT WITH-TRANSITION Parameters
(has_type(x))
HOLDS-IN-NODE ObjCMethodDecl;
SET report_when =
WHEN
method_has_parameter_type ("NSArray<id<C>>*")
HOLDS-IN-NODE ObjCMethodDecl;
SET message = "Method declared with parameter whose type NSArray<id<C>>*";
};
DEFINE-CHECKER TEST_INSTANCE_TYPE = {
SET report_when =
WHEN
has_type("instancetype")
HOLDS-IN-NODE ObjCMethodDecl;
SET message = "Method declared has return type instancetype";
};
DEFINE-CHECKER TEST_DEFINE_NAMESPACE = { DEFINE-CHECKER TEST_DEFINE_NAMESPACE = {
SET report_when = SET report_when =

@ -0,0 +1,29 @@
/*
* 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 <Foundation/Foundation.h>
@protocol A<NSObject>
@end
@protocol B<A>
@end
@protocol C<B>
@end
@protocol D<NSObject>
@end
@interface Foo : NSObject
+ (instancetype)newWithA:(id<A>)A; // A is a known "bad" protocol, so fire here
+ (instancetype)newWithB:(id<B>)B; // B inherits from A, so this line also fires
+ (instancetype)newWithC:(id<C>)C; // C eventually inherits from A; also fires
+ (instancetype)newWithCs:(NSArray<id<C>>*)Cs; // Collections should also fire
+ (void)newWithD:(id<D>)D; // D doesn't inherit from A; don't fire
@end
Loading…
Cancel
Save