| //===- CocoaConventions.h - Special handling of Cocoa conventions -*- C++ -*--// | 
 | // | 
 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
 | // See https://llvm.org/LICENSE.txt for license information. | 
 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 | // | 
 | // This file implements cocoa naming convention analysis. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "clang/Analysis/DomainSpecific/CocoaConventions.h" | 
 | #include "clang/AST/Decl.h" | 
 | #include "clang/AST/DeclObjC.h" | 
 | #include "clang/AST/Type.h" | 
 | #include "clang/Basic/CharInfo.h" | 
 | #include "llvm/ADT/StringExtras.h" | 
 | #include "llvm/Support/ErrorHandling.h" | 
 |  | 
 | using namespace clang; | 
 | using namespace ento; | 
 |  | 
 | bool cocoa::isRefType(QualType RetTy, StringRef Prefix, | 
 |                       StringRef Name) { | 
 |   // Recursively walk the typedef stack, allowing typedefs of reference types. | 
 |   while (const TypedefType *TD = RetTy->getAs<TypedefType>()) { | 
 |     StringRef TDName = TD->getDecl()->getIdentifier()->getName(); | 
 |     if (TDName.startswith(Prefix) && TDName.endswith("Ref")) | 
 |       return true; | 
 |     // XPC unfortunately uses CF-style function names, but aren't CF types. | 
 |     if (TDName.startswith("xpc_")) | 
 |       return false; | 
 |     RetTy = TD->getDecl()->getUnderlyingType(); | 
 |   } | 
 |  | 
 |   if (Name.empty()) | 
 |     return false; | 
 |  | 
 |   // Is the type void*? | 
 |   const PointerType* PT = RetTy->castAs<PointerType>(); | 
 |   if (!PT || !PT->getPointeeType().getUnqualifiedType()->isVoidType()) | 
 |     return false; | 
 |  | 
 |   // Does the name start with the prefix? | 
 |   return Name.startswith(Prefix); | 
 | } | 
 |  | 
 | /// Returns true when the passed-in type is a CF-style reference-counted | 
 | /// type from the DiskArbitration framework. | 
 | static bool isDiskArbitrationAPIRefType(QualType T) { | 
 |   return cocoa::isRefType(T, "DADisk") || | 
 |       cocoa::isRefType(T, "DADissenter") || | 
 |       cocoa::isRefType(T, "DASessionRef"); | 
 | } | 
 |  | 
 | bool coreFoundation::isCFObjectRef(QualType T) { | 
 |   return cocoa::isRefType(T, "CF") || // Core Foundation. | 
 |          cocoa::isRefType(T, "CG") || // Core Graphics. | 
 |          cocoa::isRefType(T, "CM") || // Core Media. | 
 |          isDiskArbitrationAPIRefType(T); | 
 | } | 
 |  | 
 |  | 
 | bool cocoa::isCocoaObjectRef(QualType Ty) { | 
 |   if (!Ty->isObjCObjectPointerType()) | 
 |     return false; | 
 |  | 
 |   const ObjCObjectPointerType *PT = Ty->getAs<ObjCObjectPointerType>(); | 
 |  | 
 |   // Can be true for objects with the 'NSObject' attribute. | 
 |   if (!PT) | 
 |     return true; | 
 |  | 
 |   // We assume that id<..>, id, Class, and Class<..> all represent tracked | 
 |   // objects. | 
 |   if (PT->isObjCIdType() || PT->isObjCQualifiedIdType() || | 
 |       PT->isObjCClassType() || PT->isObjCQualifiedClassType()) | 
 |     return true; | 
 |  | 
 |   // Does the interface subclass NSObject? | 
 |   // FIXME: We can memoize here if this gets too expensive. | 
 |   const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); | 
 |  | 
 |   // Assume that anything declared with a forward declaration and no | 
 |   // @interface subclasses NSObject. | 
 |   if (!ID->hasDefinition()) | 
 |     return true; | 
 |  | 
 |   for ( ; ID ; ID = ID->getSuperClass()) | 
 |     if (ID->getIdentifier()->getName() == "NSObject") | 
 |       return true; | 
 |  | 
 |   return false; | 
 | } | 
 |  | 
 | bool coreFoundation::followsCreateRule(const FunctionDecl *fn) { | 
 |   // For now, *just* base this on the function name, not on anything else. | 
 |  | 
 |   const IdentifierInfo *ident = fn->getIdentifier(); | 
 |   if (!ident) return false; | 
 |   StringRef functionName = ident->getName(); | 
 |  | 
 |   StringRef::iterator it = functionName.begin(); | 
 |   StringRef::iterator start = it; | 
 |   StringRef::iterator endI = functionName.end(); | 
 |  | 
 |   while (true) { | 
 |     // Scan for the start of 'create' or 'copy'. | 
 |     for ( ; it != endI ; ++it) { | 
 |       // Search for the first character.  It can either be 'C' or 'c'. | 
 |       char ch = *it; | 
 |       if (ch == 'C' || ch == 'c') { | 
 |         // Make sure this isn't something like 'recreate' or 'Scopy'. | 
 |         if (ch == 'c' && it != start && isLetter(*(it - 1))) | 
 |           continue; | 
 |  | 
 |         ++it; | 
 |         break; | 
 |       } | 
 |     } | 
 |  | 
 |     // Did we hit the end of the string?  If so, we didn't find a match. | 
 |     if (it == endI) | 
 |       return false; | 
 |  | 
 |     // Scan for *lowercase* 'reate' or 'opy', followed by no lowercase | 
 |     // character. | 
 |     StringRef suffix = functionName.substr(it - start); | 
 |     if (suffix.startswith("reate")) { | 
 |       it += 5; | 
 |     } | 
 |     else if (suffix.startswith("opy")) { | 
 |       it += 3; | 
 |     } else { | 
 |       // Keep scanning. | 
 |       continue; | 
 |     } | 
 |  | 
 |     if (it == endI || !isLowercase(*it)) | 
 |       return true; | 
 |  | 
 |     // If we matched a lowercase character, it isn't the end of the | 
 |     // word.  Keep scanning. | 
 |   } | 
 | } |