[cfe-commits] r126817 - in /cfe/trunk: include/clang/AST/DeclObjC.h include/clang/AST/ExprObjC.h include/clang/Analysis/DomainSpecific/CocoaConventions.h include/clang/Basic/IdentifierTable.h include/clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h lib/AST/DeclObjC.cpp lib/Analysis/CocoaConventions.cpp lib/Basic/IdentifierTable.cpp lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp lib/StaticAnalyzer/Core/ObjCMessage.cpp

John McCall rjmccall at apple.com
Tue Mar 1 17:50:55 PST 2011


Author: rjmccall
Date: Tue Mar  1 19:50:55 2011
New Revision: 126817

URL: http://llvm.org/viewvc/llvm-project?rev=126817&view=rev
Log:
Move some of the logic about classifying Objective-C methods into
conventional categories into Basic and AST.  Update the self-init checker
to use this logic;  CFRefCountChecker is complicated enough that I didn't
want to touch it.


Modified:
    cfe/trunk/include/clang/AST/DeclObjC.h
    cfe/trunk/include/clang/AST/ExprObjC.h
    cfe/trunk/include/clang/Analysis/DomainSpecific/CocoaConventions.h
    cfe/trunk/include/clang/Basic/IdentifierTable.h
    cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h
    cfe/trunk/lib/AST/DeclObjC.cpp
    cfe/trunk/lib/Analysis/CocoaConventions.cpp
    cfe/trunk/lib/Basic/IdentifierTable.cpp
    cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/ObjCMessage.cpp

Modified: cfe/trunk/include/clang/AST/DeclObjC.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclObjC.h?rev=126817&r1=126816&r2=126817&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/DeclObjC.h (original)
+++ cfe/trunk/include/clang/AST/DeclObjC.h Tue Mar  1 19:50:55 2011
@@ -112,17 +112,20 @@
 public:
   enum ImplementationControl { None, Required, Optional };
 private:
-  /// Bitfields must be first fields in this class so they pack with those
-  /// declared in class Decl.
+  // The conventional meaning of this method; an ObjCMethodFamily.
+  // This is not serialized; instead, it is computed on demand and
+  // cached.
+  mutable unsigned Family : ObjCMethodFamilyBitWidth;
+
   /// instance (true) or class (false) method.
-  bool IsInstance : 1;
-  bool IsVariadic : 1;
+  unsigned IsInstance : 1;
+  unsigned IsVariadic : 1;
 
   // Synthesized declaration method for a property setter/getter
-  bool IsSynthesized : 1;
+  unsigned IsSynthesized : 1;
   
   // Method has a definition.
-  bool IsDefined : 1;
+  unsigned IsDefined : 1;
 
   // NOTE: VC++ treats enums as signed, avoid using ImplementationControl enum
   /// @required/@optional
@@ -170,7 +173,7 @@
                  ImplementationControl impControl = None,
                  unsigned numSelectorArgs = 0)
   : NamedDecl(ObjCMethod, contextDecl, beginLoc, SelInfo),
-    DeclContext(ObjCMethod),
+    DeclContext(ObjCMethod), Family(InvalidObjCMethodFamily),
     IsInstance(isInstance), IsVariadic(isVariadic),
     IsSynthesized(isSynthesized),
     IsDefined(isDefined),
@@ -279,6 +282,9 @@
   ImplicitParamDecl * getCmdDecl() const { return CmdDecl; }
   void setCmdDecl(ImplicitParamDecl *CD) { CmdDecl = CD; }
 
+  /// Determines the family of this method.
+  ObjCMethodFamily getMethodFamily() const;
+
   bool isInstanceMethod() const { return IsInstance; }
   void setInstanceMethod(bool isInst) { IsInstance = isInst; }
   bool isVariadic() const { return IsVariadic; }

Modified: cfe/trunk/include/clang/AST/ExprObjC.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ExprObjC.h?rev=126817&r1=126816&r2=126817&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/ExprObjC.h (original)
+++ cfe/trunk/include/clang/AST/ExprObjC.h Tue Mar  1 19:50:55 2011
@@ -741,6 +741,11 @@
     SelectorOrMethod = reinterpret_cast<uintptr_t>(MD);
   }
 
+  ObjCMethodFamily getMethodFamily() const {
+    if (HasMethod) return getMethodDecl()->getMethodFamily();
+    return getSelector().getMethodFamily();
+  }
+
   /// \brief Return the number of actual arguments in this message,
   /// not counting the receiver.
   unsigned getNumArgs() const { return NumArgs; }

Modified: cfe/trunk/include/clang/Analysis/DomainSpecific/CocoaConventions.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/DomainSpecific/CocoaConventions.h?rev=126817&r1=126816&r2=126817&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/DomainSpecific/CocoaConventions.h (original)
+++ cfe/trunk/include/clang/Analysis/DomainSpecific/CocoaConventions.h Tue Mar  1 19:50:55 2011
@@ -22,7 +22,7 @@
  
   enum NamingConvention { NoConvention, CreateRule, InitRule };
 
-  NamingConvention deriveNamingConvention(Selector S, bool ignorePrefix = true);
+  NamingConvention deriveNamingConvention(Selector S);
 
   static inline bool followsFundamentalRule(Selector S) {
     return deriveNamingConvention(S) == CreateRule;

Modified: cfe/trunk/include/clang/Basic/IdentifierTable.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/IdentifierTable.h?rev=126817&r1=126816&r2=126817&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/IdentifierTable.h (original)
+++ cfe/trunk/include/clang/Basic/IdentifierTable.h Tue Mar  1 19:50:55 2011
@@ -443,6 +443,52 @@
   void AddKeywords(const LangOptions &LangOpts);
 };
 
+/// ObjCMethodFamily - A family of Objective-C methods.  These
+/// families have no inherent meaning in the language, but are
+/// nonetheless central enough in the existing implementations to
+/// merit direct AST support.  While, in theory, arbitrary methods can
+/// be considered to form families, we focus here on the methods
+/// involving allocation and retain-count management, as these are the
+/// most "core" and the most likely to be useful to diverse clients
+/// without extra information.
+///
+/// Both selectors and actual method declarations may be classified
+/// into families.  Method families may impose additional restrictions
+/// beyond their selector name; for example, a method called '_init'
+/// that returns void is not considered to be in the 'init' family
+/// (but would be if it returned 'id').  It is also possible to
+/// explicitly change or remove a method's family.  Therefore the
+/// method's family should be considered the single source of truth.
+enum ObjCMethodFamily {
+  /// \brief No particular method family.
+  OMF_None,
+
+  // Selectors in these families may have arbitrary arity, may be
+  // written with arbitrary leading underscores, and may have
+  // additional CamelCase "words" in their first selector chunk
+  // following the family name.
+  OMF_alloc,
+  OMF_copy,
+  OMF_init,
+  OMF_mutableCopy,
+  OMF_new,
+
+  // These families are singletons consisting only of the nullary
+  // selector with the given name.
+  OMF_autorelease,
+  OMF_dealloc,
+  OMF_release,
+  OMF_retain,
+  OMF_retainCount
+};
+
+/// Enough bits to store any enumerator in ObjCMethodFamily or
+/// InvalidObjCMethodFamily.
+enum { ObjCMethodFamilyBitWidth = 4 };
+
+/// An invalid value of ObjCMethodFamily.
+enum { InvalidObjCMethodFamily = (1 << ObjCMethodFamilyBitWidth) - 1 };
+
 /// Selector - This smart pointer class efficiently represents Objective-C
 /// method names. This class will either point to an IdentifierInfo or a
 /// MultiKeywordSelector (which is private). This enables us to optimize
@@ -479,6 +525,8 @@
     return InfoPtr & ArgFlags;
   }
 
+  static ObjCMethodFamily getMethodFamilyImpl(Selector sel);
+
 public:
   friend class SelectorTable; // only the SelectorTable can create these
   friend class DeclarationName; // and the AST's DeclarationName.
@@ -541,6 +589,11 @@
   /// it as an std::string.
   std::string getAsString() const;
 
+  /// getMethodFamily - Derive the conventional family of this method.
+  ObjCMethodFamily getMethodFamily() const {
+    return getMethodFamilyImpl(*this);
+  }
+
   static Selector getEmptyMarker() {
     return Selector(uintptr_t(-1));
   }

Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h?rev=126817&r1=126816&r2=126817&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h Tue Mar  1 19:50:55 2011
@@ -72,6 +72,8 @@
     return getType(ctx);
   }
 
+  ObjCMethodFamily getMethodFamily() const;
+
   Selector getSelector() const;
 
   const Expr *getInstanceReceiver() const {

Modified: cfe/trunk/lib/AST/DeclObjC.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclObjC.cpp?rev=126817&r1=126816&r2=126817&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclObjC.cpp (original)
+++ cfe/trunk/lib/AST/DeclObjC.cpp Tue Mar  1 19:50:55 2011
@@ -398,6 +398,48 @@
   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;

Modified: cfe/trunk/lib/Analysis/CocoaConventions.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CocoaConventions.cpp?rev=126817&r1=126816&r2=126817&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/CocoaConventions.cpp (original)
+++ cfe/trunk/lib/Analysis/CocoaConventions.cpp Tue Mar  1 19:50:55 2011
@@ -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 @@
 //  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)
+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;
 
-  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)
-    return NoConvention;
+  case OMF_init:
+    return InitRule;
 
-  // Parse the first word, and look for specific keywords.
-  const char *wordEnd = parseWord(s);
-  assert(wordEnd > s);
-  unsigned len = wordEnd - s;
-
-  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,

Modified: cfe/trunk/lib/Basic/IdentifierTable.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/IdentifierTable.cpp?rev=126817&r1=126816&r2=126817&view=diff
==============================================================================
--- cfe/trunk/lib/Basic/IdentifierTable.cpp (original)
+++ cfe/trunk/lib/Basic/IdentifierTable.cpp Tue Mar  1 19:50:55 2011
@@ -364,6 +364,56 @@
   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 {

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp?rev=126817&r1=126816&r2=126817&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp Tue Mar  1 19:50:55 2011
@@ -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 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;
 }
 
 //===----------------------------------------------------------------------===//

Modified: cfe/trunk/lib/StaticAnalyzer/Core/ObjCMessage.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ObjCMessage.cpp?rev=126817&r1=126816&r2=126817&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ObjCMessage.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ObjCMessage.cpp Tue Mar  1 19:50:55 2011
@@ -37,6 +37,35 @@
   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))





More information about the cfe-commits mailing list