[DFAJumpThreading] Remove incoming StartBlock from all phis when unfolding select...
[llvm-project.git] / clang / lib / StaticAnalyzer / Checkers / ObjCContainersASTChecker.cpp
blob2b008d1c775a25a2e6373a89a52ab313542e648e
1 //== ObjCContainersASTChecker.cpp - CoreFoundation containers API *- C++ -*-==//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // An AST checker that looks for common pitfalls when using 'CFArray',
10 // 'CFDictionary', 'CFSet' APIs.
12 //===----------------------------------------------------------------------===//
13 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14 #include "clang/AST/StmtVisitor.h"
15 #include "clang/Analysis/AnalysisDeclContext.h"
16 #include "clang/Basic/TargetInfo.h"
17 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
18 #include "clang/StaticAnalyzer/Core/Checker.h"
19 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
20 #include "llvm/ADT/SmallString.h"
21 #include "llvm/Support/raw_ostream.h"
23 using namespace clang;
24 using namespace ento;
26 namespace {
27 class WalkAST : public StmtVisitor<WalkAST> {
28 BugReporter &BR;
29 const CheckerBase *Checker;
30 AnalysisDeclContext* AC;
31 ASTContext &ASTC;
32 uint64_t PtrWidth;
34 /// Check if the type has pointer size (very conservative).
35 inline bool isPointerSize(const Type *T) {
36 if (!T)
37 return true;
38 if (T->isIncompleteType())
39 return true;
40 return (ASTC.getTypeSize(T) == PtrWidth);
43 /// Check if the type is a pointer/array to pointer sized values.
44 inline bool hasPointerToPointerSizedType(const Expr *E) {
45 QualType T = E->getType();
47 // The type could be either a pointer or array.
48 const Type *TP = T.getTypePtr();
49 QualType PointeeT = TP->getPointeeType();
50 if (!PointeeT.isNull()) {
51 // If the type is a pointer to an array, check the size of the array
52 // elements. To avoid false positives coming from assumption that the
53 // values x and &x are equal when x is an array.
54 if (const Type *TElem = PointeeT->getArrayElementTypeNoTypeQual())
55 if (isPointerSize(TElem))
56 return true;
58 // Else, check the pointee size.
59 return isPointerSize(PointeeT.getTypePtr());
62 if (const Type *TElem = TP->getArrayElementTypeNoTypeQual())
63 return isPointerSize(TElem);
65 // The type must be an array/pointer type.
67 // This could be a null constant, which is allowed.
68 return static_cast<bool>(
69 E->isNullPointerConstant(ASTC, Expr::NPC_ValueDependentIsNull));
72 public:
73 WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac)
74 : BR(br), Checker(checker), AC(ac), ASTC(AC->getASTContext()),
75 PtrWidth(ASTC.getTargetInfo().getPointerWidth(LangAS::Default)) {}
77 // Statement visitor methods.
78 void VisitChildren(Stmt *S);
79 void VisitStmt(Stmt *S) { VisitChildren(S); }
80 void VisitCallExpr(CallExpr *CE);
82 } // end anonymous namespace
84 static StringRef getCalleeName(CallExpr *CE) {
85 const FunctionDecl *FD = CE->getDirectCallee();
86 if (!FD)
87 return StringRef();
89 IdentifierInfo *II = FD->getIdentifier();
90 if (!II) // if no identifier, not a simple C function
91 return StringRef();
93 return II->getName();
96 void WalkAST::VisitCallExpr(CallExpr *CE) {
97 StringRef Name = getCalleeName(CE);
98 if (Name.empty())
99 return;
101 const Expr *Arg = nullptr;
102 unsigned ArgNum;
104 if (Name.equals("CFArrayCreate") || Name.equals("CFSetCreate")) {
105 if (CE->getNumArgs() != 4)
106 return;
107 ArgNum = 1;
108 Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
109 if (hasPointerToPointerSizedType(Arg))
110 return;
111 } else if (Name.equals("CFDictionaryCreate")) {
112 if (CE->getNumArgs() != 6)
113 return;
114 // Check first argument.
115 ArgNum = 1;
116 Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
117 if (hasPointerToPointerSizedType(Arg)) {
118 // Check second argument.
119 ArgNum = 2;
120 Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
121 if (hasPointerToPointerSizedType(Arg))
122 // Both are good, return.
123 return;
127 if (Arg) {
128 assert(ArgNum == 1 || ArgNum == 2);
130 SmallString<64> BufName;
131 llvm::raw_svector_ostream OsName(BufName);
132 OsName << " Invalid use of '" << Name << "'" ;
134 SmallString<256> Buf;
135 llvm::raw_svector_ostream Os(Buf);
136 // Use "second" and "third" since users will expect 1-based indexing
137 // for parameter names when mentioned in prose.
138 Os << " The " << ((ArgNum == 1) ? "second" : "third") << " argument to '"
139 << Name << "' must be a C array of pointer-sized values, not '"
140 << Arg->getType() << "'";
142 PathDiagnosticLocation CELoc =
143 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
144 BR.EmitBasicReport(AC->getDecl(), Checker, OsName.str(),
145 categories::CoreFoundationObjectiveC, Os.str(), CELoc,
146 Arg->getSourceRange());
149 // Recurse and check children.
150 VisitChildren(CE);
153 void WalkAST::VisitChildren(Stmt *S) {
154 for (Stmt *Child : S->children())
155 if (Child)
156 Visit(Child);
159 namespace {
160 class ObjCContainersASTChecker : public Checker<check::ASTCodeBody> {
161 public:
163 void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
164 BugReporter &BR) const {
165 WalkAST walker(BR, this, Mgr.getAnalysisDeclContext(D));
166 walker.Visit(D->getBody());
171 void ento::registerObjCContainersASTChecker(CheckerManager &mgr) {
172 mgr.registerChecker<ObjCContainersASTChecker>();
175 bool ento::shouldRegisterObjCContainersASTChecker(const CheckerManager &mgr) {
176 return true;