diff --git a/infer/documentation/checkers/ASTLanguage.md b/infer/documentation/checkers/ASTLanguage.md index 1608d0e0a..b67068a3a 100644 --- a/infer/documentation/checkers/ASTLanguage.md +++ b/infer/documentation/checkers/ASTLanguage.md @@ -217,6 +217,7 @@ is_class ("class_name") is_const_var () is_global_var () is_ivar_atomic () +is_ivar_readonly () is_method_property_accessor_of_ivar () is_node ("node_name") is_objc_constructor () diff --git a/infer/lib/linter_rules/linters.al b/infer/lib/linter_rules/linters.al index ad01b05a4..d913f08f7 100644 --- a/infer/lib/linter_rules/linters.al +++ b/infer/lib/linter_rules/linters.al @@ -9,7 +9,9 @@ DEFINE-CHECKER DIRECT_ATOMIC_PROPERTY_ACCESS = { SET report_when = WHEN - ((NOT context_in_synchronized_block()) AND is_ivar_atomic()) + 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() diff --git a/infer/src/al/CTL.ml b/infer/src/al/CTL.ml index 0d77819a5..106845297 100644 --- a/infer/src/al/CTL.ml +++ b/infer/src/al/CTL.ml @@ -634,6 +634,8 @@ let eval_Atomic pred_name_ args an lcxt = CPredicates.is_in_objc_category_named lcxt name | "is_in_objc_protocol_named", [name], _ -> CPredicates.is_in_objc_protocol_named lcxt name + | "is_ivar_readonly", [], an -> + CPredicates.is_ivar_readonly an | "is_ivar_atomic", [], an -> CPredicates.is_ivar_atomic an | "is_method_property_accessor_of_ivar", [], an -> diff --git a/infer/src/al/cPredicates.ml b/infer/src/al/cPredicates.ml index d86610d5c..b3dd76077 100644 --- a/infer/src/al/cPredicates.ml +++ b/infer/src/al/cPredicates.ml @@ -713,6 +713,22 @@ let is_ivar_atomic an = false +(* checks if ivar is defined among a set of fields and if it is readonly *) +let is_ivar_readonly an = + match an with + | Ctl_parser_types.Stmt (Clang_ast_t.ObjCIvarRefExpr (_, _, _, irei)) -> ( + let dr_ref = irei.Clang_ast_t.ovrei_decl_ref in + let ivar_pointer = dr_ref.Clang_ast_t.dr_decl_pointer in + match CAst_utils.get_decl ivar_pointer with + | Some d -> + let attributes = get_ivar_attributes d in + List.exists ~f:(PolyVariantEqual.( = ) `Readonly) attributes + | _ -> + false ) + | _ -> + false + + let is_method_property_accessor_of_ivar an context = let open Clang_ast_t in match an with diff --git a/infer/src/al/cPredicates.mli b/infer/src/al/cPredicates.mli index f61260a35..162fb3142 100644 --- a/infer/src/al/cPredicates.mli +++ b/infer/src/al/cPredicates.mli @@ -98,7 +98,10 @@ val context_in_synchronized_block : CLintersContext.context -> bool (** true if the current node is in the context of a synchronized objc block *) val is_ivar_atomic : Ctl_parser_types.ast_node -> bool -(** [is_ivar_atomic an] is true iff an denotes an atomi objc ivar *) +(** [is_ivar_atomic an] is true iff an denotes an atomic objc ivar *) + +val is_ivar_readonly : Ctl_parser_types.ast_node -> bool +(** [is_ivar_readonly an] is true iff an denotes a readonly objc ivar *) val is_method_property_accessor_of_ivar : Ctl_parser_types.ast_node -> CLintersContext.context -> bool diff --git a/infer/tests/codetoanalyze/objc/linters/atomic_prop.m b/infer/tests/codetoanalyze/objc/linters/atomic_prop.m index 098d7c1f7..332608fa6 100644 --- a/infer/tests/codetoanalyze/objc/linters/atomic_prop.m +++ b/infer/tests/codetoanalyze/objc/linters/atomic_prop.m @@ -25,7 +25,7 @@ @property(nonatomic) int* p; @property int* q; // atomic by default @property(atomic, assign) float f; - +@property(atomic, readonly, assign) float fReadonly; @property(strong) B* b; - (void)write_p:(int)i; @@ -115,4 +115,8 @@ b(); } +- (float)readonlyAccess { + return _fReadonly; // good access +} + @end diff --git a/website/docs/checker-linters.md b/website/docs/checker-linters.md index cc852c203..1ad42c7c4 100644 --- a/website/docs/checker-linters.md +++ b/website/docs/checker-linters.md @@ -232,6 +232,7 @@ is_class ("class_name") is_const_var () is_global_var () is_ivar_atomic () +is_ivar_readonly () is_method_property_accessor_of_ivar () is_node ("node_name") is_objc_constructor ()