1234287Sdim//== ObjCContainersASTChecker.cpp - CoreFoundation containers API *- C++ -*-==//
2234287Sdim//
3234287Sdim//                     The LLVM Compiler Infrastructure
4234287Sdim//
5234287Sdim// This file is distributed under the University of Illinois Open Source
6234287Sdim// License. See LICENSE.TXT for details.
7234287Sdim//
8234287Sdim//===----------------------------------------------------------------------===//
9234287Sdim//
10234287Sdim// An AST checker that looks for common pitfalls when using 'CFArray',
11234287Sdim// 'CFDictionary', 'CFSet' APIs.
12234287Sdim//
13234287Sdim//===----------------------------------------------------------------------===//
14234287Sdim#include "ClangSACheckers.h"
15249423Sdim#include "clang/AST/StmtVisitor.h"
16234287Sdim#include "clang/Analysis/AnalysisContext.h"
17234287Sdim#include "clang/Basic/TargetInfo.h"
18249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
19234287Sdim#include "clang/StaticAnalyzer/Core/Checker.h"
20234287Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
21234287Sdim#include "llvm/ADT/SmallString.h"
22234287Sdim#include "llvm/Support/raw_ostream.h"
23234287Sdim
24234287Sdimusing namespace clang;
25234287Sdimusing namespace ento;
26234287Sdim
27234287Sdimnamespace {
28234287Sdimclass WalkAST : public StmtVisitor<WalkAST> {
29234287Sdim  BugReporter &BR;
30234287Sdim  AnalysisDeclContext* AC;
31234287Sdim  ASTContext &ASTC;
32234287Sdim  uint64_t PtrWidth;
33234287Sdim
34234287Sdim  /// Check if the type has pointer size (very conservative).
35234287Sdim  inline bool isPointerSize(const Type *T) {
36234287Sdim    if (!T)
37234287Sdim      return true;
38234287Sdim    if (T->isIncompleteType())
39234287Sdim      return true;
40234287Sdim    return (ASTC.getTypeSize(T) == PtrWidth);
41234287Sdim  }
42234287Sdim
43234287Sdim  /// Check if the type is a pointer/array to pointer sized values.
44234287Sdim  inline bool hasPointerToPointerSizedType(const Expr *E) {
45234287Sdim    QualType T = E->getType();
46234287Sdim
47234287Sdim    // The type could be either a pointer or array.
48234287Sdim    const Type *TP = T.getTypePtr();
49234287Sdim    QualType PointeeT = TP->getPointeeType();
50234287Sdim    if (!PointeeT.isNull()) {
51234287Sdim      // If the type is a pointer to an array, check the size of the array
52234287Sdim      // elements. To avoid false positives coming from assumption that the
53234287Sdim      // values x and &x are equal when x is an array.
54234287Sdim      if (const Type *TElem = PointeeT->getArrayElementTypeNoTypeQual())
55234287Sdim        if (isPointerSize(TElem))
56234287Sdim          return true;
57234287Sdim
58234287Sdim      // Else, check the pointee size.
59234287Sdim      return isPointerSize(PointeeT.getTypePtr());
60234287Sdim    }
61234287Sdim
62234287Sdim    if (const Type *TElem = TP->getArrayElementTypeNoTypeQual())
63234287Sdim      return isPointerSize(TElem);
64234287Sdim
65234287Sdim    // The type must be an array/pointer type.
66234287Sdim
67234287Sdim    // This could be a null constant, which is allowed.
68234287Sdim    if (E->isNullPointerConstant(ASTC, Expr::NPC_ValueDependentIsNull))
69234287Sdim      return true;
70234287Sdim    return false;
71234287Sdim  }
72234287Sdim
73234287Sdimpublic:
74234287Sdim  WalkAST(BugReporter &br, AnalysisDeclContext* ac)
75234287Sdim  : BR(br), AC(ac), ASTC(AC->getASTContext()),
76234287Sdim    PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {}
77234287Sdim
78234287Sdim  // Statement visitor methods.
79234287Sdim  void VisitChildren(Stmt *S);
80234287Sdim  void VisitStmt(Stmt *S) { VisitChildren(S); }
81234287Sdim  void VisitCallExpr(CallExpr *CE);
82234287Sdim};
83234287Sdim} // end anonymous namespace
84234287Sdim
85234287Sdimstatic StringRef getCalleeName(CallExpr *CE) {
86234287Sdim  const FunctionDecl *FD = CE->getDirectCallee();
87234287Sdim  if (!FD)
88234287Sdim    return StringRef();
89234287Sdim
90234287Sdim  IdentifierInfo *II = FD->getIdentifier();
91234287Sdim  if (!II)   // if no identifier, not a simple C function
92234287Sdim    return StringRef();
93234287Sdim
94234287Sdim  return II->getName();
95234287Sdim}
96234287Sdim
97234287Sdimvoid WalkAST::VisitCallExpr(CallExpr *CE) {
98234287Sdim  StringRef Name = getCalleeName(CE);
99234287Sdim  if (Name.empty())
100234287Sdim    return;
101234287Sdim
102234287Sdim  const Expr *Arg = 0;
103243830Sdim  unsigned ArgNum;
104234287Sdim
105234287Sdim  if (Name.equals("CFArrayCreate") || Name.equals("CFSetCreate")) {
106243830Sdim    if (CE->getNumArgs() != 4)
107243830Sdim      return;
108234287Sdim    ArgNum = 1;
109234287Sdim    Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
110234287Sdim    if (hasPointerToPointerSizedType(Arg))
111234287Sdim        return;
112243830Sdim  } else if (Name.equals("CFDictionaryCreate")) {
113243830Sdim    if (CE->getNumArgs() != 6)
114243830Sdim      return;
115234287Sdim    // Check first argument.
116234287Sdim    ArgNum = 1;
117234287Sdim    Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
118234287Sdim    if (hasPointerToPointerSizedType(Arg)) {
119234287Sdim      // Check second argument.
120234287Sdim      ArgNum = 2;
121234287Sdim      Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
122234287Sdim      if (hasPointerToPointerSizedType(Arg))
123234287Sdim        // Both are good, return.
124234287Sdim        return;
125234287Sdim    }
126234287Sdim  }
127234287Sdim
128243830Sdim  if (Arg) {
129234287Sdim    assert(ArgNum == 1 || ArgNum == 2);
130234287Sdim
131243830Sdim    SmallString<64> BufName;
132234287Sdim    llvm::raw_svector_ostream OsName(BufName);
133234287Sdim    OsName << " Invalid use of '" << Name << "'" ;
134234287Sdim
135234287Sdim    SmallString<256> Buf;
136234287Sdim    llvm::raw_svector_ostream Os(Buf);
137243830Sdim    // Use "second" and "third" since users will expect 1-based indexing
138243830Sdim    // for parameter names when mentioned in prose.
139243830Sdim    Os << " The "<< ((ArgNum == 1) ? "second" : "third") << " argument to '"
140234287Sdim        << Name << "' must be a C array of pointer-sized values, not '"
141234287Sdim        << Arg->getType().getAsString() << "'";
142234287Sdim
143234287Sdim    PathDiagnosticLocation CELoc =
144234287Sdim        PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
145234287Sdim    BR.EmitBasicReport(AC->getDecl(),
146234287Sdim                       OsName.str(), categories::CoreFoundationObjectiveC,
147263508Sdim                       Os.str(), CELoc, Arg->getSourceRange());
148234287Sdim  }
149234287Sdim
150234287Sdim  // Recurse and check children.
151234287Sdim  VisitChildren(CE);
152234287Sdim}
153234287Sdim
154234287Sdimvoid WalkAST::VisitChildren(Stmt *S) {
155234287Sdim  for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I)
156234287Sdim    if (Stmt *child = *I)
157234287Sdim      Visit(child);
158234287Sdim}
159234287Sdim
160234287Sdimnamespace {
161234287Sdimclass ObjCContainersASTChecker : public Checker<check::ASTCodeBody> {
162234287Sdimpublic:
163234287Sdim
164234287Sdim  void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
165234287Sdim                        BugReporter &BR) const {
166234287Sdim    WalkAST walker(BR, Mgr.getAnalysisDeclContext(D));
167234287Sdim    walker.Visit(D->getBody());
168234287Sdim  }
169234287Sdim};
170234287Sdim}
171234287Sdim
172234287Sdimvoid ento::registerObjCContainersASTChecker(CheckerManager &mgr) {
173234287Sdim  mgr.registerChecker<ObjCContainersASTChecker>();
174234287Sdim}
175