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 // The "fundamental rule" for naming conventions of methods:
24 //  (url broken into two lines)
25 //  http://developer.apple.com/documentation/Cocoa/Conceptual/
26 //     MemoryMgmt/Tasks/MemoryManagementRules.html
27 //
28 // "You take ownership of an object if you create it using a method whose name
29 //  begins with "alloc" or "new" or contains "copy" (for example, alloc,
30 //  newObject, or mutableCopy), or if you send it a retain message. You are
31 //  responsible for relinquishing ownership of objects you own using release
32 //  or autorelease. Any other time you receive an object, you must
33 //  not release it."
34 //
35 
36 cocoa::NamingConvention cocoa::deriveNamingConvention(Selector S,
37                                                     const ObjCMethodDecl *MD) {
38   switch (MD && MD->hasAttr<ObjCMethodFamilyAttr>()? MD->getMethodFamily()
39                                                    : S.getMethodFamily()) {
40   case OMF_None:
41   case OMF_autorelease:
42   case OMF_dealloc:
43   case OMF_finalize:
44   case OMF_release:
45   case OMF_retain:
46   case OMF_retainCount:
47   case OMF_self:
48   case OMF_performSelector:
49     return NoConvention;
50 
51   case OMF_init:
52     return InitRule;
53 
54   case OMF_alloc:
55   case OMF_copy:
56   case OMF_mutableCopy:
57   case OMF_new:
58     return CreateRule;
59   }
60   llvm_unreachable("unexpected naming convention");
61 }
62 
63 bool cocoa::isRefType(QualType RetTy, StringRef Prefix,
64                       StringRef Name) {
65   // Recursively walk the typedef stack, allowing typedefs of reference types.
66   while (const TypedefType *TD = dyn_cast<TypedefType>(RetTy.getTypePtr())) {
67     StringRef TDName = TD->getDecl()->getIdentifier()->getName();
68     if (TDName.startswith(Prefix) && TDName.endswith("Ref"))
69       return true;
70     // XPC unfortunately uses CF-style function names, but aren't CF types.
71     if (TDName.startswith("xpc_"))
72       return false;
73     RetTy = TD->getDecl()->getUnderlyingType();
74   }
75 
76   if (Name.empty())
77     return false;
78 
79   // Is the type void*?
80   const PointerType* PT = RetTy->getAs<PointerType>();
81   if (!(PT->getPointeeType().getUnqualifiedType()->isVoidType()))
82     return false;
83 
84   // Does the name start with the prefix?
85   return Name.startswith(Prefix);
86 }
87 
88 bool coreFoundation::isCFObjectRef(QualType T) {
89   return cocoa::isRefType(T, "CF") || // Core Foundation.
90          cocoa::isRefType(T, "CG") || // Core Graphics.
91          cocoa::isRefType(T, "DADisk") || // Disk Arbitration API.
92          cocoa::isRefType(T, "DADissenter") ||
93          cocoa::isRefType(T, "DASessionRef");
94 }
95 
96 
97 bool cocoa::isCocoaObjectRef(QualType Ty) {
98   if (!Ty->isObjCObjectPointerType())
99     return false;
100 
101   const ObjCObjectPointerType *PT = Ty->getAs<ObjCObjectPointerType>();
102 
103   // Can be true for objects with the 'NSObject' attribute.
104   if (!PT)
105     return true;
106 
107   // We assume that id<..>, id, Class, and Class<..> all represent tracked
108   // objects.
109   if (PT->isObjCIdType() || PT->isObjCQualifiedIdType() ||
110       PT->isObjCClassType() || PT->isObjCQualifiedClassType())
111     return true;
112 
113   // Does the interface subclass NSObject?
114   // FIXME: We can memoize here if this gets too expensive.
115   const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
116 
117   // Assume that anything declared with a forward declaration and no
118   // @interface subclasses NSObject.
119   if (!ID->hasDefinition())
120     return true;
121 
122   for ( ; ID ; ID = ID->getSuperClass())
123     if (ID->getIdentifier()->getName() == "NSObject")
124       return true;
125 
126   return false;
127 }
128 
129 bool coreFoundation::followsCreateRule(const FunctionDecl *fn) {
130   // For now, *just* base this on the function name, not on anything else.
131 
132   const IdentifierInfo *ident = fn->getIdentifier();
133   if (!ident) return false;
134   StringRef functionName = ident->getName();
135 
136   StringRef::iterator it = functionName.begin();
137   StringRef::iterator start = it;
138   StringRef::iterator endI = functionName.end();
139 
140   while (true) {
141     // Scan for the start of 'create' or 'copy'.
142     for ( ; it != endI ; ++it) {
143       // Search for the first character.  It can either be 'C' or 'c'.
144       char ch = *it;
145       if (ch == 'C' || ch == 'c') {
146         // Make sure this isn't something like 'recreate' or 'Scopy'.
147         if (ch == 'c' && it != start && isalpha(*(it - 1)))
148           continue;
149 
150         ++it;
151         break;
152       }
153     }
154 
155     // Did we hit the end of the string?  If so, we didn't find a match.
156     if (it == endI)
157       return false;
158 
159     // Scan for *lowercase* 'reate' or 'opy', followed by no lowercase
160     // character.
161     StringRef suffix = functionName.substr(it - start);
162     if (suffix.startswith("reate")) {
163       it += 5;
164     }
165     else if (suffix.startswith("opy")) {
166       it += 3;
167     } else {
168       // Keep scanning.
169       continue;
170     }
171 
172     if (it == endI || !islower(*it))
173       return true;
174 
175     // If we matched a lowercase character, it isn't the end of the
176     // word.  Keep scanning.
177   }
178 }
179