1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
18 | #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" |
19 | #include "clang/StaticAnalyzer/Core/Checker.h" |
20 | |
21 | using namespace clang; |
22 | using namespace ento; |
23 | |
24 | namespace { |
25 | class ObjCPropertyChecker |
26 | : public Checker<check::ASTDecl<ObjCPropertyDecl>> { |
27 | void checkCopyMutable(const ObjCPropertyDecl *D, BugReporter &BR) const; |
28 | |
29 | public: |
30 | void checkASTDecl(const ObjCPropertyDecl *D, AnalysisManager &Mgr, |
31 | BugReporter &BR) const; |
32 | }; |
33 | } |
34 | |
35 | void ObjCPropertyChecker::checkASTDecl(const ObjCPropertyDecl *D, |
36 | AnalysisManager &Mgr, |
37 | BugReporter &BR) const { |
38 | checkCopyMutable(D, BR); |
39 | } |
40 | |
41 | void ObjCPropertyChecker::checkCopyMutable(const ObjCPropertyDecl *D, |
42 | BugReporter &BR) const { |
43 | if (D->isReadOnly() || D->getSetterKind() != ObjCPropertyDecl::Copy) |
44 | return; |
45 | |
46 | QualType T = D->getType(); |
47 | if (!T->isObjCObjectPointerType()) |
48 | return; |
49 | |
50 | const std::string &PropTypeName(T->getPointeeType().getCanonicalType() |
51 | .getUnqualifiedType() |
52 | .getAsString()); |
53 | if (!StringRef(PropTypeName).startswith("NSMutable")) |
54 | return; |
55 | |
56 | const ObjCImplDecl *ImplD = nullptr; |
57 | if (const ObjCInterfaceDecl *IntD = |
58 | dyn_cast<ObjCInterfaceDecl>(D->getDeclContext())) { |
59 | ImplD = IntD->getImplementation(); |
60 | } else if (auto *CatD = dyn_cast<ObjCCategoryDecl>(D->getDeclContext())) { |
61 | ImplD = CatD->getClassInterface()->getImplementation(); |
62 | } |
63 | |
64 | if (!ImplD || ImplD->HasUserDeclaredSetterMethod(D)) |
65 | return; |
66 | |
67 | SmallString<128> Str; |
68 | llvm::raw_svector_ostream OS(Str); |
69 | OS << "Property of mutable type '" << PropTypeName |
70 | << "' has 'copy' attribute; an immutable object will be stored instead"; |
71 | |
72 | BR.EmitBasicReport( |
73 | D, this, "Objective-C property misuse", "Logic error", OS.str(), |
74 | PathDiagnosticLocation::createBegin(D, BR.getSourceManager()), |
75 | D->getSourceRange()); |
76 | } |
77 | |
78 | void ento::registerObjCPropertyChecker(CheckerManager &Mgr) { |
79 | Mgr.registerChecker<ObjCPropertyChecker>(); |
80 | } |
81 | |
82 | bool ento::shouldRegisterObjCPropertyChecker(const LangOptions &LO) { |
83 | return true; |
84 | } |
85 | |