[cfe-commits] r149008 - in /cfe/trunk: lib/StaticAnalyzer/Checkers/CMakeLists.txt lib/StaticAnalyzer/Checkers/Checkers.td lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp test/Analysis/CFContainers.mm

Anna Zaks ganna at apple.com
Wed Jan 25 17:05:44 PST 2012


Author: zaks
Date: Wed Jan 25 19:05:43 2012
New Revision: 149008

URL: http://llvm.org/viewvc/llvm-project?rev=149008&view=rev
Log:
[analyzer] Add an AST checker that checks for a common pitfall when
using CFArrayCreate & family.

Specifically, CFArrayCreate's input should be:
'A C array of the pointer-sized values to be in the new array.'

(radar://10717339)

Added:
    cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp
    cfe/trunk/test/Analysis/CFContainers.mm
Modified:
    cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt
    cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt?rev=149008&r1=149007&r2=149008&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt Wed Jan 25 19:05:43 2012
@@ -17,6 +17,7 @@
   CallAndMessageChecker.cpp
   CastSizeChecker.cpp
   CastToStructChecker.cpp
+  ObjCContaintersASTChecker.cpp
   CheckObjCDealloc.cpp
   CheckObjCInstMethSignature.cpp
   CheckSecuritySyntaxOnly.cpp

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td?rev=149008&r1=149007&r2=149008&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td Wed Jan 25 19:05:43 2012
@@ -368,6 +368,10 @@
 
 let ParentPackage = CocoaExperimental in {
 
+def ObjCContainersASTChecker : Checker<"ContainerAPI">,
+  HelpText<"Check for common pitfalls when using 'CFArray', 'CFDictionary', 'CFSet' APIs">,
+  DescFile<"ObjCContainersASTChecker.cpp">;
+
 def ObjCSelfInitChecker : Checker<"SelfInit">,
   HelpText<"Check that 'self' is properly initialized inside an initializer method">,
   DescFile<"ObjCSelfInitChecker.cpp">;

Added: cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp?rev=149008&view=auto
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp (added)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp Wed Jan 25 19:05:43 2012
@@ -0,0 +1,163 @@
+//== ObjCContainersASTChecker.cpp - CoreFoundation containers API *- C++ -*-==//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// An AST checker that looks for common pitfalls when using 'CFArray',
+// 'CFDictionary', 'CFSet' APIs.
+//
+//===----------------------------------------------------------------------===//
+#include "ClangSACheckers.h"
+#include "clang/Analysis/AnalysisContext.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class WalkAST : public StmtVisitor<WalkAST> {
+  BugReporter &BR;
+  AnalysisDeclContext* AC;
+  ASTContext &ASTC;
+  uint64_t PtrWidth;
+
+  static const unsigned InvalidArgIndex = UINT_MAX;
+
+  /// Check if the type has pointer size (very conservative).
+  inline bool isPointerSize(const Type *T) {
+    if (!T)
+      return true;
+    if (T->isIncompleteType())
+      return true;
+    return (ASTC.getTypeSize(T) == PtrWidth);
+  }
+
+  /// Check if the type is a pointer/array to pointer sized values.
+  inline bool hasPointerToPointerSizedType(const Expr *E) {
+    QualType T = E->getType();
+
+    // The type could be either a pointer or array.
+    const Type *TP = T.getTypePtr();
+    QualType PointeeT = TP->getPointeeType();
+    if (!PointeeT.isNull())
+      return isPointerSize(PointeeT.getTypePtr());
+
+    if (const Type *TElem = TP->getArrayElementTypeNoTypeQual())
+      return isPointerSize(TElem);
+
+    // The type must be an array/pointer type.
+
+    // This could be a null constant, which is allowed.
+    if (E->isNullPointerConstant(ASTC, Expr::NPC_ValueDependentIsNull))
+      return true;
+    return false;
+  }
+
+public:
+  WalkAST(BugReporter &br, AnalysisDeclContext* ac)
+  : BR(br), AC(ac), ASTC(AC->getASTContext()),
+    PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {}
+
+  // Statement visitor methods.
+  void VisitChildren(Stmt *S);
+  void VisitStmt(Stmt *S) { VisitChildren(S); }
+  void VisitCallExpr(CallExpr *CE);
+};
+} // end anonymous namespace
+
+static StringRef getCalleeName(CallExpr *CE) {
+  const FunctionDecl *FD = CE->getDirectCallee();
+  if (!FD)
+    return StringRef();
+
+  IdentifierInfo *II = FD->getIdentifier();
+  if (!II)   // if no identifier, not a simple C function
+    return StringRef();
+
+  return II->getName();
+}
+
+void WalkAST::VisitCallExpr(CallExpr *CE) {
+  StringRef Name = getCalleeName(CE);
+  if (Name.empty())
+    return;
+
+  const Expr *Arg = 0;
+  unsigned ArgNum = InvalidArgIndex;
+
+  if (Name.equals("CFArrayCreate") || Name.equals("CFSetCreate")) {
+    ArgNum = 1;
+    Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
+    if (hasPointerToPointerSizedType(Arg))
+        return;
+  }
+
+  if (Arg == 0 && Name.equals("CFDictionaryCreate")) {
+    // Check first argument.
+    ArgNum = 1;
+    Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
+    if (hasPointerToPointerSizedType(Arg)) {
+      // Check second argument.
+      ArgNum = 2;
+      Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
+      if (hasPointerToPointerSizedType(Arg))
+        // Both are good, return.
+        return;
+    }
+  }
+
+  if (ArgNum != InvalidArgIndex) {
+    assert(ArgNum == 1 || ArgNum == 2);
+
+    llvm::SmallString<256> BufName;
+    llvm::raw_svector_ostream OsName(BufName);
+    assert(ArgNum == 1 || ArgNum == 2);
+    OsName << " Invalid use of '" << Name << "'" ;
+
+    llvm::SmallString<256> Buf;
+    llvm::raw_svector_ostream Os(Buf);
+    Os << " The "<< ((ArgNum == 1) ? "first" : "second") << " argument to '"
+        << Name << "' must be a C array of pointer-sized values, not '"
+        << Arg->getType().getAsString() << "'";
+
+    SourceRange R = Arg->getSourceRange();
+    PathDiagnosticLocation CELoc =
+        PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
+    BR.EmitBasicReport(OsName.str(), "Core Foundation/Objective-C API",
+                       Os.str(), CELoc, &R, 1);
+  }
+
+  // Recurse and check children.
+  VisitChildren(CE);
+}
+
+void WalkAST::VisitChildren(Stmt *S) {
+  for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I)
+    if (Stmt *child = *I)
+      Visit(child);
+}
+
+namespace {
+class ObjCContainersASTChecker : public Checker<check::ASTCodeBody> {
+public:
+
+  void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
+                        BugReporter &BR) const {
+    WalkAST walker(BR, Mgr.getAnalysisDeclContext(D));
+    walker.Visit(D->getBody());
+  }
+};
+}
+
+void ento::registerObjCContainersASTChecker(CheckerManager &mgr) {
+  mgr.registerChecker<ObjCContainersASTChecker>();
+}

Added: cfe/trunk/test/Analysis/CFContainers.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/CFContainers.mm?rev=149008&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/CFContainers.mm (added)
+++ cfe/trunk/test/Analysis/CFContainers.mm Wed Jan 25 19:05:43 2012
@@ -0,0 +1,126 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=experimental.osx.cocoa.ContainerAPI -analyzer-store=region -verify %s
+
+typedef const struct __CFAllocator * CFAllocatorRef;
+typedef const struct __CFString * CFStringRef;
+typedef unsigned char Boolean;
+typedef signed long CFIndex;
+extern
+const CFAllocatorRef kCFAllocatorDefault;
+typedef const void * (*CFArrayRetainCallBack)(CFAllocatorRef allocator, const void *value);
+typedef void (*CFArrayReleaseCallBack)(CFAllocatorRef allocator, const void *value);
+typedef CFStringRef (*CFArrayCopyDescriptionCallBack)(const void *value);
+typedef Boolean (*CFArrayEqualCallBack)(const void *value1, const void *value2);
+typedef struct {
+    CFIndex version;
+    CFArrayRetainCallBack retain;
+    CFArrayReleaseCallBack release;
+    CFArrayCopyDescriptionCallBack copyDescription;
+    CFArrayEqualCallBack equal;
+} CFArrayCallBacks;
+typedef const struct __CFArray * CFArrayRef;
+CFArrayRef CFArrayCreate(CFAllocatorRef allocator, const void **values, CFIndex numValues, const CFArrayCallBacks *callBacks);
+typedef const struct __CFString * CFStringRef;
+enum {
+    kCFNumberSInt8Type = 1,
+    kCFNumberSInt16Type = 2,
+    kCFNumberSInt32Type = 3,
+    kCFNumberSInt64Type = 4,
+    kCFNumberFloat32Type = 5,
+    kCFNumberFloat64Type = 6,
+    kCFNumberCharType = 7,
+    kCFNumberShortType = 8,
+    kCFNumberIntType = 9,
+    kCFNumberLongType = 10,
+    kCFNumberLongLongType = 11,
+    kCFNumberFloatType = 12,
+    kCFNumberDoubleType = 13,
+    kCFNumberCFIndexType = 14,
+    kCFNumberNSIntegerType = 15,
+    kCFNumberCGFloatType = 16,
+    kCFNumberMaxType = 16
+};
+typedef CFIndex CFNumberType;
+typedef const struct __CFNumber * CFNumberRef;
+typedef CFIndex CFComparisonResult;
+typedef const struct __CFDictionary * CFDictionaryRef;
+typedef const void * (*CFDictionaryRetainCallBack)(CFAllocatorRef allocator, const void *value);
+typedef void (*CFDictionaryReleaseCallBack)(CFAllocatorRef allocator, const void *value);
+typedef CFStringRef (*CFDictionaryCopyDescriptionCallBack)(const void *value);
+typedef Boolean (*CFDictionaryEqualCallBack)(const void *value1, const void *value2);
+typedef Boolean (*CFArrayEqualCallBack)(const void *value1, const void *value2);
+typedef Boolean (*CFSetEqualCallBack)(const void *value1, const void *value2);
+typedef const void * (*CFSetRetainCallBack)(CFAllocatorRef allocator, const void *value);
+typedef void (*CFSetReleaseCallBack)(CFAllocatorRef allocator, const void *value);
+typedef CFStringRef (*CFSetCopyDescriptionCallBack)(const void *value);
+typedef struct {
+    CFIndex version;
+    CFSetRetainCallBack retain;
+    CFSetReleaseCallBack release;
+    CFSetCopyDescriptionCallBack copyDescription;
+    CFSetEqualCallBack equal;
+} CFSetCallBacks;
+typedef struct {
+    CFIndex version;
+    CFDictionaryRetainCallBack retain;
+    CFDictionaryReleaseCallBack release;
+    CFDictionaryCopyDescriptionCallBack copyDescription;
+    CFDictionaryEqualCallBack equal;
+} CFDictionaryKeyCallBacks;
+typedef struct {
+    CFIndex version;
+    CFDictionaryRetainCallBack retain;
+    CFDictionaryReleaseCallBack release;
+    CFDictionaryCopyDescriptionCallBack copyDescription;
+    CFDictionaryEqualCallBack equal;
+} CFDictionaryValueCallBacks;
+CFDictionaryRef CFDictionaryCreate(CFAllocatorRef allocator, const void **keys, const void **values, CFIndex numValues, const CFDictionaryKeyCallBacks *keyCallBacks, const CFDictionaryValueCallBacks *valueCallBacks);
+extern
+const CFDictionaryValueCallBacks kCFTypeDictionaryValueCallBacks;
+typedef const struct __CFSet * CFSetRef;
+extern
+const CFSetCallBacks kCFTypeSetCallBacks;
+extern
+const CFDictionaryKeyCallBacks kCFCopyStringDictionaryKeyCallBacks;
+CFDictionaryRef CFDictionaryCreate(CFAllocatorRef allocator, const void **keys, const void **values, CFIndex numValues, const 
+CFDictionaryKeyCallBacks *keyCallBacks, const CFDictionaryValueCallBacks *valueCallBacks);
+CFNumberRef CFNumberCreate(CFAllocatorRef allocator, CFNumberType theType, const void *valuePtr);
+extern
+CFSetRef CFSetCreate(CFAllocatorRef allocator, const void **values, CFIndex numValues, const CFSetCallBacks *callBacks);
+#define CFSTR(cStr)  ((CFStringRef) __builtin___CFStringMakeConstantString ("" cStr ""))
+#define NULL __null
+
+// Done with the headers. 
+// Test experimental.osx.cocoa.ContainerAPI checker.
+void testContainers(int **xNoWarn, CFIndex count) {
+  int x[] = { 1, 2, 3 };
+  CFArrayRef foo = CFArrayCreate(kCFAllocatorDefault, (const void **) x, sizeof(x) / sizeof(x[0]), 0);// expected-warning {{The first argument to 'CFArrayCreate' must be a C array of pointer-sized}}
+
+  CFArrayRef fooNoWarn = CFArrayCreate(kCFAllocatorDefault, (const void **) xNoWarn, sizeof(xNoWarn) / sizeof(xNoWarn[0]), 0); // no warning
+  CFArrayRef fooNoWarn2 = CFArrayCreate(kCFAllocatorDefault, 0, sizeof(xNoWarn) / sizeof(xNoWarn[0]), 0);// no warning, passing in 0
+  CFArrayRef fooNoWarn3 = CFArrayCreate(kCFAllocatorDefault, NULL, sizeof(xNoWarn) / sizeof(xNoWarn[0]), 0);// no warning, passing in NULL
+
+  CFSetRef set = CFSetCreate(NULL, (const void **)x, 3, &kCFTypeSetCallBacks); // expected-warning {{The first argument to 'CFSetCreate' must be a C array of pointer-sized values}}
+  CFArrayRef* pairs = new CFArrayRef[count];
+  CFSetRef fSet = CFSetCreate(kCFAllocatorDefault, (const void**) pairs, count - 1, &kCFTypeSetCallBacks);// no warning
+}
+
+void CreateDict(int *elems) {
+  const short days28 = 28;
+  const short days30 = 30;
+  const short days31 = 31;
+  CFIndex numValues = 6;  
+  CFStringRef keys[6];
+  CFNumberRef values[6];
+  keys[0] = CFSTR("January");  values[0] = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &days31);
+  keys[1] = CFSTR("February"); values[1] = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &days28);
+  keys[2] = CFSTR("March"); values[2] = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &days31);
+  keys[3] = CFSTR("April"); values[3] = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &days30);
+  keys[4] = CFSTR("May"); values[4] = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &days31);
+  keys[5] = CFSTR("June"); values[5] = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &days30);
+
+  const CFDictionaryKeyCallBacks keyCB = kCFCopyStringDictionaryKeyCallBacks;
+  const CFDictionaryValueCallBacks valCB = kCFTypeDictionaryValueCallBacks;
+  CFDictionaryRef dict1 = CFDictionaryCreate(kCFAllocatorDefault, (const void**)keys, (const void**)values, numValues, &keyCB, &valCB); // no warning
+  CFDictionaryRef dict2 = CFDictionaryCreate(kCFAllocatorDefault, (const void**)elems[0], (const void**)values, numValues, &keyCB, &valCB); //expected-warning {{The first argument to 'CFDictionaryCreate' must be a C array of}}
+  CFDictionaryRef dict3 = CFDictionaryCreate(kCFAllocatorDefault, (const void**)keys, (const void**)elems, numValues, &keyCB, &valCB); // expected-warning {{The second argument to 'CFDictionaryCreate' must be a C array of pointer-sized values}}
+}
\ No newline at end of file





More information about the cfe-commits mailing list