Making easier to specify subclassing

Reviewed By: dulmarod

Differential Revision: D4699748

fbshipit-source-id: 39f7c05
master
Dino Distefano 8 years ago committed by Facebook Github Bot
parent 4a62c44a3e
commit df543b900b

@ -80,7 +80,7 @@ DIRECT_TESTS += \
endif endif
ifneq ($(XCODE_SELECT),no) ifneq ($(XCODE_SELECT),no)
DIRECT_TESTS += \ DIRECT_TESTS += \
objc_frontend objc_errors objc_linters objc_ioslints objcpp_frontend objcpp_linters objc_frontend objc_errors objc_linters objc_ioslints objcpp_frontend objcpp_linters objc_linters-for-test-only
endif endif
.PHONY: all .PHONY: all

@ -40,13 +40,13 @@ DEFINE-CHECKER ASSIGN_POINTER_WARNING = {
// Fires whenever a NSNumber is dangerously coerced to a boolean in a comparison // Fires whenever a NSNumber is dangerously coerced to a boolean in a comparison
DEFINE-CHECKER BAD_POINTER_COMPARISON = { DEFINE-CHECKER BAD_POINTER_COMPARISON = {
LET is_binop = in_node(BinaryOperator); LET is_binop = is_node(BinaryOperator);
LET is_binop_eq = is_binop_with_kind(EQ); LET is_binop_eq = is_binop_with_kind(EQ);
LET is_binop_ne = is_binop_with_kind(NE); LET is_binop_ne = is_binop_with_kind(NE);
LET is_binop_neq = is_binop_eq OR is_binop_ne; LET is_binop_neq = is_binop_eq OR is_binop_ne;
LET is_unop_lnot = is_unop_with_kind(LNot); LET is_unop_lnot = is_unop_with_kind(LNot);
LET is_implicit_cast_expr = in_node(ImplicitCastExpr); LET is_implicit_cast_expr = is_node(ImplicitCastExpr);
LET is_expr_with_cleanups = in_node(ExprWithCleanups); LET is_expr_with_cleanups = is_node(ExprWithCleanups);
LET is_nsnumber = isa(NSNumber); LET is_nsnumber = isa(NSNumber);
LET eu =( LET eu =(
@ -119,7 +119,7 @@ DEFINE-CHECKER REGISTERED_OBSERVER_BEING_DEALLOCATED = {
LET eventually_removeObserver = LET eventually_removeObserver =
IN-NODE ObjCImplementationDecl, ObjCProtocolDecl WITH-TRANSITION _ IN-NODE ObjCImplementationDecl, ObjCProtocolDecl WITH-TRANSITION _
(remove_observer_in_method OR (remove_observer_in_method OR
remove_observer_in_method INSTANCEOF ObjCImplementationDecl, ObjCProtocolDecl) remove_observer_in_method HOLDS-IN-SOME-SUPERCLASS-OF ObjCImplementationDecl)
HOLDS-EVENTUALLY; HOLDS-EVENTUALLY;
SET report_when = SET report_when =
@ -157,11 +157,11 @@ DEFINE-CHECKER GLOBAL_VARIABLE_INITIALIZED_WITH_FUNCTION_OR_METHOD_CALL = {
is_objc_extension() AND is_global_var() AND (NOT is_const_var()); is_objc_extension() AND is_global_var() AND (NOT is_const_var());
LET makes_an_expensive_call = LET makes_an_expensive_call =
(in_node(CallExpr) AND NOT call_function_named(CGPointMake)) (is_node(CallExpr) AND NOT call_function_named(CGPointMake))
OR in_node(CXXTemporaryObjectExpr) OR is_node(CXXTemporaryObjectExpr)
OR in_node(CXXMemberCallExpr) OR is_node(CXXMemberCallExpr)
OR in_node(CXXOperatorCallExpr) OR is_node(CXXOperatorCallExpr)
OR in_node(ObjCMessageExpr); OR is_node(ObjCMessageExpr);
LET is_initialized_with_expensive_call = LET is_initialized_with_expensive_call =
IN-NODE VarDecl WITH-TRANSITION InitExpr IN-NODE VarDecl WITH-TRANSITION InitExpr
@ -183,7 +183,7 @@ DEFINE-CHECKER GLOBAL_VARIABLE_INITIALIZED_WITH_FUNCTION_OR_METHOD_CALL = {
DEFINE-CHECKER CXX_REFERENCE_CAPTURED_IN_OBJC_BLOCK = { DEFINE-CHECKER CXX_REFERENCE_CAPTURED_IN_OBJC_BLOCK = {
SET report_when = SET report_when =
WHEN WHEN
((in_node(BlockDecl) AND captures_cxx_references()) ((is_node(BlockDecl) AND captures_cxx_references())
HOLDS-NEXT) HOLDS-NEXT)
HOLDS-IN-NODE BlockExpr; HOLDS-IN-NODE BlockExpr;
@ -194,7 +194,7 @@ DEFINE-CHECKER CXX_REFERENCE_CAPTURED_IN_OBJC_BLOCK = {
// HOLDS-IN-NODE BlockDecl; // HOLDS-IN-NODE BlockDecl;
// //
// SET report_when = // SET report_when =
// in_node(BlockDecl) AND captures_cxx_references(); // is_node(BlockDecl) AND captures_cxx_references();
SET message = SET message =
"C++ Reference variable(s) %cxx_ref_captured_in_block% captured by Objective-C block"; "C++ Reference variable(s) %cxx_ref_captured_in_block% captured by Objective-C block";

@ -252,5 +252,10 @@ let invoke_set_of_parsed_checkers_an parsed_linters context (an : Ctl_parser_typ
(* We decouple the hardcoded checkers from the parsed ones *) (* We decouple the hardcoded checkers from the parsed ones *)
let invoke_set_of_checkers_on_node context an = let invoke_set_of_checkers_on_node context an =
invoke_set_of_parsed_checkers_an !parsed_linters context an; (match an with
| Ctl_parser_types.Decl (Clang_ast_t.TranslationUnitDecl _) ->
(* Don't run parsed linters on TranslationUnitDecl node.
Because depending on the formula it may give an error at line -1 *)
()
| _ -> invoke_set_of_parsed_checkers_an !parsed_linters context an);
invoke_set_of_hard_coded_checkers_an context an invoke_set_of_hard_coded_checkers_an context an

@ -285,6 +285,7 @@ let is_node nodename an =
| Ctl_parser_types.Decl d -> Clang_ast_proj.get_decl_kind_string d in | Ctl_parser_types.Decl d -> Clang_ast_proj.get_decl_kind_string d in
String.equal nodename an_str String.equal nodename an_str
(* node an is of class classname *)
let isa classname an = let isa classname an =
match an with match an with
| Ctl_parser_types.Stmt stmt -> | Ctl_parser_types.Stmt stmt ->
@ -295,6 +296,34 @@ let isa classname an =
| _ -> false) | _ -> false)
| _ -> false | _ -> false
let _declaration_has_name comp an name =
match an with
| Ctl_parser_types.Decl d ->
(match Clang_ast_proj.get_named_decl_tuple d with
| Some (_, ndi) -> comp ndi.ni_name name
| _ -> false)
| _ -> false
(* an is a declaration whose name contains a regexp defined by re *)
let declaration_has_name an re =
_declaration_has_name (str_contains) an re
(* an is a declaration called precisely name *)
let declaration_has_name_strict an name =
_declaration_has_name (String.equal) an name
let _is_class comp an re =
match an with
| Ctl_parser_types.Decl (Clang_ast_t.ObjCInterfaceDecl _)
| Ctl_parser_types.Decl (Clang_ast_t.ObjCImplementationDecl _) ->
_declaration_has_name comp an re
| _ -> false
let is_class an re =
_is_class (str_contains) an re
let is_class_strict an name =
_is_class (String.equal) an name
let decl_unavailable_in_supported_ios_sdk (cxt : CLintersContext.context) an = let decl_unavailable_in_supported_ios_sdk (cxt : CLintersContext.context) an =
let allowed_os_versions = let allowed_os_versions =

@ -61,6 +61,14 @@ val isa : string -> Ctl_parser_types.ast_node -> bool
val is_node : string -> Ctl_parser_types.ast_node -> bool val is_node : string -> Ctl_parser_types.ast_node -> bool
val declaration_has_name : Ctl_parser_types.ast_node -> string -> bool
val declaration_has_name_strict : Ctl_parser_types.ast_node -> string -> bool
val is_class : Ctl_parser_types.ast_node -> string -> bool
val is_class_strict : Ctl_parser_types.ast_node -> string -> bool
val pp_predicate : Format.formatter -> t -> unit val pp_predicate : Format.formatter -> t -> unit
val decl_unavailable_in_supported_ios_sdk : CLintersContext.context -> Ctl_parser_types.ast_node val decl_unavailable_in_supported_ios_sdk : CLintersContext.context -> Ctl_parser_types.ast_node

@ -366,12 +366,21 @@ let transition_decl_to_stmt d trs =
| _ -> None | _ -> None
let transition_decl_to_decl_via_super d = let transition_decl_to_decl_via_super d =
match CAst_utils.get_impl_decl_info d with let decl_opt_to_ast_node_opt d_opt =
| Some idi -> match d_opt with
(match CAst_utils.get_super_ObjCImplementationDecl idi with | Some d' -> Some (Decl d')
| Some d -> Some (Decl d) | None -> None in
| _ -> None) let do_ObjCImplementationDecl d =
| None -> None match CAst_utils.get_impl_decl_info d with
| Some idi ->
decl_opt_to_ast_node_opt (CAst_utils.get_super_ObjCImplementationDecl idi)
| None -> None in
match d with
| Clang_ast_t.ObjCImplementationDecl _ ->
do_ObjCImplementationDecl d
| Clang_ast_t.ObjCInterfaceDecl (_, _, _, _, idi) ->
decl_opt_to_ast_node_opt (CAst_utils.get_decl_opt_with_decl_ref idi.otdi_super)
| _ -> None
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
@ -442,8 +451,14 @@ let rec eval_Atomic pred_name args an lcxt =
CPredicates.is_binop_with_kind str_kind an CPredicates.is_binop_with_kind str_kind an
| "is_unop_with_kind", [str_kind], an -> | "is_unop_with_kind", [str_kind], an ->
CPredicates.is_unop_with_kind str_kind an CPredicates.is_unop_with_kind str_kind an
| "in_node", [nodename], an -> CPredicates.is_node nodename an | "is_node", [nodename], an -> CPredicates.is_node nodename an
| "isa", [classname], an -> CPredicates.isa classname an | "isa", [classname], an -> CPredicates.isa classname an
| "declaration_has_name", [decl_name], an ->
CPredicates.declaration_has_name an decl_name
| "declaration_has_name_strict", [decl_name], an ->
CPredicates.declaration_has_name_strict an decl_name
| "is_class", [cname], an -> CPredicates.is_class an cname
| "is_class_strict", [cname], an -> CPredicates.is_class_strict an cname
| "decl_unavailable_in_supported_ios_sdk", [], an -> | "decl_unavailable_in_supported_ios_sdk", [], an ->
CPredicates.decl_unavailable_in_supported_ios_sdk lcxt an CPredicates.decl_unavailable_in_supported_ios_sdk lcxt an
| "within_responds_to_selector_block", [], an -> | "within_responds_to_selector_block", [], an ->
@ -527,7 +542,7 @@ and in_node node_type_list phi an lctx =
such that d,lcxt |= phi *) such that d,lcxt |= phi *)
and eval_EH classes phi an lcxt = and eval_EH classes phi an lcxt =
(* Define EH[Classes] phi = ET[Classes](EF[->Super] phi) *) (* Define EH[Classes] phi = ET[Classes](EF[->Super] phi) *)
let f = ET (classes, None, EF (Some Super, phi)) in let f = ET (classes, None, EX (Some Super, EF (Some Super, phi))) in
eval_formula f an lcxt eval_formula f an lcxt
(* an, lcxt |= ET[T][->l]phi <=> (* an, lcxt |= ET[T][->l]phi <=>

@ -38,7 +38,7 @@ rule token = parse
| "HOLDS-EVERYWHERE-NEXT" { AX } | "HOLDS-EVERYWHERE-NEXT" { AX }
| "ALWAYS-HOLDS" { EG } | "ALWAYS-HOLDS" { EG }
| "ALSWAYS-HOLDS-EVERYWHERE" { AG } | "ALSWAYS-HOLDS-EVERYWHERE" { AG }
| "INSTANCEOF" { EH } | "HOLDS-IN-SOME-SUPERCLASS-OF" { EH }
| "IN-NODE" { ET } | "IN-NODE" { ET }
| "IN-EXCLUSIVE-NODE" { ETX } | "IN-EXCLUSIVE-NODE" { ETX }
| "WHEN" { WHEN } | "WHEN" { WHEN }

@ -0,0 +1,19 @@
# Copyright (c) 2016 - 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.
TESTS_DIR = ../../..
ANALYZER = linters
CLANG_OPTIONS = -x objective-c -fobjc-arc -c
INFER_OPTIONS = --linters-def-file linters_example.al --no-filtering --debug-exceptions --project-root $(TESTS_DIR) --no-failures-allowed
INFERPRINT_OPTIONS = --issues-tests
SOURCES = \
$(wildcard *.m) \
$(wildcard */*.m) \
include $(TESTS_DIR)/clang.make

@ -0,0 +1,4 @@
codetoanalyze/objc/linters-for-test-only/subclassing.m, Linters_dummy_method, 26, SUBCLASSING_TEST_EXAMPLE, []
codetoanalyze/objc/linters-for-test-only/subclassing.m, Linters_dummy_method, 41, SUBCLASSING_TEST_EXAMPLE, []
codetoanalyze/objc/linters-for-test-only/subclassing.m, Linters_dummy_method, 47, SUBCLASSING_TEST_EXAMPLE, []
codetoanalyze/objc/linters-for-test-only/subclassing.m, Linters_dummy_method, 53, SUBCLASSING_TEST_EXAMPLE, []

@ -0,0 +1,10 @@
// Check that class A is not subclassed.
DEFINE-CHECKER SUBCLASSING_TEST_EXAMPLE = {
SET report_when =
is_class(A) HOLDS-IN-SOME-SUPERCLASS-OF ObjCInterfaceDecl;
SET message = "This is subclassing A. Class A should not be subclassed.";
};

@ -0,0 +1,63 @@
/*
* 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/NSObject.h>
@interface A : NSObject
- (int)foo:(int)foo_par;
@end
@implementation A
- (int)foo:(int)foo_par {
return foo_par;
}
@end
@interface B : A // Error: A subclass
- (void)bar;
@end
@implementation B
- (void)bar {
A* a = [[A alloc] init];
[a foo:5];
}
@end
@interface C : B // Error: C subclass of B subclass of A
@end
@implementation C
@end
@interface D : C // Error: D subclass of C ... subclass of A
@end
@implementation D
@end
@interface E : D // Error: E subclass of D ... subclass of A
@end
@implementation E
@end
@interface F : NSObject
@end
@implementation F
@end
Loading…
Cancel
Save