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