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/Decl.h" 16218887Sdim#include "clang/AST/DeclObjC.h" 17252723Sdim#include "clang/AST/Type.h" 18252723Sdim#include "clang/Basic/CharInfo.h" 19218887Sdim#include "llvm/ADT/StringExtras.h" 20221345Sdim#include "llvm/Support/ErrorHandling.h" 21245431Sdim 22218887Sdimusing namespace clang; 23218887Sdimusing namespace ento; 24218887Sdim 25226890Sdimbool cocoa::isRefType(QualType RetTy, StringRef Prefix, 26226890Sdim StringRef Name) { 27218887Sdim // Recursively walk the typedef stack, allowing typedefs of reference types. 28218887Sdim while (const TypedefType *TD = dyn_cast<TypedefType>(RetTy.getTypePtr())) { 29226890Sdim StringRef TDName = TD->getDecl()->getIdentifier()->getName(); 30218887Sdim if (TDName.startswith(Prefix) && TDName.endswith("Ref")) 31218887Sdim return true; 32235633Sdim // XPC unfortunately uses CF-style function names, but aren't CF types. 33235633Sdim if (TDName.startswith("xpc_")) 34235633Sdim return false; 35218887Sdim RetTy = TD->getDecl()->getUnderlyingType(); 36218887Sdim } 37218887Sdim 38218887Sdim if (Name.empty()) 39218887Sdim return false; 40218887Sdim 41218887Sdim // Is the type void*? 42218887Sdim const PointerType* PT = RetTy->getAs<PointerType>(); 43218887Sdim if (!(PT->getPointeeType().getUnqualifiedType()->isVoidType())) 44218887Sdim return false; 45218887Sdim 46218887Sdim // Does the name start with the prefix? 47218887Sdim return Name.startswith(Prefix); 48218887Sdim} 49218887Sdim 50224145Sdimbool coreFoundation::isCFObjectRef(QualType T) { 51224145Sdim return cocoa::isRefType(T, "CF") || // Core Foundation. 52224145Sdim cocoa::isRefType(T, "CG") || // Core Graphics. 53224145Sdim cocoa::isRefType(T, "DADisk") || // Disk Arbitration API. 54224145Sdim cocoa::isRefType(T, "DADissenter") || 55224145Sdim cocoa::isRefType(T, "DASessionRef"); 56218887Sdim} 57218887Sdim 58218887Sdim 59218887Sdimbool cocoa::isCocoaObjectRef(QualType Ty) { 60218887Sdim if (!Ty->isObjCObjectPointerType()) 61218887Sdim return false; 62218887Sdim 63218887Sdim const ObjCObjectPointerType *PT = Ty->getAs<ObjCObjectPointerType>(); 64218887Sdim 65218887Sdim // Can be true for objects with the 'NSObject' attribute. 66218887Sdim if (!PT) 67218887Sdim return true; 68218887Sdim 69218887Sdim // We assume that id<..>, id, Class, and Class<..> all represent tracked 70218887Sdim // objects. 71218887Sdim if (PT->isObjCIdType() || PT->isObjCQualifiedIdType() || 72218887Sdim PT->isObjCClassType() || PT->isObjCQualifiedClassType()) 73218887Sdim return true; 74218887Sdim 75218887Sdim // Does the interface subclass NSObject? 76218887Sdim // FIXME: We can memoize here if this gets too expensive. 77218887Sdim const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); 78218887Sdim 79218887Sdim // Assume that anything declared with a forward declaration and no 80218887Sdim // @interface subclasses NSObject. 81235633Sdim if (!ID->hasDefinition()) 82218887Sdim return true; 83218887Sdim 84218887Sdim for ( ; ID ; ID = ID->getSuperClass()) 85218887Sdim if (ID->getIdentifier()->getName() == "NSObject") 86218887Sdim return true; 87218887Sdim 88218887Sdim return false; 89218887Sdim} 90224145Sdim 91226890Sdimbool coreFoundation::followsCreateRule(const FunctionDecl *fn) { 92226890Sdim // For now, *just* base this on the function name, not on anything else. 93226890Sdim 94226890Sdim const IdentifierInfo *ident = fn->getIdentifier(); 95226890Sdim if (!ident) return false; 96226890Sdim StringRef functionName = ident->getName(); 97226890Sdim 98226890Sdim StringRef::iterator it = functionName.begin(); 99226890Sdim StringRef::iterator start = it; 100226890Sdim StringRef::iterator endI = functionName.end(); 101224145Sdim 102224145Sdim while (true) { 103224145Sdim // Scan for the start of 'create' or 'copy'. 104224145Sdim for ( ; it != endI ; ++it) { 105224145Sdim // Search for the first character. It can either be 'C' or 'c'. 106224145Sdim char ch = *it; 107224145Sdim if (ch == 'C' || ch == 'c') { 108226890Sdim // Make sure this isn't something like 'recreate' or 'Scopy'. 109252723Sdim if (ch == 'c' && it != start && isLetter(*(it - 1))) 110226890Sdim continue; 111226890Sdim 112224145Sdim ++it; 113224145Sdim break; 114224145Sdim } 115224145Sdim } 116224145Sdim 117224145Sdim // Did we hit the end of the string? If so, we didn't find a match. 118224145Sdim if (it == endI) 119224145Sdim return false; 120224145Sdim 121224145Sdim // Scan for *lowercase* 'reate' or 'opy', followed by no lowercase 122224145Sdim // character. 123226890Sdim StringRef suffix = functionName.substr(it - start); 124224145Sdim if (suffix.startswith("reate")) { 125224145Sdim it += 5; 126224145Sdim } 127224145Sdim else if (suffix.startswith("opy")) { 128224145Sdim it += 3; 129226890Sdim } else { 130224145Sdim // Keep scanning. 131224145Sdim continue; 132224145Sdim } 133224145Sdim 134252723Sdim if (it == endI || !isLowercase(*it)) 135224145Sdim return true; 136224145Sdim 137224145Sdim // If we matched a lowercase character, it isn't the end of the 138224145Sdim // word. Keep scanning. 139224145Sdim } 140224145Sdim} 141