// Copyright (c) Facebook, Inc. and its affiliates. // // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree. // DIRECT_ATOMIC_PROPERTY_ACCESS: // a property declared atomic should not be accessed directly via its ivar DEFINE-CHECKER DIRECT_ATOMIC_PROPERTY_ACCESS = { SET report_when = WHEN is_ivar_atomic() AND NOT context_in_synchronized_block() AND NOT is_ivar_readonly() AND NOT is_method_property_accessor_of_ivar() AND NOT is_objc_constructor() AND NOT is_objc_dealloc() HOLDS-IN-NODE ObjCIvarRefExpr; SET message = "Direct access to ivar %ivar_name% of an atomic property"; SET suggestion = "Accessing an ivar of an atomic property makes the property nonatomic."; SET severity = "WARNING"; }; // ASSIGN_POINTER_WARNING: // a property with a pointer type should not be declared `assign` DEFINE-CHECKER ASSIGN_POINTER_WARNING = { SET report_when = WHEN is_assign_property() AND is_property_pointer_type() HOLDS-IN-NODE ObjCPropertyDecl; SET message = "Property %decl_name% is a pointer type marked with the `assign` attribute"; SET suggestion = "Use a different attribute like `strong` or `weak`."; SET severity = "WARNING"; }; // BAD_POINTER_COMPARISON: // Fires whenever a NSNumber is dangerously coerced to a boolean in a comparison DEFINE-CHECKER BAD_POINTER_COMPARISON = { LET bool_op = is_binop_with_kind("LAnd") OR is_binop_with_kind("LOr") OR is_unop_with_kind("LNot") OR is_unop_with_kind("LNot"); LET comparison_with_integral = ( is_binop_with_kind("EQ") OR is_binop_with_kind("NE") OR is_binop_with_kind("GT") OR is_binop_with_kind("GE") OR is_binop_with_kind("LT") OR is_binop_with_kind("LE")) AND ( (is_node("ImplicitCastExpr") AND has_type("NSNumber *") AND has_cast_kind("IntegralToPointer") ) HOLDS-NEXT); LET root_is_stmt_expecting_bool = is_node("IfStmt") OR is_node("ForStmt") OR is_node("WhileStmt"); LET use_num_as_bool = (bool_op OR root_is_stmt_expecting_bool) AND (has_type("NSNumber *") HOLDS-NEXT); LET bad_conditional = is_node("ConditionalOperator") AND (has_type("NSNumber *") HOLDS-NEXT WITH-TRANSITION Cond); SET report_when = use_num_as_bool OR comparison_with_integral OR bad_conditional; SET message = "Implicitly checking whether NSNumber pointer is nil or comparing to integral value"; SET suggestion = "Did you mean to use/compare against the unboxed value instead? Please either explicitly compare the NSNumber instance to nil, or use one of the NSNumber accessors before the comparison."; }; DEFINE-CHECKER STRONG_DELEGATE_WARNING = { LET name_contains_delegate = declaration_has_name(REGEXP("[dD]elegate")); LET name_does_not_contain_delegates = NOT declaration_has_name(REGEXP("[dD]elegates")); LET name_does_not_contains_queue = NOT declaration_has_name(REGEXP("[qQ]ueue")); SET report_when = WHEN name_contains_delegate AND name_does_not_contain_delegates AND name_does_not_contains_queue AND is_strong_property() HOLDS-IN-NODE ObjCPropertyDecl; SET message = "Property or ivar %decl_name% declared strong"; SET suggestion = "In general delegates should be declared weak or assign."; }; DEFINE-CHECKER GLOBAL_VARIABLE_INITIALIZED_WITH_FUNCTION_OR_METHOD_CALL = { LET is_global_but_not_const_variable = is_objc_extension() AND is_global_var() AND (NOT is_const_expr_var()) AND (NOT is_init_integral_constant_expr()); LET makes_an_expensive_call = (is_node("CallExpr") AND NOT call_function("CGPointMake")) OR is_node("CXXTemporaryObjectExpr") OR is_node("CXXMemberCallExpr") OR is_node("CXXOperatorCallExpr") OR is_node("ObjCMessageExpr"); LET is_initialized_with_expensive_call = IN-NODE VarDecl WITH-TRANSITION InitExpr (makes_an_expensive_call HOLDS-EVENTUALLY) HOLDS-EVENTUALLY; SET report_when = WHEN (is_global_but_not_const_variable AND is_initialized_with_expensive_call) HOLDS-IN-NODE VarDecl; SET message = "Global variable %decl_name% is initialized using a function or method call"; SET suggestion = "If the function/method call is expensive, it can affect the starting time of the app."; }; DEFINE-CHECKER CXX_REFERENCE_CAPTURED_IN_OBJC_BLOCK = { // The current node is block definition capturing a C++ reference LET capture_reference = WHEN ((is_node("BlockDecl") AND captures_cxx_references()) HOLDS-NEXT) HOLDS-IN-NODE BlockExpr; // At some point we encounter a block definition capturing a C++ reference LET block_definition_capture_reference = capture_reference HOLDS-EVENTUALLY; // A variable definition initialized with a block capturing a C++ reference LET variable_initialized_with_block = IN-NODE VarDecl WITH-TRANSITION InitExpr (block_definition_capture_reference) HOLDS-EVENTUALLY; // Reference to a variable initialized with a capturing block LET variable_block_definition = IN-NODE DeclRefExpr WITH-TRANSITION PointerToDecl (variable_initialized_with_block) HOLDS-EVENTUALLY; // Report when a function that does not have NoEscapeAttribute call a block or a variable definied with // a block capturing a C++ ref SET report_when = WHEN (NOT has_no_escape_attribute) AND (block_definition_capture_reference OR variable_block_definition) HOLDS-IN-NODE CallExpr; SET message = "C++ Reference variable(s) %cxx_ref_captured_in_block% captured by Objective-C block"; SET suggestion = "This will very likely cause a crash because C++ References are unmanaged and may be invalid by the time the block executes."; SET severity = "ERROR"; SET mode = "ON"; }; DEFINE-CHECKER POINTER_TO_INTEGRAL_IMPLICIT_CAST = { SET report_when = WHEN has_cast_kind("PointerToIntegral") HOLDS-IN-NODE ImplicitCastExpr; SET message = "Implicit conversion from %child_type% to %type% in usage of %name%"; SET doc_url = "https://clang.llvm.org/docs/DiagnosticsReference.html#wint-conversion"; }; DEFINE-CHECKER POINTER_TO_CONST_OBJC_CLASS = { SET name = "Pointer To const Objective-C Class"; SET report_when = is_decl() AND has_type_const_ptr_to_objc_class(); SET message = "`const %class_name%*` may not mean what you want: it represents a mutable pointer pointing to an Objective-C class where the ivars cannot be changed."; SET suggestion = "Consider using `%class_name% *const` instead, meaning the destination of the pointer cannot be changed."; SET severity = "WARNING"; SET mode = "ON"; }; DEFINE-CHECKER DISCOURAGED_WEAK_PROPERTY_CUSTOM_SETTER = { LET has_body = HOLDS-NEXT WITH-TRANSITION Body (TRUE); LET is_weak_property_setter = HOLDS-NEXT WITH-TRANSITION AccessorForProperty "setter" ( is_weak_property() ); SET report_when = WHEN has_body AND is_weak_property_setter HOLDS-IN-NODE ObjCMethodDecl; SET message = "Custom setters are not called when ARC sets weak properties to nil."; SET severity = "WARNING"; SET mode = "OFF"; }; DEFINE-CHECKER WRONG_SCOPE_FOR_DISPATCH_ONCE_T = { SET report_when = WHEN NOT (is_global_var() OR is_static_local_var()) AND has_type("dispatch_once_t") HOLDS-IN-NODE VarDecl; SET message = "Variables of type dispatch_once_t must have global or static scope. The result of using this type with automatic or dynamic allocation is undefined."; SET severity = "WARNING"; SET mode = "ON"; }; DEFINE-CHECKER UNSAFE_CALL_TO_OPTIONAL_METHOD = { SET report_when = WHEN is_call_to_optional_objc_method AND (NOT objc_method_call_within_responds_to_selector_block) HOLDS-IN-NODE ObjCMessageExpr; SET message = "This is a call to an `@optional` protocol method. Calling it without checking if its implemented can lead to crashes at run time."; SET suggestion = "Please make sure to test the method is implemented by first calling `if ([object respondsToSelector:@selector(%decl_name%)]) ...` "; SET severity = "ERROR"; SET mode = "ON"; };