aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/AST/DeclObjC.cpp42
-rw-r--r--lib/Analysis/CocoaConventions.cpp92
-rw-r--r--lib/Basic/IdentifierTable.cpp50
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp9
-rw-r--r--lib/StaticAnalyzer/Core/ObjCMessage.cpp29
5 files changed, 141 insertions, 81 deletions
diff --git a/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp
index 45f5188d40..3a7c2a6e70 100644
--- a/lib/AST/DeclObjC.cpp
+++ b/lib/AST/DeclObjC.cpp
@@ -398,6 +398,48 @@ ObjCMethodDecl *ObjCMethodDecl::getCanonicalDecl() {
return this;
}
+ObjCMethodFamily ObjCMethodDecl::getMethodFamily() const {
+ ObjCMethodFamily family = static_cast<ObjCMethodFamily>(Family);
+ if (family != InvalidObjCMethodFamily)
+ return family;
+
+ family = getSelector().getMethodFamily();
+ switch (family) {
+ case OMF_None: break;
+
+ // init only has a conventional meaning for an instance method, and
+ // it has to return an object.
+ case OMF_init:
+ if (!isInstanceMethod() || !getResultType()->isObjCObjectPointerType())
+ family = OMF_None;
+ break;
+
+ // alloc/copy/new have a conventional meaning for both class and
+ // instance methods, but they require an object return.
+ case OMF_alloc:
+ case OMF_copy:
+ case OMF_mutableCopy:
+ case OMF_new:
+ if (!getResultType()->isObjCObjectPointerType())
+ family = OMF_None;
+ break;
+
+ // These selectors have a conventional meaning only for instance methods.
+ case OMF_dealloc:
+ case OMF_retain:
+ case OMF_release:
+ case OMF_autorelease:
+ case OMF_retainCount:
+ if (!isInstanceMethod())
+ family = OMF_None;
+ break;
+ }
+
+ // Cache the result.
+ Family = static_cast<unsigned>(family);
+ return family;
+}
+
void ObjCMethodDecl::createImplicitParams(ASTContext &Context,
const ObjCInterfaceDecl *OID) {
QualType selfTy;
diff --git a/lib/Analysis/CocoaConventions.cpp b/lib/Analysis/CocoaConventions.cpp
index 22b6c1aaf0..4c62f36e6c 100644
--- a/lib/Analysis/CocoaConventions.cpp
+++ b/lib/Analysis/CocoaConventions.cpp
@@ -16,6 +16,7 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclObjC.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/ErrorHandling.h"
using namespace clang;
using namespace ento;
@@ -35,84 +36,27 @@ using llvm::StringRef;
// not release it."
//
-static bool isWordEnd(char ch, char prev, char next) {
- return ch == '\0'
- || (islower(prev) && isupper(ch)) // xxxC
- || (isupper(prev) && isupper(ch) && islower(next)) // XXCreate
- || !isalpha(ch);
-}
-
-static const char* parseWord(const char* s) {
- char ch = *s, prev = '\0';
- assert(ch != '\0');
- char next = *(s+1);
- while (!isWordEnd(ch, prev, next)) {
- prev = ch;
- ch = next;
- next = *((++s)+1);
- }
- return s;
-}
-
-cocoa::NamingConvention cocoa::deriveNamingConvention(Selector S,
- bool ignorePrefix) {
- IdentifierInfo *II = S.getIdentifierInfoForSlot(0);
-
- if (!II)
- return NoConvention;
-
- const char *s = II->getNameStart();
-
- const char *orig = s;
- // A method/function name may contain a prefix. We don't know it is there,
- // however, until we encounter the first '_'.
- while (*s != '\0') {
- // Skip '_', numbers, ':', etc.
- if (*s == '_' || !isalpha(*s)) {
- ++s;
- continue;
- }
- break;
- }
-
- if (!ignorePrefix && s != orig)
+cocoa::NamingConvention cocoa::deriveNamingConvention(Selector S) {
+ switch (S.getMethodFamily()) {
+ case OMF_None:
+ case OMF_autorelease:
+ case OMF_dealloc:
+ case OMF_release:
+ case OMF_retain:
+ case OMF_retainCount:
return NoConvention;
- // Parse the first word, and look for specific keywords.
- const char *wordEnd = parseWord(s);
- assert(wordEnd > s);
- unsigned len = wordEnd - s;
+ case OMF_init:
+ return InitRule;
- switch (len) {
- default:
- return NoConvention;
- case 3:
- // Methods starting with 'new' follow the create rule.
- return (memcmp(s, "new", 3) == 0) ? CreateRule : NoConvention;
- case 4:
- // Methods starting with 'copy' follow the create rule.
- if (memcmp(s, "copy", 4) == 0)
- return CreateRule;
- // Methods starting with 'init' follow the init rule.
- if (memcmp(s, "init", 4) == 0)
- return InitRule;
- return NoConvention;
- case 5:
- return (memcmp(s, "alloc", 5) == 0) ? CreateRule : NoConvention;
- case 7:
- // Methods starting with 'mutableCopy' follow the create rule.
- if (memcmp(s, "mutable", 7) == 0) {
- // Look at the next word to see if it is "Copy".
- s = wordEnd;
- if (*s != '\0') {
- wordEnd = parseWord(s);
- len = wordEnd - s;
- if (len == 4 && memcmp(s, "Copy", 4) == 0)
- return CreateRule;
- }
- }
- return NoConvention;
+ case OMF_alloc:
+ case OMF_copy:
+ case OMF_mutableCopy:
+ case OMF_new:
+ return CreateRule;
}
+ llvm_unreachable("unexpected naming convention");
+ return NoConvention;
}
bool cocoa::isRefType(QualType RetTy, llvm::StringRef Prefix,
diff --git a/lib/Basic/IdentifierTable.cpp b/lib/Basic/IdentifierTable.cpp
index ef11d658ed..7647f0776d 100644
--- a/lib/Basic/IdentifierTable.cpp
+++ b/lib/Basic/IdentifierTable.cpp
@@ -364,6 +364,56 @@ std::string Selector::getAsString() const {
return reinterpret_cast<MultiKeywordSelector *>(InfoPtr)->getName();
}
+/// Interpreting the given string using the normal CamelCase
+/// conventions, determine whether the given string starts with the
+/// given "word", which is assumed to end in a lowercase letter.
+static bool startsWithWord(llvm::StringRef name, llvm::StringRef word) {
+ if (name.size() < word.size()) return false;
+ return ((name.size() == word.size() ||
+ !islower(name[word.size()]))
+ && name.startswith(word));
+}
+
+ObjCMethodFamily Selector::getMethodFamilyImpl(Selector sel) {
+ IdentifierInfo *first = sel.getIdentifierInfoForSlot(0);
+ if (!first) return OMF_None;
+
+ llvm::StringRef name = first->getName();
+ if (sel.isUnarySelector()) {
+ if (name == "autorelease") return OMF_autorelease;
+ if (name == "dealloc") return OMF_dealloc;
+ if (name == "release") return OMF_release;
+ if (name == "retain") return OMF_retain;
+ if (name == "retainCount") return OMF_retainCount;
+ }
+
+ // The other method families may begin with a prefix of underscores.
+ while (!name.empty() && name.front() == '_')
+ name = name.substr(1);
+
+ if (name.empty()) return OMF_None;
+ switch (name.front()) {
+ case 'a':
+ if (startsWithWord(name, "alloc")) return OMF_alloc;
+ break;
+ case 'c':
+ if (startsWithWord(name, "copy")) return OMF_copy;
+ break;
+ case 'i':
+ if (startsWithWord(name, "init")) return OMF_init;
+ break;
+ case 'm':
+ if (startsWithWord(name, "mutableCopy")) return OMF_mutableCopy;
+ break;
+ case 'n':
+ if (startsWithWord(name, "new")) return OMF_new;
+ break;
+ default:
+ break;
+ }
+
+ return OMF_None;
+}
namespace {
struct SelectorTableImpl {
diff --git a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
index cb591ac73a..617121ff0f 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
@@ -52,7 +52,6 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
-#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
#include "clang/AST/ParentMap.h"
using namespace clang;
@@ -347,15 +346,11 @@ static bool isSelfVar(SVal location, CheckerContext &C) {
}
static bool isInitializationMethod(const ObjCMethodDecl *MD) {
- // Init methods with prefix like '-(id)_init' are private and the requirements
- // are less strict so we don't check those.
- return MD->isInstanceMethod() &&
- cocoa::deriveNamingConvention(MD->getSelector(),
- /*ignorePrefix=*/false) == cocoa::InitRule;
+ return MD->getMethodFamily() == OMF_init;
}
static bool isInitMessage(const ObjCMessage &msg) {
- return cocoa::deriveNamingConvention(msg.getSelector()) == cocoa::InitRule;
+ return msg.getMethodFamily() == OMF_init;
}
//===----------------------------------------------------------------------===//
diff --git a/lib/StaticAnalyzer/Core/ObjCMessage.cpp b/lib/StaticAnalyzer/Core/ObjCMessage.cpp
index 2e370d62e4..b2272968bb 100644
--- a/lib/StaticAnalyzer/Core/ObjCMessage.cpp
+++ b/lib/StaticAnalyzer/Core/ObjCMessage.cpp
@@ -37,6 +37,35 @@ Selector ObjCMessage::getSelector() const {
return propE->getGetterSelector();
}
+ObjCMethodFamily ObjCMessage::getMethodFamily() const {
+ assert(isValid() && "This ObjCMessage is uninitialized!");
+ // Case 1. Explicit message send.
+ if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
+ return msgE->getMethodFamily();
+
+ const ObjCPropertyRefExpr *propE = cast<ObjCPropertyRefExpr>(MsgOrPropE);
+
+ // Case 2. Reference to implicit property.
+ if (propE->isImplicitProperty()) {
+ if (isPropertySetter())
+ return propE->getImplicitPropertySetter()->getMethodFamily();
+ else
+ return propE->getImplicitPropertyGetter()->getMethodFamily();
+ }
+
+ // Case 3. Reference to explicit property.
+ const ObjCPropertyDecl *prop = propE->getExplicitProperty();
+ if (isPropertySetter()) {
+ if (prop->getSetterMethodDecl())
+ return prop->getSetterMethodDecl()->getMethodFamily();
+ return prop->getSetterName().getMethodFamily();
+ } else {
+ if (prop->getGetterMethodDecl())
+ return prop->getGetterMethodDecl()->getMethodFamily();
+ return prop->getGetterName().getMethodFamily();
+ }
+}
+
const ObjCMethodDecl *ObjCMessage::getMethodDecl() const {
assert(isValid() && "This ObjCMessage is uninitialized!");
if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))