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