xref: /freebsd-src/contrib/llvm-project/clang/lib/Analysis/CocoaConventions.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
10b57cec5SDimitry Andric //===- CocoaConventions.h - Special handling of Cocoa conventions -*- C++ -*--//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file implements cocoa naming convention analysis.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric 
130b57cec5SDimitry Andric #include "clang/Analysis/DomainSpecific/CocoaConventions.h"
140b57cec5SDimitry Andric #include "clang/AST/Decl.h"
150b57cec5SDimitry Andric #include "clang/AST/DeclObjC.h"
160b57cec5SDimitry Andric #include "clang/AST/Type.h"
170b57cec5SDimitry Andric #include "clang/Basic/CharInfo.h"
180b57cec5SDimitry Andric #include "llvm/ADT/StringExtras.h"
190b57cec5SDimitry Andric #include "llvm/Support/ErrorHandling.h"
200b57cec5SDimitry Andric 
210b57cec5SDimitry Andric using namespace clang;
220b57cec5SDimitry Andric using namespace ento;
230b57cec5SDimitry Andric 
isRefType(QualType RetTy,StringRef Prefix,StringRef Name)240b57cec5SDimitry Andric bool cocoa::isRefType(QualType RetTy, StringRef Prefix,
250b57cec5SDimitry Andric                       StringRef Name) {
260b57cec5SDimitry Andric   // Recursively walk the typedef stack, allowing typedefs of reference types.
270b57cec5SDimitry Andric   while (const TypedefType *TD = RetTy->getAs<TypedefType>()) {
280b57cec5SDimitry Andric     StringRef TDName = TD->getDecl()->getIdentifier()->getName();
29*5f757f3fSDimitry Andric     if (TDName.starts_with(Prefix) && TDName.ends_with("Ref"))
300b57cec5SDimitry Andric       return true;
310b57cec5SDimitry Andric     // XPC unfortunately uses CF-style function names, but aren't CF types.
32*5f757f3fSDimitry Andric     if (TDName.starts_with("xpc_"))
330b57cec5SDimitry Andric       return false;
340b57cec5SDimitry Andric     RetTy = TD->getDecl()->getUnderlyingType();
350b57cec5SDimitry Andric   }
360b57cec5SDimitry Andric 
370b57cec5SDimitry Andric   if (Name.empty())
380b57cec5SDimitry Andric     return false;
390b57cec5SDimitry Andric 
400b57cec5SDimitry Andric   // Is the type void*?
41a7dea167SDimitry Andric   const PointerType* PT = RetTy->castAs<PointerType>();
42a7dea167SDimitry Andric   if (!PT || !PT->getPointeeType().getUnqualifiedType()->isVoidType())
430b57cec5SDimitry Andric     return false;
440b57cec5SDimitry Andric 
450b57cec5SDimitry Andric   // Does the name start with the prefix?
46*5f757f3fSDimitry Andric   return Name.starts_with(Prefix);
470b57cec5SDimitry Andric }
480b57cec5SDimitry Andric 
490b57cec5SDimitry Andric /// Returns true when the passed-in type is a CF-style reference-counted
500b57cec5SDimitry Andric /// type from the DiskArbitration framework.
isDiskArbitrationAPIRefType(QualType T)510b57cec5SDimitry Andric static bool isDiskArbitrationAPIRefType(QualType T) {
520b57cec5SDimitry Andric   return cocoa::isRefType(T, "DADisk") ||
530b57cec5SDimitry Andric       cocoa::isRefType(T, "DADissenter") ||
540b57cec5SDimitry Andric       cocoa::isRefType(T, "DASessionRef");
550b57cec5SDimitry Andric }
560b57cec5SDimitry Andric 
isCFObjectRef(QualType T)570b57cec5SDimitry Andric bool coreFoundation::isCFObjectRef(QualType T) {
580b57cec5SDimitry Andric   return cocoa::isRefType(T, "CF") || // Core Foundation.
590b57cec5SDimitry Andric          cocoa::isRefType(T, "CG") || // Core Graphics.
600b57cec5SDimitry Andric          cocoa::isRefType(T, "CM") || // Core Media.
610b57cec5SDimitry Andric          isDiskArbitrationAPIRefType(T);
620b57cec5SDimitry Andric }
630b57cec5SDimitry Andric 
640b57cec5SDimitry Andric 
isCocoaObjectRef(QualType Ty)650b57cec5SDimitry Andric bool cocoa::isCocoaObjectRef(QualType Ty) {
660b57cec5SDimitry Andric   if (!Ty->isObjCObjectPointerType())
670b57cec5SDimitry Andric     return false;
680b57cec5SDimitry Andric 
690b57cec5SDimitry Andric   const ObjCObjectPointerType *PT = Ty->getAs<ObjCObjectPointerType>();
700b57cec5SDimitry Andric 
710b57cec5SDimitry Andric   // Can be true for objects with the 'NSObject' attribute.
720b57cec5SDimitry Andric   if (!PT)
730b57cec5SDimitry Andric     return true;
740b57cec5SDimitry Andric 
750b57cec5SDimitry Andric   // We assume that id<..>, id, Class, and Class<..> all represent tracked
760b57cec5SDimitry Andric   // objects.
770b57cec5SDimitry Andric   if (PT->isObjCIdType() || PT->isObjCQualifiedIdType() ||
780b57cec5SDimitry Andric       PT->isObjCClassType() || PT->isObjCQualifiedClassType())
790b57cec5SDimitry Andric     return true;
800b57cec5SDimitry Andric 
810b57cec5SDimitry Andric   // Does the interface subclass NSObject?
820b57cec5SDimitry Andric   // FIXME: We can memoize here if this gets too expensive.
830b57cec5SDimitry Andric   const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
840b57cec5SDimitry Andric 
850b57cec5SDimitry Andric   // Assume that anything declared with a forward declaration and no
860b57cec5SDimitry Andric   // @interface subclasses NSObject.
870b57cec5SDimitry Andric   if (!ID->hasDefinition())
880b57cec5SDimitry Andric     return true;
890b57cec5SDimitry Andric 
900b57cec5SDimitry Andric   for ( ; ID ; ID = ID->getSuperClass())
910b57cec5SDimitry Andric     if (ID->getIdentifier()->getName() == "NSObject")
920b57cec5SDimitry Andric       return true;
930b57cec5SDimitry Andric 
940b57cec5SDimitry Andric   return false;
950b57cec5SDimitry Andric }
960b57cec5SDimitry Andric 
followsCreateRule(const FunctionDecl * fn)970b57cec5SDimitry Andric bool coreFoundation::followsCreateRule(const FunctionDecl *fn) {
980b57cec5SDimitry Andric   // For now, *just* base this on the function name, not on anything else.
990b57cec5SDimitry Andric 
1000b57cec5SDimitry Andric   const IdentifierInfo *ident = fn->getIdentifier();
1010b57cec5SDimitry Andric   if (!ident) return false;
1020b57cec5SDimitry Andric   StringRef functionName = ident->getName();
1030b57cec5SDimitry Andric 
1040b57cec5SDimitry Andric   StringRef::iterator it = functionName.begin();
1050b57cec5SDimitry Andric   StringRef::iterator start = it;
1060b57cec5SDimitry Andric   StringRef::iterator endI = functionName.end();
1070b57cec5SDimitry Andric 
1080b57cec5SDimitry Andric   while (true) {
1090b57cec5SDimitry Andric     // Scan for the start of 'create' or 'copy'.
1100b57cec5SDimitry Andric     for ( ; it != endI ; ++it) {
1110b57cec5SDimitry Andric       // Search for the first character.  It can either be 'C' or 'c'.
1120b57cec5SDimitry Andric       char ch = *it;
1130b57cec5SDimitry Andric       if (ch == 'C' || ch == 'c') {
1140b57cec5SDimitry Andric         // Make sure this isn't something like 'recreate' or 'Scopy'.
1150b57cec5SDimitry Andric         if (ch == 'c' && it != start && isLetter(*(it - 1)))
1160b57cec5SDimitry Andric           continue;
1170b57cec5SDimitry Andric 
1180b57cec5SDimitry Andric         ++it;
1190b57cec5SDimitry Andric         break;
1200b57cec5SDimitry Andric       }
1210b57cec5SDimitry Andric     }
1220b57cec5SDimitry Andric 
1230b57cec5SDimitry Andric     // Did we hit the end of the string?  If so, we didn't find a match.
1240b57cec5SDimitry Andric     if (it == endI)
1250b57cec5SDimitry Andric       return false;
1260b57cec5SDimitry Andric 
1270b57cec5SDimitry Andric     // Scan for *lowercase* 'reate' or 'opy', followed by no lowercase
1280b57cec5SDimitry Andric     // character.
1290b57cec5SDimitry Andric     StringRef suffix = functionName.substr(it - start);
130*5f757f3fSDimitry Andric     if (suffix.starts_with("reate")) {
1310b57cec5SDimitry Andric       it += 5;
132*5f757f3fSDimitry Andric     } else if (suffix.starts_with("opy")) {
1330b57cec5SDimitry Andric       it += 3;
1340b57cec5SDimitry Andric     } else {
1350b57cec5SDimitry Andric       // Keep scanning.
1360b57cec5SDimitry Andric       continue;
1370b57cec5SDimitry Andric     }
1380b57cec5SDimitry Andric 
1390b57cec5SDimitry Andric     if (it == endI || !isLowercase(*it))
1400b57cec5SDimitry Andric       return true;
1410b57cec5SDimitry Andric 
1420b57cec5SDimitry Andric     // If we matched a lowercase character, it isn't the end of the
1430b57cec5SDimitry Andric     // word.  Keep scanning.
1440b57cec5SDimitry Andric   }
1450b57cec5SDimitry Andric }
146