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