blob: 456b536d2dac7959df1006e6c324a2df23bd800a [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"
15#include "llvm/ADT/StringExtras.h"
16
17using namespace clang;
18
19using llvm::StringRef;
20
21// The "fundamental rule" for naming conventions of methods:
22// (url broken into two lines)
23// http://developer.apple.com/documentation/Cocoa/Conceptual/
24// MemoryMgmt/Tasks/MemoryManagementRules.html
25//
26// "You take ownership of an object if you create it using a method whose name
27// begins with "alloc" or "new" or contains "copy" (for example, alloc,
28// newObject, or mutableCopy), or if you send it a retain message. You are
29// responsible for relinquishing ownership of objects you own using release
30// or autorelease. Any other time you receive an object, you must
31// not release it."
32//
33
34static bool isWordEnd(char ch, char prev, char next) {
35 return ch == '\0'
36 || (islower(prev) && isupper(ch)) // xxxC
37 || (isupper(prev) && isupper(ch) && islower(next)) // XXCreate
38 || !isalpha(ch);
39}
40
41static const char* parseWord(const char* s) {
42 char ch = *s, prev = '\0';
43 assert(ch != '\0');
44 char next = *(s+1);
45 while (!isWordEnd(ch, prev, next)) {
46 prev = ch;
47 ch = next;
48 next = *((++s)+1);
49 }
50 return s;
51}
52
53cocoa::NamingConvention cocoa::deriveNamingConvention(Selector S) {
54 IdentifierInfo *II = S.getIdentifierInfoForSlot(0);
55
56 if (!II)
57 return NoConvention;
58
59 const char *s = II->getNameStart();
60
61 // A method/function name may contain a prefix. We don't know it is there,
62 // however, until we encounter the first '_'.
63 bool InPossiblePrefix = true;
64 bool AtBeginning = true;
65 NamingConvention C = NoConvention;
66
67 while (*s != '\0') {
68 // Skip '_'.
69 if (*s == '_') {
70 if (InPossiblePrefix) {
71 // If we already have a convention, return it. Otherwise, skip
72 // the prefix as if it wasn't there.
73 if (C != NoConvention)
74 break;
75
76 InPossiblePrefix = false;
77 AtBeginning = true;
78 assert(C == NoConvention);
79 }
80 ++s;
81 continue;
82 }
83
84 // Skip numbers, ':', etc.
85 if (!isalpha(*s)) {
86 ++s;
87 continue;
88 }
89
90 const char *wordEnd = parseWord(s);
91 assert(wordEnd > s);
92 unsigned len = wordEnd - s;
93
94 switch (len) {
95 default:
96 break;
97 case 3:
98 // Methods starting with 'new' follow the create rule.
99 if (AtBeginning && StringRef(s, len).equals_lower("new"))
100 C = CreateRule;
101 break;
102 case 4:
103 // Methods starting with 'alloc' or contain 'copy' follow the
104 // create rule
105 if (C == NoConvention && StringRef(s, len).equals_lower("copy"))
106 C = CreateRule;
107 else // Methods starting with 'init' follow the init rule.
108 if (AtBeginning && StringRef(s, len).equals_lower("init"))
109 C = InitRule;
110 break;
111 case 5:
112 if (AtBeginning && StringRef(s, len).equals_lower("alloc"))
113 C = CreateRule;
114 break;
115 }
116
117 // If we aren't in the prefix and have a derived convention then just
118 // return it now.
119 if (!InPossiblePrefix && C != NoConvention)
120 return C;
121
122 AtBeginning = false;
123 s = wordEnd;
124 }
125
126 // We will get here if there wasn't more than one word
127 // after the prefix.
128 return C;
129}