/Users/buildslave/jenkins/sharedspace/clang-stage2-coverage-R@2/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //== ObjCContainersASTChecker.cpp - CoreFoundation containers API *- 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 | | // An AST checker that looks for common pitfalls when using 'CFArray', |
11 | | // 'CFDictionary', 'CFSet' APIs. |
12 | | // |
13 | | //===----------------------------------------------------------------------===// |
14 | | #include "ClangSACheckers.h" |
15 | | #include "clang/AST/StmtVisitor.h" |
16 | | #include "clang/Analysis/AnalysisDeclContext.h" |
17 | | #include "clang/Basic/TargetInfo.h" |
18 | | #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" |
19 | | #include "clang/StaticAnalyzer/Core/Checker.h" |
20 | | #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" |
21 | | #include "llvm/ADT/SmallString.h" |
22 | | #include "llvm/Support/raw_ostream.h" |
23 | | |
24 | | using namespace clang; |
25 | | using namespace ento; |
26 | | |
27 | | namespace { |
28 | | class WalkAST : public StmtVisitor<WalkAST> { |
29 | | BugReporter &BR; |
30 | | const CheckerBase *Checker; |
31 | | AnalysisDeclContext* AC; |
32 | | ASTContext &ASTC; |
33 | | uint64_t PtrWidth; |
34 | | |
35 | | /// Check if the type has pointer size (very conservative). |
36 | 21 | inline bool isPointerSize(const Type *T) { |
37 | 21 | if (!T) |
38 | 0 | return true; |
39 | 21 | if (21 T->isIncompleteType()21 ) |
40 | 0 | return true; |
41 | 21 | return (ASTC.getTypeSize(T) == PtrWidth); |
42 | 21 | } |
43 | | |
44 | | /// Check if the type is a pointer/array to pointer sized values. |
45 | 24 | inline bool hasPointerToPointerSizedType(const Expr *E) { |
46 | 24 | QualType T = E->getType(); |
47 | 24 | |
48 | 24 | // The type could be either a pointer or array. |
49 | 24 | const Type *TP = T.getTypePtr(); |
50 | 24 | QualType PointeeT = TP->getPointeeType(); |
51 | 24 | if (!PointeeT.isNull()24 ) { |
52 | 13 | // If the type is a pointer to an array, check the size of the array |
53 | 13 | // elements. To avoid false positives coming from assumption that the |
54 | 13 | // values x and &x are equal when x is an array. |
55 | 13 | if (const Type *TElem = PointeeT->getArrayElementTypeNoTypeQual()) |
56 | 3 | if (3 isPointerSize(TElem)3 ) |
57 | 2 | return true; |
58 | 11 | |
59 | 11 | // Else, check the pointee size. |
60 | 11 | return isPointerSize(PointeeT.getTypePtr()); |
61 | 11 | } |
62 | 11 | |
63 | 11 | if (const Type *11 TElem11 = TP->getArrayElementTypeNoTypeQual()) |
64 | 7 | return isPointerSize(TElem); |
65 | 4 | |
66 | 4 | // The type must be an array/pointer type. |
67 | 4 | |
68 | 4 | // This could be a null constant, which is allowed. |
69 | 4 | return static_cast<bool>( |
70 | 4 | E->isNullPointerConstant(ASTC, Expr::NPC_ValueDependentIsNull)); |
71 | 4 | } |
72 | | |
73 | | public: |
74 | | WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac) |
75 | | : BR(br), Checker(checker), AC(ac), ASTC(AC->getASTContext()), |
76 | 58 | PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {} |
77 | | |
78 | | // Statement visitor methods. |
79 | | void VisitChildren(Stmt *S); |
80 | 1.20k | void VisitStmt(Stmt *S) { VisitChildren(S); } |
81 | | void VisitCallExpr(CallExpr *CE); |
82 | | }; |
83 | | } // end anonymous namespace |
84 | | |
85 | 91 | static StringRef getCalleeName(CallExpr *CE) { |
86 | 91 | const FunctionDecl *FD = CE->getDirectCallee(); |
87 | 91 | if (!FD) |
88 | 0 | return StringRef(); |
89 | 91 | |
90 | 91 | IdentifierInfo *II = FD->getIdentifier(); |
91 | 91 | if (!II) // if no identifier, not a simple C function |
92 | 0 | return StringRef(); |
93 | 91 | |
94 | 91 | return II->getName(); |
95 | 91 | } |
96 | | |
97 | 91 | void WalkAST::VisitCallExpr(CallExpr *CE) { |
98 | 91 | StringRef Name = getCalleeName(CE); |
99 | 91 | if (Name.empty()) |
100 | 0 | return; |
101 | 91 | |
102 | 91 | const Expr *Arg = nullptr; |
103 | 91 | unsigned ArgNum; |
104 | 91 | |
105 | 91 | if (Name.equals("CFArrayCreate") || 91 Name.equals("CFSetCreate")73 ) { |
106 | 21 | if (CE->getNumArgs() != 4) |
107 | 2 | return; |
108 | 19 | ArgNum = 1; |
109 | 19 | Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); |
110 | 19 | if (hasPointerToPointerSizedType(Arg)) |
111 | 14 | return; |
112 | 70 | } else if (70 Name.equals("CFDictionaryCreate")70 ) { |
113 | 4 | if (CE->getNumArgs() != 6) |
114 | 1 | return; |
115 | 3 | // Check first argument. |
116 | 3 | ArgNum = 1; |
117 | 3 | Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); |
118 | 3 | if (hasPointerToPointerSizedType(Arg)3 ) { |
119 | 2 | // Check second argument. |
120 | 2 | ArgNum = 2; |
121 | 2 | Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); |
122 | 2 | if (hasPointerToPointerSizedType(Arg)) |
123 | 2 | // Both are good, return. |
124 | 1 | return; |
125 | 73 | } |
126 | 70 | } |
127 | 73 | |
128 | 73 | if (73 Arg73 ) { |
129 | 7 | assert(ArgNum == 1 || ArgNum == 2); |
130 | 7 | |
131 | 7 | SmallString<64> BufName; |
132 | 7 | llvm::raw_svector_ostream OsName(BufName); |
133 | 7 | OsName << " Invalid use of '" << Name << "'" ; |
134 | 7 | |
135 | 7 | SmallString<256> Buf; |
136 | 7 | llvm::raw_svector_ostream Os(Buf); |
137 | 7 | // Use "second" and "third" since users will expect 1-based indexing |
138 | 7 | // for parameter names when mentioned in prose. |
139 | 7 | Os << " The "<< ((ArgNum == 1) ? "second"6 : "third"1 ) << " argument to '" |
140 | 7 | << Name << "' must be a C array of pointer-sized values, not '" |
141 | 7 | << Arg->getType().getAsString() << "'"; |
142 | 7 | |
143 | 7 | PathDiagnosticLocation CELoc = |
144 | 7 | PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); |
145 | 7 | BR.EmitBasicReport(AC->getDecl(), Checker, OsName.str(), |
146 | 7 | categories::CoreFoundationObjectiveC, Os.str(), CELoc, |
147 | 7 | Arg->getSourceRange()); |
148 | 7 | } |
149 | 91 | |
150 | 91 | // Recurse and check children. |
151 | 91 | VisitChildren(CE); |
152 | 91 | } |
153 | | |
154 | 1.27k | void WalkAST::VisitChildren(Stmt *S) { |
155 | 1.27k | for (Stmt *Child : S->children()) |
156 | 1.30k | if (1.30k Child1.30k ) |
157 | 1.23k | Visit(Child); |
158 | 1.27k | } |
159 | | |
160 | | namespace { |
161 | | class ObjCContainersASTChecker : public Checker<check::ASTCodeBody> { |
162 | | public: |
163 | | |
164 | | void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr, |
165 | 58 | BugReporter &BR) const { |
166 | 58 | WalkAST walker(BR, this, Mgr.getAnalysisDeclContext(D)); |
167 | 58 | walker.Visit(D->getBody()); |
168 | 58 | } |
169 | | }; |
170 | | } |
171 | | |
172 | 12 | void ento::registerObjCContainersASTChecker(CheckerManager &mgr) { |
173 | 12 | mgr.registerChecker<ObjCContainersASTChecker>(); |
174 | 12 | } |