Home | History | Annotate | Download | only in Analysis
      1 //===- CocoaConventions.h - Special handling of Cocoa conventions -*- 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 // This file implements cocoa naming convention analysis.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #include "clang/Analysis/DomainSpecific/CocoaConventions.h"
     15 #include "clang/AST/Type.h"
     16 #include "clang/AST/Decl.h"
     17 #include "clang/AST/DeclObjC.h"
     18 #include "llvm/ADT/StringExtras.h"
     19 #include "llvm/Support/ErrorHandling.h"
     20 using namespace clang;
     21 using namespace ento;
     22 
     23 bool cocoa::isRefType(QualType RetTy, StringRef Prefix,
     24                       StringRef Name) {
     25   // Recursively walk the typedef stack, allowing typedefs of reference types.
     26   while (const TypedefType *TD = dyn_cast<TypedefType>(RetTy.getTypePtr())) {
     27     StringRef TDName = TD->getDecl()->getIdentifier()->getName();
     28     if (TDName.startswith(Prefix) && TDName.endswith("Ref"))
     29       return true;
     30     // XPC unfortunately uses CF-style function names, but aren't CF types.
     31     if (TDName.startswith("xpc_"))
     32       return false;
     33     RetTy = TD->getDecl()->getUnderlyingType();
     34   }
     35 
     36   if (Name.empty())
     37     return false;
     38 
     39   // Is the type void*?
     40   const PointerType* PT = RetTy->getAs<PointerType>();
     41   if (!(PT->getPointeeType().getUnqualifiedType()->isVoidType()))
     42     return false;
     43 
     44   // Does the name start with the prefix?
     45   return Name.startswith(Prefix);
     46 }
     47 
     48 bool coreFoundation::isCFObjectRef(QualType T) {
     49   return cocoa::isRefType(T, "CF") || // Core Foundation.
     50          cocoa::isRefType(T, "CG") || // Core Graphics.
     51          cocoa::isRefType(T, "DADisk") || // Disk Arbitration API.
     52          cocoa::isRefType(T, "DADissenter") ||
     53          cocoa::isRefType(T, "DASessionRef");
     54 }
     55 
     56 
     57 bool cocoa::isCocoaObjectRef(QualType Ty) {
     58   if (!Ty->isObjCObjectPointerType())
     59     return false;
     60 
     61   const ObjCObjectPointerType *PT = Ty->getAs<ObjCObjectPointerType>();
     62 
     63   // Can be true for objects with the 'NSObject' attribute.
     64   if (!PT)
     65     return true;
     66 
     67   // We assume that id<..>, id, Class, and Class<..> all represent tracked
     68   // objects.
     69   if (PT->isObjCIdType() || PT->isObjCQualifiedIdType() ||
     70       PT->isObjCClassType() || PT->isObjCQualifiedClassType())
     71     return true;
     72 
     73   // Does the interface subclass NSObject?
     74   // FIXME: We can memoize here if this gets too expensive.
     75   const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
     76 
     77   // Assume that anything declared with a forward declaration and no
     78   // @interface subclasses NSObject.
     79   if (!ID->hasDefinition())
     80     return true;
     81 
     82   for ( ; ID ; ID = ID->getSuperClass())
     83     if (ID->getIdentifier()->getName() == "NSObject")
     84       return true;
     85 
     86   return false;
     87 }
     88 
     89 bool coreFoundation::followsCreateRule(const FunctionDecl *fn) {
     90   // For now, *just* base this on the function name, not on anything else.
     91 
     92   const IdentifierInfo *ident = fn->getIdentifier();
     93   if (!ident) return false;
     94   StringRef functionName = ident->getName();
     95 
     96   StringRef::iterator it = functionName.begin();
     97   StringRef::iterator start = it;
     98   StringRef::iterator endI = functionName.end();
     99 
    100   while (true) {
    101     // Scan for the start of 'create' or 'copy'.
    102     for ( ; it != endI ; ++it) {
    103       // Search for the first character.  It can either be 'C' or 'c'.
    104       char ch = *it;
    105       if (ch == 'C' || ch == 'c') {
    106         // Make sure this isn't something like 'recreate' or 'Scopy'.
    107         if (ch == 'c' && it != start && isalpha(*(it - 1)))
    108           continue;
    109 
    110         ++it;
    111         break;
    112       }
    113     }
    114 
    115     // Did we hit the end of the string?  If so, we didn't find a match.
    116     if (it == endI)
    117       return false;
    118 
    119     // Scan for *lowercase* 'reate' or 'opy', followed by no lowercase
    120     // character.
    121     StringRef suffix = functionName.substr(it - start);
    122     if (suffix.startswith("reate")) {
    123       it += 5;
    124     }
    125     else if (suffix.startswith("opy")) {
    126       it += 3;
    127     } else {
    128       // Keep scanning.
    129       continue;
    130     }
    131 
    132     if (it == endI || !islower(*it))
    133       return true;
    134 
    135     // If we matched a lowercase character, it isn't the end of the
    136     // word.  Keep scanning.
    137   }
    138 }
    139