xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp (revision e69d2e47e5142c3a60eab90f39f62e3e44c67ad7)
1 //==- ObjCPropertyChecker.cpp - Check ObjC properties ------------*- C++ -*-==//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 //  This checker finds issues with Objective-C properties.
11 //  Currently finds only one kind of issue:
12 //  - Find synthesized properties with copy attribute of mutable NS collection
13 //    types. Calling -copy on such collections produces an immutable copy,
14 //    which contradicts the type of the property.
15 //
16 //===----------------------------------------------------------------------===//
17 
18 #include "ClangSACheckers.h"
19 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
20 #include "clang/StaticAnalyzer/Core/Checker.h"
21 
22 using namespace clang;
23 using namespace ento;
24 
25 namespace {
26 class ObjCPropertyChecker
27     : public Checker<check::ASTDecl<ObjCPropertyDecl>> {
28   void checkCopyMutable(const ObjCPropertyDecl *D, BugReporter &BR) const;
29 
30 public:
31   void checkASTDecl(const ObjCPropertyDecl *D, AnalysisManager &Mgr,
32                     BugReporter &BR) const;
33 };
34 } // end anonymous namespace.
35 
36 void ObjCPropertyChecker::checkASTDecl(const ObjCPropertyDecl *D,
37                                        AnalysisManager &Mgr,
38                                        BugReporter &BR) const {
39   checkCopyMutable(D, BR);
40 }
41 
42 void ObjCPropertyChecker::checkCopyMutable(const ObjCPropertyDecl *D,
43                                            BugReporter &BR) const {
44   if (D->isReadOnly() || D->getSetterKind() != ObjCPropertyDecl::Copy)
45     return;
46 
47   QualType T = D->getType();
48   if (!T->isObjCObjectPointerType())
49     return;
50 
51   const std::string &PropTypeName(T->getPointeeType().getCanonicalType()
52                                                      .getUnqualifiedType()
53                                                      .getAsString());
54   if (!StringRef(PropTypeName).startswith("NSMutable"))
55     return;
56 
57   const ObjCImplDecl *ImplD = nullptr;
58   if (const ObjCInterfaceDecl *IntD =
59           dyn_cast<ObjCInterfaceDecl>(D->getDeclContext())) {
60     ImplD = IntD->getImplementation();
61   } else {
62     const ObjCCategoryDecl *CatD = cast<ObjCCategoryDecl>(D->getDeclContext());
63     ImplD = CatD->getClassInterface()->getImplementation();
64   }
65 
66   if (!ImplD || ImplD->HasUserDeclaredSetterMethod(D))
67     return;
68 
69   SmallString<128> Str;
70   llvm::raw_svector_ostream OS(Str);
71   OS << "Property of mutable type '" << PropTypeName
72      << "' has 'copy' attribute; an immutable object will be stored instead";
73 
74   BR.EmitBasicReport(
75       D, this, "Objective-C property misuse", "Logic error", OS.str(),
76       PathDiagnosticLocation::createBegin(D, BR.getSourceManager()),
77       D->getSourceRange());
78 }
79 
80 void ento::registerObjCPropertyChecker(CheckerManager &Mgr) {
81   Mgr.registerChecker<ObjCPropertyChecker>();
82 }
83