blob: 3ba887ccc7e37d06dd1f05e78d1686242d2e5bc5 [file] [log] [blame]
Ted Kremeneka64e89b2010-01-27 06:13:48 +00001//===- 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 defines
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang/Checker/DomainSpecific/CocoaConventions.h"
Ted Kremenek78acdbf2010-01-27 18:00:17 +000015#include "clang/AST/Type.h"
16#include "clang/AST/Decl.h"
17#include "clang/AST/DeclObjC.h"
Ted Kremeneka64e89b2010-01-27 06:13:48 +000018#include "llvm/ADT/StringExtras.h"
19
20using namespace clang;
21
22using llvm::StringRef;
23
24// The "fundamental rule" for naming conventions of methods:
25// (url broken into two lines)
26// http://developer.apple.com/documentation/Cocoa/Conceptual/
27// MemoryMgmt/Tasks/MemoryManagementRules.html
28//
29// "You take ownership of an object if you create it using a method whose name
30// begins with "alloc" or "new" or contains "copy" (for example, alloc,
31// newObject, or mutableCopy), or if you send it a retain message. You are
32// responsible for relinquishing ownership of objects you own using release
33// or autorelease. Any other time you receive an object, you must
34// not release it."
35//
36
37static bool isWordEnd(char ch, char prev, char next) {
38 return ch == '\0'
39 || (islower(prev) && isupper(ch)) // xxxC
40 || (isupper(prev) && isupper(ch) && islower(next)) // XXCreate
41 || !isalpha(ch);
42}
43
44static const char* parseWord(const char* s) {
45 char ch = *s, prev = '\0';
46 assert(ch != '\0');
47 char next = *(s+1);
48 while (!isWordEnd(ch, prev, next)) {
49 prev = ch;
50 ch = next;
51 next = *((++s)+1);
52 }
53 return s;
54}
55
56cocoa::NamingConvention cocoa::deriveNamingConvention(Selector S) {
57 IdentifierInfo *II = S.getIdentifierInfoForSlot(0);
58
59 if (!II)
60 return NoConvention;
61
62 const char *s = II->getNameStart();
63
64 // A method/function name may contain a prefix. We don't know it is there,
65 // however, until we encounter the first '_'.
66 bool InPossiblePrefix = true;
67 bool AtBeginning = true;
68 NamingConvention C = NoConvention;
69
70 while (*s != '\0') {
71 // Skip '_'.
72 if (*s == '_') {
73 if (InPossiblePrefix) {
74 // If we already have a convention, return it. Otherwise, skip
75 // the prefix as if it wasn't there.
76 if (C != NoConvention)
77 break;
78
79 InPossiblePrefix = false;
80 AtBeginning = true;
81 assert(C == NoConvention);
82 }
83 ++s;
84 continue;
85 }
86
87 // Skip numbers, ':', etc.
88 if (!isalpha(*s)) {
89 ++s;
90 continue;
91 }
92
93 const char *wordEnd = parseWord(s);
94 assert(wordEnd > s);
95 unsigned len = wordEnd - s;
96
97 switch (len) {
98 default:
99 break;
100 case 3:
101 // Methods starting with 'new' follow the create rule.
102 if (AtBeginning && StringRef(s, len).equals_lower("new"))
103 C = CreateRule;
104 break;
105 case 4:
106 // Methods starting with 'alloc' or contain 'copy' follow the
107 // create rule
108 if (C == NoConvention && StringRef(s, len).equals_lower("copy"))
109 C = CreateRule;
110 else // Methods starting with 'init' follow the init rule.
111 if (AtBeginning && StringRef(s, len).equals_lower("init"))
112 C = InitRule;
113 break;
114 case 5:
115 if (AtBeginning && StringRef(s, len).equals_lower("alloc"))
116 C = CreateRule;
117 break;
118 }
119
120 // If we aren't in the prefix and have a derived convention then just
121 // return it now.
122 if (!InPossiblePrefix && C != NoConvention)
123 return C;
124
125 AtBeginning = false;
126 s = wordEnd;
127 }
128
129 // We will get here if there wasn't more than one word
130 // after the prefix.
131 return C;
132}
Ted Kremenek78acdbf2010-01-27 18:00:17 +0000133
Benjamin Kramerb6f3c702010-02-08 18:38:55 +0000134bool cocoa::isRefType(QualType RetTy, llvm::StringRef Prefix,
135 llvm::StringRef Name) {
Ted Kremenek78acdbf2010-01-27 18:00:17 +0000136 // Recursively walk the typedef stack, allowing typedefs of reference types.
137 while (TypedefType* TD = dyn_cast<TypedefType>(RetTy.getTypePtr())) {
138 llvm::StringRef TDName = TD->getDecl()->getIdentifier()->getName();
Benjamin Kramerb6f3c702010-02-08 18:38:55 +0000139 if (TDName.startswith(Prefix) && TDName.endswith("Ref"))
Ted Kremenek78acdbf2010-01-27 18:00:17 +0000140 return true;
141
142 RetTy = TD->getDecl()->getUnderlyingType();
143 }
144
Benjamin Kramerb6f3c702010-02-08 18:38:55 +0000145 if (Name.empty())
Ted Kremenek78acdbf2010-01-27 18:00:17 +0000146 return false;
147
148 // Is the type void*?
149 const PointerType* PT = RetTy->getAs<PointerType>();
150 if (!(PT->getPointeeType().getUnqualifiedType()->isVoidType()))
151 return false;
152
153 // Does the name start with the prefix?
Benjamin Kramerb6f3c702010-02-08 18:38:55 +0000154 return Name.startswith(Prefix);
Ted Kremenek78acdbf2010-01-27 18:00:17 +0000155}
156
157bool cocoa::isCFObjectRef(QualType T) {
158 return isRefType(T, "CF") || // Core Foundation.
159 isRefType(T, "CG") || // Core Graphics.
160 isRefType(T, "DADisk") || // Disk Arbitration API.
161 isRefType(T, "DADissenter") ||
162 isRefType(T, "DASessionRef");
163}
164
165
166bool cocoa::isCocoaObjectRef(QualType Ty) {
167 if (!Ty->isObjCObjectPointerType())
168 return false;
169
170 const ObjCObjectPointerType *PT = Ty->getAs<ObjCObjectPointerType>();
171
172 // Can be true for objects with the 'NSObject' attribute.
173 if (!PT)
174 return true;
175
176 // We assume that id<..>, id, and "Class" all represent tracked objects.
177 if (PT->isObjCIdType() || PT->isObjCQualifiedIdType() ||
178 PT->isObjCClassType())
179 return true;
180
181 // Does the interface subclass NSObject?
182 // FIXME: We can memoize here if this gets too expensive.
183 const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
184
185 // Assume that anything declared with a forward declaration and no
186 // @interface subclasses NSObject.
187 if (ID->isForwardDecl())
188 return true;
189
190 for ( ; ID ; ID = ID->getSuperClass())
191 if (ID->getIdentifier()->getName() == "NSObject")
192 return true;
193
194 return false;
195}