ObjCContainersASTChecker.cpp revision cbd273387a61409f179fcfe8460a8733fcf8f872
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/Analysis/AnalysisContext.h"
16#include "clang/AST/StmtVisitor.h"
17#include "clang/Basic/TargetInfo.h"
18#include "clang/StaticAnalyzer/Core/Checker.h"
19#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
20#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
21#include "llvm/Support/raw_ostream.h"
22
23using namespace clang;
24using namespace ento;
25
26namespace {
27class WalkAST : public StmtVisitor<WalkAST> {
28  BugReporter &BR;
29  AnalysisDeclContext* AC;
30  ASTContext &ASTC;
31  uint64_t PtrWidth;
32
33  static const unsigned InvalidArgIndex = UINT_MAX;
34
35  /// Check if the type has pointer size (very conservative).
36  inline bool isPointerSize(const Type *T) {
37    if (!T)
38      return true;
39    if (T->isIncompleteType())
40      return true;
41    return (ASTC.getTypeSize(T) == PtrWidth);
42  }
43
44  /// Check if the type is a pointer/array to pointer sized values.
45  inline bool hasPointerToPointerSizedType(const Expr *E) {
46    QualType T = E->getType();
47
48    // The type could be either a pointer or array.
49    const Type *TP = T.getTypePtr();
50    QualType PointeeT = TP->getPointeeType();
51    if (!PointeeT.isNull())
52      return isPointerSize(PointeeT.getTypePtr());
53
54    if (const Type *TElem = TP->getArrayElementTypeNoTypeQual())
55      return isPointerSize(TElem);
56
57    // The type must be an array/pointer type.
58
59    // This could be a null constant, which is allowed.
60    if (E->isNullPointerConstant(ASTC, Expr::NPC_ValueDependentIsNull))
61      return true;
62    return false;
63  }
64
65public:
66  WalkAST(BugReporter &br, AnalysisDeclContext* ac)
67  : BR(br), AC(ac), ASTC(AC->getASTContext()),
68    PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {}
69
70  // Statement visitor methods.
71  void VisitChildren(Stmt *S);
72  void VisitStmt(Stmt *S) { VisitChildren(S); }
73  void VisitCallExpr(CallExpr *CE);
74};
75} // end anonymous namespace
76
77static StringRef getCalleeName(CallExpr *CE) {
78  const FunctionDecl *FD = CE->getDirectCallee();
79  if (!FD)
80    return StringRef();
81
82  IdentifierInfo *II = FD->getIdentifier();
83  if (!II)   // if no identifier, not a simple C function
84    return StringRef();
85
86  return II->getName();
87}
88
89void WalkAST::VisitCallExpr(CallExpr *CE) {
90  StringRef Name = getCalleeName(CE);
91  if (Name.empty())
92    return;
93
94  const Expr *Arg = 0;
95  unsigned ArgNum = InvalidArgIndex;
96
97  if (Name.equals("CFArrayCreate") || Name.equals("CFSetCreate")) {
98    ArgNum = 1;
99    Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
100    if (hasPointerToPointerSizedType(Arg))
101        return;
102  }
103
104  if (Arg == 0 && Name.equals("CFDictionaryCreate")) {
105    // Check first argument.
106    ArgNum = 1;
107    Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
108    if (hasPointerToPointerSizedType(Arg)) {
109      // Check second argument.
110      ArgNum = 2;
111      Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
112      if (hasPointerToPointerSizedType(Arg))
113        // Both are good, return.
114        return;
115    }
116  }
117
118  if (ArgNum != InvalidArgIndex) {
119    assert(ArgNum == 1 || ArgNum == 2);
120
121    llvm::SmallString<256> BufName;
122    llvm::raw_svector_ostream OsName(BufName);
123    assert(ArgNum == 1 || ArgNum == 2);
124    OsName << " Invalid use of '" << Name << "'" ;
125
126    llvm::SmallString<256> Buf;
127    llvm::raw_svector_ostream Os(Buf);
128    Os << " The "<< ((ArgNum == 1) ? "first" : "second") << " argument to '"
129        << Name << "' must be a C array of pointer-sized values, not '"
130        << Arg->getType().getAsString() << "'";
131
132    SourceRange R = Arg->getSourceRange();
133    PathDiagnosticLocation CELoc =
134        PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
135    BR.EmitBasicReport(OsName.str(), "Core Foundation/Objective-C API",
136                       Os.str(), CELoc, &R, 1);
137  }
138
139  // Recurse and check children.
140  VisitChildren(CE);
141}
142
143void WalkAST::VisitChildren(Stmt *S) {
144  for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I)
145    if (Stmt *child = *I)
146      Visit(child);
147}
148
149namespace {
150class ObjCContainersASTChecker : public Checker<check::ASTCodeBody> {
151public:
152
153  void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
154                        BugReporter &BR) const {
155    WalkAST walker(BR, Mgr.getAnalysisDeclContext(D));
156    walker.Visit(D->getBody());
157  }
158};
159}
160
161void ento::registerObjCContainersASTChecker(CheckerManager &mgr) {
162  mgr.registerChecker<ObjCContainersASTChecker>();
163}
164