1a64e89bbfa756816d1e4a48e5d6c03edf1d7f23bTed Kremenek//===- CocoaConventions.h - Special handling of Cocoa conventions -*- C++ -*--//
2a64e89bbfa756816d1e4a48e5d6c03edf1d7f23bTed Kremenek//
3a64e89bbfa756816d1e4a48e5d6c03edf1d7f23bTed Kremenek//                     The LLVM Compiler Infrastructure
4a64e89bbfa756816d1e4a48e5d6c03edf1d7f23bTed Kremenek//
5a64e89bbfa756816d1e4a48e5d6c03edf1d7f23bTed Kremenek// This file is distributed under the University of Illinois Open Source
6a64e89bbfa756816d1e4a48e5d6c03edf1d7f23bTed Kremenek// License. See LICENSE.TXT for details.
7a64e89bbfa756816d1e4a48e5d6c03edf1d7f23bTed Kremenek//
8a64e89bbfa756816d1e4a48e5d6c03edf1d7f23bTed Kremenek//===----------------------------------------------------------------------===//
9a64e89bbfa756816d1e4a48e5d6c03edf1d7f23bTed Kremenek//
106970155edde8c79cf22824322470485434b8eb83Francois Pichet// This file implements cocoa naming convention analysis.
11a64e89bbfa756816d1e4a48e5d6c03edf1d7f23bTed Kremenek//
12a64e89bbfa756816d1e4a48e5d6c03edf1d7f23bTed Kremenek//===----------------------------------------------------------------------===//
13a64e89bbfa756816d1e4a48e5d6c03edf1d7f23bTed Kremenek
14bb8fef382ad89b4bc202a1dbd4cd52ced7734479Ted Kremenek#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
1578acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek#include "clang/AST/Decl.h"
1678acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek#include "clang/AST/DeclObjC.h"
1755fc873017f10f6f566b182b70f6fc22aefa3464Chandler Carruth#include "clang/AST/Type.h"
183f6f51e28231f65de9c2dd150a2d757b2162cfa3Jordan Rose#include "clang/Basic/CharInfo.h"
19a64e89bbfa756816d1e4a48e5d6c03edf1d7f23bTed Kremenek#include "llvm/ADT/StringExtras.h"
2085f3d76c0ecfdefcf83ea44a57b7a16119c8a045John McCall#include "llvm/Support/ErrorHandling.h"
217094dee95f8c915d27097ac18b47d1ef31fd72edJoerg Sonnenberger
22a64e89bbfa756816d1e4a48e5d6c03edf1d7f23bTed Kremenekusing namespace clang;
239ef6537a894c33003359b1f9b9676e9178e028b7Ted Kremenekusing namespace ento;
24a64e89bbfa756816d1e4a48e5d6c03edf1d7f23bTed Kremenek
255f9e272e632e951b1efe824cd16acb4d96077930Chris Lattnerbool cocoa::isRefType(QualType RetTy, StringRef Prefix,
265f9e272e632e951b1efe824cd16acb4d96077930Chris Lattner                      StringRef Name) {
2778acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek  // Recursively walk the typedef stack, allowing typedefs of reference types.
28f4c7371fb1d3cebcfb40abad4537bb82515704eaJohn McCall  while (const TypedefType *TD = dyn_cast<TypedefType>(RetTy.getTypePtr())) {
295f9e272e632e951b1efe824cd16acb4d96077930Chris Lattner    StringRef TDName = TD->getDecl()->getIdentifier()->getName();
30b6f3c70fb8a0def24bcaa937dc28358f20859c73Benjamin Kramer    if (TDName.startswith(Prefix) && TDName.endswith("Ref"))
3178acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek      return true;
321c87980ef18dbf4669c7194d60138ff9747d7ab7Ted Kremenek    // XPC unfortunately uses CF-style function names, but aren't CF types.
331c87980ef18dbf4669c7194d60138ff9747d7ab7Ted Kremenek    if (TDName.startswith("xpc_"))
341c87980ef18dbf4669c7194d60138ff9747d7ab7Ted Kremenek      return false;
3578acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek    RetTy = TD->getDecl()->getUnderlyingType();
3678acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek  }
3778acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek
38b6f3c70fb8a0def24bcaa937dc28358f20859c73Benjamin Kramer  if (Name.empty())
3978acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek    return false;
4078acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek
4178acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek  // Is the type void*?
4278acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek  const PointerType* PT = RetTy->getAs<PointerType>();
4378acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek  if (!(PT->getPointeeType().getUnqualifiedType()->isVoidType()))
4478acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek    return false;
4578acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek
4678acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek  // Does the name start with the prefix?
47b6f3c70fb8a0def24bcaa937dc28358f20859c73Benjamin Kramer  return Name.startswith(Prefix);
4878acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek}
4978acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek
500556048ae8ff743d0abb9fa88a0d0ee8e9123742Ted Kremenekbool coreFoundation::isCFObjectRef(QualType T) {
510556048ae8ff743d0abb9fa88a0d0ee8e9123742Ted Kremenek  return cocoa::isRefType(T, "CF") || // Core Foundation.
520556048ae8ff743d0abb9fa88a0d0ee8e9123742Ted Kremenek         cocoa::isRefType(T, "CG") || // Core Graphics.
530556048ae8ff743d0abb9fa88a0d0ee8e9123742Ted Kremenek         cocoa::isRefType(T, "DADisk") || // Disk Arbitration API.
540556048ae8ff743d0abb9fa88a0d0ee8e9123742Ted Kremenek         cocoa::isRefType(T, "DADissenter") ||
550556048ae8ff743d0abb9fa88a0d0ee8e9123742Ted Kremenek         cocoa::isRefType(T, "DASessionRef");
5678acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek}
5778acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek
5878acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek
5978acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenekbool cocoa::isCocoaObjectRef(QualType Ty) {
6078acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek  if (!Ty->isObjCObjectPointerType())
6178acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek    return false;
6278acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek
6378acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek  const ObjCObjectPointerType *PT = Ty->getAs<ObjCObjectPointerType>();
6478acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek
6578acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek  // Can be true for objects with the 'NSObject' attribute.
6678acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek  if (!PT)
6778acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek    return true;
6878acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek
694019c4f692e7b8b2d7a7b6a377c78337596052e4Ted Kremenek  // We assume that id<..>, id, Class, and Class<..> all represent tracked
704019c4f692e7b8b2d7a7b6a377c78337596052e4Ted Kremenek  // objects.
7178acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek  if (PT->isObjCIdType() || PT->isObjCQualifiedIdType() ||
724019c4f692e7b8b2d7a7b6a377c78337596052e4Ted Kremenek      PT->isObjCClassType() || PT->isObjCQualifiedClassType())
7378acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek    return true;
7478acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek
7578acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek  // Does the interface subclass NSObject?
7678acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek  // FIXME: We can memoize here if this gets too expensive.
7778acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek  const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
7878acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek
7978acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek  // Assume that anything declared with a forward declaration and no
8078acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek  // @interface subclasses NSObject.
817723fec9b45b7258c0eddf4cbfd0d335348f5edcDouglas Gregor  if (!ID->hasDefinition())
8278acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek    return true;
8378acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek
8478acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek  for ( ; ID ; ID = ID->getSuperClass())
8578acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek    if (ID->getIdentifier()->getName() == "NSObject")
8678acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek      return true;
8778acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek
8878acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek  return false;
8978acdbfb522f62b6e8899e078e48fea44fda287dTed Kremenek}
900556048ae8ff743d0abb9fa88a0d0ee8e9123742Ted Kremenek
917df2ff45f101c87398329d0ea23c1377328dca40John McCallbool coreFoundation::followsCreateRule(const FunctionDecl *fn) {
927df2ff45f101c87398329d0ea23c1377328dca40John McCall  // For now, *just* base this on the function name, not on anything else.
937df2ff45f101c87398329d0ea23c1377328dca40John McCall
947df2ff45f101c87398329d0ea23c1377328dca40John McCall  const IdentifierInfo *ident = fn->getIdentifier();
957df2ff45f101c87398329d0ea23c1377328dca40John McCall  if (!ident) return false;
967df2ff45f101c87398329d0ea23c1377328dca40John McCall  StringRef functionName = ident->getName();
977df2ff45f101c87398329d0ea23c1377328dca40John McCall
985f9e272e632e951b1efe824cd16acb4d96077930Chris Lattner  StringRef::iterator it = functionName.begin();
995f9e272e632e951b1efe824cd16acb4d96077930Chris Lattner  StringRef::iterator start = it;
1005f9e272e632e951b1efe824cd16acb4d96077930Chris Lattner  StringRef::iterator endI = functionName.end();
101797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek
102797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek  while (true) {
103797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek    // Scan for the start of 'create' or 'copy'.
104797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek    for ( ; it != endI ; ++it) {
105797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek      // Search for the first character.  It can either be 'C' or 'c'.
106797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek      char ch = *it;
107797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek      if (ch == 'C' || ch == 'c') {
1087df2ff45f101c87398329d0ea23c1377328dca40John McCall        // Make sure this isn't something like 'recreate' or 'Scopy'.
1093f6f51e28231f65de9c2dd150a2d757b2162cfa3Jordan Rose        if (ch == 'c' && it != start && isLetter(*(it - 1)))
1107df2ff45f101c87398329d0ea23c1377328dca40John McCall          continue;
1117df2ff45f101c87398329d0ea23c1377328dca40John McCall
112797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek        ++it;
113797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek        break;
114797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek      }
115797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek    }
116797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek
117797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek    // Did we hit the end of the string?  If so, we didn't find a match.
118797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek    if (it == endI)
119797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek      return false;
120797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek
121797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek    // Scan for *lowercase* 'reate' or 'opy', followed by no lowercase
122797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek    // character.
1235f9e272e632e951b1efe824cd16acb4d96077930Chris Lattner    StringRef suffix = functionName.substr(it - start);
124797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek    if (suffix.startswith("reate")) {
125797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek      it += 5;
126797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek    }
127797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek    else if (suffix.startswith("opy")) {
128797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek      it += 3;
1293060178ad9df29789505c1e6debcfc80a3a13587Chad Rosier    } else {
130797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek      // Keep scanning.
131797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek      continue;
132797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek    }
133797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek
1343f6f51e28231f65de9c2dd150a2d757b2162cfa3Jordan Rose    if (it == endI || !isLowercase(*it))
135797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek      return true;
136797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek
137797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek    // If we matched a lowercase character, it isn't the end of the
138797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek    // word.  Keep scanning.
139797a7be0de6fbedaa85082b07ec9ce0674f30773Ted Kremenek  }
1400556048ae8ff743d0abb9fa88a0d0ee8e9123742Ted Kremenek}
141