CocoaConventions.cpp revision 224145
1218887Sdim//===- CocoaConventions.h - Special handling of Cocoa conventions -*- C++ -*--// 2218887Sdim// 3218887Sdim// The LLVM Compiler Infrastructure 4218887Sdim// 5218887Sdim// This file is distributed under the University of Illinois Open Source 6218887Sdim// License. See LICENSE.TXT for details. 7218887Sdim// 8218887Sdim//===----------------------------------------------------------------------===// 9218887Sdim// 10224145Sdim// This file implements cocoa naming convention analysis. 11218887Sdim// 12218887Sdim//===----------------------------------------------------------------------===// 13218887Sdim 14218887Sdim#include "clang/Analysis/DomainSpecific/CocoaConventions.h" 15218887Sdim#include "clang/AST/Type.h" 16218887Sdim#include "clang/AST/Decl.h" 17218887Sdim#include "clang/AST/DeclObjC.h" 18218887Sdim#include "llvm/ADT/StringExtras.h" 19221345Sdim#include "llvm/Support/ErrorHandling.h" 20218887Sdim 21218887Sdimusing namespace clang; 22218887Sdimusing namespace ento; 23218887Sdim 24218887Sdimusing llvm::StringRef; 25218887Sdim 26218887Sdim// The "fundamental rule" for naming conventions of methods: 27218887Sdim// (url broken into two lines) 28218887Sdim// http://developer.apple.com/documentation/Cocoa/Conceptual/ 29218887Sdim// MemoryMgmt/Tasks/MemoryManagementRules.html 30218887Sdim// 31218887Sdim// "You take ownership of an object if you create it using a method whose name 32218887Sdim// begins with "alloc" or "new" or contains "copy" (for example, alloc, 33218887Sdim// newObject, or mutableCopy), or if you send it a retain message. You are 34218887Sdim// responsible for relinquishing ownership of objects you own using release 35218887Sdim// or autorelease. Any other time you receive an object, you must 36218887Sdim// not release it." 37218887Sdim// 38218887Sdim 39224145Sdimcocoa::NamingConvention cocoa::deriveNamingConvention(Selector S, 40224145Sdim const ObjCMethodDecl *MD) { 41224145Sdim switch (MD && MD->hasAttr<ObjCMethodFamilyAttr>()? MD->getMethodFamily() 42224145Sdim : S.getMethodFamily()) { 43221345Sdim case OMF_None: 44221345Sdim case OMF_autorelease: 45221345Sdim case OMF_dealloc: 46221345Sdim case OMF_release: 47221345Sdim case OMF_retain: 48221345Sdim case OMF_retainCount: 49223017Sdim case OMF_self: 50224145Sdim case OMF_performSelector: 51218887Sdim return NoConvention; 52218887Sdim 53221345Sdim case OMF_init: 54221345Sdim return InitRule; 55218887Sdim 56221345Sdim case OMF_alloc: 57221345Sdim case OMF_copy: 58221345Sdim case OMF_mutableCopy: 59221345Sdim case OMF_new: 60221345Sdim return CreateRule; 61218887Sdim } 62221345Sdim llvm_unreachable("unexpected naming convention"); 63221345Sdim return NoConvention; 64218887Sdim} 65218887Sdim 66218887Sdimbool cocoa::isRefType(QualType RetTy, llvm::StringRef Prefix, 67218887Sdim llvm::StringRef Name) { 68218887Sdim // Recursively walk the typedef stack, allowing typedefs of reference types. 69218887Sdim while (const TypedefType *TD = dyn_cast<TypedefType>(RetTy.getTypePtr())) { 70218887Sdim llvm::StringRef TDName = TD->getDecl()->getIdentifier()->getName(); 71218887Sdim if (TDName.startswith(Prefix) && TDName.endswith("Ref")) 72218887Sdim return true; 73218887Sdim 74218887Sdim RetTy = TD->getDecl()->getUnderlyingType(); 75218887Sdim } 76218887Sdim 77218887Sdim if (Name.empty()) 78218887Sdim return false; 79218887Sdim 80218887Sdim // Is the type void*? 81218887Sdim const PointerType* PT = RetTy->getAs<PointerType>(); 82218887Sdim if (!(PT->getPointeeType().getUnqualifiedType()->isVoidType())) 83218887Sdim return false; 84218887Sdim 85218887Sdim // Does the name start with the prefix? 86218887Sdim return Name.startswith(Prefix); 87218887Sdim} 88218887Sdim 89224145Sdimbool coreFoundation::isCFObjectRef(QualType T) { 90224145Sdim return cocoa::isRefType(T, "CF") || // Core Foundation. 91224145Sdim cocoa::isRefType(T, "CG") || // Core Graphics. 92224145Sdim cocoa::isRefType(T, "DADisk") || // Disk Arbitration API. 93224145Sdim cocoa::isRefType(T, "DADissenter") || 94224145Sdim cocoa::isRefType(T, "DASessionRef"); 95218887Sdim} 96218887Sdim 97218887Sdim 98218887Sdimbool cocoa::isCocoaObjectRef(QualType Ty) { 99218887Sdim if (!Ty->isObjCObjectPointerType()) 100218887Sdim return false; 101218887Sdim 102218887Sdim const ObjCObjectPointerType *PT = Ty->getAs<ObjCObjectPointerType>(); 103218887Sdim 104218887Sdim // Can be true for objects with the 'NSObject' attribute. 105218887Sdim if (!PT) 106218887Sdim return true; 107218887Sdim 108218887Sdim // We assume that id<..>, id, Class, and Class<..> all represent tracked 109218887Sdim // objects. 110218887Sdim if (PT->isObjCIdType() || PT->isObjCQualifiedIdType() || 111218887Sdim PT->isObjCClassType() || PT->isObjCQualifiedClassType()) 112218887Sdim return true; 113218887Sdim 114218887Sdim // Does the interface subclass NSObject? 115218887Sdim // FIXME: We can memoize here if this gets too expensive. 116218887Sdim const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); 117218887Sdim 118218887Sdim // Assume that anything declared with a forward declaration and no 119218887Sdim // @interface subclasses NSObject. 120218887Sdim if (ID->isForwardDecl()) 121218887Sdim return true; 122218887Sdim 123218887Sdim for ( ; ID ; ID = ID->getSuperClass()) 124218887Sdim if (ID->getIdentifier()->getName() == "NSObject") 125218887Sdim return true; 126218887Sdim 127218887Sdim return false; 128218887Sdim} 129224145Sdim 130224145Sdimbool coreFoundation::followsCreateRule(llvm::StringRef functionName) { 131224145Sdim llvm::StringRef::iterator it = functionName.begin(); 132224145Sdim llvm::StringRef::iterator start = it; 133224145Sdim llvm::StringRef::iterator endI = functionName.end(); 134224145Sdim 135224145Sdim while (true) { 136224145Sdim // Scan for the start of 'create' or 'copy'. 137224145Sdim for ( ; it != endI ; ++it) { 138224145Sdim // Search for the first character. It can either be 'C' or 'c'. 139224145Sdim char ch = *it; 140224145Sdim if (ch == 'C' || ch == 'c') { 141224145Sdim ++it; 142224145Sdim break; 143224145Sdim } 144224145Sdim } 145224145Sdim 146224145Sdim // Did we hit the end of the string? If so, we didn't find a match. 147224145Sdim if (it == endI) 148224145Sdim return false; 149224145Sdim 150224145Sdim // Scan for *lowercase* 'reate' or 'opy', followed by no lowercase 151224145Sdim // character. 152224145Sdim llvm::StringRef suffix = functionName.substr(it - start); 153224145Sdim if (suffix.startswith("reate")) { 154224145Sdim it += 5; 155224145Sdim } 156224145Sdim else if (suffix.startswith("opy")) { 157224145Sdim it += 3; 158224145Sdim } 159224145Sdim else { 160224145Sdim // Keep scanning. 161224145Sdim continue; 162224145Sdim } 163224145Sdim 164224145Sdim if (it == endI || !islower(*it)) 165224145Sdim return true; 166224145Sdim 167224145Sdim // If we matched a lowercase character, it isn't the end of the 168224145Sdim // word. Keep scanning. 169224145Sdim } 170224145Sdim 171224145Sdim return false; 172224145Sdim} 173